Thu, 15 Jan 2015 21:03:48 +0100
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);
1012 }
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);
1029 }
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;
1050 }
1051 }
1053 // writes the encoded integer onto the output
1054 void
1055 Http2Compressor::EncodeInteger(uint32_t prefixLen, uint32_t val)
1056 {
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;
1065 }
1067 if (mask) {
1068 val -= mask;
1069 tmp = mask;
1070 mOutput->Append(reinterpret_cast<char *>(&tmp), 1);
1071 }
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);
1083 }
1085 void
1086 Http2Compressor::ClearHeaderTable()
1087 {
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;
1097 }
1098 }
1099 }
1102 void
1103 Http2Compressor::UpdateReferenceSet(int32_t delta)
1104 {
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;
1127 }
1128 }
1129 }
1130 }
1132 void
1133 Http2Compressor::IncrementReferenceSetIndices()
1134 {
1135 Http2BaseCompressor::IncrementReferenceSetIndices();
1137 for (int32_t i = mImpliedReferenceSet.Length() - 1; i >= 0; --i) {
1138 mImpliedReferenceSet[i] = mImpliedReferenceSet[i] + 1;
1139 }
1140 }
1142 void
1143 Http2Compressor::MakeRoom(uint32_t amount)
1144 {
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);
1160 }
1162 LOG3(("HTTP compressor header table index %u %s removed for size.\n",
1163 index, mHeaderTable[index]->mName.get()));
1164 mHeaderTable.RemoveElement();
1165 ++removedCount;
1166 }
1168 // adjust references to header table
1169 UpdateReferenceSet(removedCount);
1170 }
1172 void
1173 Http2Compressor::HuffmanAppend(const nsCString &value)
1174 {
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);
1195 }
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;
1206 }
1207 }
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;
1214 }
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);
1221 }
1222 }
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;
1231 }
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);
1242 }
1244 void
1245 Http2Compressor::ProcessHeader(const nvPair inputPair)
1246 {
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;
1260 }
1261 }
1262 }
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;
1269 }
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;
1282 }
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);
1295 }
1296 return;
1297 }
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);
1308 }
1309 return;
1310 }
1312 void
1313 Http2Compressor::SetMaxBufferSize(uint32_t maxBufferSize)
1314 {
1315 mMaxBufferSetting = maxBufferSize;
1316 SetMaxBufferSizeInternal(maxBufferSize);
1317 }
1319 nsresult
1320 Http2Compressor::SetMaxBufferSizeInternal(uint32_t maxBufferSize)
1321 {
1322 if (maxBufferSize > mMaxBufferSetting) {
1323 return NS_ERROR_ILLEGAL_VALUE;
1324 }
1326 uint32_t removedCount = 0;
1328 LOG3(("Http2Compressor::SetMaxBufferSizeInternal %u called", maxBufferSize));
1330 while (mHeaderTable.VariableLength() && (mHeaderTable.ByteCount() > maxBufferSize)) {
1331 mHeaderTable.RemoveElement();
1332 ++removedCount;
1333 }
1334 UpdateReferenceSet(removedCount);
1336 mMaxBuffer = maxBufferSize;
1338 return NS_OK;
1339 }
1341 } // namespace mozilla::net
1342 } // namespace mozilla