View Javadoc
1   /*
2    * Copyright 2015 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  
17  /*
18   * Copyright 2014 Twitter, Inc.
19   *
20   * Licensed under the Apache License, Version 2.0 (the "License");
21   * you may not use this file except in compliance with the License.
22   * You may obtain a copy of the License at
23   *
24   *     https://www.apache.org/licenses/LICENSE-2.0
25   *
26   * Unless required by applicable law or agreed to in writing, software
27   * distributed under the License is distributed on an "AS IS" BASIS,
28   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29   * See the License for the specific language governing permissions and
30   * limitations under the License.
31   */
32  package io.netty.handler.codec.http2;
33  
34  import io.netty.buffer.ByteBuf;
35  import io.netty.handler.codec.http2.HpackUtil.IndexType;
36  import io.netty.util.AsciiString;
37  
38  import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_HEADER_TABLE_SIZE;
39  import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_HEADER_LIST_SIZE;
40  import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_HEADER_TABLE_SIZE;
41  import static io.netty.handler.codec.http2.Http2CodecUtil.MIN_HEADER_LIST_SIZE;
42  import static io.netty.handler.codec.http2.Http2CodecUtil.MIN_HEADER_TABLE_SIZE;
43  import static io.netty.handler.codec.http2.Http2CodecUtil.headerListSizeExceeded;
44  import static io.netty.handler.codec.http2.Http2Error.COMPRESSION_ERROR;
45  import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
46  import static io.netty.handler.codec.http2.Http2Exception.connectionError;
47  import static io.netty.handler.codec.http2.Http2Exception.streamError;
48  import static io.netty.handler.codec.http2.Http2Headers.PseudoHeaderName.getPseudoHeader;
49  import static io.netty.handler.codec.http2.Http2Headers.PseudoHeaderName.hasPseudoHeaderFormat;
50  import static io.netty.util.AsciiString.EMPTY_STRING;
51  import static io.netty.util.internal.ObjectUtil.checkPositive;
52  
53  final class HpackDecoder {
54      private static final Http2Exception DECODE_ULE_128_DECOMPRESSION_EXCEPTION =
55              Http2Exception.newStatic(COMPRESSION_ERROR, "HPACK - decompression failure",
56                      Http2Exception.ShutdownHint.HARD_SHUTDOWN, HpackDecoder.class,
57              "decodeULE128(..)");
58      private static final Http2Exception DECODE_ULE_128_TO_LONG_DECOMPRESSION_EXCEPTION =
59              Http2Exception.newStatic(COMPRESSION_ERROR, "HPACK - long overflow",
60                      Http2Exception.ShutdownHint.HARD_SHUTDOWN, HpackDecoder.class, "decodeULE128(..)");
61      private static final Http2Exception DECODE_ULE_128_TO_INT_DECOMPRESSION_EXCEPTION =
62              Http2Exception.newStatic(COMPRESSION_ERROR, "HPACK - int overflow",
63                      Http2Exception.ShutdownHint.HARD_SHUTDOWN, HpackDecoder.class, "decodeULE128ToInt(..)");
64      private static final Http2Exception DECODE_ILLEGAL_INDEX_VALUE =
65              Http2Exception.newStatic(COMPRESSION_ERROR, "HPACK - illegal index value",
66                      Http2Exception.ShutdownHint.HARD_SHUTDOWN, HpackDecoder.class, "decode(..)");
67      private static final Http2Exception INDEX_HEADER_ILLEGAL_INDEX_VALUE =
68              Http2Exception.newStatic(COMPRESSION_ERROR, "HPACK - illegal index value",
69                      Http2Exception.ShutdownHint.HARD_SHUTDOWN, HpackDecoder.class, "indexHeader(..)");
70      private static final Http2Exception READ_NAME_ILLEGAL_INDEX_VALUE =
71              Http2Exception.newStatic(COMPRESSION_ERROR, "HPACK - illegal index value",
72                      Http2Exception.ShutdownHint.HARD_SHUTDOWN, HpackDecoder.class, "readName(..)");
73      private static final Http2Exception INVALID_MAX_DYNAMIC_TABLE_SIZE =
74              Http2Exception.newStatic(COMPRESSION_ERROR, "HPACK - invalid max dynamic table size",
75                      Http2Exception.ShutdownHint.HARD_SHUTDOWN, HpackDecoder.class,
76              "setDynamicTableSize(..)");
77      private static final Http2Exception MAX_DYNAMIC_TABLE_SIZE_CHANGE_REQUIRED =
78              Http2Exception.newStatic(COMPRESSION_ERROR, "HPACK - max dynamic table size change required",
79                      Http2Exception.ShutdownHint.HARD_SHUTDOWN, HpackDecoder.class, "decode(..)");
80      private static final byte READ_HEADER_REPRESENTATION = 0;
81      private static final byte READ_MAX_DYNAMIC_TABLE_SIZE = 1;
82      private static final byte READ_INDEXED_HEADER = 2;
83      private static final byte READ_INDEXED_HEADER_NAME = 3;
84      private static final byte READ_LITERAL_HEADER_NAME_LENGTH_PREFIX = 4;
85      private static final byte READ_LITERAL_HEADER_NAME_LENGTH = 5;
86      private static final byte READ_LITERAL_HEADER_NAME = 6;
87      private static final byte READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX = 7;
88      private static final byte READ_LITERAL_HEADER_VALUE_LENGTH = 8;
89      private static final byte READ_LITERAL_HEADER_VALUE = 9;
90  
91      private final HpackHuffmanDecoder huffmanDecoder = new HpackHuffmanDecoder();
92      private final HpackDynamicTable hpackDynamicTable;
93      private long maxHeaderListSize;
94      private long maxDynamicTableSize;
95      private long encoderMaxDynamicTableSize;
96      private boolean maxDynamicTableSizeChangeRequired;
97  
98      /**
99       * Create a new instance.
100      * @param maxHeaderListSize This is the only setting that can be configured before notifying the peer.
101      *  This is because <a href="https://tools.ietf.org/html/rfc7540#section-6.5.1">SETTINGS_MAX_HEADER_LIST_SIZE</a>
102      *  allows a lower than advertised limit from being enforced, and the default limit is unlimited
103      *  (which is dangerous).
104      */
105     HpackDecoder(long maxHeaderListSize) {
106         this(maxHeaderListSize, DEFAULT_HEADER_TABLE_SIZE);
107     }
108 
109     /**
110      * Exposed Used for testing only! Default values used in the initial settings frame are overridden intentionally
111      * for testing but violate the RFC if used outside the scope of testing.
112      */
113     HpackDecoder(long maxHeaderListSize, int maxHeaderTableSize) {
114         this.maxHeaderListSize = checkPositive(maxHeaderListSize, "maxHeaderListSize");
115 
116         maxDynamicTableSize = encoderMaxDynamicTableSize = maxHeaderTableSize;
117         maxDynamicTableSizeChangeRequired = false;
118         hpackDynamicTable = new HpackDynamicTable(maxHeaderTableSize);
119     }
120 
121     /**
122      * Decode the header block into header fields.
123      * <p>
124      * This method assumes the entire header block is contained in {@code in}.
125      */
126     public void decode(int streamId, ByteBuf in, Http2Headers headers, boolean validateHeaders) throws Http2Exception {
127         Http2HeadersSink sink = new Http2HeadersSink(streamId, headers, maxHeaderListSize, validateHeaders);
128         decode(in, sink);
129 
130         // Now that we've read all of our headers we can perform the validation steps. We must
131         // delay throwing until this point to prevent dynamic table corruption.
132         sink.finish();
133     }
134 
135     private void decode(ByteBuf in, Sink sink) throws Http2Exception {
136         int index = 0;
137         int nameLength = 0;
138         int valueLength = 0;
139         byte state = READ_HEADER_REPRESENTATION;
140         boolean huffmanEncoded = false;
141         CharSequence name = null;
142         IndexType indexType = IndexType.NONE;
143         while (in.isReadable()) {
144             switch (state) {
145                 case READ_HEADER_REPRESENTATION:
146                     byte b = in.readByte();
147                     if (maxDynamicTableSizeChangeRequired && (b & 0xE0) != 0x20) {
148                         // HpackEncoder MUST signal maximum dynamic table size change
149                         throw MAX_DYNAMIC_TABLE_SIZE_CHANGE_REQUIRED;
150                     }
151                     if (b < 0) {
152                         // Indexed Header Field
153                         index = b & 0x7F;
154                         switch (index) {
155                             case 0:
156                                 throw DECODE_ILLEGAL_INDEX_VALUE;
157                             case 0x7F:
158                                 state = READ_INDEXED_HEADER;
159                                 break;
160                             default:
161                                 HpackHeaderField indexedHeader = getIndexedHeader(index);
162                                 sink.appendToHeaderList(indexedHeader.name, indexedHeader.value);
163                         }
164                     } else if ((b & 0x40) == 0x40) {
165                         // Literal Header Field with Incremental Indexing
166                         indexType = IndexType.INCREMENTAL;
167                         index = b & 0x3F;
168                         switch (index) {
169                             case 0:
170                                 state = READ_LITERAL_HEADER_NAME_LENGTH_PREFIX;
171                                 break;
172                             case 0x3F:
173                                 state = READ_INDEXED_HEADER_NAME;
174                                 break;
175                             default:
176                                 // Index was stored as the prefix
177                                 name = readName(index);
178                                 nameLength = name.length();
179                                 state = READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX;
180                         }
181                     } else if ((b & 0x20) == 0x20) {
182                         // Dynamic Table Size Update
183                         index = b & 0x1F;
184                         if (index == 0x1F) {
185                             state = READ_MAX_DYNAMIC_TABLE_SIZE;
186                         } else {
187                             setDynamicTableSize(index);
188                             state = READ_HEADER_REPRESENTATION;
189                         }
190                     } else {
191                         // Literal Header Field without Indexing / never Indexed
192                         indexType = (b & 0x10) == 0x10 ? IndexType.NEVER : IndexType.NONE;
193                         index = b & 0x0F;
194                         switch (index) {
195                             case 0:
196                                 state = READ_LITERAL_HEADER_NAME_LENGTH_PREFIX;
197                                 break;
198                             case 0x0F:
199                                 state = READ_INDEXED_HEADER_NAME;
200                                 break;
201                             default:
202                                 // Index was stored as the prefix
203                                 name = readName(index);
204                                 nameLength = name.length();
205                                 state = READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX;
206                         }
207                     }
208                     break;
209 
210                 case READ_MAX_DYNAMIC_TABLE_SIZE:
211                     setDynamicTableSize(decodeULE128(in, (long) index));
212                     state = READ_HEADER_REPRESENTATION;
213                     break;
214 
215                 case READ_INDEXED_HEADER:
216                     HpackHeaderField indexedHeader = getIndexedHeader(decodeULE128(in, index));
217                     sink.appendToHeaderList(indexedHeader.name, indexedHeader.value);
218                     state = READ_HEADER_REPRESENTATION;
219                     break;
220 
221                 case READ_INDEXED_HEADER_NAME:
222                     // Header Name matches an entry in the Header Table
223                     name = readName(decodeULE128(in, index));
224                     nameLength = name.length();
225                     state = READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX;
226                     break;
227 
228                 case READ_LITERAL_HEADER_NAME_LENGTH_PREFIX:
229                     b = in.readByte();
230                     huffmanEncoded = (b & 0x80) == 0x80;
231                     index = b & 0x7F;
232                     if (index == 0x7f) {
233                         state = READ_LITERAL_HEADER_NAME_LENGTH;
234                     } else {
235                         nameLength = index;
236                         state = READ_LITERAL_HEADER_NAME;
237                     }
238                     break;
239 
240                 case READ_LITERAL_HEADER_NAME_LENGTH:
241                     // Header Name is a Literal String
242                     nameLength = decodeULE128(in, index);
243 
244                     state = READ_LITERAL_HEADER_NAME;
245                     break;
246 
247                 case READ_LITERAL_HEADER_NAME:
248                     // Wait until entire name is readable
249                     if (in.readableBytes() < nameLength) {
250                         throw notEnoughDataException(in);
251                     }
252 
253                     name = readStringLiteral(in, nameLength, huffmanEncoded);
254 
255                     state = READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX;
256                     break;
257 
258                 case READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX:
259                     b = in.readByte();
260                     huffmanEncoded = (b & 0x80) == 0x80;
261                     index = b & 0x7F;
262                     switch (index) {
263                         case 0x7f:
264                             state = READ_LITERAL_HEADER_VALUE_LENGTH;
265                             break;
266                         case 0:
267                             insertHeader(sink, name, EMPTY_STRING, indexType);
268                             state = READ_HEADER_REPRESENTATION;
269                             break;
270                         default:
271                             valueLength = index;
272                             state = READ_LITERAL_HEADER_VALUE;
273                     }
274 
275                     break;
276 
277                 case READ_LITERAL_HEADER_VALUE_LENGTH:
278                     // Header Value is a Literal String
279                     valueLength = decodeULE128(in, index);
280 
281                     state = READ_LITERAL_HEADER_VALUE;
282                     break;
283 
284                 case READ_LITERAL_HEADER_VALUE:
285                     // Wait until entire value is readable
286                     if (in.readableBytes() < valueLength) {
287                         throw notEnoughDataException(in);
288                     }
289 
290                     CharSequence value = readStringLiteral(in, valueLength, huffmanEncoded);
291                     insertHeader(sink, name, value, indexType);
292                     state = READ_HEADER_REPRESENTATION;
293                     break;
294 
295                 default:
296                     throw new Error("should not reach here state: " + state);
297             }
298         }
299 
300         if (state != READ_HEADER_REPRESENTATION) {
301             throw connectionError(COMPRESSION_ERROR, "Incomplete header block fragment.");
302         }
303     }
304 
305     /**
306      * Set the maximum table size. If this is below the maximum size of the dynamic table used by
307      * the encoder, the beginning of the next header block MUST signal this change.
308      */
309     public void setMaxHeaderTableSize(long maxHeaderTableSize) throws Http2Exception {
310         if (maxHeaderTableSize < MIN_HEADER_TABLE_SIZE || maxHeaderTableSize > MAX_HEADER_TABLE_SIZE) {
311             throw connectionError(PROTOCOL_ERROR, "Header Table Size must be >= %d and <= %d but was %d",
312                     MIN_HEADER_TABLE_SIZE, MAX_HEADER_TABLE_SIZE, maxHeaderTableSize);
313         }
314         maxDynamicTableSize = maxHeaderTableSize;
315         if (maxDynamicTableSize < encoderMaxDynamicTableSize) {
316             // decoder requires less space than encoder
317             // encoder MUST signal this change
318             maxDynamicTableSizeChangeRequired = true;
319             hpackDynamicTable.setCapacity(maxDynamicTableSize);
320         }
321     }
322 
323     /**
324      * @deprecated use {@link #setMaxHeaderListSize(long)}; {@code maxHeaderListSizeGoAway} is
325      *     ignored
326      */
327     @Deprecated
328     public void setMaxHeaderListSize(long maxHeaderListSize, long maxHeaderListSizeGoAway) throws Http2Exception {
329         setMaxHeaderListSize(maxHeaderListSize);
330     }
331 
332     public void setMaxHeaderListSize(long maxHeaderListSize) throws Http2Exception {
333         if (maxHeaderListSize < MIN_HEADER_LIST_SIZE || maxHeaderListSize > MAX_HEADER_LIST_SIZE) {
334             throw connectionError(PROTOCOL_ERROR, "Header List Size must be >= %d and <= %d but was %d",
335                     MIN_HEADER_TABLE_SIZE, MAX_HEADER_TABLE_SIZE, maxHeaderListSize);
336         }
337         this.maxHeaderListSize = maxHeaderListSize;
338     }
339 
340     public long getMaxHeaderListSize() {
341         return maxHeaderListSize;
342     }
343 
344     /**
345      * Return the maximum table size. This is the maximum size allowed by both the encoder and the
346      * decoder.
347      */
348     public long getMaxHeaderTableSize() {
349         return hpackDynamicTable.capacity();
350     }
351 
352     /**
353      * Return the number of header fields in the dynamic table. Exposed for testing.
354      */
355     int length() {
356         return hpackDynamicTable.length();
357     }
358 
359     /**
360      * Return the size of the dynamic table. Exposed for testing.
361      */
362     long size() {
363         return hpackDynamicTable.size();
364     }
365 
366     /**
367      * Return the header field at the given index. Exposed for testing.
368      */
369     HpackHeaderField getHeaderField(int index) {
370         return hpackDynamicTable.getEntry(index + 1);
371     }
372 
373     private void setDynamicTableSize(long dynamicTableSize) throws Http2Exception {
374         if (dynamicTableSize > maxDynamicTableSize) {
375             throw INVALID_MAX_DYNAMIC_TABLE_SIZE;
376         }
377         encoderMaxDynamicTableSize = dynamicTableSize;
378         maxDynamicTableSizeChangeRequired = false;
379         hpackDynamicTable.setCapacity(dynamicTableSize);
380     }
381 
382     private static HeaderType validate(int streamId, CharSequence name,
383                                        HeaderType previousHeaderType, Http2Headers headers) throws Http2Exception {
384         if (hasPseudoHeaderFormat(name)) {
385             if (previousHeaderType == HeaderType.REGULAR_HEADER) {
386                 throw streamError(streamId, PROTOCOL_ERROR,
387                         "Pseudo-header field '%s' found after regular header.", name);
388             }
389 
390             final Http2Headers.PseudoHeaderName pseudoHeader = getPseudoHeader(name);
391             if (pseudoHeader == null) {
392                 throw streamError(streamId, PROTOCOL_ERROR, "Invalid HTTP/2 pseudo-header '%s' encountered.", name);
393             }
394 
395             final HeaderType currentHeaderType = pseudoHeader.isRequestOnly() ?
396                     HeaderType.REQUEST_PSEUDO_HEADER : HeaderType.RESPONSE_PSEUDO_HEADER;
397             if (previousHeaderType != null && currentHeaderType != previousHeaderType) {
398                 throw streamError(streamId, PROTOCOL_ERROR, "Mix of request and response pseudo-headers.");
399             }
400 
401             if (contains(headers, name)) {
402                 throw streamError(streamId, PROTOCOL_ERROR, "Duplicate HTTP/2 pseudo-header '%s' encountered.", name);
403             }
404 
405             return currentHeaderType;
406         }
407 
408         return HeaderType.REGULAR_HEADER;
409     }
410 
411     private static boolean contains(Http2Headers headers, CharSequence name) {
412         if (headers == EmptyHttp2Headers.INSTANCE) {
413             return false;
414         }
415         if (headers instanceof DefaultHttp2Headers || headers instanceof ReadOnlyHttp2Headers) {
416             return headers.contains(name);
417         }
418         // We can't be sure the Http2Headers implementation support contains on pseudo-headers,
419         // so we have to use the direct accessors instead.
420         if (Http2Headers.PseudoHeaderName.METHOD.value().equals(name)) {
421             return headers.method() != null;
422         }
423         if (Http2Headers.PseudoHeaderName.SCHEME.value().equals(name)) {
424             return headers.scheme() != null;
425         }
426         if (Http2Headers.PseudoHeaderName.AUTHORITY.value().equals(name)) {
427             return headers.authority() != null;
428         }
429         if (Http2Headers.PseudoHeaderName.PATH.value().equals(name)) {
430             return headers.path() != null;
431         }
432         if (Http2Headers.PseudoHeaderName.STATUS.value().equals(name)) {
433             return headers.status() != null;
434         }
435         // Note: We don't check PROTOCOL because the API presents no alternative way to access it.
436         return false;
437     }
438 
439     private CharSequence readName(int index) throws Http2Exception {
440         if (index <= HpackStaticTable.length) {
441             HpackHeaderField hpackHeaderField = HpackStaticTable.getEntry(index);
442             return hpackHeaderField.name;
443         }
444         if (index - HpackStaticTable.length <= hpackDynamicTable.length()) {
445             HpackHeaderField hpackHeaderField = hpackDynamicTable.getEntry(index - HpackStaticTable.length);
446             return hpackHeaderField.name;
447         }
448         throw READ_NAME_ILLEGAL_INDEX_VALUE;
449     }
450 
451     private HpackHeaderField getIndexedHeader(int index) throws Http2Exception {
452         if (index <= HpackStaticTable.length) {
453             return HpackStaticTable.getEntry(index);
454         }
455         if (index - HpackStaticTable.length <= hpackDynamicTable.length()) {
456             return hpackDynamicTable.getEntry(index - HpackStaticTable.length);
457         }
458         throw INDEX_HEADER_ILLEGAL_INDEX_VALUE;
459     }
460 
461     private void insertHeader(Sink sink, CharSequence name, CharSequence value, IndexType indexType) {
462         sink.appendToHeaderList(name, value);
463 
464         switch (indexType) {
465             case NONE:
466             case NEVER:
467                 break;
468 
469             case INCREMENTAL:
470                 hpackDynamicTable.add(new HpackHeaderField(name, value));
471                 break;
472 
473             default:
474                 throw new Error("should not reach here");
475         }
476     }
477 
478     private CharSequence readStringLiteral(ByteBuf in, int length, boolean huffmanEncoded) throws Http2Exception {
479         if (huffmanEncoded) {
480             return huffmanDecoder.decode(in, length);
481         }
482         byte[] buf = new byte[length];
483         in.readBytes(buf);
484         return new AsciiString(buf, false);
485     }
486 
487     private static IllegalArgumentException notEnoughDataException(ByteBuf in) {
488         return new IllegalArgumentException("decode only works with an entire header block! " + in);
489     }
490 
491     /**
492      * Unsigned Little Endian Base 128 Variable-Length Integer Encoding
493      * <p>
494      * Visible for testing only!
495      */
496     static int decodeULE128(ByteBuf in, int result) throws Http2Exception {
497         final int readerIndex = in.readerIndex();
498         final long v = decodeULE128(in, (long) result);
499         if (v > Integer.MAX_VALUE) {
500             // the maximum value that can be represented by a signed 32 bit number is:
501             // [0x1,0x7f] + 0x7f + (0x7f << 7) + (0x7f << 14) + (0x7f << 21) + (0x6 << 28)
502             // OR
503             // 0x0 + 0x7f + (0x7f << 7) + (0x7f << 14) + (0x7f << 21) + (0x7 << 28)
504             // we should reset the readerIndex if we overflowed the int type.
505             in.readerIndex(readerIndex);
506             throw DECODE_ULE_128_TO_INT_DECOMPRESSION_EXCEPTION;
507         }
508         return (int) v;
509     }
510 
511     /**
512      * Unsigned Little Endian Base 128 Variable-Length Integer Encoding
513      * <p>
514      * Visible for testing only!
515      */
516     static long decodeULE128(ByteBuf in, long result) throws Http2Exception {
517         assert result <= 0x7f && result >= 0;
518         final boolean resultStartedAtZero = result == 0;
519         final int writerIndex = in.writerIndex();
520         for (int readerIndex = in.readerIndex(), shift = 0; readerIndex < writerIndex; ++readerIndex, shift += 7) {
521             byte b = in.getByte(readerIndex);
522             if (shift == 56 && ((b & 0x80) != 0 || b == 0x7F && !resultStartedAtZero)) {
523                 // the maximum value that can be represented by a signed 64 bit number is:
524                 // [0x01L, 0x7fL] + 0x7fL + (0x7fL << 7) + (0x7fL << 14) + (0x7fL << 21) + (0x7fL << 28) + (0x7fL << 35)
525                 // + (0x7fL << 42) + (0x7fL << 49) + (0x7eL << 56)
526                 // OR
527                 // 0x0L + 0x7fL + (0x7fL << 7) + (0x7fL << 14) + (0x7fL << 21) + (0x7fL << 28) + (0x7fL << 35) +
528                 // (0x7fL << 42) + (0x7fL << 49) + (0x7fL << 56)
529                 // this means any more shifts will result in overflow so we should break out and throw an error.
530                 throw DECODE_ULE_128_TO_LONG_DECOMPRESSION_EXCEPTION;
531             }
532 
533             if ((b & 0x80) == 0) {
534                 in.readerIndex(readerIndex + 1);
535                 return result + ((b & 0x7FL) << shift);
536             }
537             result += (b & 0x7FL) << shift;
538         }
539 
540         throw DECODE_ULE_128_DECOMPRESSION_EXCEPTION;
541     }
542 
543     /**
544      * HTTP/2 header types.
545      */
546     private enum HeaderType {
547         REGULAR_HEADER,
548         REQUEST_PSEUDO_HEADER,
549         RESPONSE_PSEUDO_HEADER
550     }
551 
552     private interface Sink {
553         void appendToHeaderList(CharSequence name, CharSequence value);
554         void finish() throws Http2Exception;
555     }
556 
557     private static final class Http2HeadersSink implements Sink {
558         private final Http2Headers headers;
559         private final long maxHeaderListSize;
560         private final int streamId;
561         private final boolean validate;
562         private long headersLength;
563         private boolean exceededMaxLength;
564         private HeaderType previousType;
565         private Http2Exception validationException;
566 
567         Http2HeadersSink(int streamId, Http2Headers headers, long maxHeaderListSize, boolean validate) {
568             this.headers = headers;
569             this.maxHeaderListSize = maxHeaderListSize;
570             this.streamId = streamId;
571             this.validate = validate;
572         }
573 
574         @Override
575         public void finish() throws Http2Exception {
576             if (exceededMaxLength) {
577                 headerListSizeExceeded(streamId, maxHeaderListSize, true);
578             } else if (validationException != null) {
579                 throw validationException;
580             }
581         }
582 
583         @Override
584         public void appendToHeaderList(CharSequence name, CharSequence value) {
585             headersLength += HpackHeaderField.sizeOf(name, value);
586             exceededMaxLength |= headersLength > maxHeaderListSize;
587 
588             if (exceededMaxLength || validationException != null) {
589                 // We don't store the header since we've already failed validation requirements.
590                 return;
591             }
592 
593             if (validate) {
594                 try {
595                     previousType = validate(streamId, name, previousType, headers);
596                 } catch (Http2Exception ex) {
597                     validationException = ex;
598                     return;
599                 }
600             }
601             headers.add(name, value);
602         }
603     }
604 }