1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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
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