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