|
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 } |