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 }