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.ByteArrayInputStream;
31  import java.io.ByteArrayOutputStream;
32  import java.nio.ByteBuffer;
33  import java.nio.channels.Channels;
34  import java.nio.channels.ReadableByteChannel;
35  import java.nio.channels.WritableByteChannel;
36  import java.nio.charset.CharacterCodingException;
37  import java.nio.charset.Charset;
38  import java.nio.charset.CharsetDecoder;
39  import java.nio.charset.CharsetEncoder;
40  import java.nio.charset.CodingErrorAction;
41  import java.nio.charset.StandardCharsets;
42  
43  import org.apache.hc.core5.http.MessageConstraintException;
44  import org.apache.hc.core5.http.nio.SessionInputBuffer;
45  import org.apache.hc.core5.http.nio.SessionOutputBuffer;
46  import org.apache.hc.core5.util.CharArrayBuffer;
47  import org.junit.jupiter.api.Assertions;
48  import org.junit.jupiter.api.Test;
49  
50  /**
51   * Simple tests for {@link SessionInputBuffer} and {@link SessionOutputBuffer}.
52   */
53  public class TestSessionInOutBuffers {
54  
55      private static WritableByteChannel newChannel(final ByteArrayOutputStream outStream) {
56          return Channels.newChannel(outStream);
57      }
58  
59      private static ReadableByteChannel newChannel(final byte[] bytes) {
60          return Channels.newChannel(new ByteArrayInputStream(bytes));
61      }
62  
63      private static ReadableByteChannel newChannel(final String s, final Charset charset) {
64          return Channels.newChannel(new ByteArrayInputStream(s.getBytes(charset)));
65      }
66  
67      private static ReadableByteChannel newChannel(final String s) {
68          return newChannel(s, StandardCharsets.US_ASCII);
69      }
70  
71      @Test
72      public void testReadLineChunks() throws Exception {
73          final SessionInputBuffer inbuf = new SessionInputBufferImpl(16, 16);
74  
75          ReadableByteChannel channel = newChannel("One\r\nTwo\r\nThree");
76  
77          inbuf.fill(channel);
78  
79          final CharArrayBuffer line = new CharArrayBuffer(64);
80  
81          line.clear();
82          Assertions.assertTrue(inbuf.readLine(line, false));
83          Assertions.assertEquals("One", line.toString());
84  
85          line.clear();
86          Assertions.assertTrue(inbuf.readLine(line, false));
87          Assertions.assertEquals("Two", line.toString());
88  
89          line.clear();
90          Assertions.assertFalse(inbuf.readLine(line, false));
91  
92          channel = newChannel("\r\nFour");
93          inbuf.fill(channel);
94  
95          line.clear();
96          Assertions.assertTrue(inbuf.readLine(line, false));
97          Assertions.assertEquals("Three", line.toString());
98  
99          inbuf.fill(channel);
100 
101         line.clear();
102         Assertions.assertTrue(inbuf.readLine(line, true));
103         Assertions.assertEquals("Four", line.toString());
104 
105         line.clear();
106         Assertions.assertFalse(inbuf.readLine(line, true));
107     }
108 
109     @Test
110     public void testLineLimit() throws Exception {
111         final String s = "LoooooooooooooooooooooooooOOOOOOOOOOOOOOOOOOoooooooooooooooooooooong line\r\n";
112         final CharArrayBuffer line = new CharArrayBuffer(64);
113         final SessionInputBuffer inbuf1 = new SessionInputBufferImpl(128, 128);
114         final ReadableByteChannel channel1 = newChannel(s);
115         inbuf1.fill(channel1);
116 
117         Assertions.assertTrue(inbuf1.readLine(line, false));
118 
119         line.clear();
120         final SessionInputBuffer inbuf2 = new SessionInputBufferImpl(128, 128, 10);
121         final ReadableByteChannel channel2 = newChannel(s);
122         inbuf2.fill(channel2);
123         Assertions.assertThrows(MessageConstraintException.class, () -> inbuf2.readLine(line, false));
124     }
125 
126     @Test
127     public void testLineLimitBufferFull() throws Exception {
128         final String s = "LoooooooooooooooooooooooooOOOOOOOOOOOOOOOOOOoooooooooooooooooooooong line\r\n";
129         final CharArrayBuffer line = new CharArrayBuffer(64);
130         final SessionInputBuffer inbuf1 = new SessionInputBufferImpl(32, 32);
131         final ReadableByteChannel channel1 = newChannel(s);
132         inbuf1.fill(channel1);
133 
134         Assertions.assertFalse(inbuf1.readLine(line, false));
135 
136         line.clear();
137         final SessionInputBuffer inbuf2 = new SessionInputBufferImpl(32, 32,10);
138         final ReadableByteChannel channel2 = newChannel(s);
139         inbuf2.fill(channel2);
140         Assertions.assertThrows(MessageConstraintException.class, () -> inbuf2.readLine(line, false));
141     }
142 
143     @Test
144     public void testWriteLineChunks() throws Exception {
145         final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(16, 16);
146         final SessionInputBuffer inbuf = new SessionInputBufferImpl(16, 16, 0);
147 
148         ReadableByteChannel inChannel = newChannel("One\r\nTwo\r\nThree");
149 
150         inbuf.fill(inChannel);
151 
152         final CharArrayBuffer line = new CharArrayBuffer(64);
153 
154         line.clear();
155         Assertions.assertTrue(inbuf.readLine(line, false));
156         Assertions.assertEquals("One", line.toString());
157 
158         outbuf.writeLine(line);
159 
160         line.clear();
161         Assertions.assertTrue(inbuf.readLine(line, false));
162         Assertions.assertEquals("Two", line.toString());
163 
164         outbuf.writeLine(line);
165 
166         line.clear();
167         Assertions.assertFalse(inbuf.readLine(line, false));
168 
169         inChannel = newChannel("\r\nFour");
170         inbuf.fill(inChannel);
171 
172         line.clear();
173         Assertions.assertTrue(inbuf.readLine(line, false));
174         Assertions.assertEquals("Three", line.toString());
175 
176         outbuf.writeLine(line);
177 
178         inbuf.fill(inChannel);
179 
180         line.clear();
181         Assertions.assertTrue(inbuf.readLine(line, true));
182         Assertions.assertEquals("Four", line.toString());
183 
184         outbuf.writeLine(line);
185 
186         line.clear();
187         Assertions.assertFalse(inbuf.readLine(line, true));
188 
189         final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
190         final WritableByteChannel outChannel = newChannel(outStream);
191         outbuf.flush(outChannel);
192 
193         final String s = new String(outStream.toByteArray(), StandardCharsets.US_ASCII);
194         Assertions.assertEquals("One\r\nTwo\r\nThree\r\nFour\r\n", s);
195     }
196 
197     @Test
198     public void testNonASCIIWriteLine() throws Exception {
199         final String testString = "123\u010Anew-header-from-some-header:injected-value";
200         final String expectedResult = "123?new-header-from-some-header:injected-value";
201 
202         final CharArrayBuffer chbuffer = new CharArrayBuffer(32);
203         final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 16);
204 
205         chbuffer.clear();
206         chbuffer.append(testString);
207         outbuf.writeLine(chbuffer);
208 
209         //this write operation should have no effect
210         outbuf.writeLine(null);
211 
212         final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
213         final WritableByteChannel outChannel = newChannel(outStream);
214         outbuf.flush(outChannel);
215 
216         final ReadableByteChannel channel = newChannel(outStream.toByteArray());
217 
218         final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 16, 0);
219         inbuf.fill(channel);
220         chbuffer.clear();
221         inbuf.readLine(chbuffer, true);
222         Assertions.assertEquals(expectedResult, chbuffer.toString());
223     }
224 
225     @Test
226     public void testBasicReadWriteLine() throws Exception {
227 
228         final String[] teststrs = new String[5];
229         teststrs[0] = "Hello";
230         teststrs[1] = "This string should be much longer than the size of the line buffer " +
231                 "which is only 16 bytes for this test";
232         final StringBuilder buffer = new StringBuilder();
233         for (int i = 0; i < 15; i++) {
234             buffer.append("123456789 ");
235         }
236         buffer.append("and stuff like that");
237         teststrs[2] = buffer.toString();
238         teststrs[3] = "";
239         teststrs[4] = "And goodbye";
240 
241         final CharArrayBuffer chbuffer = new CharArrayBuffer(32);
242         final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 16);
243         for (final String teststr : teststrs) {
244             chbuffer.clear();
245             chbuffer.append(teststr);
246             outbuf.writeLine(chbuffer);
247         }
248         //this write operation should have no effect
249         outbuf.writeLine(null);
250 
251         final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
252         final WritableByteChannel outChannel = newChannel(outStream);
253         outbuf.flush(outChannel);
254 
255         final ReadableByteChannel channel = newChannel(outStream.toByteArray());
256 
257         final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 16, 0);
258         inbuf.fill(channel);
259 
260         for (final String teststr : teststrs) {
261             chbuffer.clear();
262             inbuf.readLine(chbuffer, true);
263             Assertions.assertEquals(teststr, chbuffer.toString());
264         }
265         chbuffer.clear();
266         Assertions.assertFalse(inbuf.readLine(chbuffer, true));
267         chbuffer.clear();
268         Assertions.assertFalse(inbuf.readLine(chbuffer, true));
269     }
270 
271     @Test
272     public void testComplexReadWriteLine() throws Exception {
273         final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 16);
274         outbuf.write(ByteBuffer.wrap(new byte[] {'a', '\n'}));
275         outbuf.write(ByteBuffer.wrap(new byte[] {'\r', '\n'}));
276         outbuf.write(ByteBuffer.wrap(new byte[] {'\r', '\r', '\n'}));
277         outbuf.write(ByteBuffer.wrap(new byte[] {'\n'}));
278 
279         final StringBuilder buffer = new StringBuilder();
280         for (int i = 0; i < 14; i++) {
281             buffer.append("a");
282         }
283         final String s1 = buffer.toString();
284         buffer.append("\r\n");
285         outbuf.write(ByteBuffer.wrap(buffer.toString().getBytes(StandardCharsets.US_ASCII)));
286 
287         buffer.setLength(0);
288         for (int i = 0; i < 15; i++) {
289             buffer.append("a");
290         }
291         final String s2 = buffer.toString();
292         buffer.append("\r\n");
293         outbuf.write(ByteBuffer.wrap(buffer.toString().getBytes(StandardCharsets.US_ASCII)));
294 
295         buffer.setLength(0);
296         for (int i = 0; i < 16; i++) {
297             buffer.append("a");
298         }
299         final String s3 = buffer.toString();
300         buffer.append("\r\n");
301         outbuf.write(ByteBuffer.wrap(buffer.toString().getBytes(StandardCharsets.US_ASCII)));
302 
303         outbuf.write(ByteBuffer.wrap(new byte[] {'a'}));
304 
305         final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
306         final WritableByteChannel outChannel = newChannel(outStream);
307         outbuf.flush(outChannel);
308 
309         final ReadableByteChannel channel = newChannel(outStream.toByteArray());
310 
311         final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 16, 0);
312         inbuf.fill(channel);
313 
314         final CharArrayBuffer chbuffer = new CharArrayBuffer(32);
315         chbuffer.clear();
316         inbuf.readLine(chbuffer, true);
317         Assertions.assertEquals("a", chbuffer.toString());
318         chbuffer.clear();
319         inbuf.readLine(chbuffer, true);
320         Assertions.assertEquals("", chbuffer.toString());
321         chbuffer.clear();
322         inbuf.readLine(chbuffer, true);
323         Assertions.assertEquals("\r", chbuffer.toString());
324         chbuffer.clear();
325         inbuf.readLine(chbuffer, true);
326         Assertions.assertEquals("", chbuffer.toString());
327         chbuffer.clear();
328         inbuf.readLine(chbuffer, true);
329         Assertions.assertEquals(s1, chbuffer.toString());
330         chbuffer.clear();
331         inbuf.readLine(chbuffer, true);
332         Assertions.assertEquals(s2, chbuffer.toString());
333         chbuffer.clear();
334         inbuf.readLine(chbuffer, true);
335         Assertions.assertEquals(s3, chbuffer.toString());
336         chbuffer.clear();
337         inbuf.readLine(chbuffer, true);
338         Assertions.assertEquals("a", chbuffer.toString());
339         chbuffer.clear();
340         inbuf.readLine(chbuffer, true);
341         Assertions.assertFalse(inbuf.readLine(chbuffer, true));
342         chbuffer.clear();
343         inbuf.readLine(chbuffer, true);
344         Assertions.assertFalse(inbuf.readLine(chbuffer, true));
345     }
346 
347     @Test
348     public void testReadOneByte() throws Exception {
349         // make the buffer larger than that of transmitter
350         final byte[] out = new byte[40];
351         for (int i = 0; i < out.length; i++) {
352             out[i] = (byte)('0' + i);
353         }
354         final ReadableByteChannel channel = newChannel(out);
355         final SessionInputBuffer inbuf = new SessionInputBufferImpl(16, 16, 0);
356         while (inbuf.fill(channel) > 0) {
357         }
358 
359         final byte[] in = new byte[40];
360         for (int i = 0; i < in.length; i++) {
361             in[i] = (byte)inbuf.read();
362         }
363         for (int i = 0; i < out.length; i++) {
364             Assertions.assertEquals(out[i], in[i]);
365         }
366     }
367 
368     @Test
369     public void testReadByteBuffer() throws Exception {
370         final byte[] pattern = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII);
371         final ReadableByteChannel channel = newChannel(pattern);
372         final SessionInputBuffer inbuf = new SessionInputBufferImpl(4096, 1024, 0);
373         while (inbuf.fill(channel) > 0) {
374         }
375         final ByteBuffer dst = ByteBuffer.allocate(10);
376         Assertions.assertEquals(10, inbuf.read(dst));
377         dst.flip();
378         Assertions.assertEquals(dst, ByteBuffer.wrap(pattern, 0, 10));
379         dst.clear();
380         Assertions.assertEquals(6, inbuf.read(dst));
381         dst.flip();
382         Assertions.assertEquals(dst, ByteBuffer.wrap(pattern, 10, 6));
383     }
384 
385     @Test
386     public void testReadByteBufferWithMaxLen() throws Exception {
387         final byte[] pattern = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII);
388         final ReadableByteChannel channel = newChannel(pattern);
389         final SessionInputBuffer inbuf = new SessionInputBufferImpl(4096, 1024, 0);
390         while (inbuf.fill(channel) > 0) {
391         }
392         final ByteBuffer dst = ByteBuffer.allocate(16);
393         Assertions.assertEquals(10, inbuf.read(dst, 10));
394         dst.flip();
395         Assertions.assertEquals(dst, ByteBuffer.wrap(pattern, 0, 10));
396         dst.clear();
397         Assertions.assertEquals(3, inbuf.read(dst, 3));
398         dst.flip();
399         Assertions.assertEquals(dst, ByteBuffer.wrap(pattern, 10, 3));
400         Assertions.assertEquals(3, inbuf.read(dst, 20));
401         dst.flip();
402         Assertions.assertEquals(dst, ByteBuffer.wrap(pattern, 13, 3));
403     }
404 
405     @Test
406     public void testReadToChannel() throws Exception {
407         final byte[] pattern = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII);
408         final ReadableByteChannel channel = newChannel(pattern);
409         final SessionInputBuffer inbuf = new SessionInputBufferImpl(4096, 1024, 0);
410         while (inbuf.fill(channel) > 0) {
411         }
412 
413         final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
414         final WritableByteChannel dst = newChannel(outStream);
415 
416         Assertions.assertEquals(16, inbuf.read(dst));
417         Assertions.assertEquals(ByteBuffer.wrap(pattern), ByteBuffer.wrap(outStream.toByteArray()));
418     }
419 
420     @Test
421     public void testReadToChannelWithMaxLen() throws Exception {
422         final byte[] pattern = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII);
423         final ReadableByteChannel channel = newChannel(pattern);
424         final SessionInputBuffer inbuf = new SessionInputBufferImpl(4096, 1024, 0);
425         while (inbuf.fill(channel) > 0) {
426         }
427 
428         final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
429         final WritableByteChannel dst = newChannel(outStream);
430 
431         Assertions.assertEquals(10, inbuf.read(dst, 10));
432         Assertions.assertEquals(3, inbuf.read(dst, 3));
433         Assertions.assertEquals(3, inbuf.read(dst, 10));
434         Assertions.assertEquals(ByteBuffer.wrap(pattern), ByteBuffer.wrap(outStream.toByteArray()));
435     }
436 
437     @Test
438     public void testWriteByteBuffer() throws Exception {
439         final byte[] pattern = "0123456789ABCDEF0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII);
440 
441         final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(4096, 1024);
442         final ReadableByteChannel src = newChannel(pattern);
443         outbuf.write(src);
444 
445         final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
446         final WritableByteChannel channel = newChannel(outStream);
447         while (outbuf.flush(channel) > 0) {
448         }
449         Assertions.assertEquals(ByteBuffer.wrap(pattern), ByteBuffer.wrap(outStream.toByteArray()));
450     }
451 
452     @Test
453     public void testWriteFromChannel() throws Exception {
454         final byte[] pattern = "0123456789ABCDEF0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII);
455 
456         final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(4096, 1024);
457         outbuf.write(ByteBuffer.wrap(pattern, 0, 16));
458         outbuf.write(ByteBuffer.wrap(pattern, 16, 10));
459         outbuf.write(ByteBuffer.wrap(pattern, 26, 6));
460 
461         final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
462         final WritableByteChannel channel = newChannel(outStream);
463         while (outbuf.flush(channel) > 0) {
464         }
465         Assertions.assertEquals(ByteBuffer.wrap(pattern), ByteBuffer.wrap(outStream.toByteArray()));
466     }
467 
468     static final int SWISS_GERMAN_HELLO [] = {
469         0x47, 0x72, 0xFC, 0x65, 0x7A, 0x69, 0x5F, 0x7A, 0xE4, 0x6D, 0xE4
470     };
471 
472     static final int RUSSIAN_HELLO [] = {
473         0x412, 0x441, 0x435, 0x43C, 0x5F, 0x43F, 0x440, 0x438,
474         0x432, 0x435, 0x442
475     };
476 
477     private static String constructString(final int [] unicodeChars) {
478         final StringBuilder buffer = new StringBuilder();
479         if (unicodeChars != null) {
480             for (final int unicodeChar : unicodeChars) {
481                 buffer.append((char)unicodeChar);
482             }
483         }
484         return buffer.toString();
485     }
486 
487     @Test
488     public void testMultibyteCodedReadWriteLine() throws Exception {
489         final String s1 = constructString(SWISS_GERMAN_HELLO);
490         final String s2 = constructString(RUSSIAN_HELLO);
491         final String s3 = "Like hello and stuff";
492 
493         final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 16,
494                 StandardCharsets.UTF_8.newEncoder());
495 
496         final CharArrayBuffer chbuffer = new CharArrayBuffer(32);
497         for (int i = 0; i < 10; i++) {
498             chbuffer.clear();
499             chbuffer.append(s1);
500             outbuf.writeLine(chbuffer);
501             chbuffer.clear();
502             chbuffer.append(s2);
503             outbuf.writeLine(chbuffer);
504             chbuffer.clear();
505             chbuffer.append(s3);
506             outbuf.writeLine(chbuffer);
507         }
508 
509         final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
510         final WritableByteChannel outChannel = newChannel(outStream);
511         outbuf.flush(outChannel);
512 
513         final byte[] tmp = outStream.toByteArray();
514 
515         final ReadableByteChannel channel = newChannel(tmp);
516         final SessionInputBuffer inbuf = new SessionInputBufferImpl(16, 16, 0,
517                 StandardCharsets.UTF_8.newDecoder());
518 
519         while (inbuf.fill(channel) > 0) {
520         }
521 
522         for (int i = 0; i < 10; i++) {
523             chbuffer.clear();
524             inbuf.readLine(chbuffer, true);
525             Assertions.assertEquals(s1, chbuffer.toString());
526             chbuffer.clear();
527             inbuf.readLine(chbuffer, true);
528             Assertions.assertEquals(s2, chbuffer.toString());
529             chbuffer.clear();
530             inbuf.readLine(chbuffer, true);
531             Assertions.assertEquals(s3, chbuffer.toString());
532         }
533     }
534 
535     @Test
536     public void testInputMatchesBufferLength() throws Exception {
537         final String s1 = "abcde";
538         final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 5);
539         final CharArrayBuffer chbuffer = new CharArrayBuffer(16);
540         chbuffer.append(s1);
541         outbuf.writeLine(chbuffer);
542     }
543 
544     @Test
545     public void testMalformedInputActionReport() throws Exception {
546         final String s = constructString(SWISS_GERMAN_HELLO);
547         final byte[] tmp = s.getBytes(StandardCharsets.ISO_8859_1);
548 
549         final CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
550         decoder.onMalformedInput(CodingErrorAction.REPORT);
551         decoder.onUnmappableCharacter(CodingErrorAction.IGNORE);
552         final SessionInputBuffer inbuf = new SessionInputBufferImpl(16, 16, 0, decoder);
553         final ReadableByteChannel channel = newChannel(tmp);
554         while (inbuf.fill(channel) > 0) {
555         }
556         final CharArrayBuffer chbuffer = new CharArrayBuffer(16);
557         Assertions.assertThrows(CharacterCodingException.class, () ->
558                 inbuf.readLine(chbuffer, true));
559     }
560 
561     @Test
562     public void testMalformedInputActionIgnore() throws Exception {
563         final String s = constructString(SWISS_GERMAN_HELLO);
564         final byte[] tmp = s.getBytes(StandardCharsets.ISO_8859_1);
565 
566         final CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
567         decoder.onMalformedInput(CodingErrorAction.IGNORE);
568         decoder.onUnmappableCharacter(CodingErrorAction.IGNORE);
569         final SessionInputBuffer inbuf = new SessionInputBufferImpl(16, 16, 0, decoder);
570         final ReadableByteChannel channel = newChannel(tmp);
571         while (inbuf.fill(channel) > 0) {
572         }
573         final CharArrayBuffer chbuffer = new CharArrayBuffer(16);
574         inbuf.readLine(chbuffer, true);
575         Assertions.assertEquals("Grezi_zm", chbuffer.toString());
576     }
577 
578     @Test
579     public void testMalformedInputActionReplace() throws Exception {
580         final String s = constructString(SWISS_GERMAN_HELLO);
581         final byte[] tmp = s.getBytes(StandardCharsets.ISO_8859_1);
582 
583         final CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
584         decoder.onMalformedInput(CodingErrorAction.REPLACE);
585         decoder.onUnmappableCharacter(CodingErrorAction.IGNORE);
586         final SessionInputBuffer inbuf = new SessionInputBufferImpl(16, 16, 0, decoder);
587         final ReadableByteChannel channel = newChannel(tmp);
588         while (inbuf.fill(channel) > 0) {
589         }
590         final CharArrayBuffer chbuffer = new CharArrayBuffer(16);
591         inbuf.readLine(chbuffer, true);
592         Assertions.assertEquals("Gr\ufffdezi_z\ufffdm\ufffd", chbuffer.toString());
593     }
594 
595     @Test
596     public void testUnmappableInputActionReport() throws Exception {
597         final String s = "This text contains a circumflex \u0302!!!";
598         final CharsetEncoder encoder = StandardCharsets.ISO_8859_1.newEncoder();
599         encoder.onMalformedInput(CodingErrorAction.IGNORE);
600         encoder.onUnmappableCharacter(CodingErrorAction.REPORT);
601         final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 16, encoder);
602         final CharArrayBuffer chbuffer = new CharArrayBuffer(16);
603         chbuffer.append(s);
604         Assertions.assertThrows(CharacterCodingException.class, () ->
605                 outbuf.writeLine(chbuffer));
606     }
607 
608     @Test
609     public void testUnmappableInputActionIgnore() throws Exception {
610         final String s = "This text contains a circumflex \u0302!!!";
611         final CharsetEncoder encoder = StandardCharsets.ISO_8859_1.newEncoder();
612         encoder.onMalformedInput(CodingErrorAction.IGNORE);
613         encoder.onUnmappableCharacter(CodingErrorAction.IGNORE);
614         final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 16, encoder);
615         final ByteArrayOutputStream baos = new ByteArrayOutputStream();
616         final WritableByteChannel channel = newChannel(baos);
617         final CharArrayBuffer chbuffer = new CharArrayBuffer(16);
618         chbuffer.append(s);
619         outbuf.writeLine(chbuffer);
620         outbuf.flush(channel);
621 
622         final String result = new String(baos.toByteArray(), StandardCharsets.US_ASCII);
623         Assertions.assertEquals("This text contains a circumflex !!!\r\n", result);
624     }
625 
626     @Test
627     public void testUnmappableInputActionReplace() throws Exception {
628         final String s = "This text contains a circumflex \u0302 !!!";
629         final CharsetEncoder encoder = StandardCharsets.ISO_8859_1.newEncoder();
630         encoder.onMalformedInput(CodingErrorAction.IGNORE);
631         encoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
632         final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 16, encoder);
633         final ByteArrayOutputStream baos = new ByteArrayOutputStream();
634         final WritableByteChannel channel = newChannel(baos);
635         final CharArrayBuffer chbuffer = new CharArrayBuffer(16);
636         chbuffer.append(s);
637         outbuf.writeLine(chbuffer);
638         outbuf.flush(channel);
639 
640         final String result = new String(baos.toByteArray(), StandardCharsets.US_ASCII);
641         Assertions.assertEquals("This text contains a circumflex ? !!!\r\n", result);
642     }
643 
644 }