test/jdk/java/net/httpclient/http2/java.net.http/jdk/internal/net/http/hpack/DecoderTest.java
branchhttp-client-branch
changeset 56092 fd85b2bf2b0d
parent 56089 42208b2f224e
child 56369 24a8fafec3ff
equal deleted inserted replaced
56091:aedd6133e7a0 56092:fd85b2bf2b0d
       
     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 }