View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  
28  package org.apache.hc.core5.http.impl.nio;
29  
30  import java.io.File;
31  import java.io.IOException;
32  import java.io.RandomAccessFile;
33  import java.nio.ByteBuffer;
34  import java.nio.channels.FileChannel;
35  import java.nio.charset.StandardCharsets;
36  
37  import org.apache.hc.core5.http.WritableByteChannelMock;
38  import org.apache.hc.core5.http.impl.BasicHttpTransportMetrics;
39  import org.apache.hc.core5.http.nio.SessionOutputBuffer;
40  import org.apache.hc.core5.util.CharArrayBuffer;
41  import org.junit.jupiter.api.AfterEach;
42  import org.junit.jupiter.api.Assertions;
43  import org.junit.jupiter.api.Test;
44  import org.mockito.ArgumentMatchers;
45  import org.mockito.Mockito;
46  
47  /**
48   * Simple tests for {@link LengthDelimitedEncoder}.
49   */
50  public class TestLengthDelimitedEncoder {
51  
52      private File tmpfile;
53  
54      protected File createTempFile() throws IOException {
55          this.tmpfile = File.createTempFile("testFile", ".txt");
56          return this.tmpfile;
57      }
58  
59      @AfterEach
60      public void deleteTempFile() {
61          if (this.tmpfile != null && this.tmpfile.exists()) {
62              this.tmpfile.delete();
63          }
64      }
65  
66      @Test
67      public void testBasicCoding() throws Exception {
68          final WritableByteChannelMock channel = new WritableByteChannelMock(64);
69          final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128);
70          final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
71  
72          final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(
73                  channel, outbuf, metrics, 16);
74          encoder.write(CodecTestUtils.wrap("stuff;"));
75          encoder.write(CodecTestUtils.wrap("more stuff"));
76  
77          final String s = channel.dump(StandardCharsets.US_ASCII);
78  
79          Assertions.assertTrue(encoder.isCompleted());
80          Assertions.assertEquals("stuff;more stuff", s);
81          Assertions.assertEquals("[content length: 16; pos: 16; completed: true]", encoder.toString());
82      }
83  
84      @Test
85      public void testCodingBeyondContentLimit() throws Exception {
86          final WritableByteChannelMock channel = new WritableByteChannelMock(64);
87          final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128);
88          final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
89  
90          final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(
91                  channel, outbuf, metrics, 16);
92          encoder.write(CodecTestUtils.wrap("stuff;"));
93          encoder.write(CodecTestUtils.wrap("more stuff; and a lot more stuff"));
94  
95          final String s = channel.dump(StandardCharsets.US_ASCII);
96  
97          Assertions.assertTrue(encoder.isCompleted());
98          Assertions.assertEquals("stuff;more stuff", s);
99      }
100 
101     @Test
102     public void testCodingEmptyBuffer() throws Exception {
103         final WritableByteChannelMock channel = new WritableByteChannelMock(64);
104         final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128);
105         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
106 
107         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(
108                 channel, outbuf, metrics, 16);
109         encoder.write(CodecTestUtils.wrap("stuff;"));
110 
111         final ByteBuffer empty = ByteBuffer.allocate(100);
112         empty.flip();
113         encoder.write(empty);
114         encoder.write(null);
115 
116         encoder.write(CodecTestUtils.wrap("more stuff"));
117 
118         final String s = channel.dump(StandardCharsets.US_ASCII);
119 
120         Assertions.assertTrue(encoder.isCompleted());
121         Assertions.assertEquals("stuff;more stuff", s);
122     }
123 
124     @Test
125     public void testCodingCompleted() throws Exception {
126         final WritableByteChannelMock channel = new WritableByteChannelMock(64);
127         final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128);
128         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
129 
130         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(
131                 channel, outbuf, metrics, 5);
132         encoder.write(CodecTestUtils.wrap("stuff"));
133 
134         Assertions.assertThrows(IllegalStateException.class, () -> encoder.write(CodecTestUtils.wrap("more stuff")));
135     }
136 
137     @Test
138     public void testInvalidConstructor() {
139         final WritableByteChannelMock channel = new WritableByteChannelMock(64);
140         final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128);
141         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
142 
143         Assertions.assertThrows(NullPointerException.class, () -> new LengthDelimitedEncoder(null, null, null, 10));
144         Assertions.assertThrows(NullPointerException.class, () -> new LengthDelimitedEncoder(channel, null, null, 10));
145         Assertions.assertThrows(NullPointerException.class, () -> new LengthDelimitedEncoder(channel, outbuf, null, 10));
146         Assertions.assertThrows(IllegalArgumentException.class, () -> new LengthDelimitedEncoder(channel, outbuf, metrics, -10));
147     }
148 
149     @Test
150     public void testCodingBeyondContentLimitFromFile() throws Exception {
151         final WritableByteChannelMock channel = new WritableByteChannelMock(64);
152         final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128);
153         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
154 
155         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(
156                 channel, outbuf, metrics, 16);
157 
158         createTempFile();
159         RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw");
160         try {
161             testfile.write("stuff;".getBytes(StandardCharsets.US_ASCII));
162             testfile.write("more stuff; and a lot more stuff".getBytes(StandardCharsets.US_ASCII));
163         } finally {
164             testfile.close();
165         }
166 
167         testfile = new RandomAccessFile(this.tmpfile, "rw");
168         try {
169             final FileChannel fchannel = testfile.getChannel();
170             encoder.transfer(fchannel, 0, 20);
171         } finally {
172             testfile.close();
173         }
174 
175         final String s = channel.dump(StandardCharsets.US_ASCII);
176 
177         Assertions.assertTrue(encoder.isCompleted());
178         Assertions.assertEquals("stuff;more stuff", s);
179     }
180 
181     @Test
182     public void testCodingEmptyFile() throws Exception {
183         final WritableByteChannelMock channel = new WritableByteChannelMock(64);
184         final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128);
185         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
186 
187         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(
188                 channel, outbuf, metrics, 16);
189         encoder.write(CodecTestUtils.wrap("stuff;"));
190 
191         //Create an empty file
192         createTempFile();
193         RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw");
194         testfile.close();
195 
196         testfile = new RandomAccessFile(this.tmpfile, "rw");
197         try {
198             final FileChannel fchannel = testfile.getChannel();
199             encoder.transfer(fchannel, 0, 20);
200             encoder.write(CodecTestUtils.wrap("more stuff"));
201         } finally {
202             testfile.close();
203         }
204 
205         final String s = channel.dump(StandardCharsets.US_ASCII);
206 
207         Assertions.assertTrue(encoder.isCompleted());
208         Assertions.assertEquals("stuff;more stuff", s);
209     }
210 
211     @Test
212     public void testCodingCompletedFromFile() throws Exception {
213         final WritableByteChannelMock channel = new WritableByteChannelMock(64);
214         final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128);
215         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
216 
217         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(
218                 channel, outbuf, metrics, 5);
219         encoder.write(CodecTestUtils.wrap("stuff"));
220 
221         createTempFile();
222         try (final RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw")) {
223             testfile.write("more stuff".getBytes(StandardCharsets.US_ASCII));
224         }
225 
226         try (final RandomAccessFile file = new RandomAccessFile(this.tmpfile, "rw");
227              final FileChannel fchannel = file.getChannel()) {
228             Assertions.assertThrows(IllegalStateException.class, () -> encoder.transfer(fchannel, 0, 10));
229         }
230     }
231 
232     @Test
233     public void testCodingFromFileSmaller() throws Exception {
234         final WritableByteChannelMock channel = new WritableByteChannelMock(64);
235         final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128);
236         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
237 
238         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(
239                 channel, outbuf, metrics, 16);
240 
241         createTempFile();
242         RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw");
243         try {
244             testfile.write("stuff;".getBytes(StandardCharsets.US_ASCII));
245             testfile.write("more stuff".getBytes(StandardCharsets.US_ASCII));
246         } finally {
247             testfile.close();
248         }
249 
250         testfile = new RandomAccessFile(this.tmpfile, "rw");
251         try {
252             final FileChannel fchannel = testfile.getChannel();
253             encoder.transfer(fchannel, 0, 20);
254         } finally {
255             testfile.close();
256         }
257         final String s = channel.dump(StandardCharsets.US_ASCII);
258 
259         Assertions.assertTrue(encoder.isCompleted());
260         Assertions.assertEquals("stuff;more stuff", s);
261     }
262 
263     @Test
264     public void testCodingFromFileFlushBuffer() throws Exception {
265         final WritableByteChannelMock channel = new WritableByteChannelMock(64);
266         final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128);
267         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
268 
269         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(
270                 channel, outbuf, metrics, 16);
271 
272         final CharArrayBuffer chbuffer = new CharArrayBuffer(16);
273         chbuffer.append("header");
274         outbuf.writeLine(chbuffer);
275 
276         createTempFile();
277         RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw");
278         try {
279             testfile.write("stuff;".getBytes(StandardCharsets.US_ASCII));
280             testfile.write("more stuff".getBytes(StandardCharsets.US_ASCII));
281         } finally {
282             testfile.close();
283         }
284 
285         testfile = new RandomAccessFile(this.tmpfile, "rw");
286         try {
287             final FileChannel fchannel = testfile.getChannel();
288             encoder.transfer(fchannel, 0, 20);
289         } finally {
290             testfile.close();
291         }
292         final String s = channel.dump(StandardCharsets.US_ASCII);
293 
294         Assertions.assertTrue(encoder.isCompleted());
295         Assertions.assertEquals("header\r\nstuff;more stuff", s);
296     }
297 
298     @Test
299     public void testCodingFromFileChannelSaturated() throws Exception {
300         final WritableByteChannelMock channel = new WritableByteChannelMock(64, 4);
301         final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128);
302         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
303 
304         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(
305                 channel, outbuf, metrics, 16);
306 
307         final CharArrayBuffer chbuffer = new CharArrayBuffer(16);
308         chbuffer.append("header");
309         outbuf.writeLine(chbuffer);
310 
311         createTempFile();
312         RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw");
313         try {
314             testfile.write("stuff".getBytes(StandardCharsets.US_ASCII));
315         } finally {
316             testfile.close();
317         }
318 
319         testfile = new RandomAccessFile(this.tmpfile, "rw");
320         try {
321             final FileChannel fchannel = testfile.getChannel();
322             encoder.transfer(fchannel, 0, 20);
323             encoder.transfer(fchannel, 0, 20);
324         } finally {
325             testfile.close();
326         }
327         final String s = channel.dump(StandardCharsets.US_ASCII);
328 
329         Assertions.assertFalse(encoder.isCompleted());
330         Assertions.assertEquals("head", s);
331     }
332 
333     @Test
334     public void testCodingNoFragmentBuffering() throws Exception {
335         final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64));
336         final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128));
337         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
338 
339         final CharArrayBuffer chbuffer = new CharArrayBuffer(16);
340         chbuffer.append("header");
341         outbuf.writeLine(chbuffer);
342         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics,
343             100, 0);
344         Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff")));
345 
346         Mockito.verify(channel, Mockito.times(2)).write(ArgumentMatchers.any());
347         Mockito.verify(outbuf, Mockito.never()).write(ArgumentMatchers.<ByteBuffer>any());
348         Mockito.verify(outbuf, Mockito.times(1)).flush(channel);
349 
350         Assertions.assertEquals(13, metrics.getBytesTransferred());
351 
352         outbuf.flush(channel);
353         final String s = channel.dump(StandardCharsets.US_ASCII);
354 
355         Assertions.assertEquals("header\r\nstuff", s);
356     }
357 
358     @Test
359     public void testCodingFragmentBuffering() throws Exception {
360         final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64));
361         final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128));
362         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
363 
364         final CharArrayBuffer chbuffer = new CharArrayBuffer(16);
365         chbuffer.append("header");
366         outbuf.writeLine(chbuffer);
367         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics,
368             100, 32);
369         Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff")));
370 
371         Mockito.verify(channel, Mockito.never()).write(ArgumentMatchers.any());
372         Mockito.verify(outbuf, Mockito.times(1)).write(ArgumentMatchers.<ByteBuffer>any());
373         Mockito.verify(outbuf, Mockito.never()).flush(channel);
374 
375         Assertions.assertEquals(0, metrics.getBytesTransferred());
376 
377         outbuf.flush(channel);
378         final String s = channel.dump(StandardCharsets.US_ASCII);
379 
380         Assertions.assertEquals("header\r\nstuff", s);
381     }
382 
383     @Test
384     public void testCodingFragmentBufferingMultipleFragments() throws Exception {
385         final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64));
386         final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128));
387         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
388 
389         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics,
390             100, 32);
391         Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff")));
392         Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
393         Assertions.assertEquals(10, encoder.write(CodecTestUtils.wrap("more stuff")));
394 
395         Mockito.verify(channel, Mockito.never()).write(ArgumentMatchers.any());
396         Mockito.verify(outbuf, Mockito.times(3)).write(ArgumentMatchers.<ByteBuffer>any());
397         Mockito.verify(outbuf, Mockito.never()).flush(channel);
398 
399         Assertions.assertEquals(0, metrics.getBytesTransferred());
400 
401         outbuf.flush(channel);
402         final String s = channel.dump(StandardCharsets.US_ASCII);
403 
404         Assertions.assertEquals("stuff-more stuff", s);
405     }
406 
407     @Test
408     public void testCodingFragmentBufferingMultipleFragmentsBeyondContentLimit() throws Exception {
409         final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64));
410         final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128));
411         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
412 
413         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics,
414             16, 32);
415         Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff")));
416         Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
417         Assertions.assertEquals(10, encoder.write(CodecTestUtils.wrap("more stuff; and a lot more stuff")));
418 
419         Mockito.verify(channel, Mockito.never()).write(ArgumentMatchers.any());
420         Mockito.verify(outbuf, Mockito.times(3)).write(ArgumentMatchers.<ByteBuffer>any());
421         Mockito.verify(outbuf, Mockito.never()).flush(channel);
422 
423         Assertions.assertEquals(0, metrics.getBytesTransferred());
424 
425         outbuf.flush(channel);
426         final String s = channel.dump(StandardCharsets.US_ASCII);
427 
428         Assertions.assertEquals("stuff-more stuff", s);
429     }
430 
431     @Test
432     public void testCodingFragmentBufferingLargeFragment() throws Exception {
433         final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64));
434         final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128));
435         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
436 
437         final CharArrayBuffer chbuffer = new CharArrayBuffer(16);
438         chbuffer.append("header");
439         outbuf.writeLine(chbuffer);
440         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics,
441             100, 2);
442         Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff")));
443 
444         Mockito.verify(channel, Mockito.times(2)).write(ArgumentMatchers.any());
445         Mockito.verify(outbuf, Mockito.never()).write(ArgumentMatchers.<ByteBuffer>any());
446         Mockito.verify(outbuf, Mockito.times(1)).flush(channel);
447 
448         Assertions.assertEquals(13, metrics.getBytesTransferred());
449 
450         outbuf.flush(channel);
451         final String s = channel.dump(StandardCharsets.US_ASCII);
452         Assertions.assertEquals("header\r\nstuff", s);
453     }
454 
455     @Test
456     public void testCodingFragmentBufferingTinyFragments() throws Exception {
457         final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64));
458         final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128));
459         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
460 
461         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics,
462             100, 1);
463         Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff")));
464         Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
465         Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
466         Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
467         Assertions.assertEquals(10, encoder.write(CodecTestUtils.wrap("more stuff")));
468 
469         Mockito.verify(channel, Mockito.times(5)).write(ArgumentMatchers.any());
470         Mockito.verify(outbuf, Mockito.times(3)).write(ArgumentMatchers.<ByteBuffer>any());
471         Mockito.verify(outbuf, Mockito.times(3)).flush(channel);
472 
473         Assertions.assertEquals(18, metrics.getBytesTransferred());
474 
475         outbuf.flush(channel);
476         final String s = channel.dump(StandardCharsets.US_ASCII);
477 
478         Assertions.assertEquals("stuff---more stuff", s);
479     }
480 
481     @Test
482     public void testCodingFragmentBufferingTinyFragments2() throws Exception {
483         final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64));
484         final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128));
485         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
486 
487         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics,
488             100, 2);
489         Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff")));
490         Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
491         Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
492         Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
493         Assertions.assertEquals(10, encoder.write(CodecTestUtils.wrap("more stuff")));
494 
495         Mockito.verify(channel, Mockito.times(4)).write(ArgumentMatchers.any());
496         Mockito.verify(outbuf, Mockito.times(3)).write(ArgumentMatchers.<ByteBuffer>any());
497         Mockito.verify(outbuf, Mockito.times(2)).flush(channel);
498 
499         Assertions.assertEquals(18, metrics.getBytesTransferred());
500 
501         outbuf.flush(channel);
502         final String s = channel.dump(StandardCharsets.US_ASCII);
503 
504         Assertions.assertEquals("stuff---more stuff", s);
505     }
506 
507     @Test
508     public void testCodingFragmentBufferingTinyFragments3() throws Exception {
509         final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64));
510         final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128));
511         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
512 
513         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics,
514             100, 3);
515         Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff")));
516         Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
517         Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
518         Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
519         Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
520         Assertions.assertEquals(2, encoder.write(CodecTestUtils.wrap("--")));
521         Assertions.assertEquals(10, encoder.write(CodecTestUtils.wrap("more stuff")));
522 
523         Mockito.verify(channel, Mockito.times(4)).write(ArgumentMatchers.any());
524         Mockito.verify(outbuf, Mockito.times(5)).write(ArgumentMatchers.<ByteBuffer>any());
525         Mockito.verify(outbuf, Mockito.times(2)).flush(channel);
526 
527         Assertions.assertEquals(21, metrics.getBytesTransferred());
528 
529         outbuf.flush(channel);
530         final String s = channel.dump(StandardCharsets.US_ASCII);
531 
532         Assertions.assertEquals("stuff------more stuff", s);
533     }
534 
535     @Test
536     public void testCodingFragmentBufferingBufferFlush() throws Exception {
537         final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64));
538         final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128));
539         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
540 
541         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics,
542             100, 8);
543         Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff")));
544         Assertions.assertEquals(6, encoder.write(CodecTestUtils.wrap("-stuff")));
545 
546         Mockito.verify(channel, Mockito.times(1)).write(ArgumentMatchers.any());
547         Mockito.verify(outbuf, Mockito.times(3)).write(ArgumentMatchers.<ByteBuffer>any());
548         Mockito.verify(outbuf, Mockito.times(1)).flush(channel);
549 
550         Assertions.assertEquals(8, metrics.getBytesTransferred());
551         Assertions.assertEquals(3, outbuf.length());
552 
553         outbuf.flush(channel);
554         final String s = channel.dump(StandardCharsets.US_ASCII);
555 
556         Assertions.assertEquals("stuff-stuff", s);
557     }
558 
559     @Test
560     public void testCodingFragmentBufferingBufferFlush2() throws Exception {
561         final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64));
562         final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128));
563         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
564 
565         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics,
566             100, 8);
567         Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff")));
568         Assertions.assertEquals(16, encoder.write(CodecTestUtils.wrap("-much more stuff")));
569 
570         Mockito.verify(channel, Mockito.times(2)).write(ArgumentMatchers.any());
571         Mockito.verify(outbuf, Mockito.times(1)).write(ArgumentMatchers.<ByteBuffer>any());
572         Mockito.verify(outbuf, Mockito.times(1)).flush(channel);
573 
574         Assertions.assertEquals(21, metrics.getBytesTransferred());
575         Assertions.assertEquals(0, outbuf.length());
576 
577         outbuf.flush(channel);
578         final String s = channel.dump(StandardCharsets.US_ASCII);
579 
580         Assertions.assertEquals("stuff-much more stuff", s);
581     }
582 
583     @Test
584     public void testCodingFragmentBufferingChannelSaturated() throws Exception {
585         final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64, 8));
586         final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128));
587         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
588 
589         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics,
590             100, 3);
591         Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff")));
592         Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
593         Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
594         Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
595         Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
596         Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
597         Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
598         Assertions.assertEquals(0, encoder.write(CodecTestUtils.wrap("-")));
599         Assertions.assertEquals(0, encoder.write(CodecTestUtils.wrap("more stuff")));
600 
601         Mockito.verify(channel, Mockito.times(5)).write(ArgumentMatchers.any());
602         Mockito.verify(outbuf, Mockito.times(6)).write(ArgumentMatchers.<ByteBuffer>any());
603         Mockito.verify(outbuf, Mockito.times(4)).flush(channel);
604 
605         Assertions.assertEquals(8, metrics.getBytesTransferred());
606 
607         outbuf.flush(channel);
608         final String s = channel.dump(StandardCharsets.US_ASCII);
609 
610         Assertions.assertEquals("stuff---", s);
611         Assertions.assertEquals(3, outbuf.length());
612     }
613 
614     @Test
615     public void testCodingFragmentBufferingChannelSaturated2() throws Exception {
616         final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64, 8));
617         final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128));
618         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
619 
620         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics,
621             100, 8);
622         Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff")));
623         Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
624         Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
625         Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("much more stuff")));
626 
627         Mockito.verify(channel, Mockito.times(3)).write(ArgumentMatchers.any());
628         Mockito.verify(outbuf, Mockito.times(3)).write(ArgumentMatchers.<ByteBuffer>any());
629         Mockito.verify(outbuf, Mockito.times(1)).flush(channel);
630 
631         Assertions.assertEquals(8, metrics.getBytesTransferred());
632 
633         outbuf.flush(channel);
634         final String s = channel.dump(StandardCharsets.US_ASCII);
635 
636         Assertions.assertEquals("stuff--m", s);
637         Assertions.assertEquals(0, outbuf.length());
638     }
639 
640 }