|
1 /* |
|
2 * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. |
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
4 * |
|
5 * This code is free software; you can redistribute it and/or modify it |
|
6 * under the terms of the GNU General Public License version 2 only, as |
|
7 * published by the Free Software Foundation. |
|
8 * |
|
9 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
12 * version 2 for more details (a copy is included in the LICENSE file that |
|
13 * accompanied this code). |
|
14 * |
|
15 * You should have received a copy of the GNU General Public License version |
|
16 * 2 along with this work; if not, write to the Free Software Foundation, |
|
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
18 * |
|
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
20 * or visit www.oracle.com if you need additional information or have any |
|
21 * questions. |
|
22 */ |
|
23 package jdk.internal.net.http.hpack; |
|
24 |
|
25 import org.testng.annotations.Test; |
|
26 |
|
27 import java.io.IOException; |
|
28 import java.io.UncheckedIOException; |
|
29 import java.nio.ByteBuffer; |
|
30 import java.util.Iterator; |
|
31 import java.util.LinkedList; |
|
32 import java.util.List; |
|
33 import java.util.function.Supplier; |
|
34 import java.util.stream.Collectors; |
|
35 |
|
36 import static org.testng.Assert.assertEquals; |
|
37 import static org.testng.Assert.assertNotNull; |
|
38 import static jdk.internal.net.http.hpack.TestHelper.*; |
|
39 |
|
40 // |
|
41 // Tests whose names start with "testX" are the ones captured from real HPACK |
|
42 // use cases |
|
43 // |
|
44 public final class DecoderTest { |
|
45 |
|
46 // |
|
47 // http://tools.ietf.org/html/rfc7541#appendix-C.2.1 |
|
48 // |
|
49 @Test |
|
50 public void example1() { |
|
51 // @formatter:off |
|
52 test("400a 6375 7374 6f6d 2d6b 6579 0d63 7573\n" + |
|
53 "746f 6d2d 6865 6164 6572", |
|
54 |
|
55 "[ 1] (s = 55) custom-key: custom-header\n" + |
|
56 " Table size: 55", |
|
57 |
|
58 "custom-key: custom-header"); |
|
59 // @formatter:on |
|
60 } |
|
61 |
|
62 // |
|
63 // http://tools.ietf.org/html/rfc7541#appendix-C.2.2 |
|
64 // |
|
65 @Test |
|
66 public void example2() { |
|
67 // @formatter:off |
|
68 test("040c 2f73 616d 706c 652f 7061 7468", |
|
69 "empty.", |
|
70 ":path: /sample/path"); |
|
71 // @formatter:on |
|
72 } |
|
73 |
|
74 // |
|
75 // http://tools.ietf.org/html/rfc7541#appendix-C.2.3 |
|
76 // |
|
77 @Test |
|
78 public void example3() { |
|
79 // @formatter:off |
|
80 test("1008 7061 7373 776f 7264 0673 6563 7265\n" + |
|
81 "74", |
|
82 "empty.", |
|
83 "password: secret"); |
|
84 // @formatter:on |
|
85 } |
|
86 |
|
87 // |
|
88 // http://tools.ietf.org/html/rfc7541#appendix-C.2.4 |
|
89 // |
|
90 @Test |
|
91 public void example4() { |
|
92 // @formatter:off |
|
93 test("82", |
|
94 "empty.", |
|
95 ":method: GET"); |
|
96 // @formatter:on |
|
97 } |
|
98 |
|
99 // |
|
100 // http://tools.ietf.org/html/rfc7541#appendix-C.3 |
|
101 // |
|
102 @Test |
|
103 public void example5() { |
|
104 // @formatter:off |
|
105 Decoder d = new Decoder(256); |
|
106 |
|
107 test(d, "8286 8441 0f77 7777 2e65 7861 6d70 6c65\n" + |
|
108 "2e63 6f6d", |
|
109 |
|
110 "[ 1] (s = 57) :authority: www.example.com\n" + |
|
111 " Table size: 57", |
|
112 |
|
113 ":method: GET\n" + |
|
114 ":scheme: http\n" + |
|
115 ":path: /\n" + |
|
116 ":authority: www.example.com"); |
|
117 |
|
118 test(d, "8286 84be 5808 6e6f 2d63 6163 6865", |
|
119 |
|
120 "[ 1] (s = 53) cache-control: no-cache\n" + |
|
121 "[ 2] (s = 57) :authority: www.example.com\n" + |
|
122 " Table size: 110", |
|
123 |
|
124 ":method: GET\n" + |
|
125 ":scheme: http\n" + |
|
126 ":path: /\n" + |
|
127 ":authority: www.example.com\n" + |
|
128 "cache-control: no-cache"); |
|
129 |
|
130 test(d, "8287 85bf 400a 6375 7374 6f6d 2d6b 6579\n" + |
|
131 "0c63 7573 746f 6d2d 7661 6c75 65", |
|
132 |
|
133 "[ 1] (s = 54) custom-key: custom-value\n" + |
|
134 "[ 2] (s = 53) cache-control: no-cache\n" + |
|
135 "[ 3] (s = 57) :authority: www.example.com\n" + |
|
136 " Table size: 164", |
|
137 |
|
138 ":method: GET\n" + |
|
139 ":scheme: https\n" + |
|
140 ":path: /index.html\n" + |
|
141 ":authority: www.example.com\n" + |
|
142 "custom-key: custom-value"); |
|
143 |
|
144 // @formatter:on |
|
145 } |
|
146 |
|
147 @Test |
|
148 public void example5AllSplits() { |
|
149 // @formatter:off |
|
150 testAllSplits( |
|
151 "8286 8441 0f77 7777 2e65 7861 6d70 6c65\n" + |
|
152 "2e63 6f6d", |
|
153 |
|
154 "[ 1] (s = 57) :authority: www.example.com\n" + |
|
155 " Table size: 57", |
|
156 |
|
157 ":method: GET\n" + |
|
158 ":scheme: http\n" + |
|
159 ":path: /\n" + |
|
160 ":authority: www.example.com"); |
|
161 // @formatter:on |
|
162 } |
|
163 |
|
164 // |
|
165 // http://tools.ietf.org/html/rfc7541#appendix-C.4 |
|
166 // |
|
167 @Test |
|
168 public void example6() { |
|
169 // @formatter:off |
|
170 Decoder d = new Decoder(256); |
|
171 |
|
172 test(d, "8286 8441 8cf1 e3c2 e5f2 3a6b a0ab 90f4\n" + |
|
173 "ff", |
|
174 |
|
175 "[ 1] (s = 57) :authority: www.example.com\n" + |
|
176 " Table size: 57", |
|
177 |
|
178 ":method: GET\n" + |
|
179 ":scheme: http\n" + |
|
180 ":path: /\n" + |
|
181 ":authority: www.example.com"); |
|
182 |
|
183 test(d, "8286 84be 5886 a8eb 1064 9cbf", |
|
184 |
|
185 "[ 1] (s = 53) cache-control: no-cache\n" + |
|
186 "[ 2] (s = 57) :authority: www.example.com\n" + |
|
187 " Table size: 110", |
|
188 |
|
189 ":method: GET\n" + |
|
190 ":scheme: http\n" + |
|
191 ":path: /\n" + |
|
192 ":authority: www.example.com\n" + |
|
193 "cache-control: no-cache"); |
|
194 |
|
195 test(d, "8287 85bf 4088 25a8 49e9 5ba9 7d7f 8925\n" + |
|
196 "a849 e95b b8e8 b4bf", |
|
197 |
|
198 "[ 1] (s = 54) custom-key: custom-value\n" + |
|
199 "[ 2] (s = 53) cache-control: no-cache\n" + |
|
200 "[ 3] (s = 57) :authority: www.example.com\n" + |
|
201 " Table size: 164", |
|
202 |
|
203 ":method: GET\n" + |
|
204 ":scheme: https\n" + |
|
205 ":path: /index.html\n" + |
|
206 ":authority: www.example.com\n" + |
|
207 "custom-key: custom-value"); |
|
208 // @formatter:on |
|
209 } |
|
210 |
|
211 // |
|
212 // http://tools.ietf.org/html/rfc7541#appendix-C.5 |
|
213 // |
|
214 @Test |
|
215 public void example7() { |
|
216 // @formatter:off |
|
217 Decoder d = new Decoder(256); |
|
218 |
|
219 test(d, "4803 3330 3258 0770 7269 7661 7465 611d\n" + |
|
220 "4d6f 6e2c 2032 3120 4f63 7420 3230 3133\n" + |
|
221 "2032 303a 3133 3a32 3120 474d 546e 1768\n" + |
|
222 "7474 7073 3a2f 2f77 7777 2e65 7861 6d70\n" + |
|
223 "6c65 2e63 6f6d", |
|
224 |
|
225 "[ 1] (s = 63) location: https://www.example.com\n" + |
|
226 "[ 2] (s = 65) date: Mon, 21 Oct 2013 20:13:21 GMT\n" + |
|
227 "[ 3] (s = 52) cache-control: private\n" + |
|
228 "[ 4] (s = 42) :status: 302\n" + |
|
229 " Table size: 222", |
|
230 |
|
231 ":status: 302\n" + |
|
232 "cache-control: private\n" + |
|
233 "date: Mon, 21 Oct 2013 20:13:21 GMT\n" + |
|
234 "location: https://www.example.com"); |
|
235 |
|
236 test(d, "4803 3330 37c1 c0bf", |
|
237 |
|
238 "[ 1] (s = 42) :status: 307\n" + |
|
239 "[ 2] (s = 63) location: https://www.example.com\n" + |
|
240 "[ 3] (s = 65) date: Mon, 21 Oct 2013 20:13:21 GMT\n" + |
|
241 "[ 4] (s = 52) cache-control: private\n" + |
|
242 " Table size: 222", |
|
243 |
|
244 ":status: 307\n" + |
|
245 "cache-control: private\n" + |
|
246 "date: Mon, 21 Oct 2013 20:13:21 GMT\n" + |
|
247 "location: https://www.example.com"); |
|
248 |
|
249 test(d, "88c1 611d 4d6f 6e2c 2032 3120 4f63 7420\n" + |
|
250 "3230 3133 2032 303a 3133 3a32 3220 474d\n" + |
|
251 "54c0 5a04 677a 6970 7738 666f 6f3d 4153\n" + |
|
252 "444a 4b48 514b 425a 584f 5157 454f 5049\n" + |
|
253 "5541 5851 5745 4f49 553b 206d 6178 2d61\n" + |
|
254 "6765 3d33 3630 303b 2076 6572 7369 6f6e\n" + |
|
255 "3d31", |
|
256 |
|
257 "[ 1] (s = 98) set-cookie: foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1\n" + |
|
258 "[ 2] (s = 52) content-encoding: gzip\n" + |
|
259 "[ 3] (s = 65) date: Mon, 21 Oct 2013 20:13:22 GMT\n" + |
|
260 " Table size: 215", |
|
261 |
|
262 ":status: 200\n" + |
|
263 "cache-control: private\n" + |
|
264 "date: Mon, 21 Oct 2013 20:13:22 GMT\n" + |
|
265 "location: https://www.example.com\n" + |
|
266 "content-encoding: gzip\n" + |
|
267 "set-cookie: foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"); |
|
268 // @formatter:on |
|
269 } |
|
270 |
|
271 // |
|
272 // http://tools.ietf.org/html/rfc7541#appendix-C.6 |
|
273 // |
|
274 @Test |
|
275 public void example8() { |
|
276 // @formatter:off |
|
277 Decoder d = new Decoder(256); |
|
278 |
|
279 test(d, "4882 6402 5885 aec3 771a 4b61 96d0 7abe\n" + |
|
280 "9410 54d4 44a8 2005 9504 0b81 66e0 82a6\n" + |
|
281 "2d1b ff6e 919d 29ad 1718 63c7 8f0b 97c8\n" + |
|
282 "e9ae 82ae 43d3", |
|
283 |
|
284 "[ 1] (s = 63) location: https://www.example.com\n" + |
|
285 "[ 2] (s = 65) date: Mon, 21 Oct 2013 20:13:21 GMT\n" + |
|
286 "[ 3] (s = 52) cache-control: private\n" + |
|
287 "[ 4] (s = 42) :status: 302\n" + |
|
288 " Table size: 222", |
|
289 |
|
290 ":status: 302\n" + |
|
291 "cache-control: private\n" + |
|
292 "date: Mon, 21 Oct 2013 20:13:21 GMT\n" + |
|
293 "location: https://www.example.com"); |
|
294 |
|
295 test(d, "4883 640e ffc1 c0bf", |
|
296 |
|
297 "[ 1] (s = 42) :status: 307\n" + |
|
298 "[ 2] (s = 63) location: https://www.example.com\n" + |
|
299 "[ 3] (s = 65) date: Mon, 21 Oct 2013 20:13:21 GMT\n" + |
|
300 "[ 4] (s = 52) cache-control: private\n" + |
|
301 " Table size: 222", |
|
302 |
|
303 ":status: 307\n" + |
|
304 "cache-control: private\n" + |
|
305 "date: Mon, 21 Oct 2013 20:13:21 GMT\n" + |
|
306 "location: https://www.example.com"); |
|
307 |
|
308 test(d, "88c1 6196 d07a be94 1054 d444 a820 0595\n" + |
|
309 "040b 8166 e084 a62d 1bff c05a 839b d9ab\n" + |
|
310 "77ad 94e7 821d d7f2 e6c7 b335 dfdf cd5b\n" + |
|
311 "3960 d5af 2708 7f36 72c1 ab27 0fb5 291f\n" + |
|
312 "9587 3160 65c0 03ed 4ee5 b106 3d50 07", |
|
313 |
|
314 "[ 1] (s = 98) set-cookie: foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1\n" + |
|
315 "[ 2] (s = 52) content-encoding: gzip\n" + |
|
316 "[ 3] (s = 65) date: Mon, 21 Oct 2013 20:13:22 GMT\n" + |
|
317 " Table size: 215", |
|
318 |
|
319 ":status: 200\n" + |
|
320 "cache-control: private\n" + |
|
321 "date: Mon, 21 Oct 2013 20:13:22 GMT\n" + |
|
322 "location: https://www.example.com\n" + |
|
323 "content-encoding: gzip\n" + |
|
324 "set-cookie: foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"); |
|
325 // @formatter:on |
|
326 } |
|
327 |
|
328 @Test |
|
329 // One of responses from Apache Server that helped to catch a bug |
|
330 public void testX() { |
|
331 Decoder d = new Decoder(4096); |
|
332 // @formatter:off |
|
333 test(d, "3fe1 1f88 6196 d07a be94 03ea 693f 7504\n" + |
|
334 "00b6 a05c b827 2e32 fa98 b46f 769e 86b1\n" + |
|
335 "9272 b025 da5c 2ea9 fd70 a8de 7fb5 3556\n" + |
|
336 "5ab7 6ece c057 02e2 2ad2 17bf 6c96 d07a\n" + |
|
337 "be94 0854 cb6d 4a08 0075 40bd 71b6 6e05\n" + |
|
338 "a531 68df 0f13 8efe 4522 cd32 21b6 5686\n" + |
|
339 "eb23 781f cf52 848f d24a 8f0f 0d02 3435\n" + |
|
340 "5f87 497c a589 d34d 1f", |
|
341 |
|
342 "[ 1] (s = 53) content-type: text/html\n" + |
|
343 "[ 2] (s = 50) accept-ranges: bytes\n" + |
|
344 "[ 3] (s = 74) last-modified: Mon, 11 Jun 2007 18:53:14 GMT\n" + |
|
345 "[ 4] (s = 77) server: Apache/2.4.17 (Unix) OpenSSL/1.0.2e-dev\n" + |
|
346 "[ 5] (s = 65) date: Mon, 09 Nov 2015 16:26:39 GMT\n" + |
|
347 " Table size: 319", |
|
348 |
|
349 ":status: 200\n" + |
|
350 "date: Mon, 09 Nov 2015 16:26:39 GMT\n" + |
|
351 "server: Apache/2.4.17 (Unix) OpenSSL/1.0.2e-dev\n" + |
|
352 "last-modified: Mon, 11 Jun 2007 18:53:14 GMT\n" + |
|
353 "etag: \"2d-432a5e4a73a80\"\n" + |
|
354 "accept-ranges: bytes\n" + |
|
355 "content-length: 45\n" + |
|
356 "content-type: text/html"); |
|
357 // @formatter:on |
|
358 } |
|
359 |
|
360 @Test |
|
361 public void testX1() { |
|
362 // Supplier of a decoder with a particular state |
|
363 Supplier<Decoder> s = () -> { |
|
364 Decoder d = new Decoder(4096); |
|
365 // @formatter:off |
|
366 test(d, "88 76 92 ca 54 a7 d7 f4 fa ec af ed 6d da 61 d7 bb 1e ad ff" + |
|
367 "df 61 97 c3 61 be 94 13 4a 65 b6 a5 04 00 b8 a0 5a b8 db 77" + |
|
368 "1b 71 4c 5a 37 ff 0f 0d 84 08 00 00 03", |
|
369 |
|
370 "[ 1] (s = 65) date: Fri, 24 Jun 2016 14:55:56 GMT\n" + |
|
371 "[ 2] (s = 59) server: Jetty(9.3.z-SNAPSHOT)\n" + |
|
372 " Table size: 124", |
|
373 |
|
374 ":status: 200\n" + |
|
375 "server: Jetty(9.3.z-SNAPSHOT)\n" + |
|
376 "date: Fri, 24 Jun 2016 14:55:56 GMT\n" + |
|
377 "content-length: 100000" |
|
378 ); |
|
379 // @formatter:on |
|
380 return d; |
|
381 }; |
|
382 // For all splits of the following data fed to the supplied decoder we |
|
383 // must get what's expected |
|
384 // @formatter:off |
|
385 testAllSplits(s, |
|
386 "88 bf be 0f 0d 84 08 00 00 03", |
|
387 |
|
388 "[ 1] (s = 65) date: Fri, 24 Jun 2016 14:55:56 GMT\n" + |
|
389 "[ 2] (s = 59) server: Jetty(9.3.z-SNAPSHOT)\n" + |
|
390 " Table size: 124", |
|
391 |
|
392 ":status: 200\n" + |
|
393 "server: Jetty(9.3.z-SNAPSHOT)\n" + |
|
394 "date: Fri, 24 Jun 2016 14:55:56 GMT\n" + |
|
395 "content-length: 100000"); |
|
396 // @formatter:on |
|
397 } |
|
398 |
|
399 // |
|
400 // This test is missing in the spec |
|
401 // |
|
402 @Test |
|
403 public void sizeUpdate() throws IOException { |
|
404 Decoder d = new Decoder(4096); |
|
405 assertEquals(d.getTable().maxSize(), 4096); |
|
406 d.decode(ByteBuffer.wrap(new byte[]{0b00111110}), true, nopCallback()); // newSize = 30 |
|
407 assertEquals(d.getTable().maxSize(), 30); |
|
408 } |
|
409 |
|
410 @Test |
|
411 public void incorrectSizeUpdate() { |
|
412 ByteBuffer b = ByteBuffer.allocate(8); |
|
413 Encoder e = new Encoder(8192) { |
|
414 @Override |
|
415 protected int calculateCapacity(int maxCapacity) { |
|
416 return maxCapacity; |
|
417 } |
|
418 }; |
|
419 e.header("a", "b"); |
|
420 e.encode(b); |
|
421 b.flip(); |
|
422 { |
|
423 Decoder d = new Decoder(4096); |
|
424 assertVoidThrows(IOException.class, |
|
425 () -> d.decode(b, true, (name, value) -> { })); |
|
426 } |
|
427 b.flip(); |
|
428 { |
|
429 Decoder d = new Decoder(4096); |
|
430 assertVoidThrows(IOException.class, |
|
431 () -> d.decode(b, false, (name, value) -> { })); |
|
432 } |
|
433 } |
|
434 |
|
435 @Test |
|
436 public void corruptedHeaderBlockInteger() { |
|
437 Decoder d = new Decoder(4096); |
|
438 ByteBuffer data = ByteBuffer.wrap(new byte[]{ |
|
439 (byte) 0b11111111, // indexed |
|
440 (byte) 0b10011010 // 25 + ... |
|
441 }); |
|
442 IOException e = assertVoidThrows(IOException.class, |
|
443 () -> d.decode(data, true, nopCallback())); |
|
444 assertExceptionMessageContains(e, "Unexpected end of header block"); |
|
445 } |
|
446 |
|
447 // 5.1. Integer Representation |
|
448 // ... |
|
449 // Integer encodings that exceed implementation limits -- in value or octet |
|
450 // length -- MUST be treated as decoding errors. Different limits can |
|
451 // be set for each of the different uses of integers, based on |
|
452 // implementation constraints. |
|
453 @Test |
|
454 public void headerBlockIntegerNoOverflow() { |
|
455 Decoder d = new Decoder(4096); |
|
456 ByteBuffer data = ByteBuffer.wrap(new byte[]{ |
|
457 (byte) 0b11111111, // indexed + 127 |
|
458 // Integer.MAX_VALUE - 127 (base 128, little-endian): |
|
459 (byte) 0b10000000, |
|
460 (byte) 0b11111111, |
|
461 (byte) 0b11111111, |
|
462 (byte) 0b11111111, |
|
463 (byte) 0b00000111 |
|
464 }); |
|
465 |
|
466 IOException e = assertVoidThrows(IOException.class, |
|
467 () -> d.decode(data, true, nopCallback())); |
|
468 |
|
469 assertExceptionMessageContains(e.getCause(), "index=2147483647"); |
|
470 } |
|
471 |
|
472 @Test |
|
473 public void headerBlockIntegerOverflow() { |
|
474 Decoder d = new Decoder(4096); |
|
475 ByteBuffer data = ByteBuffer.wrap(new byte[]{ |
|
476 (byte) 0b11111111, // indexed + 127 |
|
477 // Integer.MAX_VALUE - 127 + 1 (base 128, little endian): |
|
478 (byte) 0b10000001, |
|
479 (byte) 0b11111111, |
|
480 (byte) 0b11111111, |
|
481 (byte) 0b11111111, |
|
482 (byte) 0b00000111 |
|
483 }); |
|
484 |
|
485 IOException e = assertVoidThrows(IOException.class, |
|
486 () -> d.decode(data, true, nopCallback())); |
|
487 |
|
488 assertExceptionMessageContains(e, "Integer overflow"); |
|
489 } |
|
490 |
|
491 @Test |
|
492 public void corruptedHeaderBlockString1() { |
|
493 Decoder d = new Decoder(4096); |
|
494 ByteBuffer data = ByteBuffer.wrap(new byte[]{ |
|
495 0b00001111, // literal, index=15 |
|
496 0b00000000, |
|
497 0b00001000, // huffman=false, length=8 |
|
498 0b00000000, // \ |
|
499 0b00000000, // but only 3 octets available... |
|
500 0b00000000 // / |
|
501 }); |
|
502 IOException e = assertVoidThrows(IOException.class, |
|
503 () -> d.decode(data, true, nopCallback())); |
|
504 assertExceptionMessageContains(e, "Unexpected end of header block"); |
|
505 } |
|
506 |
|
507 @Test |
|
508 public void corruptedHeaderBlockString2() { |
|
509 Decoder d = new Decoder(4096); |
|
510 ByteBuffer data = ByteBuffer.wrap(new byte[]{ |
|
511 0b00001111, // literal, index=15 |
|
512 0b00000000, |
|
513 (byte) 0b10001000, // huffman=true, length=8 |
|
514 0b00000000, // \ |
|
515 0b00000000, // \ |
|
516 0b00000000, // but only 5 octets available... |
|
517 0b00000000, // / |
|
518 0b00000000 // / |
|
519 }); |
|
520 IOException e = assertVoidThrows(IOException.class, |
|
521 () -> d.decode(data, true, nopCallback())); |
|
522 assertExceptionMessageContains(e, "Unexpected end of header block"); |
|
523 } |
|
524 |
|
525 // 5.2. String Literal Representation |
|
526 // ...A Huffman-encoded string literal containing the EOS symbol MUST be |
|
527 // treated as a decoding error... |
|
528 @Test |
|
529 public void corruptedHeaderBlockHuffmanStringEOS() { |
|
530 Decoder d = new Decoder(4096); |
|
531 ByteBuffer data = ByteBuffer.wrap(new byte[]{ |
|
532 0b00001111, // literal, index=15 |
|
533 0b00000000, |
|
534 (byte) 0b10000110, // huffman=true, length=6 |
|
535 0b00011001, 0b01001101, (byte) 0b11111111, |
|
536 (byte) 0b11111111, (byte) 0b11111111, (byte) 0b11111100 |
|
537 }); |
|
538 IOException e = assertVoidThrows(IOException.class, |
|
539 () -> d.decode(data, true, nopCallback())); |
|
540 |
|
541 assertExceptionMessageContains(e, "Encountered EOS"); |
|
542 } |
|
543 |
|
544 // 5.2. String Literal Representation |
|
545 // ...A padding strictly longer than 7 bits MUST be treated as a decoding |
|
546 // error... |
|
547 @Test |
|
548 public void corruptedHeaderBlockHuffmanStringLongPadding1() { |
|
549 Decoder d = new Decoder(4096); |
|
550 ByteBuffer data = ByteBuffer.wrap(new byte[]{ |
|
551 0b00001111, // literal, index=15 |
|
552 0b00000000, |
|
553 (byte) 0b10000011, // huffman=true, length=3 |
|
554 0b00011001, 0b01001101, (byte) 0b11111111 |
|
555 // len("aei") + len(padding) = (5 + 5 + 5) + (9) |
|
556 }); |
|
557 IOException e = assertVoidThrows(IOException.class, |
|
558 () -> d.decode(data, true, nopCallback())); |
|
559 |
|
560 assertExceptionMessageContains(e, "Padding is too long", "len=9"); |
|
561 } |
|
562 |
|
563 @Test |
|
564 public void corruptedHeaderBlockHuffmanStringLongPadding2() { |
|
565 Decoder d = new Decoder(4096); |
|
566 ByteBuffer data = ByteBuffer.wrap(new byte[]{ |
|
567 0b00001111, // literal, index=15 |
|
568 0b00000000, |
|
569 (byte) 0b10000011, // huffman=true, length=3 |
|
570 0b00011001, 0b01111010, (byte) 0b11111111 |
|
571 // len("aek") + len(padding) = (5 + 5 + 7) + (7) |
|
572 }); |
|
573 assertVoidDoesNotThrow(() -> d.decode(data, true, nopCallback())); |
|
574 } |
|
575 |
|
576 // 5.2. String Literal Representation |
|
577 // ...A padding not corresponding to the most significant bits of the code |
|
578 // for the EOS symbol MUST be treated as a decoding error... |
|
579 @Test |
|
580 public void corruptedHeaderBlockHuffmanStringNotEOSPadding() { |
|
581 Decoder d = new Decoder(4096); |
|
582 ByteBuffer data = ByteBuffer.wrap(new byte[]{ |
|
583 0b00001111, // literal, index=15 |
|
584 0b00000000, |
|
585 (byte) 0b10000011, // huffman=true, length=3 |
|
586 0b00011001, 0b01111010, (byte) 0b11111110 |
|
587 }); |
|
588 IOException e = assertVoidThrows(IOException.class, |
|
589 () -> d.decode(data, true, nopCallback())); |
|
590 |
|
591 assertExceptionMessageContains(e, "Not a EOS prefix"); |
|
592 } |
|
593 |
|
594 @Test |
|
595 public void argsTestBiConsumerIsNull() { |
|
596 Decoder decoder = new Decoder(4096); |
|
597 assertVoidThrows(NullPointerException.class, |
|
598 () -> decoder.decode(ByteBuffer.allocate(16), true, null)); |
|
599 } |
|
600 |
|
601 @Test |
|
602 public void argsTestByteBufferIsNull() { |
|
603 Decoder decoder = new Decoder(4096); |
|
604 assertVoidThrows(NullPointerException.class, |
|
605 () -> decoder.decode(null, true, nopCallback())); |
|
606 } |
|
607 |
|
608 @Test |
|
609 public void argsTestBothAreNull() { |
|
610 Decoder decoder = new Decoder(4096); |
|
611 assertVoidThrows(NullPointerException.class, |
|
612 () -> decoder.decode(null, true, null)); |
|
613 } |
|
614 |
|
615 private static void test(String hexdump, |
|
616 String headerTable, String headerList) { |
|
617 test(new Decoder(4096), hexdump, headerTable, headerList); |
|
618 } |
|
619 |
|
620 private static void testAllSplits(String hexdump, |
|
621 String expectedHeaderTable, |
|
622 String expectedHeaderList) { |
|
623 testAllSplits(() -> new Decoder(256), hexdump, expectedHeaderTable, expectedHeaderList); |
|
624 } |
|
625 |
|
626 private static void testAllSplits(Supplier<Decoder> supplier, String hexdump, |
|
627 String expectedHeaderTable, String expectedHeaderList) { |
|
628 ByteBuffer source = SpecHelper.toBytes(hexdump); |
|
629 |
|
630 BuffersTestingKit.forEachSplit(source, iterable -> { |
|
631 List<String> actual = new LinkedList<>(); |
|
632 Iterator<? extends ByteBuffer> i = iterable.iterator(); |
|
633 if (!i.hasNext()) { |
|
634 return; |
|
635 } |
|
636 Decoder d = supplier.get(); |
|
637 do { |
|
638 ByteBuffer n = i.next(); |
|
639 try { |
|
640 d.decode(n, !i.hasNext(), (name, value) -> { |
|
641 if (value == null) { |
|
642 actual.add(name.toString()); |
|
643 } else { |
|
644 actual.add(name + ": " + value); |
|
645 } |
|
646 }); |
|
647 } catch (IOException e) { |
|
648 throw new UncheckedIOException(e); |
|
649 } |
|
650 } while (i.hasNext()); |
|
651 assertEquals(d.getTable().getStateString(), expectedHeaderTable); |
|
652 assertEquals(actual.stream().collect(Collectors.joining("\n")), expectedHeaderList); |
|
653 }); |
|
654 } |
|
655 |
|
656 // |
|
657 // Sometimes we need to keep the same decoder along several runs, |
|
658 // as it models the same connection |
|
659 // |
|
660 private static void test(Decoder d, String hexdump, |
|
661 String expectedHeaderTable, String expectedHeaderList) { |
|
662 |
|
663 ByteBuffer source = SpecHelper.toBytes(hexdump); |
|
664 |
|
665 List<String> actual = new LinkedList<>(); |
|
666 try { |
|
667 d.decode(source, true, (name, value) -> { |
|
668 if (value == null) { |
|
669 actual.add(name.toString()); |
|
670 } else { |
|
671 actual.add(name + ": " + value); |
|
672 } |
|
673 }); |
|
674 } catch (IOException e) { |
|
675 throw new UncheckedIOException(e); |
|
676 } |
|
677 |
|
678 assertEquals(d.getTable().getStateString(), expectedHeaderTable); |
|
679 assertEquals(actual.stream().collect(Collectors.joining("\n")), expectedHeaderList); |
|
680 } |
|
681 |
|
682 private static DecodingCallback nopCallback() { |
|
683 return (t, u) -> { }; |
|
684 } |
|
685 } |