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 jdk.incubator.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 jdk.incubator.http.internal.hpack.BuffersTestingKit.concat;
  39 import static jdk.incubator.http.internal.hpack.BuffersTestingKit.forEachSplit;
  40 import static jdk.incubator.http.internal.hpack.SpecHelper.toHexdump;
  41 import static jdk.incubator.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 }