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