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