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