netwerk/protocol/http/Http2Compression.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set sw=2 ts=8 et tw=80 : */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 // HttpLog.h should generally be included first
     8 #include "HttpLog.h"
    10 // Log on level :5, instead of default :4.
    11 #undef LOG
    12 #define LOG(args) LOG5(args)
    13 #undef LOG_ENABLED
    14 #define LOG_ENABLED() LOG5_ENABLED()
    16 #include "Http2Compression.h"
    17 #include "Http2HuffmanIncoming.h"
    18 #include "Http2HuffmanOutgoing.h"
    20 extern PRThread *gSocketThread;
    22 namespace mozilla {
    23 namespace net {
    25 static nsDeque *gStaticHeaders = nullptr;
    27 void
    28 Http2CompressionCleanup()
    29 {
    30   // this happens after the socket thread has been destroyed
    31   delete gStaticHeaders;
    32   gStaticHeaders = nullptr;
    33 }
    35 static void
    36 AddStaticElement(const nsCString &name, const nsCString &value)
    37 {
    38   nvPair *pair = new nvPair(name, value);
    39   gStaticHeaders->Push(pair);
    40 }
    42 static void
    43 AddStaticElement(const nsCString &name)
    44 {
    45   AddStaticElement(name, EmptyCString());
    46 }
    48 static void
    49 InitializeStaticHeaders()
    50 {
    51   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
    52   if (!gStaticHeaders) {
    53     gStaticHeaders = new nsDeque();
    54     AddStaticElement(NS_LITERAL_CSTRING(":authority"));
    55     AddStaticElement(NS_LITERAL_CSTRING(":method"), NS_LITERAL_CSTRING("GET"));
    56     AddStaticElement(NS_LITERAL_CSTRING(":method"), NS_LITERAL_CSTRING("POST"));
    57     AddStaticElement(NS_LITERAL_CSTRING(":path"), NS_LITERAL_CSTRING("/"));
    58     AddStaticElement(NS_LITERAL_CSTRING(":path"), NS_LITERAL_CSTRING("/index.html"));
    59     AddStaticElement(NS_LITERAL_CSTRING(":scheme"), NS_LITERAL_CSTRING("http"));
    60     AddStaticElement(NS_LITERAL_CSTRING(":scheme"), NS_LITERAL_CSTRING("https"));
    61     AddStaticElement(NS_LITERAL_CSTRING(":status"), NS_LITERAL_CSTRING("200"));
    62     AddStaticElement(NS_LITERAL_CSTRING(":status"), NS_LITERAL_CSTRING("500"));
    63     AddStaticElement(NS_LITERAL_CSTRING(":status"), NS_LITERAL_CSTRING("404"));
    64     AddStaticElement(NS_LITERAL_CSTRING(":status"), NS_LITERAL_CSTRING("403"));
    65     AddStaticElement(NS_LITERAL_CSTRING(":status"), NS_LITERAL_CSTRING("400"));
    66     AddStaticElement(NS_LITERAL_CSTRING(":status"), NS_LITERAL_CSTRING("401"));
    67     AddStaticElement(NS_LITERAL_CSTRING("accept-charset"));
    68     AddStaticElement(NS_LITERAL_CSTRING("accept-encoding"));
    69     AddStaticElement(NS_LITERAL_CSTRING("accept-language"));
    70     AddStaticElement(NS_LITERAL_CSTRING("accept-ranges"));
    71     AddStaticElement(NS_LITERAL_CSTRING("accept"));
    72     AddStaticElement(NS_LITERAL_CSTRING("access-control-allow-origin"));
    73     AddStaticElement(NS_LITERAL_CSTRING("age"));
    74     AddStaticElement(NS_LITERAL_CSTRING("allow"));
    75     AddStaticElement(NS_LITERAL_CSTRING("authorization"));
    76     AddStaticElement(NS_LITERAL_CSTRING("cache-control"));
    77     AddStaticElement(NS_LITERAL_CSTRING("content-disposition"));
    78     AddStaticElement(NS_LITERAL_CSTRING("content-encoding"));
    79     AddStaticElement(NS_LITERAL_CSTRING("content-language"));
    80     AddStaticElement(NS_LITERAL_CSTRING("content-length"));
    81     AddStaticElement(NS_LITERAL_CSTRING("content-location"));
    82     AddStaticElement(NS_LITERAL_CSTRING("content-range"));
    83     AddStaticElement(NS_LITERAL_CSTRING("content-type"));
    84     AddStaticElement(NS_LITERAL_CSTRING("cookie"));
    85     AddStaticElement(NS_LITERAL_CSTRING("date"));
    86     AddStaticElement(NS_LITERAL_CSTRING("etag"));
    87     AddStaticElement(NS_LITERAL_CSTRING("expect"));
    88     AddStaticElement(NS_LITERAL_CSTRING("expires"));
    89     AddStaticElement(NS_LITERAL_CSTRING("from"));
    90     AddStaticElement(NS_LITERAL_CSTRING("host"));
    91     AddStaticElement(NS_LITERAL_CSTRING("if-match"));
    92     AddStaticElement(NS_LITERAL_CSTRING("if-modified-since"));
    93     AddStaticElement(NS_LITERAL_CSTRING("if-none-match"));
    94     AddStaticElement(NS_LITERAL_CSTRING("if-range"));
    95     AddStaticElement(NS_LITERAL_CSTRING("if-unmodified-since"));
    96     AddStaticElement(NS_LITERAL_CSTRING("last-modified"));
    97     AddStaticElement(NS_LITERAL_CSTRING("link"));
    98     AddStaticElement(NS_LITERAL_CSTRING("location"));
    99     AddStaticElement(NS_LITERAL_CSTRING("max-forwards"));
   100     AddStaticElement(NS_LITERAL_CSTRING("proxy-authenticate"));
   101     AddStaticElement(NS_LITERAL_CSTRING("proxy-authorization"));
   102     AddStaticElement(NS_LITERAL_CSTRING("range"));
   103     AddStaticElement(NS_LITERAL_CSTRING("referer"));
   104     AddStaticElement(NS_LITERAL_CSTRING("refresh"));
   105     AddStaticElement(NS_LITERAL_CSTRING("retry-after"));
   106     AddStaticElement(NS_LITERAL_CSTRING("server"));
   107     AddStaticElement(NS_LITERAL_CSTRING("set-cookie"));
   108     AddStaticElement(NS_LITERAL_CSTRING("strict-transport-security"));
   109     AddStaticElement(NS_LITERAL_CSTRING("transfer-encoding"));
   110     AddStaticElement(NS_LITERAL_CSTRING("user-agent"));
   111     AddStaticElement(NS_LITERAL_CSTRING("vary"));
   112     AddStaticElement(NS_LITERAL_CSTRING("via"));
   113     AddStaticElement(NS_LITERAL_CSTRING("www-authenticate"));
   114   }
   115 }
   117 nvFIFO::nvFIFO()
   118   : mByteCount(0)
   119   , mTable()
   120 {
   121   InitializeStaticHeaders();
   122 }
   124 nvFIFO::~nvFIFO()
   125 {
   126   Clear();
   127 }
   129 void
   130 nvFIFO::AddElement(const nsCString &name, const nsCString &value)
   131 {
   132   mByteCount += name.Length() + value.Length() + 32;
   133   nvPair *pair = new nvPair(name, value);
   134   mTable.PushFront(pair);
   135 }
   137 void
   138 nvFIFO::AddElement(const nsCString &name)
   139 {
   140   AddElement(name, EmptyCString());
   141 }
   143 void
   144 nvFIFO::RemoveElement()
   145 {
   146   nvPair *pair = static_cast<nvPair *>(mTable.Pop());
   147   if (pair) {
   148     mByteCount -= pair->Size();
   149     delete pair;
   150   }
   151 }
   153 uint32_t
   154 nvFIFO::ByteCount() const
   155 {
   156   return mByteCount;
   157 }
   159 uint32_t
   160 nvFIFO::Length() const
   161 {
   162   return mTable.GetSize() + gStaticHeaders->GetSize();
   163 }
   165 uint32_t
   166 nvFIFO::VariableLength() const
   167 {
   168   return mTable.GetSize();
   169 }
   171 void
   172 nvFIFO::Clear()
   173 {
   174   mByteCount = 0;
   175   while (mTable.GetSize())
   176     delete static_cast<nvPair *>(mTable.Pop());
   177 }
   179 const nvPair *
   180 nvFIFO::operator[] (int32_t index) const
   181 {
   182   if (index >= (mTable.GetSize() + gStaticHeaders->GetSize())) {
   183     MOZ_ASSERT(false);
   184     NS_WARNING("nvFIFO Table Out of Range");
   185     return nullptr;
   186   }
   187   if (index >= mTable.GetSize()) {
   188     return static_cast<nvPair *>(gStaticHeaders->ObjectAt(index - mTable.GetSize()));
   189   }
   190   return static_cast<nvPair *>(mTable.ObjectAt(index));
   191 }
   193 Http2BaseCompressor::Http2BaseCompressor()
   194   : mOutput(nullptr)
   195   , mMaxBuffer(kDefaultMaxBuffer)
   196 {
   197 }
   199 void
   200 Http2BaseCompressor::ClearHeaderTable()
   201 {
   202   uint32_t dynamicCount = mHeaderTable.VariableLength();
   203   mHeaderTable.Clear();
   205   for (int32_t i = mReferenceSet.Length() - 1; i >= 0; --i) {
   206     if (mReferenceSet[i] < dynamicCount) {
   207       mReferenceSet.RemoveElementAt(i);
   208     } else {
   209       mReferenceSet[i] -= dynamicCount;
   210     }
   211   }
   213   for (int32_t i = mAlternateReferenceSet.Length() - 1; i >= 0; --i) {
   214     if (mAlternateReferenceSet[i] < dynamicCount) {
   215       mAlternateReferenceSet.RemoveElementAt(i);
   216     } else {
   217       mAlternateReferenceSet[i] -= dynamicCount;
   218     }
   219   }
   220 }
   222 void
   223 Http2BaseCompressor::UpdateReferenceSet(int32_t delta)
   224 {
   225   if (!delta)
   226     return;
   228   uint32_t headerTableSize = mHeaderTable.VariableLength();
   229   uint32_t oldHeaderTableSize = headerTableSize + delta;
   231   for (int32_t i = mReferenceSet.Length() - 1; i >= 0; --i) {
   232     uint32_t indexRef = mReferenceSet[i];
   233     if (indexRef >= headerTableSize) {
   234       if (indexRef < oldHeaderTableSize) {
   235         // This one got dropped
   236         LOG3(("HTTP base compressor reference to index %u removed.\n",
   237               indexRef));
   238         mReferenceSet.RemoveElementAt(i);
   239       } else {
   240         // This pointed to the static table, need to adjust
   241         uint32_t newRef = indexRef - delta;
   242         LOG3(("HTTP base compressor reference to index %u changed to %d (%s)\n",
   243               mReferenceSet[i], newRef, mHeaderTable[newRef]->mName.get()));
   244         mReferenceSet[i] = newRef;
   245       }
   246     }
   247   }
   249   for (int32_t i = mAlternateReferenceSet.Length() - 1; i >= 0; --i) {
   250     uint32_t indexRef = mAlternateReferenceSet[i];
   251     if (indexRef >= headerTableSize) {
   252       if (indexRef < oldHeaderTableSize) {
   253         // This one got dropped
   254         LOG3(("HTTP base compressor new reference to index %u removed.\n",
   255               indexRef));
   256         mAlternateReferenceSet.RemoveElementAt(i);
   257       } else {
   258         // This pointed to the static table, need to adjust
   259         uint32_t newRef = indexRef - delta;
   260         LOG3(("HTTP base compressor new reference to index %u changed to %d (%s)\n",
   261               mAlternateReferenceSet[i], newRef, mHeaderTable[newRef]->mName.get()));
   262         mAlternateReferenceSet[i] = newRef;
   263       }
   264     }
   265   }
   266 }
   268 void
   269 Http2BaseCompressor::IncrementReferenceSetIndices()
   270 {
   271   for (int32_t i = mReferenceSet.Length() - 1; i >= 0; --i) {
   272     mReferenceSet[i] = mReferenceSet[i] + 1;
   273   }
   275   for (int32_t i = mAlternateReferenceSet.Length() - 1; i >= 0; --i) {
   276     mAlternateReferenceSet[i] = mAlternateReferenceSet[i] + 1;
   277   }
   278 }
   280 nsresult
   281 Http2Decompressor::DecodeHeaderBlock(const uint8_t *data, uint32_t datalen,
   282                                      nsACString &output)
   283 {
   284   mAlternateReferenceSet.Clear();
   285   mOffset = 0;
   286   mData = data;
   287   mDataLen = datalen;
   288   mOutput = &output;
   289   mOutput->Truncate();
   290   mHeaderStatus.Truncate();
   291   mHeaderHost.Truncate();
   292   mHeaderScheme.Truncate();
   293   mHeaderPath.Truncate();
   294   mHeaderMethod.Truncate();
   296   nsresult rv = NS_OK;
   297   while (NS_SUCCEEDED(rv) && (mOffset < datalen)) {
   298     if (mData[mOffset] & 0x80) {
   299       rv = DoIndexed();
   300     } else if (mData[mOffset] & 0x40) {
   301       rv = DoLiteralWithoutIndex();
   302     } else {
   303       rv = DoLiteralWithIncremental();
   304     }
   305   }
   307   // after processing the input the decompressor comapres the alternate
   308   // set to the inherited reference set and generates headers for
   309   // anything implicit in reference - alternate.
   311   uint32_t setLen = mReferenceSet.Length();
   312   for (uint32_t index = 0; index < setLen; ++index) {
   313     if (!mAlternateReferenceSet.Contains(mReferenceSet[index])) {
   314       LOG3(("HTTP decompressor carryover in reference set with index %u %s %s\n",
   315             mReferenceSet[index],
   316             mHeaderTable[mReferenceSet[index]]->mName.get(),
   317             mHeaderTable[mReferenceSet[index]]->mValue.get()));
   318       OutputHeader(mReferenceSet[index]);
   319     }
   320   }
   322   mAlternateReferenceSet.Clear();
   323   return rv;
   324 }
   326 nsresult
   327 Http2Decompressor::DecodeInteger(uint32_t prefixLen, uint32_t &accum)
   328 {
   329   accum = 0;
   331   if (prefixLen) {
   332     uint32_t mask = (1 << prefixLen) - 1;
   334     accum = mData[mOffset] & mask;
   335     ++mOffset;
   337     if (accum != mask) {
   338       // the simple case for small values
   339       return NS_OK;
   340     }
   341   }
   343   uint32_t factor = 1; // 128 ^ 0
   345   // we need a series of bytes. The high bit signifies if we need another one.
   346   // The first one is a a factor of 128 ^ 0, the next 128 ^1, the next 128 ^2, ..
   348   if (mOffset >= mDataLen) {
   349     NS_WARNING("Ran out of data to decode integer");
   350     return NS_ERROR_ILLEGAL_VALUE;
   351   }
   352   bool chainBit = mData[mOffset] & 0x80;
   353   accum += (mData[mOffset] & 0x7f) * factor;
   355   ++mOffset;
   356   factor = factor * 128;
   358   while (chainBit) {
   359     // really big offsets are just trawling for overflows
   360     if (accum >= 0x800000) {
   361       NS_WARNING("Decoding integer >= 0x800000");
   362       return NS_ERROR_ILLEGAL_VALUE;
   363     }
   365     if (mOffset >= mDataLen) {
   366       NS_WARNING("Ran out of data to decode integer");
   367       return NS_ERROR_ILLEGAL_VALUE;
   368     }
   369     chainBit = mData[mOffset] & 0x80;
   370     accum += (mData[mOffset] & 0x7f) * factor;
   371     ++mOffset;
   372     factor = factor * 128;
   373   }
   374   return NS_OK;
   375 }
   377 nsresult
   378 Http2Decompressor::OutputHeader(const nsACString &name, const nsACString &value)
   379 {
   380     // exclusions
   381   if (name.Equals(NS_LITERAL_CSTRING("connection")) ||
   382       name.Equals(NS_LITERAL_CSTRING("host")) ||
   383       name.Equals(NS_LITERAL_CSTRING("keep-alive")) ||
   384       name.Equals(NS_LITERAL_CSTRING("proxy-connection")) ||
   385       name.Equals(NS_LITERAL_CSTRING("te")) ||
   386       name.Equals(NS_LITERAL_CSTRING("transfer-encoding")) ||
   387       name.Equals(NS_LITERAL_CSTRING("upgrade")) ||
   388       name.Equals(("accept-encoding"))) {
   389     nsCString toLog(name);
   390     LOG3(("HTTP Decompressor illegal response header found : %s",
   391           toLog.get()));
   392     return NS_ERROR_ILLEGAL_VALUE;
   393   }
   395   // Look for upper case characters in the name.
   396   for (const char *cPtr = name.BeginReading();
   397        cPtr && cPtr < name.EndReading();
   398        ++cPtr) {
   399     if (*cPtr <= 'Z' && *cPtr >= 'A') {
   400       nsCString toLog(name);
   401       LOG3(("HTTP Decompressor upper case response header found. [%s]\n",
   402             toLog.get()));
   403       return NS_ERROR_ILLEGAL_VALUE;
   404     }
   405   }
   407   // Look for CR OR LF in value - could be smuggling Sec 10.3
   408   // can map to space safely
   409   for (const char *cPtr = value.BeginReading();
   410        cPtr && cPtr < value.EndReading();
   411        ++cPtr) {
   412     if (*cPtr == '\r' || *cPtr== '\n') {
   413       char *wPtr = const_cast<char *>(cPtr);
   414       *wPtr = ' ';
   415     }
   416   }
   418   // Status comes first
   419   if (name.Equals(NS_LITERAL_CSTRING(":status"))) {
   420     nsAutoCString status(NS_LITERAL_CSTRING("HTTP/2.0 "));
   421     status.Append(value);
   422     status.Append(NS_LITERAL_CSTRING("\r\n"));
   423     mOutput->Insert(status, 0);
   424     mHeaderStatus = value;
   425   } else if (name.Equals(NS_LITERAL_CSTRING(":authority"))) {
   426     mHeaderHost = value;
   427   } else if (name.Equals(NS_LITERAL_CSTRING(":scheme"))) {
   428     mHeaderScheme = value;
   429   } else if (name.Equals(NS_LITERAL_CSTRING(":path"))) {
   430     mHeaderPath = value;
   431   } else if (name.Equals(NS_LITERAL_CSTRING(":method"))) {
   432     mHeaderMethod = value;
   433   }
   435   // http/2 transport level headers shouldn't be gatewayed into http/1
   436   if(*(name.BeginReading()) == ':') {
   437     LOG3(("HTTP Decompressor not gatewaying %s into http/1",
   438           name.BeginReading()));
   439     return NS_OK;
   440   }
   442   mOutput->Append(name);
   443   mOutput->Append(NS_LITERAL_CSTRING(": "));
   444   // Special handling for set-cookie according to the spec
   445   bool isSetCookie = name.Equals(NS_LITERAL_CSTRING("set-cookie"));
   446   int32_t valueLen = value.Length();
   447   for (int32_t i = 0; i < valueLen; ++i) {
   448     if (value[i] == '\0') {
   449       if (isSetCookie) {
   450         mOutput->Append(NS_LITERAL_CSTRING("\r\n"));
   451         mOutput->Append(name);
   452         mOutput->Append(NS_LITERAL_CSTRING(": "));
   453       } else {
   454         mOutput->Append(NS_LITERAL_CSTRING(", "));
   455       }
   456     } else {
   457       mOutput->Append(value[i]);
   458     }
   459   }
   460   mOutput->Append(NS_LITERAL_CSTRING("\r\n"));
   461   return NS_OK;
   462 }
   464 nsresult
   465 Http2Decompressor::OutputHeader(uint32_t index)
   466 {
   467   // bounds check
   468   if (mHeaderTable.Length() <= index)
   469     return NS_ERROR_ILLEGAL_VALUE;
   471   return OutputHeader(mHeaderTable[index]->mName,
   472                       mHeaderTable[index]->mValue);
   473 }
   475 nsresult
   476 Http2Decompressor::CopyHeaderString(uint32_t index, nsACString &name)
   477 {
   478   // bounds check
   479   if (mHeaderTable.Length() <= index)
   480     return NS_ERROR_ILLEGAL_VALUE;
   482   name = mHeaderTable[index]->mName;
   483   return NS_OK;
   484 }
   486 nsresult
   487 Http2Decompressor::CopyStringFromInput(uint32_t bytes, nsACString &val)
   488 {
   489   if (mOffset + bytes > mDataLen)
   490     return NS_ERROR_ILLEGAL_VALUE;
   492   val.Assign(reinterpret_cast<const char *>(mData) + mOffset, bytes);
   493   mOffset += bytes;
   494   return NS_OK;
   495 }
   497 nsresult
   498 Http2Decompressor::DecodeFinalHuffmanCharacter(HuffmanIncomingTable *table,
   499                                                uint8_t &c, uint8_t &bitsLeft)
   500 {
   501   uint8_t mask = (1 << bitsLeft) - 1;
   502   uint8_t idx = mData[mOffset - 1] & mask;
   503   idx <<= (8 - bitsLeft);
   504   // Don't update bitsLeft yet, because we need to check that value against the
   505   // number of bits used by our encoding later on. We'll update when we are sure
   506   // how many bits we've actually used.
   508   HuffmanIncomingEntry *entry = &(table->mEntries[idx]);
   510   if (entry->mPtr) {
   511     // Can't chain to another table when we're all out of bits in the encoding
   512     LOG3(("DecodeFinalHuffmanCharacter trying to chain when we're out of bits"));
   513     return NS_ERROR_ILLEGAL_VALUE;
   514   }
   516   if (bitsLeft < entry->mPrefixLen) {
   517     // We don't have enough bits to actually make a match, this is some sort of
   518     // invalid coding
   519     LOG3(("DecodeFinalHuffmanCharacter does't have enough bits to match"));
   520     return NS_ERROR_ILLEGAL_VALUE;
   521   }
   523   // This is a character!
   524   if (entry->mValue == 256) {
   525     // EOS
   526     LOG3(("DecodeFinalHuffmanCharacter actually decoded an EOS"));
   527     return NS_ERROR_ILLEGAL_VALUE;
   528   }
   529   c = static_cast<uint8_t>(entry->mValue & 0xFF);
   530   bitsLeft -= entry->mPrefixLen;
   532   return NS_OK;
   533 }
   535 uint8_t
   536 Http2Decompressor::ExtractByte(uint8_t bitsLeft, uint32_t &bytesConsumed)
   537 {
   538   uint8_t rv;
   540   if (bitsLeft) {
   541     // Need to extract bitsLeft bits from the previous byte, and 8 - bitsLeft
   542     // bits from the current byte
   543     uint8_t mask = (1 << bitsLeft) - 1;
   544     rv = (mData[mOffset - 1] & mask) << (8 - bitsLeft);
   545     rv |= (mData[mOffset] & ~mask) >> bitsLeft;
   546   } else {
   547     rv = mData[mOffset];
   548   }
   550   // We always update these here, under the assumption that all 8 bits we got
   551   // here will be used. These may be re-adjusted later in the case that we don't
   552   // use up all 8 bits of the byte.
   553   ++mOffset;
   554   ++bytesConsumed;
   556   return rv;
   557 }
   559 nsresult
   560 Http2Decompressor::DecodeHuffmanCharacter(HuffmanIncomingTable *table,
   561                                           uint8_t &c, uint32_t &bytesConsumed,
   562                                           uint8_t &bitsLeft)
   563 {
   564   uint8_t idx = ExtractByte(bitsLeft, bytesConsumed);
   565   HuffmanIncomingEntry *entry = &(table->mEntries[idx]);
   567   if (entry->mPtr) {
   568     if (bytesConsumed >= mDataLen) {
   569       if (!bitsLeft || (bytesConsumed > mDataLen)) {
   570         // TODO - does this get me into trouble in the new world?
   571         // No info left in input to try to consume, we're done
   572         LOG3(("DecodeHuffmanCharacter all out of bits to consume, can't chain"));
   573         return NS_ERROR_ILLEGAL_VALUE;
   574       }
   576       // We might get lucky here!
   577       return DecodeFinalHuffmanCharacter(entry->mPtr, c, bitsLeft);
   578     }
   580     // We're sorry, Mario, but your princess is in another castle
   581     return DecodeHuffmanCharacter(entry->mPtr, c, bytesConsumed, bitsLeft);
   582   }
   584   if (entry->mValue == 256) {
   585     LOG3(("DecodeHuffmanCharacter found an actual EOS"));
   586     return NS_ERROR_ILLEGAL_VALUE;
   587   }
   588   c = static_cast<uint8_t>(entry->mValue & 0xFF);
   590   // Need to adjust bitsLeft (and possibly other values) because we may not have
   591   // consumed all of the bits of the byte we extracted.
   592   if (entry->mPrefixLen <= bitsLeft) {
   593     bitsLeft -= entry->mPrefixLen;
   594     --mOffset;
   595     --bytesConsumed;
   596   } else {
   597     bitsLeft = 8 - (entry->mPrefixLen - bitsLeft);
   598   }
   599   MOZ_ASSERT(bitsLeft < 8);
   601   return NS_OK;
   602 }
   604 nsresult
   605 Http2Decompressor::CopyHuffmanStringFromInput(uint32_t bytes, nsACString &val)
   606 {
   607   if (mOffset + bytes > mDataLen) {
   608     LOG3(("CopyHuffmanStringFromInput not enough data"));
   609     return NS_ERROR_ILLEGAL_VALUE;
   610   }
   612   uint32_t bytesRead = 0;
   613   uint8_t bitsLeft = 0;
   614   nsAutoCString buf;
   615   nsresult rv;
   616   uint8_t c;
   618   while (bytesRead < bytes) {
   619     uint32_t bytesConsumed = 0;
   620     rv = DecodeHuffmanCharacter(&HuffmanIncomingRoot, c, bytesConsumed,
   621                                 bitsLeft);
   622     if (NS_FAILED(rv)) {
   623       LOG3(("CopyHuffmanStringFromInput failed to decode a character"));
   624       return rv;
   625     }
   627     bytesRead += bytesConsumed;
   628     buf.Append(c);
   629   }
   631   if (bytesRead > bytes) {
   632     LOG3(("CopyHuffmanStringFromInput read more bytes than was allowed!"));
   633     return NS_ERROR_ILLEGAL_VALUE;
   634   }
   636   if (bitsLeft) {
   637     // The shortest valid code is 4 bits, so we know there can be at most one
   638     // character left that our loop didn't decode. Check to see if that's the
   639     // case, and if so, add it to our output.
   640     rv = DecodeFinalHuffmanCharacter(&HuffmanIncomingRoot, c, bitsLeft);
   641     if (NS_SUCCEEDED(rv)) {
   642       buf.Append(c);
   643     }
   644   }
   646   if (bitsLeft) {
   647     // Any bits left at this point must belong to the EOS symbol, so make sure
   648     // they make sense (ie, are all ones)
   649     uint8_t mask = (1 << bitsLeft) - 1;
   650     uint8_t bits = mData[mOffset - 1] & mask;
   651     if (bits != mask) {
   652       LOG3(("CopyHuffmanStringFromInput ran out of data but found possible "
   653             "non-EOS symbol"));
   654       return NS_ERROR_ILLEGAL_VALUE;
   655     }
   656   }
   658   val = buf;
   659   LOG3(("CopyHuffmanStringFromInput decoded a full string!"));
   660   return NS_OK;
   661 }
   663 void
   664 Http2Decompressor::MakeRoom(uint32_t amount)
   665 {
   666   // make room in the header table
   667   uint32_t removedCount = 0;
   668   while (mHeaderTable.VariableLength() && ((mHeaderTable.ByteCount() + amount) > mMaxBuffer)) {
   669     uint32_t index = mHeaderTable.VariableLength() - 1;
   670     mHeaderTable.RemoveElement();
   671     ++removedCount;
   672     LOG3(("HTTP decompressor header table index %u removed for size.\n",
   673           index));
   674   }
   676   // adjust references to header table
   677   UpdateReferenceSet(removedCount);
   678 }
   680 nsresult
   681 Http2Decompressor::DoIndexed()
   682 {
   683   // this starts with a 1 bit pattern
   684   MOZ_ASSERT(mData[mOffset] & 0x80);
   686   // Indexed entries toggle the reference set
   687   // This is a 7 bit prefix
   689   uint32_t index;
   690   nsresult rv = DecodeInteger(7, index);
   691   if (NS_FAILED(rv))
   692     return rv;
   694   LOG3(("HTTP decompressor indexed entry %u\n", index));
   696   if (index == 0) {
   697     // Index 0 is a special case - it has extra data tacked on the end to
   698     // determine what kind of change to make to the encoding context.
   699     //
   700     if (mData[mOffset] & 0x80) {
   701       // This means we have to clear out the reference set
   702       mReferenceSet.Clear();
   703       mAlternateReferenceSet.Clear();
   704       ++mOffset;
   705       return NS_OK;
   706     }
   708     // Getting here means we have to adjust the max table size
   709     uint32_t newMaxSize;
   710     rv = DecodeInteger(7, newMaxSize);
   711     if (NS_FAILED(rv))
   712       return rv;
   713     return mCompressor->SetMaxBufferSizeInternal(newMaxSize);
   714   }
   715   index--; // Internally, we 0-index everything, since this is, y'know, C++
   717   // Toggle this in the reference set..
   718   // if its not currently in the reference set then add it and
   719   // also emit it. If it is currently in the reference set then just
   720   // remove it from there.
   721   if (mReferenceSet.RemoveElement(index)) {
   722     mAlternateReferenceSet.RemoveElement(index);
   723     return NS_OK;
   724   }
   726   rv = OutputHeader(index);
   727   if (index >= mHeaderTable.VariableLength()) {
   728     const nvPair *pair = mHeaderTable[index];
   729     uint32_t room = pair->Size();
   731     if (room > mMaxBuffer) {
   732       ClearHeaderTable();
   733       LOG3(("HTTP decompressor index not referenced due to size %u %s\n",
   734             room, pair->mName.get()));
   735       return rv;
   736     }
   738     MakeRoom(room);
   739     mHeaderTable.AddElement(pair->mName, pair->mValue);
   740     IncrementReferenceSetIndices();
   741     index = 0;
   742   }
   744   mReferenceSet.AppendElement(index);
   745   mAlternateReferenceSet.AppendElement(index);
   746   return rv;
   747 }
   749 nsresult
   750 Http2Decompressor::DoLiteralInternal(nsACString &name, nsACString &value)
   751 {
   752   // guts of doliteralwithoutindex and doliteralwithincremental
   753   MOZ_ASSERT(((mData[mOffset] & 0xC0) == 0x40) ||  // withoutindex
   754              ((mData[mOffset] & 0xC0) == 0x00));   // withincremental
   756   // first let's get the name
   757   uint32_t index;
   758   nsresult rv = DecodeInteger(6, index);
   759   if (NS_FAILED(rv))
   760     return rv;
   762   bool isHuffmanEncoded;
   764   if (!index) {
   765     // name is embedded as a literal
   766     uint32_t nameLen;
   767     isHuffmanEncoded = mData[mOffset] & (1 << 7);
   768     rv = DecodeInteger(7, nameLen);
   769     if (NS_SUCCEEDED(rv)) {
   770       if (isHuffmanEncoded) {
   771         rv = CopyHuffmanStringFromInput(nameLen, name);
   772       } else {
   773         rv = CopyStringFromInput(nameLen, name);
   774       }
   775     }
   776   } else {
   777     // name is from headertable
   778     rv = CopyHeaderString(index - 1, name);
   779   }
   780   if (NS_FAILED(rv))
   781     return rv;
   783   // now the value
   784   uint32_t valueLen;
   785   isHuffmanEncoded = mData[mOffset] & (1 << 7);
   786   rv = DecodeInteger(7, valueLen);
   787   if (NS_SUCCEEDED(rv)) {
   788     if (isHuffmanEncoded) {
   789       rv = CopyHuffmanStringFromInput(valueLen, value);
   790     } else {
   791       rv = CopyStringFromInput(valueLen, value);
   792     }
   793   }
   794   if (NS_FAILED(rv))
   795     return rv;
   796   return NS_OK;
   797 }
   799 nsresult
   800 Http2Decompressor::DoLiteralWithoutIndex()
   801 {
   802   // this starts with 01 bit pattern
   803   MOZ_ASSERT((mData[mOffset] & 0xC0) == 0x40);
   805   // This is not indexed so there is no adjustment to the
   806   // persistent reference set
   807   nsAutoCString name, value;
   808   nsresult rv = DoLiteralInternal(name, value);
   810   LOG3(("HTTP decompressor literal without index %s %s\n",
   811         name.get(), value.get()));
   813   // Output the header now because we don't keep void
   814   // indicies in the reference set
   815   if (NS_SUCCEEDED(rv))
   816     rv = OutputHeader(name, value);
   817   return rv;
   818 }
   820 nsresult
   821 Http2Decompressor::DoLiteralWithIncremental()
   822 {
   823   // this starts with 00 bit pattern
   824   MOZ_ASSERT((mData[mOffset] & 0xC0) == 0x00);
   826   nsAutoCString name, value;
   827   nsresult rv = DoLiteralInternal(name, value);
   828   if (NS_SUCCEEDED(rv))
   829     rv = OutputHeader(name, value);
   830   if (NS_FAILED(rv))
   831     return rv;
   833   uint32_t room = nvPair(name, value).Size();
   834   if (room > mMaxBuffer) {
   835     ClearHeaderTable();
   836     LOG3(("HTTP decompressor literal with index not referenced due to size %u %s\n",
   837           room, name.get()));
   838     return NS_OK;
   839   }
   841   MakeRoom(room);
   843   // Incremental Indexing implicitly adds a row to the header table.
   844   // It also adds the new row to the Reference Set
   845   mHeaderTable.AddElement(name, value);
   846   IncrementReferenceSetIndices();
   847   mReferenceSet.AppendElement(0);
   848   mAlternateReferenceSet.AppendElement(0);
   850   LOG3(("HTTP decompressor literal with index 0 %s %s\n",
   851         name.get(), value.get()));
   853   return NS_OK;
   854 }
   856 /////////////////////////////////////////////////////////////////
   858 nsresult
   859 Http2Compressor::EncodeHeaderBlock(const nsCString &nvInput,
   860                                    const nsACString &method, const nsACString &path,
   861                                    const nsACString &host, const nsACString &scheme,
   862                                    nsACString &output)
   863 {
   864   mAlternateReferenceSet.Clear();
   865   mImpliedReferenceSet.Clear();
   866   mOutput = &output;
   867   output.SetCapacity(1024);
   868   output.Truncate();
   869   mParsedContentLength = -1;
   871   // colon headers first
   872   ProcessHeader(nvPair(NS_LITERAL_CSTRING(":method"), method));
   873   ProcessHeader(nvPair(NS_LITERAL_CSTRING(":path"), path));
   874   ProcessHeader(nvPair(NS_LITERAL_CSTRING(":authority"), host));
   875   ProcessHeader(nvPair(NS_LITERAL_CSTRING(":scheme"), scheme));
   877   // now the non colon headers
   878   const char *beginBuffer = nvInput.BeginReading();
   880   int32_t crlfIndex = nvInput.Find("\r\n");
   881   while (true) {
   882     int32_t startIndex = crlfIndex + 2;
   884     crlfIndex = nvInput.Find("\r\n", false, startIndex);
   885     if (crlfIndex == -1)
   886       break;
   888     int32_t colonIndex = nvInput.Find(":", false, startIndex,
   889                                       crlfIndex - startIndex);
   890     if (colonIndex == -1)
   891       break;
   893     nsDependentCSubstring name = Substring(beginBuffer + startIndex,
   894                                            beginBuffer + colonIndex);
   895     // all header names are lower case in http/2
   896     ToLowerCase(name);
   898     // exclusions
   899     if (name.Equals("connection") ||
   900         name.Equals("host") ||
   901         name.Equals("keep-alive") ||
   902         name.Equals("proxy-connection") ||
   903         name.Equals("te") ||
   904         name.Equals("transfer-encoding") ||
   905         name.Equals("upgrade") ||
   906         name.Equals("accept-encoding")) {
   907       continue;
   908     }
   910     // colon headers are for http/2 and this is http/1 input, so that
   911     // is probably a smuggling attack of some kind
   912     if(*(name.BeginReading()) == ':') {
   913       continue;
   914     }
   916     int32_t valueIndex = colonIndex + 1;
   918     // if we have Expect: *100-continue,*" redact the 100-continue
   919     // as we don't have a good mechanism for clients to make use of it
   920     // anyhow
   921     if (name.Equals("expect")) {
   922       const char *continueHeader =
   923         nsHttp::FindToken(beginBuffer + valueIndex, "100-continue",
   924                           HTTP_HEADER_VALUE_SEPS);
   925       if (continueHeader) {
   926         char *writableVal = const_cast<char *>(continueHeader);
   927         memset(writableVal, 0, 12);
   928         writableVal += 12;
   929         // this will terminate safely because CRLF EOL has been confirmed
   930         while ((*writableVal == ' ') || (*writableVal == '\t') ||
   931                (*writableVal == ',')) {
   932           *writableVal = ' ';
   933           ++writableVal;
   934         }
   935       }
   936     }
   938     while (valueIndex < crlfIndex && beginBuffer[valueIndex] == ' ')
   939       ++valueIndex;
   941     nsDependentCSubstring value = Substring(beginBuffer + valueIndex,
   942                                             beginBuffer + crlfIndex);
   944     if (name.Equals("content-length")) {
   945       int64_t len;
   946       nsCString tmp(value);
   947       if (nsHttp::ParseInt64(tmp.get(), nullptr, &len))
   948         mParsedContentLength = len;
   949     }
   951     if (name.Equals("cookie")) {
   952       // cookie crumbling
   953       bool haveMoreCookies = true;
   954       int32_t nextCookie = valueIndex;
   955       while (haveMoreCookies) {
   956         int32_t semiSpaceIndex = nvInput.Find("; ", false, nextCookie,
   957                                               crlfIndex - nextCookie);
   958         if (semiSpaceIndex == -1) {
   959           haveMoreCookies = false;
   960           semiSpaceIndex = crlfIndex;
   961         }
   962         nsDependentCSubstring cookie = Substring(beginBuffer + nextCookie,
   963                                                  beginBuffer + semiSpaceIndex);
   964         ProcessHeader(nvPair(name, cookie));
   965         nextCookie = semiSpaceIndex + 2;
   966       }
   967     } else {
   968       ProcessHeader(nvPair(name, value));
   969     }
   970   }
   972   // iterate mreference set and if !alternate.contains(old[i])
   973   // toggle off
   974   uint32_t setLen = mReferenceSet.Length();
   975   for (uint32_t index = 0; index < setLen; ++index) {
   976     if (!mAlternateReferenceSet.Contains(mReferenceSet[index])) {
   977       DoOutput(kToggleOff, mHeaderTable[mReferenceSet[index]],
   978                mReferenceSet[index]);
   979     }
   980   }
   982   mReferenceSet = mAlternateReferenceSet;
   983   mAlternateReferenceSet.Clear();
   984   mImpliedReferenceSet.Clear();
   985   mOutput = nullptr;
   986   return NS_OK;
   987 }
   989 void
   990 Http2Compressor::DoOutput(Http2Compressor::outputCode code,
   991                           const class nvPair *pair, uint32_t index)
   992 {
   993   // start Byte needs to be calculated from the offset after
   994   // the opcode has been written out in case the output stream
   995   // buffer gets resized/relocated
   996   uint32_t offset = mOutput->Length();
   997   uint8_t *startByte;
   999   switch (code) {
  1000   case kPlainLiteral:
  1001     LOG3(("HTTP compressor %p noindex literal with name reference %u %s: %s\n",
  1002           this, index, pair->mName.get(), pair->mValue.get()));
  1004     // In this case, the index will have already been adjusted to be 1-based
  1005     // instead of 0-based.
  1006     EncodeInteger(6, index); // 01 2 bit prefix
  1007     startByte = reinterpret_cast<unsigned char *>(mOutput->BeginWriting()) + offset;
  1008     *startByte = (*startByte & 0x3f) | 0x40;
  1010     if (!index) {
  1011       HuffmanAppend(pair->mName);
  1014     HuffmanAppend(pair->mValue);
  1015     break;
  1017   case kIndexedLiteral:
  1018     LOG3(("HTTP compressor %p literal with name reference %u %s: %s\n",
  1019           this, index, pair->mName.get(), pair->mValue.get()));
  1021     // In this case, the index will have already been adjusted to be 1-based
  1022     // instead of 0-based.
  1023     EncodeInteger(6, index); // 00 2 bit prefix
  1024     startByte = reinterpret_cast<unsigned char *>(mOutput->BeginWriting()) + offset;
  1025     *startByte = *startByte & 0x3f;
  1027     if (!index) {
  1028       HuffmanAppend(pair->mName);
  1031     HuffmanAppend(pair->mValue);
  1032     break;
  1034   case kToggleOff:
  1035   case kToggleOn:
  1036     LOG3(("HTTP compressor %p toggle %s index %u %s\n",
  1037           this, (code == kToggleOff) ? "off" : "on",
  1038           index, pair->mName.get()));
  1039     // In this case, we are passed the raw 0-based C index, and need to
  1040     // increment to make it 1-based and comply with the spec
  1041     EncodeInteger(7, index + 1);
  1042     startByte = reinterpret_cast<unsigned char *>(mOutput->BeginWriting()) + offset;
  1043     *startByte = *startByte | 0x80; // 1 1 bit prefix
  1044     break;
  1046   case kNop:
  1047     LOG3(("HTTP compressor %p implied in reference set index %u %s\n",
  1048           this, index, pair->mName.get()));
  1049     break;
  1053 // writes the encoded integer onto the output
  1054 void
  1055 Http2Compressor::EncodeInteger(uint32_t prefixLen, uint32_t val)
  1057   uint32_t mask = (1 << prefixLen) - 1;
  1058   uint8_t tmp;
  1060   if (val < mask) {
  1061     // 1 byte encoding!
  1062     tmp = val;
  1063     mOutput->Append(reinterpret_cast<char *>(&tmp), 1);
  1064     return;
  1067   if (mask) {
  1068     val -= mask;
  1069     tmp = mask;
  1070     mOutput->Append(reinterpret_cast<char *>(&tmp), 1);
  1073   uint32_t q, r;
  1074   do {
  1075     q = val / 128;
  1076     r = val % 128;
  1077     tmp = r;
  1078     if (q)
  1079       tmp |= 0x80; // chain bit
  1080     val = q;
  1081     mOutput->Append(reinterpret_cast<char *>(&tmp), 1);
  1082   } while (q);
  1085 void
  1086 Http2Compressor::ClearHeaderTable()
  1088   uint32_t dynamicCount = mHeaderTable.VariableLength();
  1090   Http2BaseCompressor::ClearHeaderTable();
  1092   for (int32_t i = mImpliedReferenceSet.Length() - 1; i >= 0; --i) {
  1093     if (mImpliedReferenceSet[i] < dynamicCount) {
  1094       mImpliedReferenceSet.RemoveElementAt(i);
  1095     } else {
  1096       mImpliedReferenceSet[i] -= dynamicCount;
  1102 void
  1103 Http2Compressor::UpdateReferenceSet(int32_t delta)
  1105   if (!delta)
  1106     return;
  1108   Http2BaseCompressor::UpdateReferenceSet(delta);
  1110   uint32_t headerTableSize = mHeaderTable.VariableLength();
  1111   uint32_t oldHeaderTableSize = headerTableSize + delta;
  1113   for (int32_t i = mImpliedReferenceSet.Length() - 1; i >= 0; --i) {
  1114     uint32_t indexRef = mImpliedReferenceSet[i];
  1115     if (indexRef >= headerTableSize) {
  1116       if (indexRef < oldHeaderTableSize) {
  1117         // This one got dropped
  1118         LOG3(("HTTP compressor implied reference to index %u removed.\n",
  1119               indexRef));
  1120         mImpliedReferenceSet.RemoveElementAt(i);
  1121       } else {
  1122         // This pointed to the static table, need to adjust
  1123         uint32_t newRef = indexRef - delta;
  1124         LOG3(("HTTP compressor implied reference to index %u changed to %d (%s)\n",
  1125               mImpliedReferenceSet[i], newRef, mHeaderTable[newRef]->mName.get()));
  1126         mImpliedReferenceSet[i] = newRef;
  1132 void
  1133 Http2Compressor::IncrementReferenceSetIndices()
  1135   Http2BaseCompressor::IncrementReferenceSetIndices();
  1137   for (int32_t i = mImpliedReferenceSet.Length() - 1; i >= 0; --i) {
  1138     mImpliedReferenceSet[i] = mImpliedReferenceSet[i] + 1;
  1142 void
  1143 Http2Compressor::MakeRoom(uint32_t amount)
  1145   // make room in the header table
  1146   uint32_t removedCount = 0;
  1147   while (mHeaderTable.VariableLength() && ((mHeaderTable.ByteCount() + amount) > mMaxBuffer)) {
  1149     // if there is a reference to removedCount (~0) in the implied reference set we need,
  1150     // to toggle it off/on so that the implied reference is not lost when the
  1151     // table is trimmed
  1152     uint32_t index = mHeaderTable.VariableLength() - 1;
  1153     if (mImpliedReferenceSet.Contains(index) ) {
  1154       LOG3(("HTTP compressor header table index %u %s about to be "
  1155             "removed for size but has an implied reference. Will Toggle.\n",
  1156             index, mHeaderTable[index]->mName.get()));
  1158       DoOutput(kToggleOff, mHeaderTable[index], index);
  1159       DoOutput(kToggleOn, mHeaderTable[index], index);
  1162     LOG3(("HTTP compressor header table index %u %s removed for size.\n",
  1163           index, mHeaderTable[index]->mName.get()));
  1164     mHeaderTable.RemoveElement();
  1165     ++removedCount;
  1168   // adjust references to header table
  1169   UpdateReferenceSet(removedCount);
  1172 void
  1173 Http2Compressor::HuffmanAppend(const nsCString &value)
  1175   nsAutoCString buf;
  1176   uint8_t bitsLeft = 8;
  1177   uint32_t length = value.Length();
  1178   uint32_t offset;
  1179   uint8_t *startByte;
  1181   for (uint32_t i = 0; i < length; ++i) {
  1182     uint8_t idx = static_cast<uint8_t>(value[i]);
  1183     uint8_t huffLength = HuffmanOutgoing[idx].mLength;
  1184     uint32_t huffValue = HuffmanOutgoing[idx].mValue;
  1186     if (bitsLeft < 8) {
  1187       // Fill in the least significant <bitsLeft> bits of the previous byte
  1188       // first
  1189       uint32_t val;
  1190       if (huffLength >= bitsLeft) {
  1191         val = huffValue & ~((1 << (huffLength - bitsLeft)) - 1);
  1192         val >>= (huffLength - bitsLeft);
  1193       } else {
  1194         val = huffValue << (bitsLeft - huffLength);
  1196       val &= ((1 << bitsLeft) - 1);
  1197       offset = buf.Length() - 1;
  1198       startByte = reinterpret_cast<unsigned char *>(buf.BeginWriting()) + offset;
  1199       *startByte = *startByte | static_cast<uint8_t>(val & 0xFF);
  1200       if (huffLength >= bitsLeft) {
  1201         huffLength -= bitsLeft;
  1202         bitsLeft = 8;
  1203       } else {
  1204         bitsLeft -= huffLength;
  1205         huffLength = 0;
  1209     while (huffLength > 8) {
  1210       uint32_t mask = ~((1 << (huffLength - 8)) - 1);
  1211       uint8_t val = ((huffValue & mask) >> (huffLength - 8)) & 0xFF;
  1212       buf.Append(reinterpret_cast<char *>(&val), 1);
  1213       huffLength -= 8;
  1216     if (huffLength) {
  1217       // Fill in the most significant <huffLength> bits of the next byte
  1218       bitsLeft = 8 - huffLength;
  1219       uint8_t val = (huffValue & ((1 << huffLength) - 1)) << bitsLeft;
  1220       buf.Append(reinterpret_cast<char *>(&val), 1);
  1224   if (bitsLeft != 8) {
  1225     // Pad the last <bitsLeft> bits with ones, which corresponds to the EOS
  1226     // encoding
  1227     uint8_t val = (1 << bitsLeft) - 1;
  1228     offset = buf.Length() - 1;
  1229     startByte = reinterpret_cast<unsigned char *>(buf.BeginWriting()) + offset;
  1230     *startByte = *startByte | val;
  1233   // Now we know how long our encoded string is, we can fill in our length
  1234   uint32_t bufLength = buf.Length();
  1235   offset = mOutput->Length();
  1236   EncodeInteger(7, bufLength);
  1237   startByte = reinterpret_cast<unsigned char *>(mOutput->BeginWriting()) + offset;
  1238   *startByte = *startByte | 0x80;
  1240   // Finally, we can add our REAL data!
  1241   mOutput->Append(buf);
  1244 void
  1245 Http2Compressor::ProcessHeader(const nvPair inputPair)
  1247   uint32_t newSize = inputPair.Size();
  1248   uint32_t headerTableSize = mHeaderTable.Length();
  1249   uint32_t matchedIndex;
  1250   uint32_t nameReference = 0;
  1251   bool match = false;
  1253   for (uint32_t index = 0; index < headerTableSize; ++index) {
  1254     if (mHeaderTable[index]->mName.Equals(inputPair.mName)) {
  1255       nameReference = index + 1;
  1256       if (mHeaderTable[index]->mValue.Equals(inputPair.mValue)) {
  1257         match = true;
  1258         matchedIndex = index;
  1259         break;
  1264   // We need to emit a new literal
  1265   if (!match) {
  1266     if ((newSize > (mMaxBuffer / 2)) || (mMaxBuffer < 128)) {
  1267       DoOutput(kPlainLiteral, &inputPair, nameReference);
  1268       return;
  1271     // make sure to makeroom() first so that any implied items
  1272     // get preserved.
  1273     MakeRoom(newSize);
  1274     DoOutput(kIndexedLiteral, &inputPair, nameReference);
  1276     mHeaderTable.AddElement(inputPair.mName, inputPair.mValue);
  1277     IncrementReferenceSetIndices();
  1278     LOG3(("HTTP compressor %p new literal placed at index 0\n",
  1279           this));
  1280     mAlternateReferenceSet.AppendElement(0);
  1281     return;
  1284   // It is in the reference set. just check to see if it is
  1285   // a duplicate for output purposes
  1286   if (mReferenceSet.Contains(matchedIndex)) {
  1287     if (mAlternateReferenceSet.Contains(matchedIndex)) {
  1288       DoOutput(kToggleOff, &inputPair, matchedIndex);
  1289       DoOutput(kToggleOn, &inputPair, matchedIndex);
  1290     } else {
  1291       DoOutput(kNop, &inputPair, matchedIndex);
  1292       if (!mImpliedReferenceSet.Contains(matchedIndex))
  1293         mImpliedReferenceSet.AppendElement(matchedIndex);
  1294       mAlternateReferenceSet.AppendElement(matchedIndex);
  1296     return;
  1299   // emit an index to add to reference set
  1300   DoOutput(kToggleOn, &inputPair, matchedIndex);
  1301   if (matchedIndex >= mHeaderTable.VariableLength()) {
  1302     MakeRoom(newSize);
  1303     mHeaderTable.AddElement(inputPair.mName, inputPair.mValue);
  1304     IncrementReferenceSetIndices();
  1305     mAlternateReferenceSet.AppendElement(0);
  1306   } else {
  1307     mAlternateReferenceSet.AppendElement(matchedIndex);
  1309   return;
  1312 void
  1313 Http2Compressor::SetMaxBufferSize(uint32_t maxBufferSize)
  1315   mMaxBufferSetting = maxBufferSize;
  1316   SetMaxBufferSizeInternal(maxBufferSize);
  1319 nsresult
  1320 Http2Compressor::SetMaxBufferSizeInternal(uint32_t maxBufferSize)
  1322   if (maxBufferSize > mMaxBufferSetting) {
  1323     return NS_ERROR_ILLEGAL_VALUE;
  1326   uint32_t removedCount = 0;
  1328   LOG3(("Http2Compressor::SetMaxBufferSizeInternal %u called", maxBufferSize));
  1330   while (mHeaderTable.VariableLength() && (mHeaderTable.ByteCount() > maxBufferSize)) {
  1331     mHeaderTable.RemoveElement();
  1332     ++removedCount;
  1334   UpdateReferenceSet(removedCount);
  1336   mMaxBuffer = maxBufferSize;
  1338   return NS_OK;
  1341 } // namespace mozilla::net
  1342 } // namespace mozilla

mercurial