1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #ifndef mozilla_net_Http2Compression_Internal_h
7 #define mozilla_net_Http2Compression_Internal_h
8 
9 // HPACK - RFC 7541
10 // https://www.rfc-editor.org/rfc/rfc7541.txt
11 
12 #include "mozilla/Attributes.h"
13 #include "nsDeque.h"
14 #include "nsString.h"
15 #include "mozilla/Telemetry.h"
16 
17 namespace mozilla {
18 namespace net {
19 
20 struct HuffmanIncomingTable;
21 
22 void Http2CompressionCleanup();
23 
24 class nvPair {
25  public:
nvPair(const nsACString & name,const nsACString & value)26   nvPair(const nsACString& name, const nsACString& value)
27       : mName(name), mValue(value) {}
28 
Size()29   uint32_t Size() const { return mName.Length() + mValue.Length() + 32; }
30   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const;
31   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
32 
33   nsCString mName;
34   nsCString mValue;
35 };
36 
37 class nvFIFO {
38  public:
39   nvFIFO();
40   ~nvFIFO();
41   void AddElement(const nsCString& name, const nsCString& value);
42   void AddElement(const nsCString& name);
43   void RemoveElement();
44   uint32_t ByteCount() const;
45   uint32_t Length() const;
46   uint32_t VariableLength() const;
47   size_t StaticLength() const;
48   void Clear();
49   const nvPair* operator[](size_t index) const;
50 
51  private:
52   uint32_t mByteCount{0};
53   nsDeque<nvPair> mTable;
54 };
55 
56 class HpackDynamicTableReporter;
57 
58 class Http2BaseCompressor {
59  public:
60   Http2BaseCompressor();
61   virtual ~Http2BaseCompressor();
62   size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
63   nsresult SetInitialMaxBufferSize(uint32_t maxBufferSize);
64   void SetDumpTables(bool dumpTables);
65 
66  protected:
67   const static uint32_t kDefaultMaxBuffer = 4096;
68 
69   virtual void ClearHeaderTable();
70   virtual void MakeRoom(uint32_t amount, const char* direction);
71   virtual void DumpState(const char*);
72   virtual void SetMaxBufferSizeInternal(uint32_t maxBufferSize);
73 
74   nsACString* mOutput{nullptr};
75   nvFIFO mHeaderTable;
76 
77   uint32_t mMaxBuffer{kDefaultMaxBuffer};
78   uint32_t mMaxBufferSetting{kDefaultMaxBuffer};
79   bool mSetInitialMaxBufferSizeAllowed{true};
80 
81   uint32_t mPeakSize{0};
82   uint32_t mPeakCount{0};
83   MOZ_INIT_OUTSIDE_CTOR
84   Telemetry::HistogramID mPeakSizeID;
85   MOZ_INIT_OUTSIDE_CTOR
86   Telemetry::HistogramID mPeakCountID;
87 
88   bool mDumpTables{false};
89 
90  private:
91   RefPtr<HpackDynamicTableReporter> mDynamicReporter;
92 };
93 
94 class Http2Compressor;
95 
96 class Http2Decompressor final : public Http2BaseCompressor {
97  public:
Http2Decompressor()98   Http2Decompressor() {
99     mPeakSizeID = Telemetry::HPACK_PEAK_SIZE_DECOMPRESSOR;
100     mPeakCountID = Telemetry::HPACK_PEAK_COUNT_DECOMPRESSOR;
101   };
102   virtual ~Http2Decompressor() = default;
103 
104   // NS_OK: Produces the working set of HTTP/1 formatted headers
105   [[nodiscard]] nsresult DecodeHeaderBlock(const uint8_t* data,
106                                            uint32_t datalen, nsACString& output,
107                                            bool isPush);
108 
GetStatus(nsACString & hdr)109   void GetStatus(nsACString& hdr) { hdr = mHeaderStatus; }
GetHost(nsACString & hdr)110   void GetHost(nsACString& hdr) { hdr = mHeaderHost; }
GetScheme(nsACString & hdr)111   void GetScheme(nsACString& hdr) { hdr = mHeaderScheme; }
GetPath(nsACString & hdr)112   void GetPath(nsACString& hdr) { hdr = mHeaderPath; }
GetMethod(nsACString & hdr)113   void GetMethod(nsACString& hdr) { hdr = mHeaderMethod; }
114 
115  private:
116   [[nodiscard]] nsresult DoIndexed();
117   [[nodiscard]] nsresult DoLiteralWithoutIndex();
118   [[nodiscard]] nsresult DoLiteralWithIncremental();
119   [[nodiscard]] nsresult DoLiteralInternal(nsACString&, nsACString&, uint32_t);
120   [[nodiscard]] nsresult DoLiteralNeverIndexed();
121   [[nodiscard]] nsresult DoContextUpdate();
122 
123   [[nodiscard]] nsresult DecodeInteger(uint32_t prefixLen, uint32_t& accum);
124   [[nodiscard]] nsresult OutputHeader(uint32_t index);
125   [[nodiscard]] nsresult OutputHeader(const nsACString& name,
126                                       const nsACString& value);
127 
128   [[nodiscard]] nsresult CopyHeaderString(uint32_t index, nsACString& name);
129   [[nodiscard]] nsresult CopyStringFromInput(uint32_t bytes, nsACString& val);
130   uint8_t ExtractByte(uint8_t bitsLeft, uint32_t& bytesConsumed);
131   [[nodiscard]] nsresult CopyHuffmanStringFromInput(uint32_t bytes,
132                                                     nsACString& val);
133   [[nodiscard]] nsresult DecodeHuffmanCharacter(
134       const HuffmanIncomingTable* table, uint8_t& c, uint32_t& bytesConsumed,
135       uint8_t& bitsLeft);
136   [[nodiscard]] nsresult DecodeFinalHuffmanCharacter(
137       const HuffmanIncomingTable* table, uint8_t& c, uint8_t& bitsLeft);
138 
139   nsCString mHeaderStatus;
140   nsCString mHeaderHost;
141   nsCString mHeaderScheme;
142   nsCString mHeaderPath;
143   nsCString mHeaderMethod;
144 
145   // state variables when DecodeBlock() is on the stack
146   uint32_t mOffset{0};
147   const uint8_t* mData{nullptr};
148   uint32_t mDataLen{0};
149   bool mSeenNonColonHeader{false};
150   bool mIsPush{false};
151 };
152 
153 class Http2Compressor final : public Http2BaseCompressor {
154  public:
Http2Compressor()155   Http2Compressor() {
156     mPeakSizeID = Telemetry::HPACK_PEAK_SIZE_COMPRESSOR;
157     mPeakCountID = Telemetry::HPACK_PEAK_COUNT_COMPRESSOR;
158   };
159   virtual ~Http2Compressor() = default;
160 
161   // HTTP/1 formatted header block as input - HTTP/2 formatted
162   // header block as output
163   [[nodiscard]] nsresult EncodeHeaderBlock(
164       const nsCString& nvInput, const nsACString& method,
165       const nsACString& path, const nsACString& host, const nsACString& scheme,
166       const nsACString& protocol, bool simpleConnectForm, nsACString& output);
167 
GetParsedContentLength()168   int64_t GetParsedContentLength() {
169     return mParsedContentLength;
170   }  // -1 on not found
171 
172   void SetMaxBufferSize(uint32_t maxBufferSize);
173 
174  private:
175   enum outputCode {
176     kNeverIndexedLiteral,
177     kPlainLiteral,
178     kIndexedLiteral,
179     kIndex
180   };
181 
182   void DoOutput(Http2Compressor::outputCode code, const class nvPair* pair,
183                 uint32_t index);
184   void EncodeInteger(uint32_t prefixLen, uint32_t val);
185   void ProcessHeader(const nvPair inputPair, bool noLocalIndex,
186                      bool neverIndex);
187   void HuffmanAppend(const nsCString& value);
188   void EncodeTableSizeChange(uint32_t newMaxSize);
189 
190   int64_t mParsedContentLength{-1};
191   bool mBufferSizeChangeWaiting{false};
192   uint32_t mLowestBufferSizeWaiting{0};
193 };
194 
195 }  // namespace net
196 }  // namespace mozilla
197 
198 #endif  // mozilla_net_Http2Compression_Internal_h
199