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 FileChannel fchannel = new RandomAccessFile(this.tmpfile, "rw").getChannel()) {
227             Assertions.assertThrows(IllegalStateException.class, () -> encoder.transfer(fchannel, 0, 10));
228         }
229     }
230 
231     @Test
232     public void testCodingFromFileSmaller() throws Exception {
233         final WritableByteChannelMock channel = new WritableByteChannelMock(64);
234         final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128);
235         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
236 
237         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(
238                 channel, outbuf, metrics, 16);
239 
240         createTempFile();
241         RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw");
242         try {
243             testfile.write("stuff;".getBytes(StandardCharsets.US_ASCII));
244             testfile.write("more stuff".getBytes(StandardCharsets.US_ASCII));
245         } finally {
246             testfile.close();
247         }
248 
249         testfile = new RandomAccessFile(this.tmpfile, "rw");
250         try {
251             final FileChannel fchannel = testfile.getChannel();
252             encoder.transfer(fchannel, 0, 20);
253         } finally {
254             testfile.close();
255         }
256         final String s = channel.dump(StandardCharsets.US_ASCII);
257 
258         Assertions.assertTrue(encoder.isCompleted());
259         Assertions.assertEquals("stuff;more stuff", s);
260     }
261 
262     @Test
263     public void testCodingFromFileFlushBuffer() throws Exception {
264         final WritableByteChannelMock channel = new WritableByteChannelMock(64);
265         final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128);
266         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
267 
268         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(
269                 channel, outbuf, metrics, 16);
270 
271         final CharArrayBuffer chbuffer = new CharArrayBuffer(16);
272         chbuffer.append("header");
273         outbuf.writeLine(chbuffer);
274 
275         createTempFile();
276         RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw");
277         try {
278             testfile.write("stuff;".getBytes(StandardCharsets.US_ASCII));
279             testfile.write("more stuff".getBytes(StandardCharsets.US_ASCII));
280         } finally {
281             testfile.close();
282         }
283 
284         testfile = new RandomAccessFile(this.tmpfile, "rw");
285         try {
286             final FileChannel fchannel = testfile.getChannel();
287             encoder.transfer(fchannel, 0, 20);
288         } finally {
289             testfile.close();
290         }
291         final String s = channel.dump(StandardCharsets.US_ASCII);
292 
293         Assertions.assertTrue(encoder.isCompleted());
294         Assertions.assertEquals("header\r\nstuff;more stuff", s);
295     }
296 
297     @Test
298     public void testCodingFromFileChannelSaturated() throws Exception {
299         final WritableByteChannelMock channel = new WritableByteChannelMock(64, 4);
300         final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128);
301         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
302 
303         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(
304                 channel, outbuf, metrics, 16);
305 
306         final CharArrayBuffer chbuffer = new CharArrayBuffer(16);
307         chbuffer.append("header");
308         outbuf.writeLine(chbuffer);
309 
310         createTempFile();
311         RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw");
312         try {
313             testfile.write("stuff".getBytes(StandardCharsets.US_ASCII));
314         } finally {
315             testfile.close();
316         }
317 
318         testfile = new RandomAccessFile(this.tmpfile, "rw");
319         try {
320             final FileChannel fchannel = testfile.getChannel();
321             encoder.transfer(fchannel, 0, 20);
322             encoder.transfer(fchannel, 0, 20);
323         } finally {
324             testfile.close();
325         }
326         final String s = channel.dump(StandardCharsets.US_ASCII);
327 
328         Assertions.assertFalse(encoder.isCompleted());
329         Assertions.assertEquals("head", s);
330     }
331 
332     @Test
333     public void testCodingNoFragmentBuffering() throws Exception {
334         final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64));
335         final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128));
336         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
337 
338         final CharArrayBuffer chbuffer = new CharArrayBuffer(16);
339         chbuffer.append("header");
340         outbuf.writeLine(chbuffer);
341         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics,
342             100, 0);
343         Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff")));
344 
345         Mockito.verify(channel, Mockito.times(2)).write(ArgumentMatchers.any());
346         Mockito.verify(outbuf, Mockito.never()).write(ArgumentMatchers.<ByteBuffer>any());
347         Mockito.verify(outbuf, Mockito.times(1)).flush(channel);
348 
349         Assertions.assertEquals(13, metrics.getBytesTransferred());
350 
351         outbuf.flush(channel);
352         final String s = channel.dump(StandardCharsets.US_ASCII);
353 
354         Assertions.assertEquals("header\r\nstuff", s);
355     }
356 
357     @Test
358     public void testCodingFragmentBuffering() throws Exception {
359         final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64));
360         final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128));
361         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
362 
363         final CharArrayBuffer chbuffer = new CharArrayBuffer(16);
364         chbuffer.append("header");
365         outbuf.writeLine(chbuffer);
366         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics,
367             100, 32);
368         Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff")));
369 
370         Mockito.verify(channel, Mockito.never()).write(ArgumentMatchers.any());
371         Mockito.verify(outbuf, Mockito.times(1)).write(ArgumentMatchers.<ByteBuffer>any());
372         Mockito.verify(outbuf, Mockito.never()).flush(channel);
373 
374         Assertions.assertEquals(0, metrics.getBytesTransferred());
375 
376         outbuf.flush(channel);
377         final String s = channel.dump(StandardCharsets.US_ASCII);
378 
379         Assertions.assertEquals("header\r\nstuff", s);
380     }
381 
382     @Test
383     public void testCodingFragmentBufferingMultipleFragments() throws Exception {
384         final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64));
385         final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128));
386         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
387 
388         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics,
389             100, 32);
390         Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff")));
391         Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
392         Assertions.assertEquals(10, encoder.write(CodecTestUtils.wrap("more stuff")));
393 
394         Mockito.verify(channel, Mockito.never()).write(ArgumentMatchers.any());
395         Mockito.verify(outbuf, Mockito.times(3)).write(ArgumentMatchers.<ByteBuffer>any());
396         Mockito.verify(outbuf, Mockito.never()).flush(channel);
397 
398         Assertions.assertEquals(0, metrics.getBytesTransferred());
399 
400         outbuf.flush(channel);
401         final String s = channel.dump(StandardCharsets.US_ASCII);
402 
403         Assertions.assertEquals("stuff-more stuff", s);
404     }
405 
406     @Test
407     public void testCodingFragmentBufferingMultipleFragmentsBeyondContentLimit() throws Exception {
408         final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64));
409         final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128));
410         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
411 
412         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics,
413             16, 32);
414         Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff")));
415         Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
416         Assertions.assertEquals(10, encoder.write(CodecTestUtils.wrap("more stuff; and a lot more stuff")));
417 
418         Mockito.verify(channel, Mockito.never()).write(ArgumentMatchers.any());
419         Mockito.verify(outbuf, Mockito.times(3)).write(ArgumentMatchers.<ByteBuffer>any());
420         Mockito.verify(outbuf, Mockito.never()).flush(channel);
421 
422         Assertions.assertEquals(0, metrics.getBytesTransferred());
423 
424         outbuf.flush(channel);
425         final String s = channel.dump(StandardCharsets.US_ASCII);
426 
427         Assertions.assertEquals("stuff-more stuff", s);
428     }
429 
430     @Test
431     public void testCodingFragmentBufferingLargeFragment() throws Exception {
432         final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64));
433         final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128));
434         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
435 
436         final CharArrayBuffer chbuffer = new CharArrayBuffer(16);
437         chbuffer.append("header");
438         outbuf.writeLine(chbuffer);
439         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics,
440             100, 2);
441         Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff")));
442 
443         Mockito.verify(channel, Mockito.times(2)).write(ArgumentMatchers.any());
444         Mockito.verify(outbuf, Mockito.never()).write(ArgumentMatchers.<ByteBuffer>any());
445         Mockito.verify(outbuf, Mockito.times(1)).flush(channel);
446 
447         Assertions.assertEquals(13, metrics.getBytesTransferred());
448 
449         outbuf.flush(channel);
450         final String s = channel.dump(StandardCharsets.US_ASCII);
451         Assertions.assertEquals("header\r\nstuff", s);
452     }
453 
454     @Test
455     public void testCodingFragmentBufferingTinyFragments() throws Exception {
456         final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64));
457         final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128));
458         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
459 
460         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics,
461             100, 1);
462         Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff")));
463         Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
464         Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
465         Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
466         Assertions.assertEquals(10, encoder.write(CodecTestUtils.wrap("more stuff")));
467 
468         Mockito.verify(channel, Mockito.times(5)).write(ArgumentMatchers.any());
469         Mockito.verify(outbuf, Mockito.times(3)).write(ArgumentMatchers.<ByteBuffer>any());
470         Mockito.verify(outbuf, Mockito.times(3)).flush(channel);
471 
472         Assertions.assertEquals(18, metrics.getBytesTransferred());
473 
474         outbuf.flush(channel);
475         final String s = channel.dump(StandardCharsets.US_ASCII);
476 
477         Assertions.assertEquals("stuff---more stuff", s);
478     }
479 
480     @Test
481     public void testCodingFragmentBufferingTinyFragments2() throws Exception {
482         final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64));
483         final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128));
484         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
485 
486         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics,
487             100, 2);
488         Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff")));
489         Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
490         Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
491         Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
492         Assertions.assertEquals(10, encoder.write(CodecTestUtils.wrap("more stuff")));
493 
494         Mockito.verify(channel, Mockito.times(4)).write(ArgumentMatchers.any());
495         Mockito.verify(outbuf, Mockito.times(3)).write(ArgumentMatchers.<ByteBuffer>any());
496         Mockito.verify(outbuf, Mockito.times(2)).flush(channel);
497 
498         Assertions.assertEquals(18, metrics.getBytesTransferred());
499 
500         outbuf.flush(channel);
501         final String s = channel.dump(StandardCharsets.US_ASCII);
502 
503         Assertions.assertEquals("stuff---more stuff", s);
504     }
505 
506     @Test
507     public void testCodingFragmentBufferingTinyFragments3() throws Exception {
508         final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64));
509         final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128));
510         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
511 
512         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics,
513             100, 3);
514         Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff")));
515         Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
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(2, encoder.write(CodecTestUtils.wrap("--")));
520         Assertions.assertEquals(10, encoder.write(CodecTestUtils.wrap("more stuff")));
521 
522         Mockito.verify(channel, Mockito.times(4)).write(ArgumentMatchers.any());
523         Mockito.verify(outbuf, Mockito.times(5)).write(ArgumentMatchers.<ByteBuffer>any());
524         Mockito.verify(outbuf, Mockito.times(2)).flush(channel);
525 
526         Assertions.assertEquals(21, metrics.getBytesTransferred());
527 
528         outbuf.flush(channel);
529         final String s = channel.dump(StandardCharsets.US_ASCII);
530 
531         Assertions.assertEquals("stuff------more stuff", s);
532     }
533 
534     @Test
535     public void testCodingFragmentBufferingBufferFlush() throws Exception {
536         final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64));
537         final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128));
538         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
539 
540         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics,
541             100, 8);
542         Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff")));
543         Assertions.assertEquals(6, encoder.write(CodecTestUtils.wrap("-stuff")));
544 
545         Mockito.verify(channel, Mockito.times(1)).write(ArgumentMatchers.any());
546         Mockito.verify(outbuf, Mockito.times(3)).write(ArgumentMatchers.<ByteBuffer>any());
547         Mockito.verify(outbuf, Mockito.times(1)).flush(channel);
548 
549         Assertions.assertEquals(8, metrics.getBytesTransferred());
550         Assertions.assertEquals(3, outbuf.length());
551 
552         outbuf.flush(channel);
553         final String s = channel.dump(StandardCharsets.US_ASCII);
554 
555         Assertions.assertEquals("stuff-stuff", s);
556     }
557 
558     @Test
559     public void testCodingFragmentBufferingBufferFlush2() throws Exception {
560         final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64));
561         final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128));
562         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
563 
564         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics,
565             100, 8);
566         Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff")));
567         Assertions.assertEquals(16, encoder.write(CodecTestUtils.wrap("-much more stuff")));
568 
569         Mockito.verify(channel, Mockito.times(2)).write(ArgumentMatchers.any());
570         Mockito.verify(outbuf, Mockito.times(1)).write(ArgumentMatchers.<ByteBuffer>any());
571         Mockito.verify(outbuf, Mockito.times(1)).flush(channel);
572 
573         Assertions.assertEquals(21, metrics.getBytesTransferred());
574         Assertions.assertEquals(0, outbuf.length());
575 
576         outbuf.flush(channel);
577         final String s = channel.dump(StandardCharsets.US_ASCII);
578 
579         Assertions.assertEquals("stuff-much more stuff", s);
580     }
581 
582     @Test
583     public void testCodingFragmentBufferingChannelSaturated() throws Exception {
584         final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64, 8));
585         final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128));
586         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
587 
588         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics,
589             100, 3);
590         Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff")));
591         Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
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(0, encoder.write(CodecTestUtils.wrap("-")));
598         Assertions.assertEquals(0, encoder.write(CodecTestUtils.wrap("more stuff")));
599 
600         Mockito.verify(channel, Mockito.times(5)).write(ArgumentMatchers.any());
601         Mockito.verify(outbuf, Mockito.times(6)).write(ArgumentMatchers.<ByteBuffer>any());
602         Mockito.verify(outbuf, Mockito.times(4)).flush(channel);
603 
604         Assertions.assertEquals(8, metrics.getBytesTransferred());
605 
606         outbuf.flush(channel);
607         final String s = channel.dump(StandardCharsets.US_ASCII);
608 
609         Assertions.assertEquals("stuff---", s);
610         Assertions.assertEquals(3, outbuf.length());
611     }
612 
613     @Test
614     public void testCodingFragmentBufferingChannelSaturated2() throws Exception {
615         final WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64, 8));
616         final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128));
617         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
618 
619         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics,
620             100, 8);
621         Assertions.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff")));
622         Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
623         Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
624         Assertions.assertEquals(1, encoder.write(CodecTestUtils.wrap("much more stuff")));
625 
626         Mockito.verify(channel, Mockito.times(3)).write(ArgumentMatchers.any());
627         Mockito.verify(outbuf, Mockito.times(3)).write(ArgumentMatchers.<ByteBuffer>any());
628         Mockito.verify(outbuf, Mockito.times(1)).flush(channel);
629 
630         Assertions.assertEquals(8, metrics.getBytesTransferred());
631 
632         outbuf.flush(channel);
633         final String s = channel.dump(StandardCharsets.US_ASCII);
634 
635         Assertions.assertEquals("stuff--m", s);
636         Assertions.assertEquals(0, outbuf.length());
637     }
638 
639 }