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.http.impl.nio.codecs;
29  
30  import java.io.File;
31  import java.io.IOException;
32  import java.io.RandomAccessFile;
33  import java.nio.ByteBuffer;
34  import java.nio.channels.FileChannel;
35  import java.nio.channels.ReadableByteChannel;
36  
37  import org.apache.http.ConnectionClosedException;
38  import org.apache.http.Consts;
39  import org.apache.http.ReadableByteChannelMock;
40  import org.apache.http.impl.io.HttpTransportMetricsImpl;
41  import org.apache.http.impl.nio.reactor.SessionInputBufferImpl;
42  import org.apache.http.nio.reactor.SessionInputBuffer;
43  import org.apache.http.util.EncodingUtils;
44  import org.junit.After;
45  import org.junit.Assert;
46  import org.junit.Test;
47  
48  /**
49   * Simple tests for {@link LengthDelimitedDecoder}.
50   */
51  public class TestLengthDelimitedDecoder {
52  
53      private File tmpfile;
54  
55      protected File createTempFile() throws IOException {
56          this.tmpfile = File.createTempFile("testFile", ".txt");
57          return this.tmpfile;
58      }
59  
60      @After
61      public void deleteTempFile() {
62          if (this.tmpfile != null && this.tmpfile.exists()) {
63              this.tmpfile.delete();
64          }
65      }
66  
67      @Test
68      public void testBasicDecoding() throws Exception {
69          final ReadableByteChannel channel = new ReadableByteChannelMock(
70                  new String[] {"stuff;", "more stuff"}, Consts.ASCII);
71  
72          final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, Consts.ASCII);
73          final HttpTransportMetricsImpl metrics = new HttpTransportMetricsImpl();
74          final LengthDelimitedDecoder decoder = new LengthDelimitedDecoder(
75                  channel, inbuf, metrics, 16);
76  
77          final ByteBuffer dst = ByteBuffer.allocate(1024);
78  
79          int bytesRead = decoder.read(dst);
80          Assert.assertEquals(6, bytesRead);
81          Assert.assertEquals("stuff;", CodecTestUtils.convert(dst));
82          Assert.assertFalse(decoder.isCompleted());
83          Assert.assertEquals(6, metrics.getBytesTransferred());
84  
85          dst.clear();
86          bytesRead = decoder.read(dst);
87          Assert.assertEquals(10, bytesRead);
88          Assert.assertEquals("more stuff", CodecTestUtils.convert(dst));
89          Assert.assertTrue(decoder.isCompleted());
90          Assert.assertEquals(16, metrics.getBytesTransferred());
91  
92          dst.clear();
93          bytesRead = decoder.read(dst);
94          Assert.assertEquals(-1, bytesRead);
95          Assert.assertTrue(decoder.isCompleted());
96          Assert.assertEquals(16, metrics.getBytesTransferred());
97  
98          Assert.assertEquals("[content length: 16; pos: 16; completed: true]", decoder.toString());
99      }
100 
101     @Test
102     public void testCodingBeyondContentLimit() throws Exception {
103         final ReadableByteChannel channel = new ReadableByteChannelMock(
104                 new String[] {
105                         "stuff;",
106                         "more stuff; and a lot more stuff"}, Consts.ASCII);
107 
108         final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, Consts.ASCII);
109         final HttpTransportMetricsImpl metrics = new HttpTransportMetricsImpl();
110         final LengthDelimitedDecoder decoder = new LengthDelimitedDecoder(
111                 channel, inbuf, metrics, 16);
112 
113         final ByteBuffer dst = ByteBuffer.allocate(1024);
114 
115         int bytesRead = decoder.read(dst);
116         Assert.assertEquals(6, bytesRead);
117         Assert.assertEquals("stuff;", CodecTestUtils.convert(dst));
118         Assert.assertFalse(decoder.isCompleted());
119         Assert.assertEquals(6, metrics.getBytesTransferred());
120 
121         dst.clear();
122         bytesRead = decoder.read(dst);
123         Assert.assertEquals(10, bytesRead);
124         Assert.assertEquals("more stuff", CodecTestUtils.convert(dst));
125         Assert.assertTrue(decoder.isCompleted());
126         Assert.assertEquals(16, metrics.getBytesTransferred());
127 
128         dst.clear();
129         bytesRead = decoder.read(dst);
130         Assert.assertEquals(-1, bytesRead);
131         Assert.assertTrue(decoder.isCompleted());
132         Assert.assertEquals(16, metrics.getBytesTransferred());
133     }
134 
135     @Test
136     public void testBasicDecodingSmallBuffer() throws Exception {
137         final ReadableByteChannel channel = new ReadableByteChannelMock(
138                 new String[] {"stuff;", "more stuff"}, Consts.ASCII);
139 
140         final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, Consts.ASCII);
141         final HttpTransportMetricsImpl metrics = new HttpTransportMetricsImpl();
142         final LengthDelimitedDecoder decoder = new LengthDelimitedDecoder(
143                 channel, inbuf, metrics, 16);
144 
145         final ByteBuffer dst = ByteBuffer.allocate(4);
146 
147         int bytesRead = decoder.read(dst);
148         Assert.assertEquals(4, bytesRead);
149         Assert.assertEquals("stuf", CodecTestUtils.convert(dst));
150         Assert.assertFalse(decoder.isCompleted());
151         Assert.assertEquals(4, metrics.getBytesTransferred());
152 
153         dst.clear();
154         bytesRead = decoder.read(dst);
155         Assert.assertEquals(2, bytesRead);
156         Assert.assertEquals("f;", CodecTestUtils.convert(dst));
157         Assert.assertFalse(decoder.isCompleted());
158         Assert.assertEquals(6, metrics.getBytesTransferred());
159 
160         dst.clear();
161         bytesRead = decoder.read(dst);
162         Assert.assertEquals(4, bytesRead);
163         Assert.assertEquals("more", CodecTestUtils.convert(dst));
164         Assert.assertFalse(decoder.isCompleted());
165         Assert.assertEquals(10, metrics.getBytesTransferred());
166 
167         dst.clear();
168         bytesRead = decoder.read(dst);
169         Assert.assertEquals(4, bytesRead);
170         Assert.assertEquals(" stu", CodecTestUtils.convert(dst));
171         Assert.assertFalse(decoder.isCompleted());
172         Assert.assertEquals(14, metrics.getBytesTransferred());
173 
174         dst.clear();
175         bytesRead = decoder.read(dst);
176         Assert.assertEquals(2, bytesRead);
177         Assert.assertEquals("ff", CodecTestUtils.convert(dst));
178         Assert.assertTrue(decoder.isCompleted());
179         Assert.assertEquals(16, metrics.getBytesTransferred());
180 
181         dst.clear();
182         bytesRead = decoder.read(dst);
183         Assert.assertEquals(-1, bytesRead);
184         Assert.assertTrue(decoder.isCompleted());
185         Assert.assertEquals(16, metrics.getBytesTransferred());
186     }
187 
188     @Test
189     public void testDecodingFromSessionBuffer1() throws Exception {
190         final ReadableByteChannel channel = new ReadableByteChannelMock(
191                 new String[] {"stuff;", "more stuff"}, Consts.ASCII);
192 
193         final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, Consts.ASCII);
194         final HttpTransportMetricsImpl metrics = new HttpTransportMetricsImpl();
195 
196         inbuf.fill(channel);
197 
198         Assert.assertEquals(6, inbuf.length());
199 
200         final LengthDelimitedDecoder decoder = new LengthDelimitedDecoder(
201                 channel, inbuf, metrics, 16);
202 
203         final ByteBuffer dst = ByteBuffer.allocate(1024);
204 
205         int bytesRead = decoder.read(dst);
206         Assert.assertEquals(6, bytesRead);
207         Assert.assertEquals("stuff;", CodecTestUtils.convert(dst));
208         Assert.assertFalse(decoder.isCompleted());
209         Assert.assertEquals(0, metrics.getBytesTransferred());
210 
211         dst.clear();
212         bytesRead = decoder.read(dst);
213         Assert.assertEquals(10, bytesRead);
214         Assert.assertEquals("more stuff", CodecTestUtils.convert(dst));
215         Assert.assertTrue(decoder.isCompleted());
216         Assert.assertEquals(10, metrics.getBytesTransferred());
217 
218         dst.clear();
219         bytesRead = decoder.read(dst);
220         Assert.assertEquals(-1, bytesRead);
221         Assert.assertTrue(decoder.isCompleted());
222         Assert.assertEquals(10, metrics.getBytesTransferred());
223     }
224 
225     @Test
226     public void testDecodingFromSessionBuffer2() throws Exception {
227         final ReadableByteChannel channel = new ReadableByteChannelMock(
228                 new String[] {
229                         "stuff;",
230                         "more stuff; and a lot more stuff"}, Consts.ASCII);
231 
232         final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, Consts.ASCII);
233         final HttpTransportMetricsImpl metrics = new HttpTransportMetricsImpl();
234 
235         inbuf.fill(channel);
236         inbuf.fill(channel);
237 
238         Assert.assertEquals(38, inbuf.length());
239 
240         final LengthDelimitedDecoder decoder = new LengthDelimitedDecoder(
241                 channel, inbuf, metrics, 16);
242 
243         final ByteBuffer dst = ByteBuffer.allocate(1024);
244 
245         int bytesRead = decoder.read(dst);
246         Assert.assertEquals(16, bytesRead);
247         Assert.assertEquals("stuff;more stuff", CodecTestUtils.convert(dst));
248         Assert.assertTrue(decoder.isCompleted());
249         Assert.assertEquals(0, metrics.getBytesTransferred());
250 
251         dst.clear();
252         bytesRead = decoder.read(dst);
253         Assert.assertEquals(-1, bytesRead);
254         Assert.assertTrue(decoder.isCompleted());
255         Assert.assertEquals(0, metrics.getBytesTransferred());
256     }
257 
258     /* ----------------- FileChannel Part testing --------------------------- */
259     @Test
260     public void testBasicDecodingFile() throws Exception {
261         final ReadableByteChannel channel = new ReadableByteChannelMock(
262                 new String[] {"stuff; ", "more stuff; ", "a lot more stuff!!!"}, Consts.ASCII);
263 
264         final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, Consts.ASCII);
265         final HttpTransportMetricsImpl metrics = new HttpTransportMetricsImpl();
266         final LengthDelimitedDecoder decoder = new LengthDelimitedDecoder(
267                 channel, inbuf, metrics, 36);
268 
269         createTempFile();
270         final RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw");
271         try {
272             final FileChannel fchannel = testfile.getChannel();
273             long pos = 0;
274             while (!decoder.isCompleted()) {
275                 final long bytesRead = decoder.transfer(fchannel, pos, 10);
276                 if (bytesRead > 0) {
277                     pos += bytesRead;
278                 }
279             }
280         } finally {
281             testfile.close();
282         }
283         Assert.assertEquals(this.tmpfile.length(), metrics.getBytesTransferred());
284         Assert.assertEquals("stuff; more stuff; a lot more stuff!",
285             CodecTestUtils.readFromFile(this.tmpfile));
286     }
287 
288     @Test
289     public void testDecodingFileWithBufferedSessionData() throws Exception {
290         final ReadableByteChannel channel = new ReadableByteChannelMock(
291                 new String[] {"stuff; ", "more stuff; ", "a lot more stuff!!!"}, Consts.ASCII);
292 
293         final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, Consts.ASCII);
294         final HttpTransportMetricsImpl metrics = new HttpTransportMetricsImpl();
295         final LengthDelimitedDecoder decoder = new LengthDelimitedDecoder(
296                 channel, inbuf, metrics, 36);
297 
298         final int i = inbuf.fill(channel);
299         Assert.assertEquals(7, i);
300 
301         createTempFile();
302         final RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw");
303         try {
304             final FileChannel fchannel = testfile.getChannel();
305             long pos = 0;
306             while (!decoder.isCompleted()) {
307                 final long bytesRead = decoder.transfer(fchannel, pos, 10);
308                 if (bytesRead > 0) {
309                     pos += bytesRead;
310                 }
311             }
312         } finally {
313             testfile.close();
314         }
315         Assert.assertEquals(this.tmpfile.length() - 7, metrics.getBytesTransferred());
316         Assert.assertEquals("stuff; more stuff; a lot more stuff!",
317             CodecTestUtils.readFromFile(this.tmpfile));
318     }
319 
320     @Test
321     public void testDecodingFileWithOffsetAndBufferedSessionData() throws Exception {
322         final ReadableByteChannel channel = new ReadableByteChannelMock(
323                 new String[] {"stuff; ", "more stuff; ", "a lot more stuff!"}, Consts.ASCII);
324 
325         final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, Consts.ASCII);
326         final HttpTransportMetricsImpl metrics = new HttpTransportMetricsImpl();
327         final LengthDelimitedDecoder decoder = new LengthDelimitedDecoder(
328                 channel, inbuf, metrics, 36);
329 
330         final int i = inbuf.fill(channel);
331         Assert.assertEquals(7, i);
332 
333         final byte[] beginning =  EncodingUtils.getAsciiBytes("beginning; ");
334 
335         createTempFile();
336         RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw");
337         try {
338             testfile.write(beginning);
339         } finally {
340             testfile.close();
341         }
342 
343         testfile = new RandomAccessFile(this.tmpfile, "rw");
344         try {
345             final FileChannel fchannel = testfile.getChannel();
346 
347             long pos = beginning.length;
348             while (!decoder.isCompleted()) {
349                 if(testfile.length() < pos) {
350                     testfile.setLength(pos);
351                 }
352                 final long bytesRead = decoder.transfer(fchannel, pos, 10);
353                 if (bytesRead > 0) {
354                     pos += bytesRead;
355                 }
356             }
357         } finally {
358             testfile.close();
359         }
360 
361         // count everything except the initial 7 bytes that went to the session buffer
362         Assert.assertEquals(this.tmpfile.length() - 7 - beginning.length, metrics.getBytesTransferred());
363         Assert.assertEquals("beginning; stuff; more stuff; a lot more stuff!",
364             CodecTestUtils.readFromFile(this.tmpfile));
365     }
366 
367     @Test
368     public void testWriteBeyondFileSize() throws Exception {
369         final ReadableByteChannel channel = new ReadableByteChannelMock(
370                 new String[] {"a"}, Consts.ASCII);
371 
372         final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, Consts.ASCII);
373         final HttpTransportMetricsImpl metrics = new HttpTransportMetricsImpl();
374         final LengthDelimitedDecoder decoder = new LengthDelimitedDecoder(
375                 channel, inbuf, metrics, 1);
376 
377         createTempFile();
378         final RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw");
379         try {
380             final FileChannel fchannel = testfile.getChannel();
381             Assert.assertEquals(0, testfile.length());
382             try {
383                 decoder.transfer(fchannel, 5, 10);
384                 Assert.fail("IOException should have been thrown");
385             } catch(final IOException expected) {
386             }
387         } finally {
388             testfile.close();
389         }
390     }
391 
392     @Test
393     public void testCodingBeyondContentLimitFile() throws Exception {
394         final ReadableByteChannel channel = new ReadableByteChannelMock(
395                 new String[] {
396                         "stuff;",
397                         "more stuff; and a lot more stuff"}, Consts.ASCII);
398 
399         final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, Consts.ASCII);
400         final HttpTransportMetricsImpl metrics = new HttpTransportMetricsImpl();
401         final LengthDelimitedDecoder decoder = new LengthDelimitedDecoder(
402                 channel, inbuf, metrics, 16);
403 
404         createTempFile();
405         final RandomAccessFile testfile  = new RandomAccessFile(this.tmpfile, "rw");
406         try {
407             final FileChannel fchannel = testfile.getChannel();
408 
409             long bytesRead = decoder.transfer(fchannel, 0, 6);
410             Assert.assertEquals(6, bytesRead);
411             Assert.assertFalse(decoder.isCompleted());
412             Assert.assertEquals(6, metrics.getBytesTransferred());
413 
414             bytesRead = decoder.transfer(fchannel,0 , 10);
415             Assert.assertEquals(10, bytesRead);
416             Assert.assertTrue(decoder.isCompleted());
417             Assert.assertEquals(16, metrics.getBytesTransferred());
418 
419             bytesRead = decoder.transfer(fchannel, 0, 1);
420             Assert.assertEquals(-1, bytesRead);
421             Assert.assertTrue(decoder.isCompleted());
422             Assert.assertEquals(16, metrics.getBytesTransferred());
423         } finally {
424             testfile.close();
425         }
426     }
427 
428     @Test
429     public void testInvalidConstructor() {
430         final ReadableByteChannel channel = new ReadableByteChannelMock(
431                 new String[] {"stuff;", "more stuff"}, Consts.ASCII);
432 
433         final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, Consts.ASCII);
434         final HttpTransportMetricsImpl metrics = new HttpTransportMetricsImpl();
435         try {
436             new LengthDelimitedDecoder(null, null, null, 10);
437             Assert.fail("IllegalArgumentException should have been thrown");
438         } catch (final IllegalArgumentException ex) {
439             // ignore
440         }
441         try {
442             new LengthDelimitedDecoder(channel, null, null, 10);
443             Assert.fail("IllegalArgumentException should have been thrown");
444         } catch (final IllegalArgumentException ex) {
445             // ignore
446         }
447         try {
448             new LengthDelimitedDecoder(channel, inbuf, null, 10);
449             Assert.fail("IllegalArgumentException should have been thrown");
450         } catch (final IllegalArgumentException ex) {
451             // ignore
452         }
453         try {
454             new LengthDelimitedDecoder(channel, inbuf, metrics, -10);
455             Assert.fail("IllegalArgumentException should have been thrown");
456         } catch (final IllegalArgumentException ex) {
457             // ignore
458         }
459     }
460 
461     @Test
462     public void testInvalidInput() throws Exception {
463         final String s = "stuff";
464         final ReadableByteChannel channel = new ReadableByteChannelMock(
465                 new String[] {s}, Consts.ASCII);
466 
467         final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, Consts.ASCII);
468         final HttpTransportMetricsImpl metrics = new HttpTransportMetricsImpl();
469         final LengthDelimitedDecoder decoder = new LengthDelimitedDecoder(
470                 channel, inbuf, metrics, 3);
471 
472         try {
473             decoder.read(null);
474             Assert.fail("IllegalArgumentException should have been thrown");
475         } catch (final IllegalArgumentException ex) {
476             // expected
477         }
478     }
479 
480     @Test
481     public void testZeroLengthDecoding() throws Exception {
482         final ReadableByteChannel channel = new ReadableByteChannelMock(
483                 new String[] {"stuff"}, Consts.ASCII);
484 
485         final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, Consts.ASCII);
486         final HttpTransportMetricsImpl metrics = new HttpTransportMetricsImpl();
487         final LengthDelimitedDecoder decoder = new LengthDelimitedDecoder(
488                 channel, inbuf, metrics, 0);
489 
490         final ByteBuffer dst = ByteBuffer.allocate(1024);
491 
492         final int bytesRead = decoder.read(dst);
493         Assert.assertEquals(-1, bytesRead);
494         Assert.assertTrue(decoder.isCompleted());
495         Assert.assertEquals(0, metrics.getBytesTransferred());
496     }
497 
498     @Test(expected=ConnectionClosedException.class)
499     public void testTruncatedContent() throws Exception {
500         final ReadableByteChannel channel = new ReadableByteChannelMock(
501                 new String[] {"1234567890"}, Consts.ASCII);
502 
503         final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, Consts.ASCII);
504         final HttpTransportMetricsImpl metrics = new HttpTransportMetricsImpl();
505         final LengthDelimitedDecoder decoder = new LengthDelimitedDecoder(
506                 channel, inbuf, metrics, 20);
507 
508         final ByteBuffer dst = ByteBuffer.allocate(1024);
509 
510         final int bytesRead = decoder.read(dst);
511         Assert.assertEquals(10, bytesRead);
512         decoder.read(dst);
513     }
514 
515     @Test(expected=ConnectionClosedException.class)
516     public void testTruncatedContentWithFile() throws Exception {
517         final ReadableByteChannel channel = new ReadableByteChannelMock(
518                 new String[] {"1234567890"}, Consts.ASCII);
519 
520         final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, Consts.ASCII);
521         final HttpTransportMetricsImpl metrics = new HttpTransportMetricsImpl();
522         final LengthDelimitedDecoder decoder = new LengthDelimitedDecoder(
523                 channel, inbuf, metrics, 20);
524 
525         createTempFile();
526         final RandomAccessFile testfile  = new RandomAccessFile(this.tmpfile, "rw");
527         try {
528             final FileChannel fchannel = testfile.getChannel();
529             final long bytesRead = decoder.transfer(fchannel, 0, Integer.MAX_VALUE);
530             Assert.assertEquals(10, bytesRead);
531             decoder.transfer(fchannel, 0, Integer.MAX_VALUE);
532         } finally {
533             testfile.close();
534         }
535     }
536 
537 }