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.After;
42  import org.junit.Assert;
43  import org.junit.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      @After
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 WritableByteChannelMockChannelMock.html#WritableByteChannelMock">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          Assert.assertTrue(encoder.isCompleted());
80          Assert.assertEquals("stuff;more stuff", s);
81          Assert.assertEquals("[content length: 16; pos: 16; completed: true]", encoder.toString());
82      }
83  
84      @Test
85      public void testCodingBeyondContentLimit() throws Exception {
86          final WritableByteChannelMockChannelMock.html#WritableByteChannelMock">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          Assert.assertTrue(encoder.isCompleted());
98          Assert.assertEquals("stuff;more stuff", s);
99      }
100 
101     @Test
102     public void testCodingEmptyBuffer() throws Exception {
103         final WritableByteChannelMockChannelMock.html#WritableByteChannelMock">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         Assert.assertTrue(encoder.isCompleted());
121         Assert.assertEquals("stuff;more stuff", s);
122     }
123 
124     @Test
125     public void testCodingCompleted() throws Exception {
126         final WritableByteChannelMockChannelMock.html#WritableByteChannelMock">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         try {
135             encoder.write(CodecTestUtils.wrap("more stuff"));
136             Assert.fail("IllegalStateException should have been thrown");
137         } catch (final IllegalStateException ex) {
138             // ignore
139         }
140     }
141 
142     @Test
143     public void testInvalidConstructor() {
144         final WritableByteChannelMockChannelMock.html#WritableByteChannelMock">WritableByteChannelMock channel = new WritableByteChannelMock(64);
145         final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128);
146         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
147 
148         try {
149             new LengthDelimitedEncoder(null, null, null, 10);
150             Assert.fail("NullPointerException should have been thrown");
151         } catch (final NullPointerException ex) {
152             // ignore
153         }
154         try {
155             new LengthDelimitedEncoder(channel, null, null, 10);
156             Assert.fail("NullPointerException should have been thrown");
157         } catch (final NullPointerException ex) {
158             // ignore
159         }
160         try {
161             new LengthDelimitedEncoder(channel, outbuf, null, 10);
162             Assert.fail("NullPointerException should have been thrown");
163         } catch (final NullPointerException ex) {
164             // ignore
165         }
166         try {
167             new LengthDelimitedEncoder(channel, outbuf, metrics, -10);
168             Assert.fail("IllegalArgumentException should have been thrown");
169         } catch (final IllegalArgumentException ex) {
170             // ignore
171         }
172     }
173 
174     @Test
175     public void testCodingBeyondContentLimitFromFile() throws Exception {
176         final WritableByteChannelMockChannelMock.html#WritableByteChannelMock">WritableByteChannelMock channel = new WritableByteChannelMock(64);
177         final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128);
178         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
179 
180         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(
181                 channel, outbuf, metrics, 16);
182 
183         createTempFile();
184         RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw");
185         try {
186             testfile.write("stuff;".getBytes(StandardCharsets.US_ASCII));
187             testfile.write("more stuff; and a lot more stuff".getBytes(StandardCharsets.US_ASCII));
188         } finally {
189             testfile.close();
190         }
191 
192         testfile = new RandomAccessFile(this.tmpfile, "rw");
193         try {
194             final FileChannel fchannel = testfile.getChannel();
195             encoder.transfer(fchannel, 0, 20);
196         } finally {
197             testfile.close();
198         }
199 
200         final String s = channel.dump(StandardCharsets.US_ASCII);
201 
202         Assert.assertTrue(encoder.isCompleted());
203         Assert.assertEquals("stuff;more stuff", s);
204     }
205 
206     @Test
207     public void testCodingEmptyFile() throws Exception {
208         final WritableByteChannelMockChannelMock.html#WritableByteChannelMock">WritableByteChannelMock channel = new WritableByteChannelMock(64);
209         final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128);
210         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
211 
212         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(
213                 channel, outbuf, metrics, 16);
214         encoder.write(CodecTestUtils.wrap("stuff;"));
215 
216         //Create an empty file
217         createTempFile();
218         RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw");
219         testfile.close();
220 
221         testfile = new RandomAccessFile(this.tmpfile, "rw");
222         try {
223             final FileChannel fchannel = testfile.getChannel();
224             encoder.transfer(fchannel, 0, 20);
225             encoder.write(CodecTestUtils.wrap("more stuff"));
226         } finally {
227             testfile.close();
228         }
229 
230         final String s = channel.dump(StandardCharsets.US_ASCII);
231 
232         Assert.assertTrue(encoder.isCompleted());
233         Assert.assertEquals("stuff;more stuff", s);
234     }
235 
236     @Test
237     public void testCodingCompletedFromFile() throws Exception {
238         final WritableByteChannelMockChannelMock.html#WritableByteChannelMock">WritableByteChannelMock channel = new WritableByteChannelMock(64);
239         final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128);
240         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
241 
242         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(
243                 channel, outbuf, metrics, 5);
244         encoder.write(CodecTestUtils.wrap("stuff"));
245 
246         createTempFile();
247         RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw");
248         try {
249             testfile.write("more stuff".getBytes(StandardCharsets.US_ASCII));
250         } finally {
251             testfile.close();
252         }
253 
254         testfile = new RandomAccessFile(this.tmpfile, "rw");
255         try {
256             final FileChannel fchannel = testfile.getChannel();
257             encoder.transfer(fchannel, 0, 10);
258             Assert.fail("IllegalStateException should have been thrown");
259         } catch (final IllegalStateException ex) {
260             // ignore
261         } finally {
262             testfile.close();
263         }
264     }
265 
266     @Test
267     public void testCodingFromFileSmaller() throws Exception {
268         final WritableByteChannelMockChannelMock.html#WritableByteChannelMock">WritableByteChannelMock channel = new WritableByteChannelMock(64);
269         final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128);
270         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
271 
272         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(
273                 channel, outbuf, metrics, 16);
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         Assert.assertTrue(encoder.isCompleted());
294         Assert.assertEquals("stuff;more stuff", s);
295     }
296 
297     @Test
298     public void testCodingFromFileFlushBuffer() throws Exception {
299         final WritableByteChannelMockChannelMock.html#WritableByteChannelMock">WritableByteChannelMock channel = new WritableByteChannelMock(64);
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             testfile.write("more 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         } finally {
324             testfile.close();
325         }
326         final String s = channel.dump(StandardCharsets.US_ASCII);
327 
328         Assert.assertTrue(encoder.isCompleted());
329         Assert.assertEquals("header\r\nstuff;more stuff", s);
330     }
331 
332     @Test
333     public void testCodingFromFileChannelSaturated() throws Exception {
334         final WritableByteChannelMockChannelMock.html#WritableByteChannelMock">WritableByteChannelMock channel = new WritableByteChannelMock(64, 4);
335         final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 128);
336         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
337 
338         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(
339                 channel, outbuf, metrics, 16);
340 
341         final CharArrayBuffer chbuffer = new CharArrayBuffer(16);
342         chbuffer.append("header");
343         outbuf.writeLine(chbuffer);
344 
345         createTempFile();
346         RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw");
347         try {
348             testfile.write("stuff".getBytes(StandardCharsets.US_ASCII));
349         } finally {
350             testfile.close();
351         }
352 
353         testfile = new RandomAccessFile(this.tmpfile, "rw");
354         try {
355             final FileChannel fchannel = testfile.getChannel();
356             encoder.transfer(fchannel, 0, 20);
357             encoder.transfer(fchannel, 0, 20);
358         } finally {
359             testfile.close();
360         }
361         final String s = channel.dump(StandardCharsets.US_ASCII);
362 
363         Assert.assertFalse(encoder.isCompleted());
364         Assert.assertEquals("head", s);
365     }
366 
367     @Test
368     public void testCodingNoFragmentBuffering() throws Exception {
369         final WritableByteChannelMockhtml#WritableByteChannelMock">WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64));
370         final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128));
371         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
372 
373         final CharArrayBuffer chbuffer = new CharArrayBuffer(16);
374         chbuffer.append("header");
375         outbuf.writeLine(chbuffer);
376         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics,
377             100, 0);
378         Assert.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff")));
379 
380         Mockito.verify(channel, Mockito.times(2)).write(ArgumentMatchers.<ByteBuffer>any());
381         Mockito.verify(outbuf, Mockito.never()).write(ArgumentMatchers.<ByteBuffer>any());
382         Mockito.verify(outbuf, Mockito.times(1)).flush(channel);
383 
384         Assert.assertEquals(13, metrics.getBytesTransferred());
385 
386         outbuf.flush(channel);
387         final String s = channel.dump(StandardCharsets.US_ASCII);
388 
389         Assert.assertEquals("header\r\nstuff", s);
390     }
391 
392     @Test
393     public void testCodingFragmentBuffering() throws Exception {
394         final WritableByteChannelMockhtml#WritableByteChannelMock">WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64));
395         final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128));
396         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
397 
398         final CharArrayBuffer chbuffer = new CharArrayBuffer(16);
399         chbuffer.append("header");
400         outbuf.writeLine(chbuffer);
401         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics,
402             100, 32);
403         Assert.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff")));
404 
405         Mockito.verify(channel, Mockito.never()).write(ArgumentMatchers.<ByteBuffer>any());
406         Mockito.verify(outbuf, Mockito.times(1)).write(ArgumentMatchers.<ByteBuffer>any());
407         Mockito.verify(outbuf, Mockito.never()).flush(channel);
408 
409         Assert.assertEquals(0, metrics.getBytesTransferred());
410 
411         outbuf.flush(channel);
412         final String s = channel.dump(StandardCharsets.US_ASCII);
413 
414         Assert.assertEquals("header\r\nstuff", s);
415     }
416 
417     @Test
418     public void testCodingFragmentBufferingMultipleFragments() throws Exception {
419         final WritableByteChannelMockhtml#WritableByteChannelMock">WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64));
420         final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128));
421         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
422 
423         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics,
424             100, 32);
425         Assert.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff")));
426         Assert.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
427         Assert.assertEquals(10, encoder.write(CodecTestUtils.wrap("more stuff")));
428 
429         Mockito.verify(channel, Mockito.never()).write(ArgumentMatchers.<ByteBuffer>any());
430         Mockito.verify(outbuf, Mockito.times(3)).write(ArgumentMatchers.<ByteBuffer>any());
431         Mockito.verify(outbuf, Mockito.never()).flush(channel);
432 
433         Assert.assertEquals(0, metrics.getBytesTransferred());
434 
435         outbuf.flush(channel);
436         final String s = channel.dump(StandardCharsets.US_ASCII);
437 
438         Assert.assertEquals("stuff-more stuff", s);
439     }
440 
441     @Test
442     public void testCodingFragmentBufferingMultipleFragmentsBeyondContentLimit() throws Exception {
443         final WritableByteChannelMockhtml#WritableByteChannelMock">WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64));
444         final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128));
445         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
446 
447         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics,
448             16, 32);
449         Assert.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff")));
450         Assert.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
451         Assert.assertEquals(10, encoder.write(CodecTestUtils.wrap("more stuff; and a lot more stuff")));
452 
453         Mockito.verify(channel, Mockito.never()).write(ArgumentMatchers.<ByteBuffer>any());
454         Mockito.verify(outbuf, Mockito.times(3)).write(ArgumentMatchers.<ByteBuffer>any());
455         Mockito.verify(outbuf, Mockito.never()).flush(channel);
456 
457         Assert.assertEquals(0, metrics.getBytesTransferred());
458 
459         outbuf.flush(channel);
460         final String s = channel.dump(StandardCharsets.US_ASCII);
461 
462         Assert.assertEquals("stuff-more stuff", s);
463     }
464 
465     @Test
466     public void testCodingFragmentBufferingLargeFragment() throws Exception {
467         final WritableByteChannelMockhtml#WritableByteChannelMock">WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64));
468         final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128));
469         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
470 
471         final CharArrayBuffer chbuffer = new CharArrayBuffer(16);
472         chbuffer.append("header");
473         outbuf.writeLine(chbuffer);
474         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics,
475             100, 2);
476         Assert.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff")));
477 
478         Mockito.verify(channel, Mockito.times(2)).write(ArgumentMatchers.<ByteBuffer>any());
479         Mockito.verify(outbuf, Mockito.never()).write(ArgumentMatchers.<ByteBuffer>any());
480         Mockito.verify(outbuf, Mockito.times(1)).flush(channel);
481 
482         Assert.assertEquals(13, metrics.getBytesTransferred());
483 
484         outbuf.flush(channel);
485         final String s = channel.dump(StandardCharsets.US_ASCII);
486         Assert.assertEquals("header\r\nstuff", s);
487     }
488 
489     @Test
490     public void testCodingFragmentBufferingTinyFragments() throws Exception {
491         final WritableByteChannelMockhtml#WritableByteChannelMock">WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64));
492         final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128));
493         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
494 
495         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics,
496             100, 1);
497         Assert.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff")));
498         Assert.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
499         Assert.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
500         Assert.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
501         Assert.assertEquals(10, encoder.write(CodecTestUtils.wrap("more stuff")));
502 
503         Mockito.verify(channel, Mockito.times(5)).write(ArgumentMatchers.<ByteBuffer>any());
504         Mockito.verify(outbuf, Mockito.times(3)).write(ArgumentMatchers.<ByteBuffer>any());
505         Mockito.verify(outbuf, Mockito.times(3)).flush(channel);
506 
507         Assert.assertEquals(18, metrics.getBytesTransferred());
508 
509         outbuf.flush(channel);
510         final String s = channel.dump(StandardCharsets.US_ASCII);
511 
512         Assert.assertEquals("stuff---more stuff", s);
513     }
514 
515     @Test
516     public void testCodingFragmentBufferingTinyFragments2() throws Exception {
517         final WritableByteChannelMockhtml#WritableByteChannelMock">WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64));
518         final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128));
519         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
520 
521         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics,
522             100, 2);
523         Assert.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff")));
524         Assert.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
525         Assert.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
526         Assert.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
527         Assert.assertEquals(10, encoder.write(CodecTestUtils.wrap("more stuff")));
528 
529         Mockito.verify(channel, Mockito.times(4)).write(ArgumentMatchers.<ByteBuffer>any());
530         Mockito.verify(outbuf, Mockito.times(3)).write(ArgumentMatchers.<ByteBuffer>any());
531         Mockito.verify(outbuf, Mockito.times(2)).flush(channel);
532 
533         Assert.assertEquals(18, metrics.getBytesTransferred());
534 
535         outbuf.flush(channel);
536         final String s = channel.dump(StandardCharsets.US_ASCII);
537 
538         Assert.assertEquals("stuff---more stuff", s);
539     }
540 
541     @Test
542     public void testCodingFragmentBufferingTinyFragments3() throws Exception {
543         final WritableByteChannelMockhtml#WritableByteChannelMock">WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64));
544         final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128));
545         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
546 
547         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics,
548             100, 3);
549         Assert.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff")));
550         Assert.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
551         Assert.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
552         Assert.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
553         Assert.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
554         Assert.assertEquals(2, encoder.write(CodecTestUtils.wrap("--")));
555         Assert.assertEquals(10, encoder.write(CodecTestUtils.wrap("more stuff")));
556 
557         Mockito.verify(channel, Mockito.times(4)).write(ArgumentMatchers.<ByteBuffer>any());
558         Mockito.verify(outbuf, Mockito.times(5)).write(ArgumentMatchers.<ByteBuffer>any());
559         Mockito.verify(outbuf, Mockito.times(2)).flush(channel);
560 
561         Assert.assertEquals(21, metrics.getBytesTransferred());
562 
563         outbuf.flush(channel);
564         final String s = channel.dump(StandardCharsets.US_ASCII);
565 
566         Assert.assertEquals("stuff------more stuff", s);
567     }
568 
569     @Test
570     public void testCodingFragmentBufferingBufferFlush() throws Exception {
571         final WritableByteChannelMockhtml#WritableByteChannelMock">WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64));
572         final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128));
573         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
574 
575         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics,
576             100, 8);
577         Assert.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff")));
578         Assert.assertEquals(6, encoder.write(CodecTestUtils.wrap("-stuff")));
579 
580         Mockito.verify(channel, Mockito.times(1)).write(ArgumentMatchers.<ByteBuffer>any());
581         Mockito.verify(outbuf, Mockito.times(3)).write(ArgumentMatchers.<ByteBuffer>any());
582         Mockito.verify(outbuf, Mockito.times(1)).flush(channel);
583 
584         Assert.assertEquals(8, metrics.getBytesTransferred());
585         Assert.assertEquals(3, outbuf.length());
586 
587         outbuf.flush(channel);
588         final String s = channel.dump(StandardCharsets.US_ASCII);
589 
590         Assert.assertEquals("stuff-stuff", s);
591     }
592 
593     @Test
594     public void testCodingFragmentBufferingBufferFlush2() throws Exception {
595         final WritableByteChannelMockhtml#WritableByteChannelMock">WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64));
596         final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128));
597         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
598 
599         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics,
600             100, 8);
601         Assert.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff")));
602         Assert.assertEquals(16, encoder.write(CodecTestUtils.wrap("-much more stuff")));
603 
604         Mockito.verify(channel, Mockito.times(2)).write(ArgumentMatchers.<ByteBuffer>any());
605         Mockito.verify(outbuf, Mockito.times(1)).write(ArgumentMatchers.<ByteBuffer>any());
606         Mockito.verify(outbuf, Mockito.times(1)).flush(channel);
607 
608         Assert.assertEquals(21, metrics.getBytesTransferred());
609         Assert.assertEquals(0, outbuf.length());
610 
611         outbuf.flush(channel);
612         final String s = channel.dump(StandardCharsets.US_ASCII);
613 
614         Assert.assertEquals("stuff-much more stuff", s);
615     }
616 
617     @Test
618     public void testCodingFragmentBufferingChannelSaturated() throws Exception {
619         final WritableByteChannelMockhtml#WritableByteChannelMock">WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64, 8));
620         final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128));
621         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
622 
623         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics,
624             100, 3);
625         Assert.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff")));
626         Assert.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
627         Assert.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
628         Assert.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
629         Assert.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
630         Assert.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
631         Assert.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
632         Assert.assertEquals(0, encoder.write(CodecTestUtils.wrap("-")));
633         Assert.assertEquals(0, encoder.write(CodecTestUtils.wrap("more stuff")));
634 
635         Mockito.verify(channel, Mockito.times(5)).write(ArgumentMatchers.<ByteBuffer>any());
636         Mockito.verify(outbuf, Mockito.times(6)).write(ArgumentMatchers.<ByteBuffer>any());
637         Mockito.verify(outbuf, Mockito.times(4)).flush(channel);
638 
639         Assert.assertEquals(8, metrics.getBytesTransferred());
640 
641         outbuf.flush(channel);
642         final String s = channel.dump(StandardCharsets.US_ASCII);
643 
644         Assert.assertEquals("stuff---", s);
645         Assert.assertEquals(3, outbuf.length());
646     }
647 
648     @Test
649     public void testCodingFragmentBufferingChannelSaturated2() throws Exception {
650         final WritableByteChannelMockhtml#WritableByteChannelMock">WritableByteChannelMock channel = Mockito.spy(new WritableByteChannelMock(64, 8));
651         final SessionOutputBuffer outbuf = Mockito.spy(new SessionOutputBufferImpl(1024, 128));
652         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
653 
654         final LengthDelimitedEncoder encoder = new LengthDelimitedEncoder(channel, outbuf, metrics,
655             100, 8);
656         Assert.assertEquals(5, encoder.write(CodecTestUtils.wrap("stuff")));
657         Assert.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
658         Assert.assertEquals(1, encoder.write(CodecTestUtils.wrap("-")));
659         Assert.assertEquals(1, encoder.write(CodecTestUtils.wrap("much more stuff")));
660 
661         Mockito.verify(channel, Mockito.times(3)).write(ArgumentMatchers.<ByteBuffer>any());
662         Mockito.verify(outbuf, Mockito.times(3)).write(ArgumentMatchers.<ByteBuffer>any());
663         Mockito.verify(outbuf, Mockito.times(1)).flush(channel);
664 
665         Assert.assertEquals(8, metrics.getBytesTransferred());
666 
667         outbuf.flush(channel);
668         final String s = channel.dump(StandardCharsets.US_ASCII);
669 
670         Assert.assertEquals("stuff--m", s);
671         Assert.assertEquals(0, outbuf.length());
672     }
673 
674 }