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