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.incubator.http.internal.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.incubator.http.internal.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 }