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 testBasicReadWriteLine() throws Exception {
199 
200         final String[] teststrs = new String[5];
201         teststrs[0] = "Hello";
202         teststrs[1] = "This string should be much longer than the size of the line buffer " +
203                 "which is only 16 bytes for this test";
204         final StringBuilder buffer = new StringBuilder();
205         for (int i = 0; i < 15; i++) {
206             buffer.append("123456789 ");
207         }
208         buffer.append("and stuff like that");
209         teststrs[2] = buffer.toString();
210         teststrs[3] = "";
211         teststrs[4] = "And goodbye";
212 
213         final CharArrayBuffer chbuffer = new CharArrayBuffer(32);
214         final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 16);
215         for (final String teststr : teststrs) {
216             chbuffer.clear();
217             chbuffer.append(teststr);
218             outbuf.writeLine(chbuffer);
219         }
220         //this write operation should have no effect
221         outbuf.writeLine(null);
222 
223         final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
224         final WritableByteChannel outChannel = newChannel(outStream);
225         outbuf.flush(outChannel);
226 
227         final ReadableByteChannel channel = newChannel(outStream.toByteArray());
228 
229         final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 16, 0);
230         inbuf.fill(channel);
231 
232         for (final String teststr : teststrs) {
233             chbuffer.clear();
234             inbuf.readLine(chbuffer, true);
235             Assertions.assertEquals(teststr, chbuffer.toString());
236         }
237         chbuffer.clear();
238         Assertions.assertFalse(inbuf.readLine(chbuffer, true));
239         chbuffer.clear();
240         Assertions.assertFalse(inbuf.readLine(chbuffer, true));
241     }
242 
243     @Test
244     public void testComplexReadWriteLine() throws Exception {
245         final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 16);
246         outbuf.write(ByteBuffer.wrap(new byte[] {'a', '\n'}));
247         outbuf.write(ByteBuffer.wrap(new byte[] {'\r', '\n'}));
248         outbuf.write(ByteBuffer.wrap(new byte[] {'\r', '\r', '\n'}));
249         outbuf.write(ByteBuffer.wrap(new byte[] {'\n'}));
250 
251         final StringBuilder buffer = new StringBuilder();
252         for (int i = 0; i < 14; i++) {
253             buffer.append("a");
254         }
255         final String s1 = buffer.toString();
256         buffer.append("\r\n");
257         outbuf.write(ByteBuffer.wrap(buffer.toString().getBytes(StandardCharsets.US_ASCII)));
258 
259         buffer.setLength(0);
260         for (int i = 0; i < 15; i++) {
261             buffer.append("a");
262         }
263         final String s2 = buffer.toString();
264         buffer.append("\r\n");
265         outbuf.write(ByteBuffer.wrap(buffer.toString().getBytes(StandardCharsets.US_ASCII)));
266 
267         buffer.setLength(0);
268         for (int i = 0; i < 16; i++) {
269             buffer.append("a");
270         }
271         final String s3 = buffer.toString();
272         buffer.append("\r\n");
273         outbuf.write(ByteBuffer.wrap(buffer.toString().getBytes(StandardCharsets.US_ASCII)));
274 
275         outbuf.write(ByteBuffer.wrap(new byte[] {'a'}));
276 
277         final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
278         final WritableByteChannel outChannel = newChannel(outStream);
279         outbuf.flush(outChannel);
280 
281         final ReadableByteChannel channel = newChannel(outStream.toByteArray());
282 
283         final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 16, 0);
284         inbuf.fill(channel);
285 
286         final CharArrayBuffer chbuffer = new CharArrayBuffer(32);
287         chbuffer.clear();
288         inbuf.readLine(chbuffer, true);
289         Assertions.assertEquals("a", chbuffer.toString());
290         chbuffer.clear();
291         inbuf.readLine(chbuffer, true);
292         Assertions.assertEquals("", chbuffer.toString());
293         chbuffer.clear();
294         inbuf.readLine(chbuffer, true);
295         Assertions.assertEquals("\r", chbuffer.toString());
296         chbuffer.clear();
297         inbuf.readLine(chbuffer, true);
298         Assertions.assertEquals("", chbuffer.toString());
299         chbuffer.clear();
300         inbuf.readLine(chbuffer, true);
301         Assertions.assertEquals(s1, chbuffer.toString());
302         chbuffer.clear();
303         inbuf.readLine(chbuffer, true);
304         Assertions.assertEquals(s2, chbuffer.toString());
305         chbuffer.clear();
306         inbuf.readLine(chbuffer, true);
307         Assertions.assertEquals(s3, chbuffer.toString());
308         chbuffer.clear();
309         inbuf.readLine(chbuffer, true);
310         Assertions.assertEquals("a", chbuffer.toString());
311         chbuffer.clear();
312         inbuf.readLine(chbuffer, true);
313         Assertions.assertFalse(inbuf.readLine(chbuffer, true));
314         chbuffer.clear();
315         inbuf.readLine(chbuffer, true);
316         Assertions.assertFalse(inbuf.readLine(chbuffer, true));
317     }
318 
319     @Test
320     public void testReadOneByte() throws Exception {
321         // make the buffer larger than that of transmitter
322         final byte[] out = new byte[40];
323         for (int i = 0; i < out.length; i++) {
324             out[i] = (byte)('0' + i);
325         }
326         final ReadableByteChannel channel = newChannel(out);
327         final SessionInputBuffer inbuf = new SessionInputBufferImpl(16, 16, 0);
328         while (inbuf.fill(channel) > 0) {
329         }
330 
331         final byte[] in = new byte[40];
332         for (int i = 0; i < in.length; i++) {
333             in[i] = (byte)inbuf.read();
334         }
335         for (int i = 0; i < out.length; i++) {
336             Assertions.assertEquals(out[i], in[i]);
337         }
338     }
339 
340     @Test
341     public void testReadByteBuffer() throws Exception {
342         final byte[] pattern = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII);
343         final ReadableByteChannel channel = newChannel(pattern);
344         final SessionInputBuffer inbuf = new SessionInputBufferImpl(4096, 1024, 0);
345         while (inbuf.fill(channel) > 0) {
346         }
347         final ByteBuffer dst = ByteBuffer.allocate(10);
348         Assertions.assertEquals(10, inbuf.read(dst));
349         dst.flip();
350         Assertions.assertEquals(dst, ByteBuffer.wrap(pattern, 0, 10));
351         dst.clear();
352         Assertions.assertEquals(6, inbuf.read(dst));
353         dst.flip();
354         Assertions.assertEquals(dst, ByteBuffer.wrap(pattern, 10, 6));
355     }
356 
357     @Test
358     public void testReadByteBufferWithMaxLen() throws Exception {
359         final byte[] pattern = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII);
360         final ReadableByteChannel channel = newChannel(pattern);
361         final SessionInputBuffer inbuf = new SessionInputBufferImpl(4096, 1024, 0);
362         while (inbuf.fill(channel) > 0) {
363         }
364         final ByteBuffer dst = ByteBuffer.allocate(16);
365         Assertions.assertEquals(10, inbuf.read(dst, 10));
366         dst.flip();
367         Assertions.assertEquals(dst, ByteBuffer.wrap(pattern, 0, 10));
368         dst.clear();
369         Assertions.assertEquals(3, inbuf.read(dst, 3));
370         dst.flip();
371         Assertions.assertEquals(dst, ByteBuffer.wrap(pattern, 10, 3));
372         Assertions.assertEquals(3, inbuf.read(dst, 20));
373         dst.flip();
374         Assertions.assertEquals(dst, ByteBuffer.wrap(pattern, 13, 3));
375     }
376 
377     @Test
378     public void testReadToChannel() throws Exception {
379         final byte[] pattern = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII);
380         final ReadableByteChannel channel = newChannel(pattern);
381         final SessionInputBuffer inbuf = new SessionInputBufferImpl(4096, 1024, 0);
382         while (inbuf.fill(channel) > 0) {
383         }
384 
385         final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
386         final WritableByteChannel dst = newChannel(outStream);
387 
388         Assertions.assertEquals(16, inbuf.read(dst));
389         Assertions.assertEquals(ByteBuffer.wrap(pattern), ByteBuffer.wrap(outStream.toByteArray()));
390     }
391 
392     @Test
393     public void testReadToChannelWithMaxLen() throws Exception {
394         final byte[] pattern = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII);
395         final ReadableByteChannel channel = newChannel(pattern);
396         final SessionInputBuffer inbuf = new SessionInputBufferImpl(4096, 1024, 0);
397         while (inbuf.fill(channel) > 0) {
398         }
399 
400         final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
401         final WritableByteChannel dst = newChannel(outStream);
402 
403         Assertions.assertEquals(10, inbuf.read(dst, 10));
404         Assertions.assertEquals(3, inbuf.read(dst, 3));
405         Assertions.assertEquals(3, inbuf.read(dst, 10));
406         Assertions.assertEquals(ByteBuffer.wrap(pattern), ByteBuffer.wrap(outStream.toByteArray()));
407     }
408 
409     @Test
410     public void testWriteByteBuffer() throws Exception {
411         final byte[] pattern = "0123456789ABCDEF0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII);
412 
413         final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(4096, 1024);
414         final ReadableByteChannel src = newChannel(pattern);
415         outbuf.write(src);
416 
417         final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
418         final WritableByteChannel channel = newChannel(outStream);
419         while (outbuf.flush(channel) > 0) {
420         }
421         Assertions.assertEquals(ByteBuffer.wrap(pattern), ByteBuffer.wrap(outStream.toByteArray()));
422     }
423 
424     @Test
425     public void testWriteFromChannel() throws Exception {
426         final byte[] pattern = "0123456789ABCDEF0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII);
427 
428         final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(4096, 1024);
429         outbuf.write(ByteBuffer.wrap(pattern, 0, 16));
430         outbuf.write(ByteBuffer.wrap(pattern, 16, 10));
431         outbuf.write(ByteBuffer.wrap(pattern, 26, 6));
432 
433         final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
434         final WritableByteChannel channel = newChannel(outStream);
435         while (outbuf.flush(channel) > 0) {
436         }
437         Assertions.assertEquals(ByteBuffer.wrap(pattern), ByteBuffer.wrap(outStream.toByteArray()));
438     }
439 
440     static final int SWISS_GERMAN_HELLO [] = {
441         0x47, 0x72, 0xFC, 0x65, 0x7A, 0x69, 0x5F, 0x7A, 0xE4, 0x6D, 0xE4
442     };
443 
444     static final int RUSSIAN_HELLO [] = {
445         0x412, 0x441, 0x435, 0x43C, 0x5F, 0x43F, 0x440, 0x438,
446         0x432, 0x435, 0x442
447     };
448 
449     private static String constructString(final int [] unicodeChars) {
450         final StringBuilder buffer = new StringBuilder();
451         if (unicodeChars != null) {
452             for (final int unicodeChar : unicodeChars) {
453                 buffer.append((char)unicodeChar);
454             }
455         }
456         return buffer.toString();
457     }
458 
459     @Test
460     public void testMultibyteCodedReadWriteLine() throws Exception {
461         final String s1 = constructString(SWISS_GERMAN_HELLO);
462         final String s2 = constructString(RUSSIAN_HELLO);
463         final String s3 = "Like hello and stuff";
464 
465         final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 16,
466                 StandardCharsets.UTF_8.newEncoder());
467 
468         final CharArrayBuffer chbuffer = new CharArrayBuffer(32);
469         for (int i = 0; i < 10; i++) {
470             chbuffer.clear();
471             chbuffer.append(s1);
472             outbuf.writeLine(chbuffer);
473             chbuffer.clear();
474             chbuffer.append(s2);
475             outbuf.writeLine(chbuffer);
476             chbuffer.clear();
477             chbuffer.append(s3);
478             outbuf.writeLine(chbuffer);
479         }
480 
481         final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
482         final WritableByteChannel outChannel = newChannel(outStream);
483         outbuf.flush(outChannel);
484 
485         final byte[] tmp = outStream.toByteArray();
486 
487         final ReadableByteChannel channel = newChannel(tmp);
488         final SessionInputBuffer inbuf = new SessionInputBufferImpl(16, 16, 0,
489                 StandardCharsets.UTF_8.newDecoder());
490 
491         while (inbuf.fill(channel) > 0) {
492         }
493 
494         for (int i = 0; i < 10; i++) {
495             chbuffer.clear();
496             inbuf.readLine(chbuffer, true);
497             Assertions.assertEquals(s1, chbuffer.toString());
498             chbuffer.clear();
499             inbuf.readLine(chbuffer, true);
500             Assertions.assertEquals(s2, chbuffer.toString());
501             chbuffer.clear();
502             inbuf.readLine(chbuffer, true);
503             Assertions.assertEquals(s3, chbuffer.toString());
504         }
505     }
506 
507     @Test
508     public void testInputMatchesBufferLength() throws Exception {
509         final String s1 = "abcde";
510         final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 5);
511         final CharArrayBuffer chbuffer = new CharArrayBuffer(16);
512         chbuffer.append(s1);
513         outbuf.writeLine(chbuffer);
514     }
515 
516     @Test
517     public void testMalformedInputActionReport() throws Exception {
518         final String s = constructString(SWISS_GERMAN_HELLO);
519         final byte[] tmp = s.getBytes(StandardCharsets.ISO_8859_1);
520 
521         final CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
522         decoder.onMalformedInput(CodingErrorAction.REPORT);
523         decoder.onUnmappableCharacter(CodingErrorAction.IGNORE);
524         final SessionInputBuffer inbuf = new SessionInputBufferImpl(16, 16, 0, decoder);
525         final ReadableByteChannel channel = newChannel(tmp);
526         while (inbuf.fill(channel) > 0) {
527         }
528         final CharArrayBuffer chbuffer = new CharArrayBuffer(16);
529         Assertions.assertThrows(CharacterCodingException.class, () ->
530                 inbuf.readLine(chbuffer, true));
531     }
532 
533     @Test
534     public void testMalformedInputActionIgnore() throws Exception {
535         final String s = constructString(SWISS_GERMAN_HELLO);
536         final byte[] tmp = s.getBytes(StandardCharsets.ISO_8859_1);
537 
538         final CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
539         decoder.onMalformedInput(CodingErrorAction.IGNORE);
540         decoder.onUnmappableCharacter(CodingErrorAction.IGNORE);
541         final SessionInputBuffer inbuf = new SessionInputBufferImpl(16, 16, 0, decoder);
542         final ReadableByteChannel channel = newChannel(tmp);
543         while (inbuf.fill(channel) > 0) {
544         }
545         final CharArrayBuffer chbuffer = new CharArrayBuffer(16);
546         inbuf.readLine(chbuffer, true);
547         Assertions.assertEquals("Grezi_zm", chbuffer.toString());
548     }
549 
550     @Test
551     public void testMalformedInputActionReplace() throws Exception {
552         final String s = constructString(SWISS_GERMAN_HELLO);
553         final byte[] tmp = s.getBytes(StandardCharsets.ISO_8859_1);
554 
555         final CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
556         decoder.onMalformedInput(CodingErrorAction.REPLACE);
557         decoder.onUnmappableCharacter(CodingErrorAction.IGNORE);
558         final SessionInputBuffer inbuf = new SessionInputBufferImpl(16, 16, 0, decoder);
559         final ReadableByteChannel channel = newChannel(tmp);
560         while (inbuf.fill(channel) > 0) {
561         }
562         final CharArrayBuffer chbuffer = new CharArrayBuffer(16);
563         inbuf.readLine(chbuffer, true);
564         Assertions.assertEquals("Gr\ufffdezi_z\ufffdm\ufffd", chbuffer.toString());
565     }
566 
567     @Test
568     public void testUnmappableInputActionReport() throws Exception {
569         final String s = "This text contains a circumflex \u0302!!!";
570         final CharsetEncoder encoder = StandardCharsets.ISO_8859_1.newEncoder();
571         encoder.onMalformedInput(CodingErrorAction.IGNORE);
572         encoder.onUnmappableCharacter(CodingErrorAction.REPORT);
573         final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 16, encoder);
574         final CharArrayBuffer chbuffer = new CharArrayBuffer(16);
575         chbuffer.append(s);
576         Assertions.assertThrows(CharacterCodingException.class, () ->
577                 outbuf.writeLine(chbuffer));
578     }
579 
580     @Test
581     public void testUnmappableInputActionIgnore() throws Exception {
582         final String s = "This text contains a circumflex \u0302!!!";
583         final CharsetEncoder encoder = StandardCharsets.ISO_8859_1.newEncoder();
584         encoder.onMalformedInput(CodingErrorAction.IGNORE);
585         encoder.onUnmappableCharacter(CodingErrorAction.IGNORE);
586         final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 16, encoder);
587         final ByteArrayOutputStream baos = new ByteArrayOutputStream();
588         final WritableByteChannel channel = newChannel(baos);
589         final CharArrayBuffer chbuffer = new CharArrayBuffer(16);
590         chbuffer.append(s);
591         outbuf.writeLine(chbuffer);
592         outbuf.flush(channel);
593 
594         final String result = new String(baos.toByteArray(), StandardCharsets.US_ASCII);
595         Assertions.assertEquals("This text contains a circumflex !!!\r\n", result);
596     }
597 
598     @Test
599     public void testUnmappableInputActionReplace() throws Exception {
600         final String s = "This text contains a circumflex \u0302 !!!";
601         final CharsetEncoder encoder = StandardCharsets.ISO_8859_1.newEncoder();
602         encoder.onMalformedInput(CodingErrorAction.IGNORE);
603         encoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
604         final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 16, encoder);
605         final ByteArrayOutputStream baos = new ByteArrayOutputStream();
606         final WritableByteChannel channel = newChannel(baos);
607         final CharArrayBuffer chbuffer = new CharArrayBuffer(16);
608         chbuffer.append(s);
609         outbuf.writeLine(chbuffer);
610         outbuf.flush(channel);
611 
612         final String result = new String(baos.toByteArray(), StandardCharsets.US_ASCII);
613         Assertions.assertEquals("This text contains a circumflex ? !!!\r\n", result);
614     }
615 
616 }