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