test/jdk/java/net/httpclient/http2/java.net.http/java/net/http/internal/hpack/EncoderTest.java
branchhttp-client-branch
changeset 56089 42208b2f224e
parent 48083 b1c1b4ef4be2
equal deleted inserted replaced
56088:38fac6d0521d 56089:42208b2f224e
       
     1 /*
       
     2  * Copyright (c) 2014, 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 java.net.http.internal.hpack;
       
    24 
       
    25 import org.testng.annotations.Test;
       
    26 
       
    27 import java.io.IOException;
       
    28 import java.nio.Buffer;
       
    29 import java.nio.ByteBuffer;
       
    30 import java.util.ArrayList;
       
    31 import java.util.Collections;
       
    32 import java.util.Iterator;
       
    33 import java.util.LinkedList;
       
    34 import java.util.List;
       
    35 import java.util.function.Consumer;
       
    36 import java.util.function.Function;
       
    37 
       
    38 import static java.net.http.internal.hpack.BuffersTestingKit.concat;
       
    39 import static java.net.http.internal.hpack.BuffersTestingKit.forEachSplit;
       
    40 import static java.net.http.internal.hpack.SpecHelper.toHexdump;
       
    41 import static java.net.http.internal.hpack.TestHelper.assertVoidThrows;
       
    42 import static java.util.Arrays.asList;
       
    43 import static org.testng.Assert.assertEquals;
       
    44 import static org.testng.Assert.assertTrue;
       
    45 
       
    46 // TODO: map textual representation of commands from the spec to actual
       
    47 // calls to encoder (actually, this is a good idea for decoder as well)
       
    48 public final class EncoderTest {
       
    49 
       
    50     //
       
    51     // http://tools.ietf.org/html/rfc7541#appendix-C.2.1
       
    52     //
       
    53     @Test
       
    54     public void example1() {
       
    55 
       
    56         Encoder e = newCustomEncoder(256);
       
    57         drainInitialUpdate(e);
       
    58 
       
    59         e.literalWithIndexing("custom-key", false, "custom-header", false);
       
    60         // @formatter:off
       
    61         test(e,
       
    62 
       
    63              "400a 6375 7374 6f6d 2d6b 6579 0d63 7573\n" +
       
    64              "746f 6d2d 6865 6164 6572",
       
    65 
       
    66              "[  1] (s =  55) custom-key: custom-header\n" +
       
    67              "      Table size:  55");
       
    68         // @formatter:on
       
    69     }
       
    70 
       
    71     //
       
    72     // http://tools.ietf.org/html/rfc7541#appendix-C.2.2
       
    73     //
       
    74     @Test
       
    75     public void example2() {
       
    76 
       
    77         Encoder e = newCustomEncoder(256);
       
    78         drainInitialUpdate(e);
       
    79 
       
    80         e.literal(4, "/sample/path", false);
       
    81         // @formatter:off
       
    82         test(e,
       
    83 
       
    84              "040c 2f73 616d 706c 652f 7061 7468",
       
    85 
       
    86              "empty.");
       
    87         // @formatter:on
       
    88     }
       
    89 
       
    90     //
       
    91     // http://tools.ietf.org/html/rfc7541#appendix-C.2.3
       
    92     //
       
    93     @Test
       
    94     public void example3() {
       
    95 
       
    96         Encoder e = newCustomEncoder(256);
       
    97         drainInitialUpdate(e);
       
    98 
       
    99         e.literalNeverIndexed("password", false, "secret", false);
       
   100         // @formatter:off
       
   101         test(e,
       
   102 
       
   103              "1008 7061 7373 776f 7264 0673 6563 7265\n" +
       
   104              "74",
       
   105 
       
   106              "empty.");
       
   107         // @formatter:on
       
   108     }
       
   109 
       
   110     //
       
   111     // http://tools.ietf.org/html/rfc7541#appendix-C.2.4
       
   112     //
       
   113     @Test
       
   114     public void example4() {
       
   115 
       
   116         Encoder e = newCustomEncoder(256);
       
   117         drainInitialUpdate(e);
       
   118 
       
   119         e.indexed(2);
       
   120         // @formatter:off
       
   121         test(e,
       
   122 
       
   123              "82",
       
   124 
       
   125              "empty.");
       
   126         // @formatter:on
       
   127     }
       
   128 
       
   129     //
       
   130     // http://tools.ietf.org/html/rfc7541#appendix-C.3
       
   131     //
       
   132     @Test
       
   133     public void example5() {
       
   134         Encoder e = newCustomEncoder(256);
       
   135         drainInitialUpdate(e);
       
   136 
       
   137         ByteBuffer output = ByteBuffer.allocate(64);
       
   138         e.indexed(2);
       
   139         e.encode(output);
       
   140         e.indexed(6);
       
   141         e.encode(output);
       
   142         e.indexed(4);
       
   143         e.encode(output);
       
   144         e.literalWithIndexing(1, "www.example.com", false);
       
   145         e.encode(output);
       
   146 
       
   147         output.flip();
       
   148 
       
   149         // @formatter:off
       
   150         test(e, output,
       
   151 
       
   152              "8286 8441 0f77 7777 2e65 7861 6d70 6c65\n" +
       
   153              "2e63 6f6d",
       
   154 
       
   155              "[  1] (s =  57) :authority: www.example.com\n" +
       
   156              "      Table size:  57");
       
   157 
       
   158         output.clear();
       
   159 
       
   160         e.indexed( 2);
       
   161         e.encode(output);
       
   162         e.indexed( 6);
       
   163         e.encode(output);
       
   164         e.indexed( 4);
       
   165         e.encode(output);
       
   166         e.indexed(62);
       
   167         e.encode(output);
       
   168         e.literalWithIndexing(24, "no-cache", false);
       
   169         e.encode(output);
       
   170 
       
   171         output.flip();
       
   172 
       
   173         test(e, output,
       
   174 
       
   175              "8286 84be 5808 6e6f 2d63 6163 6865",
       
   176 
       
   177              "[  1] (s =  53) cache-control: no-cache\n" +
       
   178              "[  2] (s =  57) :authority: www.example.com\n" +
       
   179              "      Table size: 110");
       
   180 
       
   181         output.clear();
       
   182 
       
   183         e.indexed( 2);
       
   184         e.encode(output);
       
   185         e.indexed( 7);
       
   186         e.encode(output);
       
   187         e.indexed( 5);
       
   188         e.encode(output);
       
   189         e.indexed(63);
       
   190         e.encode(output);
       
   191         e.literalWithIndexing("custom-key", false, "custom-value", false);
       
   192         e.encode(output);
       
   193 
       
   194         output.flip();
       
   195 
       
   196         test(e, output,
       
   197 
       
   198              "8287 85bf 400a 6375 7374 6f6d 2d6b 6579\n" +
       
   199              "0c63 7573 746f 6d2d 7661 6c75 65",
       
   200 
       
   201              "[  1] (s =  54) custom-key: custom-value\n" +
       
   202              "[  2] (s =  53) cache-control: no-cache\n" +
       
   203              "[  3] (s =  57) :authority: www.example.com\n" +
       
   204              "      Table size: 164");
       
   205         // @formatter:on
       
   206     }
       
   207 
       
   208     @Test
       
   209     public void example5AllSplits() {
       
   210 
       
   211         List<Consumer<Encoder>> actions = new LinkedList<>();
       
   212         actions.add(e -> e.indexed(2));
       
   213         actions.add(e -> e.indexed(6));
       
   214         actions.add(e -> e.indexed(4));
       
   215         actions.add(e -> e.literalWithIndexing(1, "www.example.com", false));
       
   216 
       
   217         encodeAllSplits(
       
   218                 actions,
       
   219 
       
   220                 "8286 8441 0f77 7777 2e65 7861 6d70 6c65\n" +
       
   221                 "2e63 6f6d",
       
   222 
       
   223                 "[  1] (s =  57) :authority: www.example.com\n" +
       
   224                 "      Table size:  57");
       
   225     }
       
   226 
       
   227     private static void encodeAllSplits(Iterable<Consumer<Encoder>> consumers,
       
   228                                         String expectedHexdump,
       
   229                                         String expectedTableState) {
       
   230         ByteBuffer buffer = SpecHelper.toBytes(expectedHexdump);
       
   231         erase(buffer); // Zeroed buffer of size needed to hold the encoding
       
   232         forEachSplit(buffer, iterable -> {
       
   233             List<ByteBuffer> copy = new LinkedList<>();
       
   234             iterable.forEach(b -> copy.add(ByteBuffer.allocate(b.remaining())));
       
   235             Iterator<ByteBuffer> output = copy.iterator();
       
   236             if (!output.hasNext()) {
       
   237                 throw new IllegalStateException("No buffers to encode to");
       
   238             }
       
   239             Encoder e = newCustomEncoder(256); // FIXME: pull up (as a parameter)
       
   240             drainInitialUpdate(e);
       
   241             boolean encoded;
       
   242             ByteBuffer b = output.next();
       
   243             for (Consumer<Encoder> c : consumers) {
       
   244                 c.accept(e);
       
   245                 do {
       
   246                     encoded = e.encode(b);
       
   247                     if (!encoded) {
       
   248                         if (output.hasNext()) {
       
   249                             b = output.next();
       
   250                         } else {
       
   251                             throw new IllegalStateException("No room for encoding");
       
   252                         }
       
   253                     }
       
   254                 }
       
   255                 while (!encoded);
       
   256             }
       
   257             copy.forEach(Buffer::flip);
       
   258             ByteBuffer data = concat(copy);
       
   259             test(e, data, expectedHexdump, expectedTableState);
       
   260         });
       
   261     }
       
   262 
       
   263     //
       
   264     // http://tools.ietf.org/html/rfc7541#appendix-C.4
       
   265     //
       
   266     @Test
       
   267     public void example6() {
       
   268         Encoder e = newCustomEncoder(256);
       
   269         drainInitialUpdate(e);
       
   270 
       
   271         ByteBuffer output = ByteBuffer.allocate(64);
       
   272         e.indexed(2);
       
   273         e.encode(output);
       
   274         e.indexed(6);
       
   275         e.encode(output);
       
   276         e.indexed(4);
       
   277         e.encode(output);
       
   278         e.literalWithIndexing(1, "www.example.com", true);
       
   279         e.encode(output);
       
   280 
       
   281         output.flip();
       
   282 
       
   283         // @formatter:off
       
   284         test(e, output,
       
   285 
       
   286              "8286 8441 8cf1 e3c2 e5f2 3a6b a0ab 90f4\n" +
       
   287              "ff",
       
   288 
       
   289              "[  1] (s =  57) :authority: www.example.com\n" +
       
   290              "      Table size:  57");
       
   291 
       
   292         output.clear();
       
   293 
       
   294         e.indexed( 2);
       
   295         e.encode(output);
       
   296         e.indexed( 6);
       
   297         e.encode(output);
       
   298         e.indexed( 4);
       
   299         e.encode(output);
       
   300         e.indexed(62);
       
   301         e.encode(output);
       
   302         e.literalWithIndexing(24, "no-cache", true);
       
   303         e.encode(output);
       
   304 
       
   305         output.flip();
       
   306 
       
   307         test(e, output,
       
   308 
       
   309              "8286 84be 5886 a8eb 1064 9cbf",
       
   310 
       
   311              "[  1] (s =  53) cache-control: no-cache\n" +
       
   312              "[  2] (s =  57) :authority: www.example.com\n" +
       
   313              "      Table size: 110");
       
   314 
       
   315         output.clear();
       
   316 
       
   317         e.indexed( 2);
       
   318         e.encode(output);
       
   319         e.indexed( 7);
       
   320         e.encode(output);
       
   321         e.indexed( 5);
       
   322         e.encode(output);
       
   323         e.indexed(63);
       
   324         e.encode(output);
       
   325         e.literalWithIndexing("custom-key", true, "custom-value", true);
       
   326         e.encode(output);
       
   327 
       
   328         output.flip();
       
   329 
       
   330         test(e, output,
       
   331 
       
   332              "8287 85bf 4088 25a8 49e9 5ba9 7d7f 8925\n" +
       
   333              "a849 e95b b8e8 b4bf",
       
   334 
       
   335              "[  1] (s =  54) custom-key: custom-value\n" +
       
   336              "[  2] (s =  53) cache-control: no-cache\n" +
       
   337              "[  3] (s =  57) :authority: www.example.com\n" +
       
   338              "      Table size: 164");
       
   339         // @formatter:on
       
   340     }
       
   341 
       
   342     //
       
   343     // http://tools.ietf.org/html/rfc7541#appendix-C.5
       
   344     //
       
   345     @Test
       
   346     public void example7() {
       
   347         Encoder e = newCustomEncoder(256);
       
   348         drainInitialUpdate(e);
       
   349 
       
   350         ByteBuffer output = ByteBuffer.allocate(128);
       
   351         // @formatter:off
       
   352         e.literalWithIndexing( 8, "302", false);
       
   353         e.encode(output);
       
   354         e.literalWithIndexing(24, "private", false);
       
   355         e.encode(output);
       
   356         e.literalWithIndexing(33, "Mon, 21 Oct 2013 20:13:21 GMT", false);
       
   357         e.encode(output);
       
   358         e.literalWithIndexing(46, "https://www.example.com", false);
       
   359         e.encode(output);
       
   360 
       
   361         output.flip();
       
   362 
       
   363         test(e, output,
       
   364 
       
   365              "4803 3330 3258 0770 7269 7661 7465 611d\n" +
       
   366              "4d6f 6e2c 2032 3120 4f63 7420 3230 3133\n" +
       
   367              "2032 303a 3133 3a32 3120 474d 546e 1768\n" +
       
   368              "7474 7073 3a2f 2f77 7777 2e65 7861 6d70\n" +
       
   369              "6c65 2e63 6f6d",
       
   370 
       
   371              "[  1] (s =  63) location: https://www.example.com\n" +
       
   372              "[  2] (s =  65) date: Mon, 21 Oct 2013 20:13:21 GMT\n" +
       
   373              "[  3] (s =  52) cache-control: private\n" +
       
   374              "[  4] (s =  42) :status: 302\n" +
       
   375              "      Table size: 222");
       
   376 
       
   377         output.clear();
       
   378 
       
   379         e.literalWithIndexing( 8, "307", false);
       
   380         e.encode(output);
       
   381         e.indexed(65);
       
   382         e.encode(output);
       
   383         e.indexed(64);
       
   384         e.encode(output);
       
   385         e.indexed(63);
       
   386         e.encode(output);
       
   387 
       
   388         output.flip();
       
   389 
       
   390         test(e, output,
       
   391 
       
   392              "4803 3330 37c1 c0bf",
       
   393 
       
   394              "[  1] (s =  42) :status: 307\n" +
       
   395              "[  2] (s =  63) location: https://www.example.com\n" +
       
   396              "[  3] (s =  65) date: Mon, 21 Oct 2013 20:13:21 GMT\n" +
       
   397              "[  4] (s =  52) cache-control: private\n" +
       
   398              "      Table size: 222");
       
   399 
       
   400         output.clear();
       
   401 
       
   402         e.indexed( 8);
       
   403         e.encode(output);
       
   404         e.indexed(65);
       
   405         e.encode(output);
       
   406         e.literalWithIndexing(33, "Mon, 21 Oct 2013 20:13:22 GMT", false);
       
   407         e.encode(output);
       
   408         e.indexed(64);
       
   409         e.encode(output);
       
   410         e.literalWithIndexing(26, "gzip", false);
       
   411         e.encode(output);
       
   412         e.literalWithIndexing(55, "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1", false);
       
   413         e.encode(output);
       
   414 
       
   415         output.flip();
       
   416 
       
   417         test(e, output,
       
   418 
       
   419              "88c1 611d 4d6f 6e2c 2032 3120 4f63 7420\n" +
       
   420              "3230 3133 2032 303a 3133 3a32 3220 474d\n" +
       
   421              "54c0 5a04 677a 6970 7738 666f 6f3d 4153\n" +
       
   422              "444a 4b48 514b 425a 584f 5157 454f 5049\n" +
       
   423              "5541 5851 5745 4f49 553b 206d 6178 2d61\n" +
       
   424              "6765 3d33 3630 303b 2076 6572 7369 6f6e\n" +
       
   425              "3d31",
       
   426 
       
   427              "[  1] (s =  98) set-cookie: foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1\n" +
       
   428              "[  2] (s =  52) content-encoding: gzip\n" +
       
   429              "[  3] (s =  65) date: Mon, 21 Oct 2013 20:13:22 GMT\n" +
       
   430              "      Table size: 215");
       
   431         // @formatter:on
       
   432     }
       
   433 
       
   434     //
       
   435     // http://tools.ietf.org/html/rfc7541#appendix-C.6
       
   436     //
       
   437     @Test
       
   438     public void example8() {
       
   439         Encoder e = newCustomEncoder(256);
       
   440         drainInitialUpdate(e);
       
   441 
       
   442         ByteBuffer output = ByteBuffer.allocate(128);
       
   443         // @formatter:off
       
   444         e.literalWithIndexing( 8, "302", true);
       
   445         e.encode(output);
       
   446         e.literalWithIndexing(24, "private", true);
       
   447         e.encode(output);
       
   448         e.literalWithIndexing(33, "Mon, 21 Oct 2013 20:13:21 GMT", true);
       
   449         e.encode(output);
       
   450         e.literalWithIndexing(46, "https://www.example.com", true);
       
   451         e.encode(output);
       
   452 
       
   453         output.flip();
       
   454 
       
   455         test(e, output,
       
   456 
       
   457              "4882 6402 5885 aec3 771a 4b61 96d0 7abe\n" +
       
   458              "9410 54d4 44a8 2005 9504 0b81 66e0 82a6\n" +
       
   459              "2d1b ff6e 919d 29ad 1718 63c7 8f0b 97c8\n" +
       
   460              "e9ae 82ae 43d3",
       
   461 
       
   462              "[  1] (s =  63) location: https://www.example.com\n" +
       
   463              "[  2] (s =  65) date: Mon, 21 Oct 2013 20:13:21 GMT\n" +
       
   464              "[  3] (s =  52) cache-control: private\n" +
       
   465              "[  4] (s =  42) :status: 302\n" +
       
   466              "      Table size: 222");
       
   467 
       
   468         output.clear();
       
   469 
       
   470         e.literalWithIndexing( 8, "307", true);
       
   471         e.encode(output);
       
   472         e.indexed(65);
       
   473         e.encode(output);
       
   474         e.indexed(64);
       
   475         e.encode(output);
       
   476         e.indexed(63);
       
   477         e.encode(output);
       
   478 
       
   479         output.flip();
       
   480 
       
   481         test(e, output,
       
   482 
       
   483              "4883 640e ffc1 c0bf",
       
   484 
       
   485              "[  1] (s =  42) :status: 307\n" +
       
   486              "[  2] (s =  63) location: https://www.example.com\n" +
       
   487              "[  3] (s =  65) date: Mon, 21 Oct 2013 20:13:21 GMT\n" +
       
   488              "[  4] (s =  52) cache-control: private\n" +
       
   489              "      Table size: 222");
       
   490 
       
   491         output.clear();
       
   492 
       
   493         e.indexed( 8);
       
   494         e.encode(output);
       
   495         e.indexed(65);
       
   496         e.encode(output);
       
   497         e.literalWithIndexing(33, "Mon, 21 Oct 2013 20:13:22 GMT", true);
       
   498         e.encode(output);
       
   499         e.indexed(64);
       
   500         e.encode(output);
       
   501         e.literalWithIndexing(26, "gzip", true);
       
   502         e.encode(output);
       
   503         e.literalWithIndexing(55, "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1", true);
       
   504         e.encode(output);
       
   505 
       
   506         output.flip();
       
   507 
       
   508         test(e, output,
       
   509 
       
   510              "88c1 6196 d07a be94 1054 d444 a820 0595\n" +
       
   511              "040b 8166 e084 a62d 1bff c05a 839b d9ab\n" +
       
   512              "77ad 94e7 821d d7f2 e6c7 b335 dfdf cd5b\n" +
       
   513              "3960 d5af 2708 7f36 72c1 ab27 0fb5 291f\n" +
       
   514              "9587 3160 65c0 03ed 4ee5 b106 3d50 07",
       
   515 
       
   516              "[  1] (s =  98) set-cookie: foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1\n" +
       
   517              "[  2] (s =  52) content-encoding: gzip\n" +
       
   518              "[  3] (s =  65) date: Mon, 21 Oct 2013 20:13:22 GMT\n" +
       
   519              "      Table size: 215");
       
   520         // @formatter:on
       
   521     }
       
   522 
       
   523     @Test
       
   524     public void initialSizeUpdateDefaultEncoder() throws IOException {
       
   525         Function<Integer, Encoder> e = Encoder::new;
       
   526         testSizeUpdate(e, 1024, asList(), asList(0));
       
   527         testSizeUpdate(e, 1024, asList(1024), asList(0));
       
   528         testSizeUpdate(e, 1024, asList(1024, 1024), asList(0));
       
   529         testSizeUpdate(e, 1024, asList(1024, 512), asList(0));
       
   530         testSizeUpdate(e, 1024, asList(512, 1024), asList(0));
       
   531         testSizeUpdate(e, 1024, asList(512, 2048), asList(0));
       
   532     }
       
   533 
       
   534     @Test
       
   535     public void initialSizeUpdateCustomEncoder() throws IOException {
       
   536         Function<Integer, Encoder> e = EncoderTest::newCustomEncoder;
       
   537         testSizeUpdate(e, 1024, asList(), asList(1024));
       
   538         testSizeUpdate(e, 1024, asList(1024), asList(1024));
       
   539         testSizeUpdate(e, 1024, asList(1024, 1024), asList(1024));
       
   540         testSizeUpdate(e, 1024, asList(1024, 512), asList(512));
       
   541         testSizeUpdate(e, 1024, asList(512, 1024), asList(1024));
       
   542         testSizeUpdate(e, 1024, asList(512, 2048), asList(2048));
       
   543     }
       
   544 
       
   545     @Test
       
   546     public void seriesOfSizeUpdatesDefaultEncoder() throws IOException {
       
   547         Function<Integer, Encoder> e = c -> {
       
   548             Encoder encoder = new Encoder(c);
       
   549             drainInitialUpdate(encoder);
       
   550             return encoder;
       
   551         };
       
   552         testSizeUpdate(e, 0, asList(0), asList());
       
   553         testSizeUpdate(e, 1024, asList(1024), asList());
       
   554         testSizeUpdate(e, 1024, asList(2048), asList());
       
   555         testSizeUpdate(e, 1024, asList(512), asList());
       
   556         testSizeUpdate(e, 1024, asList(1024, 1024), asList());
       
   557         testSizeUpdate(e, 1024, asList(1024, 2048), asList());
       
   558         testSizeUpdate(e, 1024, asList(2048, 1024), asList());
       
   559         testSizeUpdate(e, 1024, asList(1024, 512), asList());
       
   560         testSizeUpdate(e, 1024, asList(512, 1024), asList());
       
   561     }
       
   562 
       
   563     //
       
   564     // https://tools.ietf.org/html/rfc7541#section-4.2
       
   565     //
       
   566     @Test
       
   567     public void seriesOfSizeUpdatesCustomEncoder() throws IOException {
       
   568         Function<Integer, Encoder> e = c -> {
       
   569             Encoder encoder = newCustomEncoder(c);
       
   570             drainInitialUpdate(encoder);
       
   571             return encoder;
       
   572         };
       
   573         testSizeUpdate(e, 0, asList(0), asList());
       
   574         testSizeUpdate(e, 1024, asList(1024), asList());
       
   575         testSizeUpdate(e, 1024, asList(2048), asList(2048));
       
   576         testSizeUpdate(e, 1024, asList(512), asList(512));
       
   577         testSizeUpdate(e, 1024, asList(1024, 1024), asList());
       
   578         testSizeUpdate(e, 1024, asList(1024, 2048), asList(2048));
       
   579         testSizeUpdate(e, 1024, asList(2048, 1024), asList());
       
   580         testSizeUpdate(e, 1024, asList(1024, 512), asList(512));
       
   581         testSizeUpdate(e, 1024, asList(512, 1024), asList(512, 1024));
       
   582     }
       
   583 
       
   584     @Test
       
   585     public void callSequenceViolations() {
       
   586         {   // Hasn't set up a header
       
   587             Encoder e = new Encoder(0);
       
   588             assertVoidThrows(IllegalStateException.class, () -> e.encode(ByteBuffer.allocate(16)));
       
   589         }
       
   590         {   // Can't set up header while there's an unfinished encoding
       
   591             Encoder e = new Encoder(0);
       
   592             e.indexed(32);
       
   593             assertVoidThrows(IllegalStateException.class, () -> e.indexed(32));
       
   594         }
       
   595         {   // Can't setMaxCapacity while there's an unfinished encoding
       
   596             Encoder e = new Encoder(0);
       
   597             e.indexed(32);
       
   598             assertVoidThrows(IllegalStateException.class, () -> e.setMaxCapacity(512));
       
   599         }
       
   600         {   // Hasn't set up a header
       
   601             Encoder e = new Encoder(0);
       
   602             e.setMaxCapacity(256);
       
   603             assertVoidThrows(IllegalStateException.class, () -> e.encode(ByteBuffer.allocate(16)));
       
   604         }
       
   605         {   // Hasn't set up a header after the previous encoding
       
   606             Encoder e = new Encoder(0);
       
   607             e.indexed(0);
       
   608             boolean encoded = e.encode(ByteBuffer.allocate(16));
       
   609             assertTrue(encoded); // assumption
       
   610             assertVoidThrows(IllegalStateException.class, () -> e.encode(ByteBuffer.allocate(16)));
       
   611         }
       
   612     }
       
   613 
       
   614     private static void test(Encoder encoder,
       
   615                              String expectedTableState,
       
   616                              String expectedHexdump) {
       
   617 
       
   618         ByteBuffer b = ByteBuffer.allocate(128);
       
   619         encoder.encode(b);
       
   620         b.flip();
       
   621         test(encoder, b, expectedTableState, expectedHexdump);
       
   622     }
       
   623 
       
   624     private static void test(Encoder encoder,
       
   625                              ByteBuffer output,
       
   626                              String expectedHexdump,
       
   627                              String expectedTableState) {
       
   628 
       
   629         String actualTableState = encoder.getHeaderTable().getStateString();
       
   630         assertEquals(actualTableState, expectedTableState);
       
   631 
       
   632         String actualHexdump = toHexdump(output);
       
   633         assertEquals(actualHexdump, expectedHexdump.replaceAll("\\n", " "));
       
   634     }
       
   635 
       
   636     // initial size - the size encoder is constructed with
       
   637     // updates      - a sequence of values for consecutive calls to encoder.setMaxCapacity
       
   638     // expected     - a sequence of values expected to be decoded by a decoder
       
   639     private void testSizeUpdate(Function<Integer, Encoder> encoder,
       
   640                                 int initialSize,
       
   641                                 List<Integer> updates,
       
   642                                 List<Integer> expected) throws IOException {
       
   643         Encoder e = encoder.apply(initialSize);
       
   644         updates.forEach(e::setMaxCapacity);
       
   645         ByteBuffer b = ByteBuffer.allocate(64);
       
   646         e.header("a", "b");
       
   647         e.encode(b);
       
   648         b.flip();
       
   649         Decoder d = new Decoder(updates.isEmpty() ? initialSize : Collections.max(updates));
       
   650         List<Integer> actual = new ArrayList<>();
       
   651         d.decode(b, true, new DecodingCallback() {
       
   652             @Override
       
   653             public void onDecoded(CharSequence name, CharSequence value) { }
       
   654 
       
   655             @Override
       
   656             public void onSizeUpdate(int capacity) {
       
   657                 actual.add(capacity);
       
   658             }
       
   659         });
       
   660         assertEquals(actual, expected);
       
   661     }
       
   662 
       
   663     //
       
   664     // Default encoder does not need any table, therefore a subclass that
       
   665     // behaves differently is needed
       
   666     //
       
   667     private static Encoder newCustomEncoder(int maxCapacity) {
       
   668         return new Encoder(maxCapacity) {
       
   669             @Override
       
   670             protected int calculateCapacity(int maxCapacity) {
       
   671                 return maxCapacity;
       
   672             }
       
   673         };
       
   674     }
       
   675 
       
   676     private static void drainInitialUpdate(Encoder e) {
       
   677         ByteBuffer b = ByteBuffer.allocate(4);
       
   678         e.header("a", "b");
       
   679         boolean done;
       
   680         do {
       
   681             done = e.encode(b);
       
   682             b.flip();
       
   683         } while (!done);
       
   684     }
       
   685 
       
   686     private static void erase(ByteBuffer buffer) {
       
   687         buffer.clear();
       
   688         while (buffer.hasRemaining()) {
       
   689             buffer.put((byte) 0);
       
   690         }
       
   691         buffer.clear();
       
   692     }
       
   693 }