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 }