View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  
28  package org.apache.hc.core5.http2.hpack;
29  
30  import java.nio.ByteBuffer;
31  import java.nio.charset.Charset;
32  import java.nio.charset.StandardCharsets;
33  import java.util.Arrays;
34  import java.util.List;
35  
36  import org.apache.hc.core5.http.Header;
37  import org.apache.hc.core5.http.message.BasicHeader;
38  import org.apache.hc.core5.util.ByteArrayBuffer;
39  import org.hamcrest.CoreMatchers;
40  import org.junit.Assert;
41  import org.junit.Test;
42  
43  public class TestHPackCoding {
44  
45      @Test
46      public void testIntegerEncodingRFC7541Examples() throws Exception {
47  
48          final ByteArrayBuffer buffer = new ByteArrayBuffer(16);
49          HPackEncoder.encodeInt(buffer, 5, 10, 0x0);
50  
51          Assert.assertEquals(1, buffer.length());
52          Assert.assertEquals(0b00001010, buffer.byteAt(0) & 0xFF);
53  
54          buffer.clear();
55          HPackEncoder.encodeInt(buffer, 5, 1337, 0x0);
56  
57          Assert.assertEquals(3, buffer.length());
58          Assert.assertEquals(0b00011111, buffer.byteAt(0) & 0xFF);
59          Assert.assertEquals(0b10011010, buffer.byteAt(1) & 0xFF);
60          Assert.assertEquals(0b00001010, buffer.byteAt(2) & 0xFF);
61  
62          buffer.clear();
63          HPackEncoder.encodeInt(buffer, 8, 42, 0x0);
64          Assert.assertEquals(1, buffer.length());
65          Assert.assertEquals(0b00101010, buffer.byteAt(0) & 0xFF);
66      }
67  
68      static ByteBuffer wrap(final ByteArrayBuffer src) {
69          // Use buffers with array offsets to verify correcness in additional cases
70          final byte[] originalArray = src.array();
71          final byte[] newArray = new byte[originalArray.length + 2];
72          System.arraycopy(originalArray, 0, newArray, 1, src.length());
73          return ByteBuffer.wrap(newArray, 1, src.length()).slice();
74      }
75  
76      private static byte[] toArray(final ByteBuffer buffer) {
77          final byte[] result = new byte[buffer.remaining()];
78          buffer.get(result);
79          return result;
80      }
81  
82      @Test
83      public void testIntegerCoding() throws Exception {
84  
85          final ByteArrayBuffer buffer = new ByteArrayBuffer(16);
86  
87          for (int n = 4; n <= 8; n++) {
88  
89              buffer.clear();
90  
91              HPackEncoder.encodeInt(buffer, n, 10, 0x0);
92              Assert.assertEquals(10, HPackDecoder.decodeInt(wrap(buffer), n));
93  
94              buffer.clear();
95  
96              HPackEncoder.encodeInt(buffer, n, 123456, 0x0);
97              Assert.assertEquals(123456, HPackDecoder.decodeInt(wrap(buffer), n));
98  
99              buffer.clear();
100 
101             HPackEncoder.encodeInt(buffer, n, Integer.MAX_VALUE, 0x0);
102             Assert.assertEquals(Integer.MAX_VALUE, HPackDecoder.decodeInt(wrap(buffer), n));
103         }
104 
105     }
106 
107     @Test
108     public void testIntegerCodingLimit() throws Exception {
109 
110         final ByteBuffer src1 = createByteBuffer(0x7f, 0x80, 0xff, 0xff, 0xff, 0x07);
111         Assert.assertEquals(Integer.MAX_VALUE, HPackDecoder.decodeInt(src1, 7));
112 
113         final ByteBuffer src2 = createByteBuffer(0x7f, 0x80, 0xff, 0xff, 0xff, 0x08);
114         try {
115             HPackDecoder.decodeInt(src2, 7);
116         } catch (final HPackException expected) {
117         }
118         final ByteBuffer src3 = createByteBuffer(0x7f, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01);
119         try {
120             HPackDecoder.decodeInt(src3, 7);
121         } catch (final HPackException expected) {
122         }
123     }
124 
125     private static ByteBuffer createByteBuffer(final int... bytes) {
126 
127         final ByteBuffer buffer = ByteBuffer.allocate(bytes.length);
128         for (final int b : bytes) {
129             buffer.put((byte) b);
130         }
131         buffer.flip();
132         return buffer;
133     }
134 
135     @Test
136     public void testPlainStringDecoding() throws Exception {
137 
138         final ByteBuffer src = createByteBuffer(
139                 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, 0x79);
140 
141         final ByteArrayBuffer buffer = new ByteArrayBuffer(16);
142         HPackDecoder.decodePlainString(buffer, src);
143         Assert.assertEquals("custom-key", new String(buffer.array(), 0, buffer.length(), StandardCharsets.US_ASCII));
144         Assert.assertFalse("Decoding completed", src.hasRemaining());
145     }
146 
147     @Test
148     public void testPlainStringDecodingRemainingContent() throws Exception {
149 
150         final ByteBuffer src = createByteBuffer(
151                 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, 0x79, 0x01, 0x01, 0x01, 0x01);
152 
153         final ByteArrayBuffer buffer = new ByteArrayBuffer(16);
154         HPackDecoder.decodePlainString(buffer, src);
155         Assert.assertEquals("custom-key", new String(buffer.array(), 0, buffer.length(), StandardCharsets.US_ASCII));
156         Assert.assertEquals(4, src.remaining());
157     }
158 
159     @Test
160     public void testPlainStringDecodingReadOnly() throws Exception {
161 
162         final ByteBuffer src = createByteBuffer(
163                 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, 0x79, 0x50, 0x50, 0x50, 0x50);
164 
165         final ByteBuffer srcRO = src.asReadOnlyBuffer();
166         final ByteArrayBuffer buffer = new ByteArrayBuffer(16);
167         HPackDecoder.decodePlainString(buffer, srcRO);
168         Assert.assertEquals("custom-key", new String(buffer.array(), 0, buffer.length(), StandardCharsets.US_ASCII));
169         Assert.assertEquals(4, srcRO.remaining());
170     }
171 
172     @Test(expected = HPackException.class)
173     public void testPlainStringDecodingTruncated() throws Exception {
174 
175         final ByteBuffer src = createByteBuffer(
176                 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65);
177 
178         final ByteArrayBuffer buffer = new ByteArrayBuffer(16);
179         HPackDecoder.decodePlainString(buffer, src);
180     }
181 
182     @Test
183     public void testHuffmanDecodingRFC7541Examples() throws Exception {
184         final ByteBuffer src = createByteBuffer(
185                 0x8c, 0xf1, 0xe3, 0xc2, 0xe5, 0xf2, 0x3a, 0x6b, 0xa0, 0xab, 0x90, 0xf4, 0xff);
186 
187         final ByteArrayBuffer buffer = new ByteArrayBuffer(16);
188         HPackDecoder.decodeHuffman(buffer, src);
189         Assert.assertEquals("www.example.com", new String(buffer.array(), 0, buffer.length(), StandardCharsets.US_ASCII));
190         Assert.assertFalse("Decoding completed", src.hasRemaining());
191     }
192 
193     private static ByteBuffer createByteBuffer(final String s, final Charset charset) {
194 
195         return ByteBuffer.wrap(s.getBytes(charset));
196     }
197 
198     @Test
199     public void testHuffmanEncoding() throws Exception {
200         final ByteArrayBuffer buffer = new ByteArrayBuffer(16);
201         HPackEncoder.encodeHuffman(buffer, createByteBuffer("www.example.com", StandardCharsets.US_ASCII));
202         final ByteBuffer expected = createByteBuffer(
203                 0xf1, 0xe3, 0xc2, 0xe5, 0xf2, 0x3a, 0x6b, 0xa0, 0xab, 0x90, 0xf4, 0xff);
204         Assert.assertArrayEquals(toArray(expected), buffer.toByteArray());
205     }
206 
207     @Test
208     public void testBasicStringCoding() throws Exception {
209 
210         final HPackEncoder encoder = new HPackEncoder(StandardCharsets.US_ASCII);
211         final HPackDecoder decoder = new HPackDecoder(StandardCharsets.US_ASCII);
212 
213         final ByteArrayBuffer buffer = new ByteArrayBuffer(16);
214         encoder.encodeString(buffer, "this and that", false);
215 
216         final StringBuilder strBuf = new StringBuilder();
217         decoder.decodeString(wrap(buffer), strBuf);
218         Assert.assertEquals("this and that", strBuf.toString());
219 
220         buffer.clear();
221         strBuf.setLength(0);
222         encoder.encodeString(buffer, "this and that and Huffman", true);
223         decoder.decodeString(wrap(buffer), strBuf);
224         Assert.assertEquals("this and that and Huffman", strBuf.toString());
225     }
226 
227     static final int SWISS_GERMAN_HELLO[] = {
228             0x47, 0x72, 0xFC, 0x65, 0x7A, 0x69, 0x5F, 0x7A, 0xE4, 0x6D, 0xE4
229     };
230 
231     static final int RUSSIAN_HELLO[] = {
232             0x412, 0x441, 0x435, 0x43C, 0x5F, 0x43F, 0x440, 0x438,
233             0x432, 0x435, 0x442
234     };
235 
236     private static String constructHelloString(final int[] raw, final int n) {
237         final StringBuilder buffer = new StringBuilder();
238         for (int j = 0; j < n; j++) {
239             if (j > 0) {
240                 buffer.append("; ");
241             }
242             for (int i = 0; i < raw.length; i++) {
243                 buffer.append((char) raw[i]);
244             }
245         }
246         return buffer.toString();
247     }
248 
249     @Test
250     public void testComplexStringCoding1() throws Exception {
251 
252         for (final Charset charset : new Charset[]{StandardCharsets.ISO_8859_1, StandardCharsets.UTF_8, StandardCharsets.UTF_16}) {
253 
254             final ByteArrayBuffer buffer = new ByteArrayBuffer(16);
255             final StringBuilder strBuf = new StringBuilder();
256 
257             final HPackEncoder encoder = new HPackEncoder(charset);
258             final HPackDecoder decoder = new HPackDecoder(charset);
259 
260             for (int n = 0; n < 10; n++) {
261 
262                 final String hello = constructHelloString(SWISS_GERMAN_HELLO, 1 + 10 * n);
263 
264                 for (final boolean b : new boolean[]{false, true}) {
265 
266                     buffer.clear();
267                     encoder.encodeString(buffer, hello, b);
268                     strBuf.setLength(0);
269                     decoder.decodeString(wrap(buffer), strBuf);
270                     final String helloBack = strBuf.toString();
271                     Assert.assertEquals("charset: " + charset + "; huffman: " + b, hello, helloBack);
272                 }
273             }
274         }
275     }
276 
277     @Test
278     public void testComplexStringCoding2() throws Exception {
279 
280         for (final Charset charset : new Charset[]{Charset.forName("KOI8-R"), StandardCharsets.UTF_8, StandardCharsets.UTF_16}) {
281 
282             final ByteArrayBuffer buffer = new ByteArrayBuffer(16);
283             final StringBuilder strBuf = new StringBuilder();
284 
285             final HPackEncoder encoder = new HPackEncoder(charset);
286             final HPackDecoder decoder = new HPackDecoder(charset);
287 
288             for (int n = 0; n < 10; n++) {
289 
290                 final String hello = constructHelloString(RUSSIAN_HELLO, 1 + 10 * n);
291 
292                 for (final boolean b : new boolean[]{false, true}) {
293 
294                     buffer.clear();
295                     strBuf.setLength(0);
296                     encoder.encodeString(buffer, hello, b);
297                     decoder.decodeString(wrap(buffer), strBuf);
298                     final String helloBack = strBuf.toString();
299                     Assert.assertEquals("charset: " + charset + "; huffman: " + b, hello, helloBack);
300                 }
301             }
302         }
303     }
304 
305     private static void assertHeaderEquals(final Header expected, final Header actual) {
306 
307         Assert.assertNotNull(actual);
308         Assert.assertEquals("Header name", expected.getName(), actual.getName());
309         Assert.assertEquals("Header value", expected.getValue(), actual.getValue());
310         Assert.assertEquals("Header sensitive flag", expected.isSensitive(), actual.isSensitive());
311     }
312 
313     @Test
314     public void testLiteralHeaderWithIndexingDecodingRFC7541Examples() throws Exception {
315 
316         final ByteBuffer src = createByteBuffer(
317                 0x40, 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, 0x79, 0x0d, 0x63, 0x75, 0x73,
318                 0x74, 0x6f, 0x6d, 0x2d, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72);
319 
320         final InboundDynamicTable dynamicTable = new InboundDynamicTable();
321         final HPackDecoder decoder = new HPackDecoder(dynamicTable, StandardCharsets.US_ASCII);
322         final Header header = decoder.decodeLiteralHeader(src, HPackRepresentation.WITH_INDEXING);
323         assertHeaderEquals(new BasicHeader("custom-key", "custom-header"), header);
324         Assert.assertFalse("Decoding completed", src.hasRemaining());
325 
326         Assert.assertEquals(1, dynamicTable.dynamicLength());
327         assertHeaderEquals(header, dynamicTable.getDynamicEntry(0));
328     }
329 
330     @Test
331     public void testLiteralHeaderWithoutIndexingDecodingRFC7541Examples() throws Exception {
332 
333         final ByteBuffer src = createByteBuffer(
334                 0x04, 0x0c, 0x2f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2f, 0x70, 0x61, 0x74, 0x68);
335 
336         final InboundDynamicTable dynamicTable = new InboundDynamicTable();
337         final HPackDecoder decoder = new HPackDecoder(dynamicTable, StandardCharsets.US_ASCII);
338         final Header header = decoder.decodeLiteralHeader(src, HPackRepresentation.WITHOUT_INDEXING);
339         assertHeaderEquals(new BasicHeader(":path", "/sample/path"), header);
340         Assert.assertFalse("Decoding completed", src.hasRemaining());
341 
342         Assert.assertEquals(0, dynamicTable.dynamicLength());
343     }
344 
345     @Test
346     public void testLiteralHeaderNeverIndexedDecodingRFC7541Examples() throws Exception {
347 
348         final ByteBuffer src = createByteBuffer(
349                 0x10, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74);
350 
351         final InboundDynamicTable dynamicTable = new InboundDynamicTable();
352         final HPackDecoder decoder = new HPackDecoder(dynamicTable, StandardCharsets.US_ASCII);
353         final Header header = decoder.decodeLiteralHeader(src, HPackRepresentation.NEVER_INDEXED);
354         assertHeaderEquals(new BasicHeader("password", "secret", true), header);
355         Assert.assertFalse("Decoding completed", src.hasRemaining());
356 
357         Assert.assertEquals(0, dynamicTable.dynamicLength());
358     }
359 
360     @Test
361     public void testIndexedHeaderDecodingRFC7541Examples() throws Exception {
362 
363         final ByteBuffer src = createByteBuffer(0x82);
364 
365         final InboundDynamicTable dynamicTable = new InboundDynamicTable();
366         final HPackDecoder decoder = new HPackDecoder(dynamicTable, StandardCharsets.US_ASCII);
367         final Header header = decoder.decodeIndexedHeader(src);
368         assertHeaderEquals(new BasicHeader(":method", "GET"), header);
369         Assert.assertFalse("Decoding completed", src.hasRemaining());
370 
371         Assert.assertEquals(0, dynamicTable.dynamicLength());
372     }
373 
374     @Test
375     public void testRequestDecodingWithoutHuffmanRFC7541Examples() throws Exception {
376 
377         final ByteBuffer src1 = createByteBuffer(
378                 0x82, 0x86, 0x84, 0x41, 0x0f, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e,
379                 0x63, 0x6f, 0x6d);
380 
381         final InboundDynamicTable dynamicTable = new InboundDynamicTable();
382         final HPackDecoder decoder = new HPackDecoder(dynamicTable, StandardCharsets.US_ASCII);
383         final List<Header> headers1 = decoder.decodeHeaders(src1);
384 
385         Assert.assertEquals(4, headers1.size());
386         assertHeaderEquals(new BasicHeader(":method", "GET"), headers1.get(0));
387         assertHeaderEquals(new BasicHeader(":scheme", "http"), headers1.get(1));
388         assertHeaderEquals(new BasicHeader(":path", "/"), headers1.get(2));
389         assertHeaderEquals(new BasicHeader(":authority", "www.example.com"), headers1.get(3));
390 
391         Assert.assertEquals(1, dynamicTable.dynamicLength());
392         assertHeaderEquals(new BasicHeader(":authority", "www.example.com"), dynamicTable.getDynamicEntry(0));
393         Assert.assertEquals(57, dynamicTable.getCurrentSize());
394 
395         final ByteBuffer src2 = createByteBuffer(
396                 0x82, 0x86, 0x84, 0xbe, 0x58, 0x08, 0x6e, 0x6f, 0x2d, 0x63, 0x61, 0x63, 0x68, 0x65);
397 
398         final List<Header> headers2 = decoder.decodeHeaders(src2);
399 
400         Assert.assertEquals(5, headers2.size());
401         assertHeaderEquals(new BasicHeader(":method", "GET"), headers2.get(0));
402         assertHeaderEquals(new BasicHeader(":scheme", "http"), headers2.get(1));
403         assertHeaderEquals(new BasicHeader(":path", "/"), headers2.get(2));
404         assertHeaderEquals(new BasicHeader(":authority", "www.example.com"), headers2.get(3));
405         assertHeaderEquals(new BasicHeader("cache-control", "no-cache"), headers2.get(4));
406 
407         Assert.assertEquals(2, dynamicTable.dynamicLength());
408         assertHeaderEquals(new BasicHeader("cache-control", "no-cache"), dynamicTable.getDynamicEntry(0));
409         assertHeaderEquals(new BasicHeader(":authority", "www.example.com"), dynamicTable.getDynamicEntry(1));
410         Assert.assertEquals(110, dynamicTable.getCurrentSize());
411 
412         final ByteBuffer src3 = createByteBuffer(
413                 0x82, 0x87, 0x85, 0xbf, 0x40, 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, 0x79,
414                 0x0c, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x76, 0x61, 0x6c, 0x75, 0x65);
415 
416         final List<Header> headers3 = decoder.decodeHeaders(src3);
417 
418         Assert.assertEquals(5, headers3.size());
419         assertHeaderEquals(new BasicHeader(":method", "GET"), headers3.get(0));
420         assertHeaderEquals(new BasicHeader(":scheme", "https"), headers3.get(1));
421         assertHeaderEquals(new BasicHeader(":path", "/index.html"), headers3.get(2));
422         assertHeaderEquals(new BasicHeader(":authority", "www.example.com"), headers3.get(3));
423         assertHeaderEquals(new BasicHeader("custom-key", "custom-value"), headers3.get(4));
424 
425         Assert.assertEquals(3, dynamicTable.dynamicLength());
426         assertHeaderEquals(new BasicHeader("custom-key", "custom-value"), dynamicTable.getDynamicEntry(0));
427         assertHeaderEquals(new BasicHeader("cache-control", "no-cache"), dynamicTable.getDynamicEntry(1));
428         assertHeaderEquals(new BasicHeader(":authority", "www.example.com"), dynamicTable.getDynamicEntry(2));
429         Assert.assertEquals(164, dynamicTable.getCurrentSize());
430     }
431 
432     @Test
433     public void testRequestDecodingWithHuffmanRFC7541Examples() throws Exception {
434 
435         final ByteBuffer src1 = createByteBuffer(
436                 0x82, 0x86, 0x84, 0x41, 0x8c, 0xf1, 0xe3, 0xc2, 0xe5, 0xf2, 0x3a, 0x6b, 0xa0, 0xab, 0x90, 0xf4, 0xff);
437 
438         final InboundDynamicTable dynamicTable = new InboundDynamicTable();
439         final HPackDecoder decoder = new HPackDecoder(dynamicTable, StandardCharsets.US_ASCII);
440         final List<Header> headers1 = decoder.decodeHeaders(src1);
441 
442         Assert.assertEquals(4, headers1.size());
443         assertHeaderEquals(new BasicHeader(":method", "GET"), headers1.get(0));
444         assertHeaderEquals(new BasicHeader(":scheme", "http"), headers1.get(1));
445         assertHeaderEquals(new BasicHeader(":path", "/"), headers1.get(2));
446         assertHeaderEquals(new BasicHeader(":authority", "www.example.com"), headers1.get(3));
447 
448         Assert.assertEquals(1, dynamicTable.dynamicLength());
449         assertHeaderEquals(new BasicHeader(":authority", "www.example.com"), dynamicTable.getDynamicEntry(0));
450         Assert.assertEquals(57, dynamicTable.getCurrentSize());
451 
452         final ByteBuffer src2 = createByteBuffer(
453                 0x82, 0x86, 0x84, 0xbe, 0x58, 0x86, 0xa8, 0xeb, 0x10, 0x64, 0x9c, 0xbf);
454 
455         final List<Header> headers2 = decoder.decodeHeaders(src2);
456 
457         Assert.assertEquals(5, headers2.size());
458         assertHeaderEquals(new BasicHeader(":method", "GET"), headers2.get(0));
459         assertHeaderEquals(new BasicHeader(":scheme", "http"), headers2.get(1));
460         assertHeaderEquals(new BasicHeader(":path", "/"), headers2.get(2));
461         assertHeaderEquals(new BasicHeader(":authority", "www.example.com"), headers2.get(3));
462         assertHeaderEquals(new BasicHeader("cache-control", "no-cache"), headers2.get(4));
463 
464         Assert.assertEquals(2, dynamicTable.dynamicLength());
465         assertHeaderEquals(new BasicHeader("cache-control", "no-cache"), dynamicTable.getDynamicEntry(0));
466         assertHeaderEquals(new BasicHeader(":authority", "www.example.com"), dynamicTable.getDynamicEntry(1));
467         Assert.assertEquals(110, dynamicTable.getCurrentSize());
468 
469         final ByteBuffer src3 = createByteBuffer(
470                 0x82, 0x87, 0x85, 0xbf, 0x40, 0x88, 0x25, 0xa8, 0x49, 0xe9, 0x5b, 0xa9, 0x7d, 0x7f, 0x89, 0x25,
471                 0xa8, 0x49, 0xe9, 0x5b, 0xb8, 0xe8, 0xb4, 0xbf);
472 
473         final List<Header> headers3 = decoder.decodeHeaders(src3);
474 
475         Assert.assertEquals(5, headers3.size());
476         assertHeaderEquals(new BasicHeader(":method", "GET"), headers3.get(0));
477         assertHeaderEquals(new BasicHeader(":scheme", "https"), headers3.get(1));
478         assertHeaderEquals(new BasicHeader(":path", "/index.html"), headers3.get(2));
479         assertHeaderEquals(new BasicHeader(":authority", "www.example.com"), headers3.get(3));
480         assertHeaderEquals(new BasicHeader("custom-key", "custom-value"), headers3.get(4));
481 
482         Assert.assertEquals(3, dynamicTable.dynamicLength());
483         assertHeaderEquals(new BasicHeader("custom-key", "custom-value"), dynamicTable.getDynamicEntry(0));
484         assertHeaderEquals(new BasicHeader("cache-control", "no-cache"), dynamicTable.getDynamicEntry(1));
485         assertHeaderEquals(new BasicHeader(":authority", "www.example.com"), dynamicTable.getDynamicEntry(2));
486         Assert.assertEquals(164, dynamicTable.getCurrentSize());
487     }
488 
489     @Test
490     public void testResponseDecodingWithoutHuffmanRFC7541Examples() throws Exception {
491 
492         final ByteBuffer src1 = createByteBuffer(
493                 0x48, 0x03, 0x33, 0x30, 0x32, 0x58, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x61, 0x1d, 0x4d,
494                 0x6f, 0x6e, 0x2c, 0x20, 0x32, 0x31, 0x20, 0x4f, 0x63, 0x74, 0x20, 0x32, 0x30, 0x31, 0x33, 0x20, 0x32,
495                 0x30, 0x3a, 0x31, 0x33, 0x3a, 0x32, 0x31, 0x20, 0x47, 0x4d, 0x54, 0x6e, 0x17, 0x68, 0x74, 0x74, 0x70,
496                 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63,
497                 0x6f, 0x6d);
498 
499         final InboundDynamicTable dynamicTable = new InboundDynamicTable();
500         dynamicTable.setMaxSize(256);
501         final HPackDecoder decoder = new HPackDecoder(dynamicTable, StandardCharsets.US_ASCII);
502         final List<Header> headers1 = decoder.decodeHeaders(src1);
503 
504         Assert.assertEquals(4, headers1.size());
505         assertHeaderEquals(new BasicHeader(":status", "302"), headers1.get(0));
506         assertHeaderEquals(new BasicHeader("cache-control", "private"), headers1.get(1));
507         assertHeaderEquals(new BasicHeader("date", "Mon, 21 Oct 2013 20:13:21 GMT"), headers1.get(2));
508         assertHeaderEquals(new BasicHeader("location", "https://www.example.com"), headers1.get(3));
509 
510         Assert.assertEquals(4, dynamicTable.dynamicLength());
511         assertHeaderEquals(new BasicHeader("location", "https://www.example.com"), dynamicTable.getDynamicEntry(0));
512         assertHeaderEquals(new BasicHeader("date", "Mon, 21 Oct 2013 20:13:21 GMT"), dynamicTable.getDynamicEntry(1));
513         assertHeaderEquals(new BasicHeader("cache-control", "private"), dynamicTable.getDynamicEntry(2));
514         assertHeaderEquals(new BasicHeader(":status", "302"), dynamicTable.getDynamicEntry(3));
515         Assert.assertEquals(222, dynamicTable.getCurrentSize());
516 
517         final ByteBuffer src2 = createByteBuffer(
518                 0x48, 0x03, 0x33, 0x30, 0x37, 0xc1, 0xc0, 0xbf);
519 
520         final List<Header> headers2 = decoder.decodeHeaders(src2);
521 
522         Assert.assertEquals(4, headers2.size());
523         assertHeaderEquals(new BasicHeader(":status", "307"), headers2.get(0));
524         assertHeaderEquals(new BasicHeader("cache-control", "private"), headers2.get(1));
525         assertHeaderEquals(new BasicHeader("date", "Mon, 21 Oct 2013 20:13:21 GMT"), headers2.get(2));
526         assertHeaderEquals(new BasicHeader("location", "https://www.example.com"), headers2.get(3));
527 
528         Assert.assertEquals(4, dynamicTable.dynamicLength());
529         assertHeaderEquals(new BasicHeader(":status", "307"), dynamicTable.getDynamicEntry(0));
530         assertHeaderEquals(new BasicHeader("location", "https://www.example.com"), dynamicTable.getDynamicEntry(1));
531         assertHeaderEquals(new BasicHeader("date", "Mon, 21 Oct 2013 20:13:21 GMT"), dynamicTable.getDynamicEntry(2));
532         assertHeaderEquals(new BasicHeader("cache-control", "private"), dynamicTable.getDynamicEntry(3));
533 
534         Assert.assertEquals(222, dynamicTable.getCurrentSize());
535 
536         final ByteBuffer src3 = createByteBuffer(
537                 0x88, 0xc1, 0x61, 0x1d, 0x4d, 0x6f, 0x6e, 0x2c, 0x20, 0x32, 0x31, 0x20, 0x4f, 0x63, 0x74, 0x20, 0x32,
538                 0x30, 0x31, 0x33, 0x20, 0x32, 0x30, 0x3a, 0x31, 0x33, 0x3a, 0x32, 0x32, 0x20, 0x47, 0x4d, 0x54, 0xc0,
539                 0x5a, 0x04, 0x67, 0x7a, 0x69, 0x70, 0x77, 0x38, 0x66, 0x6f, 0x6f, 0x3d, 0x41, 0x53, 0x44, 0x4a, 0x4b,
540                 0x48, 0x51, 0x4b, 0x42, 0x5a, 0x58, 0x4f, 0x51, 0x57, 0x45, 0x4f, 0x50, 0x49, 0x55, 0x41, 0x58, 0x51,
541                 0x57, 0x45, 0x4f, 0x49, 0x55, 0x3b, 0x20, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65, 0x3d, 0x33, 0x36,
542                 0x30, 0x30, 0x3b, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31);
543 
544         final List<Header> headers3 = decoder.decodeHeaders(src3);
545 
546         Assert.assertEquals(6, headers3.size());
547         assertHeaderEquals(new BasicHeader(":status", "200"), headers3.get(0));
548         assertHeaderEquals(new BasicHeader("cache-control", "private"), headers3.get(1));
549         assertHeaderEquals(new BasicHeader("date", "Mon, 21 Oct 2013 20:13:22 GMT"), headers3.get(2));
550         assertHeaderEquals(new BasicHeader("location", "https://www.example.com"), headers3.get(3));
551         assertHeaderEquals(new BasicHeader("content-encoding", "gzip"), headers3.get(4));
552         assertHeaderEquals(new BasicHeader("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"), headers3.get(5));
553 
554         Assert.assertEquals(3, dynamicTable.dynamicLength());
555         assertHeaderEquals(new BasicHeader("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"), dynamicTable.getDynamicEntry(0));
556         assertHeaderEquals(new BasicHeader("content-encoding", "gzip"), dynamicTable.getDynamicEntry(1));
557         assertHeaderEquals(new BasicHeader("date", "Mon, 21 Oct 2013 20:13:22 GMT"), dynamicTable.getDynamicEntry(2));
558 
559         Assert.assertEquals(215, dynamicTable.getCurrentSize());
560     }
561 
562     @Test
563     public void testResponseDecodingWithHuffmanRFC7541Examples() throws Exception {
564 
565         final ByteBuffer src1 = createByteBuffer(
566                 0x48, 0x82, 0x64, 0x02, 0x58, 0x85, 0xae, 0xc3, 0x77, 0x1a, 0x4b, 0x61, 0x96, 0xd0, 0x7a, 0xbe, 0x94,
567                 0x10, 0x54, 0xd4, 0x44, 0xa8, 0x20, 0x05, 0x95, 0x04, 0x0b, 0x81, 0x66, 0xe0, 0x82, 0xa6, 0x2d, 0x1b,
568                 0xff, 0x6e, 0x91, 0x9d, 0x29, 0xad, 0x17, 0x18, 0x63, 0xc7, 0x8f, 0x0b, 0x97, 0xc8, 0xe9, 0xae, 0x82,
569                 0xae, 0x43, 0xd3);
570 
571         final InboundDynamicTable dynamicTable = new InboundDynamicTable();
572         dynamicTable.setMaxSize(256);
573         final HPackDecoder decoder = new HPackDecoder(dynamicTable, StandardCharsets.US_ASCII);
574         final List<Header> headers1 = decoder.decodeHeaders(src1);
575 
576         Assert.assertEquals(4, headers1.size());
577         assertHeaderEquals(new BasicHeader(":status", "302"), headers1.get(0));
578         assertHeaderEquals(new BasicHeader("cache-control", "private"), headers1.get(1));
579         assertHeaderEquals(new BasicHeader("date", "Mon, 21 Oct 2013 20:13:21 GMT"), headers1.get(2));
580         assertHeaderEquals(new BasicHeader("location", "https://www.example.com"), headers1.get(3));
581 
582         Assert.assertEquals(4, dynamicTable.dynamicLength());
583         assertHeaderEquals(new BasicHeader("location", "https://www.example.com"), dynamicTable.getDynamicEntry(0));
584         assertHeaderEquals(new BasicHeader("date", "Mon, 21 Oct 2013 20:13:21 GMT"), dynamicTable.getDynamicEntry(1));
585         assertHeaderEquals(new BasicHeader("cache-control", "private"), dynamicTable.getDynamicEntry(2));
586         assertHeaderEquals(new BasicHeader(":status", "302"), dynamicTable.getDynamicEntry(3));
587         Assert.assertEquals(222, dynamicTable.getCurrentSize());
588 
589         final ByteBuffer src2 = createByteBuffer(
590                 0x48, 0x83, 0x64, 0x0e, 0xff, 0xc1, 0xc0, 0xbf);
591 
592         final List<Header> headers2 = decoder.decodeHeaders(src2);
593 
594         Assert.assertEquals(4, headers2.size());
595         assertHeaderEquals(new BasicHeader(":status", "307"), headers2.get(0));
596         assertHeaderEquals(new BasicHeader("cache-control", "private"), headers2.get(1));
597         assertHeaderEquals(new BasicHeader("date", "Mon, 21 Oct 2013 20:13:21 GMT"), headers2.get(2));
598         assertHeaderEquals(new BasicHeader("location", "https://www.example.com"), headers2.get(3));
599 
600         Assert.assertEquals(4, dynamicTable.dynamicLength());
601         assertHeaderEquals(new BasicHeader(":status", "307"), dynamicTable.getDynamicEntry(0));
602         assertHeaderEquals(new BasicHeader("location", "https://www.example.com"), dynamicTable.getDynamicEntry(1));
603         assertHeaderEquals(new BasicHeader("date", "Mon, 21 Oct 2013 20:13:21 GMT"), dynamicTable.getDynamicEntry(2));
604         assertHeaderEquals(new BasicHeader("cache-control", "private"), dynamicTable.getDynamicEntry(3));
605 
606         Assert.assertEquals(222, dynamicTable.getCurrentSize());
607 
608         final ByteBuffer src3 = createByteBuffer(
609                 0x88, 0xc1, 0x61, 0x96, 0xd0, 0x7a, 0xbe, 0x94, 0x10, 0x54, 0xd4, 0x44, 0xa8, 0x20, 0x05, 0x95, 0x04,
610                 0x0b, 0x81, 0x66, 0xe0, 0x84, 0xa6, 0x2d, 0x1b, 0xff, 0xc0, 0x5a, 0x83, 0x9b, 0xd9, 0xab, 0x77, 0xad,
611                 0x94, 0xe7, 0x82, 0x1d, 0xd7, 0xf2, 0xe6, 0xc7, 0xb3, 0x35, 0xdf, 0xdf, 0xcd, 0x5b, 0x39, 0x60, 0xd5,
612                 0xaf, 0x27, 0x08, 0x7f, 0x36, 0x72, 0xc1, 0xab, 0x27, 0x0f, 0xb5, 0x29, 0x1f, 0x95, 0x87, 0x31, 0x60,
613                 0x65, 0xc0, 0x03, 0xed, 0x4e, 0xe5, 0xb1, 0x06, 0x3d, 0x50, 0x07);
614 
615         final List<Header> headers3 = decoder.decodeHeaders(src3);
616 
617         Assert.assertEquals(6, headers3.size());
618         assertHeaderEquals(new BasicHeader(":status", "200"), headers3.get(0));
619         assertHeaderEquals(new BasicHeader("cache-control", "private"), headers3.get(1));
620         assertHeaderEquals(new BasicHeader("date", "Mon, 21 Oct 2013 20:13:22 GMT"), headers3.get(2));
621         assertHeaderEquals(new BasicHeader("location", "https://www.example.com"), headers3.get(3));
622         assertHeaderEquals(new BasicHeader("content-encoding", "gzip"), headers3.get(4));
623         assertHeaderEquals(new BasicHeader("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"), headers3.get(5));
624 
625         Assert.assertEquals(3, dynamicTable.dynamicLength());
626         assertHeaderEquals(new BasicHeader("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"), dynamicTable.getDynamicEntry(0));
627         assertHeaderEquals(new BasicHeader("content-encoding", "gzip"), dynamicTable.getDynamicEntry(1));
628         assertHeaderEquals(new BasicHeader("date", "Mon, 21 Oct 2013 20:13:22 GMT"), dynamicTable.getDynamicEntry(2));
629 
630         Assert.assertEquals(215, dynamicTable.getCurrentSize());
631     }
632 
633     private static byte[] createByteArray(final int... bytes) {
634         final byte[] buffer = new byte[bytes.length];
635         for (int i = 0; i < bytes.length; i++) {
636             buffer[i] = (byte) bytes[i];
637         }
638         return buffer;
639     }
640 
641     @Test
642     public void testLiteralHeaderWithIndexingEncodingRFC7541Examples() throws Exception {
643 
644         final OutboundDynamicTable dynamicTable = new OutboundDynamicTable();
645         final HPackEncoder encoder = new HPackEncoder(dynamicTable, StandardCharsets.US_ASCII);
646 
647         final ByteArrayBuffer buf = new ByteArrayBuffer(128);
648 
649         final Header header = new BasicHeader("custom-key", "custom-header");
650         encoder.encodeLiteralHeader(buf, null, header, HPackRepresentation.WITH_INDEXING, false);
651 
652         final byte[] expected = createByteArray(
653                 0x40, 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, 0x79, 0x0d, 0x63, 0x75, 0x73,
654                 0x74, 0x6f, 0x6d, 0x2d, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72);
655 
656         Assert.assertArrayEquals(expected, buf.toByteArray());
657     }
658 
659     @Test
660     public void testLiteralHeaderWithoutIndexingEncodingRFC7541Examples() throws Exception {
661 
662         final OutboundDynamicTable dynamicTable = new OutboundDynamicTable();
663         final HPackEncoder encoder = new HPackEncoder(dynamicTable, StandardCharsets.US_ASCII);
664 
665         final ByteArrayBuffer buf = new ByteArrayBuffer(128);
666 
667         final Header header = new BasicHeader(":path", "/sample/path");
668         encoder.encodeLiteralHeader(buf, new HPackEntry() {
669             @Override
670             public int getIndex() {
671                 return 4;
672             }
673 
674             @Override
675             public HPackHeader getHeader() {
676                 return new HPackHeader(header);
677             }
678         }, header, HPackRepresentation.WITHOUT_INDEXING, false);
679 
680         final byte[] expected = createByteArray(
681                 0x04, 0x0c, 0x2f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2f, 0x70, 0x61, 0x74, 0x68);
682         Assert.assertArrayEquals(expected, buf.toByteArray());
683     }
684 
685     @Test
686     public void testLiteralHeaderNeverIndexedEncodingRFC7541Examples() throws Exception {
687 
688         final OutboundDynamicTable dynamicTable = new OutboundDynamicTable();
689         final HPackEncoder encoder = new HPackEncoder(dynamicTable, StandardCharsets.US_ASCII);
690 
691         final ByteArrayBuffer buf = new ByteArrayBuffer(128);
692 
693         final Header header = new BasicHeader("password", "secret", true);
694         encoder.encodeLiteralHeader(buf, null, header, HPackRepresentation.NEVER_INDEXED, false);
695 
696         final byte[] expected = createByteArray(
697                 0x10, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74);
698         Assert.assertArrayEquals(expected, buf.toByteArray());
699     }
700 
701     @Test
702     public void testIndexedHeaderEncodingRFC7541Examples() throws Exception {
703 
704         final OutboundDynamicTable dynamicTable = new OutboundDynamicTable();
705         final HPackEncoder encoder = new HPackEncoder(dynamicTable, StandardCharsets.US_ASCII);
706 
707         final ByteArrayBuffer buf = new ByteArrayBuffer(128);
708         encoder.encodeIndex(buf, 2);
709 
710         final byte[] expected = createByteArray(0x82);
711         Assert.assertArrayEquals(expected, buf.toByteArray());
712     }
713 
714     @Test
715     public void testRequestEncodingWithoutHuffmanRFC7541Examples() throws Exception {
716 
717         final OutboundDynamicTable dynamicTable = new OutboundDynamicTable();
718         final HPackEncoder encoder = new HPackEncoder(dynamicTable, StandardCharsets.US_ASCII);
719 
720         final ByteArrayBuffer buf = new ByteArrayBuffer(256);
721         final List<Header> headers1 = Arrays.<Header>asList(
722                 new BasicHeader(":method", "GET"),
723                 new BasicHeader(":scheme", "http"),
724                 new BasicHeader(":path", "/"),
725                 new BasicHeader(":authority", "www.example.com"));
726 
727         encoder.encodeHeaders(buf, headers1, false, false);
728 
729         final byte[] expected1 = createByteArray(
730                 0x82, 0x86, 0x84, 0x41, 0x0f, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e,
731                 0x63, 0x6f, 0x6d);
732         Assert.assertArrayEquals(expected1, buf.toByteArray());
733 
734         Assert.assertEquals(1, dynamicTable.dynamicLength());
735         assertHeaderEquals(new BasicHeader(":authority", "www.example.com"), dynamicTable.getDynamicEntry(0));
736         Assert.assertEquals(57, dynamicTable.getCurrentSize());
737 
738         final List<Header> headers2 = Arrays.<Header>asList(
739                 new BasicHeader(":method", "GET"),
740                 new BasicHeader(":scheme", "http"),
741                 new BasicHeader(":path", "/"),
742                 new BasicHeader(":authority", "www.example.com"),
743                 new BasicHeader("cache-control", "no-cache"));
744 
745         buf.clear();
746         encoder.encodeHeaders(buf, headers2, false, false);
747 
748         final byte[] expected2 = createByteArray(
749                 0x82, 0x86, 0x84, 0xbe, 0x58, 0x08, 0x6e, 0x6f, 0x2d, 0x63, 0x61, 0x63, 0x68, 0x65);
750         Assert.assertArrayEquals(expected2, buf.toByteArray());
751 
752         Assert.assertEquals(2, dynamicTable.dynamicLength());
753         assertHeaderEquals(new BasicHeader("cache-control", "no-cache"), dynamicTable.getDynamicEntry(0));
754         assertHeaderEquals(new BasicHeader(":authority", "www.example.com"), dynamicTable.getDynamicEntry(1));
755         Assert.assertEquals(110, dynamicTable.getCurrentSize());
756 
757         final List<Header> headers3 = Arrays.<Header>asList(
758                 new BasicHeader(":method", "GET"),
759                 new BasicHeader(":scheme", "https"),
760                 new BasicHeader(":path", "/index.html"),
761                 new BasicHeader(":authority", "www.example.com"),
762                 new BasicHeader("custom-key", "custom-value"));
763 
764         buf.clear();
765         encoder.encodeHeaders(buf, headers3, false, false);
766 
767         final byte[] expected3 = createByteArray(
768                 0x82, 0x87, 0x85, 0xbf, 0x40, 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, 0x79,
769                 0x0c, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x76, 0x61, 0x6c, 0x75, 0x65);
770         Assert.assertArrayEquals(expected3, buf.toByteArray());
771 
772         Assert.assertEquals(3, dynamicTable.dynamicLength());
773         assertHeaderEquals(new BasicHeader("custom-key", "custom-value"), dynamicTable.getDynamicEntry(0));
774         assertHeaderEquals(new BasicHeader("cache-control", "no-cache"), dynamicTable.getDynamicEntry(1));
775         assertHeaderEquals(new BasicHeader(":authority", "www.example.com"), dynamicTable.getDynamicEntry(2));
776         Assert.assertEquals(164, dynamicTable.getCurrentSize());
777     }
778 
779     @Test
780     public void testRequestEncodingWithHuffmanRFC7541Examples() throws Exception {
781 
782         final OutboundDynamicTable dynamicTable = new OutboundDynamicTable();
783         final HPackEncoder encoder = new HPackEncoder(dynamicTable, StandardCharsets.US_ASCII);
784 
785         final ByteArrayBuffer buf = new ByteArrayBuffer(256);
786         final List<Header> headers1 = Arrays.<Header>asList(
787                 new BasicHeader(":method", "GET"),
788                 new BasicHeader(":scheme", "http"),
789                 new BasicHeader(":path", "/"),
790                 new BasicHeader(":authority", "www.example.com"));
791 
792         encoder.encodeHeaders(buf, headers1, false, true);
793 
794         final byte[] expected1 = createByteArray(
795                 0x82, 0x86, 0x84, 0x41, 0x8c, 0xf1, 0xe3, 0xc2, 0xe5, 0xf2, 0x3a, 0x6b, 0xa0, 0xab, 0x90, 0xf4, 0xff);
796         Assert.assertArrayEquals(expected1, buf.toByteArray());
797 
798         Assert.assertEquals(1, dynamicTable.dynamicLength());
799         assertHeaderEquals(new BasicHeader(":authority", "www.example.com"), dynamicTable.getDynamicEntry(0));
800         Assert.assertEquals(57, dynamicTable.getCurrentSize());
801 
802         final List<Header> headers2 = Arrays.<Header>asList(
803                 new BasicHeader(":method", "GET"),
804                 new BasicHeader(":scheme", "http"),
805                 new BasicHeader(":path", "/"),
806                 new BasicHeader(":authority", "www.example.com"),
807                 new BasicHeader("cache-control", "no-cache"));
808 
809         buf.clear();
810         encoder.encodeHeaders(buf, headers2, false, true);
811 
812         final byte[] expected2 = createByteArray(
813                 0x82, 0x86, 0x84, 0xbe, 0x58, 0x86, 0xa8, 0xeb, 0x10, 0x64, 0x9c, 0xbf);
814         Assert.assertArrayEquals(expected2, buf.toByteArray());
815 
816         Assert.assertEquals(2, dynamicTable.dynamicLength());
817         assertHeaderEquals(new BasicHeader("cache-control", "no-cache"), dynamicTable.getDynamicEntry(0));
818         assertHeaderEquals(new BasicHeader(":authority", "www.example.com"), dynamicTable.getDynamicEntry(1));
819         Assert.assertEquals(110, dynamicTable.getCurrentSize());
820 
821         final List<Header> headers3 = Arrays.<Header>asList(
822                 new BasicHeader(":method", "GET"),
823                 new BasicHeader(":scheme", "https"),
824                 new BasicHeader(":path", "/index.html"),
825                 new BasicHeader(":authority", "www.example.com"),
826                 new BasicHeader("custom-key", "custom-value"));
827 
828         buf.clear();
829         encoder.encodeHeaders(buf, headers3, false, true);
830 
831         final byte[] expected3 = createByteArray(
832                 0x82, 0x87, 0x85, 0xbf, 0x40, 0x88, 0x25, 0xa8, 0x49, 0xe9, 0x5b, 0xa9, 0x7d, 0x7f, 0x89, 0x25,
833                 0xa8, 0x49, 0xe9, 0x5b, 0xb8, 0xe8, 0xb4, 0xbf);
834         Assert.assertArrayEquals(expected3, buf.toByteArray());
835 
836         Assert.assertEquals(3, dynamicTable.dynamicLength());
837         assertHeaderEquals(new BasicHeader("custom-key", "custom-value"), dynamicTable.getDynamicEntry(0));
838         assertHeaderEquals(new BasicHeader("cache-control", "no-cache"), dynamicTable.getDynamicEntry(1));
839         assertHeaderEquals(new BasicHeader(":authority", "www.example.com"), dynamicTable.getDynamicEntry(2));
840         Assert.assertEquals(164, dynamicTable.getCurrentSize());
841     }
842 
843     @Test
844     public void testResponseEncodingWithoutHuffmanRFC7541Examples() throws Exception {
845 
846         final OutboundDynamicTable dynamicTable = new OutboundDynamicTable();
847         dynamicTable.setMaxSize(256);
848         final HPackEncoder encoder = new HPackEncoder(dynamicTable, StandardCharsets.US_ASCII);
849 
850         final ByteArrayBuffer buf = new ByteArrayBuffer(256);
851         final List<Header> headers1 = Arrays.<Header>asList(
852                 new BasicHeader(":status", "302"),
853                 new BasicHeader("cache-control", "private"),
854                 new BasicHeader("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
855                 new BasicHeader("location", "https://www.example.com"));
856 
857         encoder.encodeHeaders(buf, headers1, false, false);
858 
859         final byte[] expected1 = createByteArray(
860                 0x48, 0x03, 0x33, 0x30, 0x32, 0x58, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x61, 0x1d, 0x4d,
861                 0x6f, 0x6e, 0x2c, 0x20, 0x32, 0x31, 0x20, 0x4f, 0x63, 0x74, 0x20, 0x32, 0x30, 0x31, 0x33, 0x20, 0x32,
862                 0x30, 0x3a, 0x31, 0x33, 0x3a, 0x32, 0x31, 0x20, 0x47, 0x4d, 0x54, 0x6e, 0x17, 0x68, 0x74, 0x74, 0x70,
863                 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63,
864                 0x6f, 0x6d);
865         Assert.assertArrayEquals(expected1, buf.toByteArray());
866 
867         Assert.assertEquals(4, dynamicTable.dynamicLength());
868         assertHeaderEquals(new BasicHeader("location", "https://www.example.com"), dynamicTable.getDynamicEntry(0));
869         assertHeaderEquals(new BasicHeader("date", "Mon, 21 Oct 2013 20:13:21 GMT"), dynamicTable.getDynamicEntry(1));
870         assertHeaderEquals(new BasicHeader("cache-control", "private"), dynamicTable.getDynamicEntry(2));
871         assertHeaderEquals(new BasicHeader(":status", "302"), dynamicTable.getDynamicEntry(3));
872         Assert.assertEquals(222, dynamicTable.getCurrentSize());
873 
874         final List<Header> headers2 = Arrays.<Header>asList(
875                 new BasicHeader(":status", "307"),
876                 new BasicHeader("cache-control", "private"),
877                 new BasicHeader("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
878                 new BasicHeader("location", "https://www.example.com"));
879 
880         buf.clear();
881         encoder.encodeHeaders(buf, headers2, false, false);
882 
883         final byte[] expected2 = createByteArray(
884                 0x48, 0x03, 0x33, 0x30, 0x37, 0xc1, 0xc0, 0xbf);
885         Assert.assertArrayEquals(expected2, buf.toByteArray());
886 
887         Assert.assertEquals(4, dynamicTable.dynamicLength());
888         assertHeaderEquals(new BasicHeader(":status", "307"), dynamicTable.getDynamicEntry(0));
889         assertHeaderEquals(new BasicHeader("location", "https://www.example.com"), dynamicTable.getDynamicEntry(1));
890         assertHeaderEquals(new BasicHeader("date", "Mon, 21 Oct 2013 20:13:21 GMT"), dynamicTable.getDynamicEntry(2));
891         assertHeaderEquals(new BasicHeader("cache-control", "private"), dynamicTable.getDynamicEntry(3));
892 
893         Assert.assertEquals(222, dynamicTable.getCurrentSize());
894 
895         final List<Header> headers3 = Arrays.<Header>asList(
896         new BasicHeader(":status", "200"),
897                 new BasicHeader("cache-control", "private"),
898                 new BasicHeader("date", "Mon, 21 Oct 2013 20:13:22 GMT"),
899                 new BasicHeader("location", "https://www.example.com"),
900                 new BasicHeader("content-encoding", "gzip"),
901                 new BasicHeader("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"));
902 
903         buf.clear();
904         encoder.encodeHeaders(buf, headers3, false, false);
905 
906         final byte[] expected3 = createByteArray(
907                 0x88, 0xc1, 0x61, 0x1d, 0x4d, 0x6f, 0x6e, 0x2c, 0x20, 0x32, 0x31, 0x20, 0x4f, 0x63, 0x74, 0x20, 0x32,
908                 0x30, 0x31, 0x33, 0x20, 0x32, 0x30, 0x3a, 0x31, 0x33, 0x3a, 0x32, 0x32, 0x20, 0x47, 0x4d, 0x54, 0xc0,
909                 0x5a, 0x04, 0x67, 0x7a, 0x69, 0x70, 0x77, 0x38, 0x66, 0x6f, 0x6f, 0x3d, 0x41, 0x53, 0x44, 0x4a, 0x4b,
910                 0x48, 0x51, 0x4b, 0x42, 0x5a, 0x58, 0x4f, 0x51, 0x57, 0x45, 0x4f, 0x50, 0x49, 0x55, 0x41, 0x58, 0x51,
911                 0x57, 0x45, 0x4f, 0x49, 0x55, 0x3b, 0x20, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65, 0x3d, 0x33, 0x36,
912                 0x30, 0x30, 0x3b, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x31);
913         Assert.assertArrayEquals(expected3, buf.toByteArray());
914 
915         Assert.assertEquals(3, dynamicTable.dynamicLength());
916         assertHeaderEquals(new BasicHeader("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"), dynamicTable.getDynamicEntry(0));
917         assertHeaderEquals(new BasicHeader("content-encoding", "gzip"), dynamicTable.getDynamicEntry(1));
918         assertHeaderEquals(new BasicHeader("date", "Mon, 21 Oct 2013 20:13:22 GMT"), dynamicTable.getDynamicEntry(2));
919 
920         Assert.assertEquals(215, dynamicTable.getCurrentSize());
921     }
922 
923     @Test
924     public void testResponseEncodingWithHuffmanRFC7541Examples() throws Exception {
925 
926         final OutboundDynamicTable dynamicTable = new OutboundDynamicTable();
927         dynamicTable.setMaxSize(256);
928         final HPackEncoder encoder = new HPackEncoder(dynamicTable, StandardCharsets.US_ASCII);
929 
930         final ByteArrayBuffer buf = new ByteArrayBuffer(256);
931         final List<Header> headers1 = Arrays.<Header>asList(
932                 new BasicHeader(":status", "302"),
933                 new BasicHeader("cache-control", "private"),
934                 new BasicHeader("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
935                 new BasicHeader("location", "https://www.example.com"));
936 
937         encoder.encodeHeaders(buf, headers1, false, true);
938 
939         final byte[] expected1 = createByteArray(
940                 0x48, 0x82, 0x64, 0x02, 0x58, 0x85, 0xae, 0xc3, 0x77, 0x1a, 0x4b, 0x61, 0x96, 0xd0, 0x7a, 0xbe, 0x94,
941                 0x10, 0x54, 0xd4, 0x44, 0xa8, 0x20, 0x05, 0x95, 0x04, 0x0b, 0x81, 0x66, 0xe0, 0x82, 0xa6, 0x2d, 0x1b,
942                 0xff, 0x6e, 0x91, 0x9d, 0x29, 0xad, 0x17, 0x18, 0x63, 0xc7, 0x8f, 0x0b, 0x97, 0xc8, 0xe9, 0xae, 0x82,
943                 0xae, 0x43, 0xd3);
944         Assert.assertArrayEquals(expected1, buf.toByteArray());
945 
946         Assert.assertEquals(4, dynamicTable.dynamicLength());
947         assertHeaderEquals(new BasicHeader("location", "https://www.example.com"), dynamicTable.getDynamicEntry(0));
948         assertHeaderEquals(new BasicHeader("date", "Mon, 21 Oct 2013 20:13:21 GMT"), dynamicTable.getDynamicEntry(1));
949         assertHeaderEquals(new BasicHeader("cache-control", "private"), dynamicTable.getDynamicEntry(2));
950         assertHeaderEquals(new BasicHeader(":status", "302"), dynamicTable.getDynamicEntry(3));
951         Assert.assertEquals(222, dynamicTable.getCurrentSize());
952 
953         final List<Header> headers2 = Arrays.<Header>asList(
954                 new BasicHeader(":status", "307"),
955                 new BasicHeader("cache-control", "private"),
956                 new BasicHeader("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
957                 new BasicHeader("location", "https://www.example.com"));
958 
959         buf.clear();
960         encoder.encodeHeaders(buf, headers2, false, true);
961 
962         final byte[] expected2 = createByteArray(
963                 0x48, 0x83, 0x64, 0x0e, 0xff, 0xc1, 0xc0, 0xbf);
964         Assert.assertArrayEquals(expected2, buf.toByteArray());
965 
966         Assert.assertEquals(4, dynamicTable.dynamicLength());
967         assertHeaderEquals(new BasicHeader(":status", "307"), dynamicTable.getDynamicEntry(0));
968         assertHeaderEquals(new BasicHeader("location", "https://www.example.com"), dynamicTable.getDynamicEntry(1));
969         assertHeaderEquals(new BasicHeader("date", "Mon, 21 Oct 2013 20:13:21 GMT"), dynamicTable.getDynamicEntry(2));
970         assertHeaderEquals(new BasicHeader("cache-control", "private"), dynamicTable.getDynamicEntry(3));
971 
972         Assert.assertEquals(222, dynamicTable.getCurrentSize());
973 
974         final List<Header> headers3 = Arrays.<Header>asList(
975                 new BasicHeader(":status", "200"),
976                 new BasicHeader("cache-control", "private"),
977                 new BasicHeader("date", "Mon, 21 Oct 2013 20:13:22 GMT"),
978                 new BasicHeader("location", "https://www.example.com"),
979                 new BasicHeader("content-encoding", "gzip"),
980                 new BasicHeader("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"));
981 
982         buf.clear();
983         encoder.encodeHeaders(buf, headers3, false, true);
984 
985         final byte[] expected3 = createByteArray(
986                 0x88, 0xc1, 0x61, 0x96, 0xd0, 0x7a, 0xbe, 0x94, 0x10, 0x54, 0xd4, 0x44, 0xa8, 0x20, 0x05, 0x95, 0x04,
987                 0x0b, 0x81, 0x66, 0xe0, 0x84, 0xa6, 0x2d, 0x1b, 0xff, 0xc0, 0x5a, 0x83, 0x9b, 0xd9, 0xab, 0x77, 0xad,
988                 0x94, 0xe7, 0x82, 0x1d, 0xd7, 0xf2, 0xe6, 0xc7, 0xb3, 0x35, 0xdf, 0xdf, 0xcd, 0x5b, 0x39, 0x60, 0xd5,
989                 0xaf, 0x27, 0x08, 0x7f, 0x36, 0x72, 0xc1, 0xab, 0x27, 0x0f, 0xb5, 0x29, 0x1f, 0x95, 0x87, 0x31, 0x60,
990                 0x65, 0xc0, 0x03, 0xed, 0x4e, 0xe5, 0xb1, 0x06, 0x3d, 0x50, 0x07);
991         Assert.assertArrayEquals(expected3, buf.toByteArray());
992 
993         Assert.assertEquals(3, dynamicTable.dynamicLength());
994         assertHeaderEquals(new BasicHeader("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"), dynamicTable.getDynamicEntry(0));
995         assertHeaderEquals(new BasicHeader("content-encoding", "gzip"), dynamicTable.getDynamicEntry(1));
996         assertHeaderEquals(new BasicHeader("date", "Mon, 21 Oct 2013 20:13:22 GMT"), dynamicTable.getDynamicEntry(2));
997 
998         Assert.assertEquals(215, dynamicTable.getCurrentSize());
999     }
1000 
1001     @Test
1002     public void testHeaderEntrySizeNonAscii() throws Exception {
1003 
1004         final ByteArrayBuffer buffer = new ByteArrayBuffer(128);
1005         final Header header = new BasicHeader("hello", constructHelloString(SWISS_GERMAN_HELLO, 1));
1006 
1007         final OutboundDynamicTable outboundTable1 = new OutboundDynamicTable();
1008         final HPackEncoder encoder1 = new HPackEncoder(outboundTable1, StandardCharsets.ISO_8859_1);
1009         final InboundDynamicTable inboundTable1 = new InboundDynamicTable();
1010         final HPackDecoder decoder1 = new HPackDecoder(inboundTable1, StandardCharsets.ISO_8859_1);
1011 
1012         encoder1.setMaxTableSize(48);
1013         decoder1.setMaxTableSize(48);
1014 
1015         encoder1.encodeHeader(buffer, header);
1016         assertHeaderEquals(header, decoder1.decodeHeader(wrap(buffer)));
1017 
1018         Assert.assertEquals(1, outboundTable1.dynamicLength());
1019         Assert.assertEquals(1, inboundTable1.dynamicLength());
1020 
1021         assertHeaderEquals(header, outboundTable1.getDynamicEntry(0));
1022         assertHeaderEquals(header, inboundTable1.getDynamicEntry(0));
1023 
1024         buffer.clear();
1025 
1026         final OutboundDynamicTable outboundTable2 = new OutboundDynamicTable();
1027         final HPackEncoder encoder2 = new HPackEncoder(outboundTable2, StandardCharsets.UTF_8);
1028         final InboundDynamicTable inboundTable2 = new InboundDynamicTable();
1029         final HPackDecoder decoder2 = new HPackDecoder(inboundTable2, StandardCharsets.UTF_8);
1030 
1031         encoder2.setMaxTableSize(48);
1032         decoder2.setMaxTableSize(48);
1033 
1034         encoder2.encodeHeader(buffer, header);
1035         assertHeaderEquals(header, decoder2.decodeHeader(wrap(buffer)));
1036 
1037         Assert.assertEquals(0, outboundTable2.dynamicLength());
1038         Assert.assertEquals(0, inboundTable2.dynamicLength());
1039     }
1040 
1041     @Test(expected = HeaderListConstraintException.class)
1042     public void testHeaderSizeLimit() throws Exception {
1043 
1044         final HPackEncoder encoder = new HPackEncoder(StandardCharsets.US_ASCII);
1045         final HPackDecoder decoder = new HPackDecoder(StandardCharsets.US_ASCII);
1046 
1047         final ByteArrayBuffer buf = new ByteArrayBuffer(128);
1048 
1049         encoder.encodeHeaders(buf,
1050                 Arrays.asList(
1051                         new BasicHeader("regular-header", "blah"),
1052                         new BasicHeader("big-f-header", "12345678901234567890123456789012345678901234567890" +
1053                                 "123456789012345678901234567890123456789012345678901234567890")),
1054                 false);
1055 
1056         Assert.assertThat(decoder.decodeHeaders(wrap(buf)).size(), CoreMatchers.equalTo(2));
1057 
1058         decoder.setMaxListSize(1000000);
1059         Assert.assertThat(decoder.decodeHeaders(wrap(buf)).size(), CoreMatchers.equalTo(2));
1060 
1061         decoder.setMaxListSize(200);
1062         decoder.decodeHeaders(wrap(buf));
1063     }
1064 
1065 }
1066