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.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  import java.nio.charset.StandardCharsets;
37  
38  import org.apache.hc.core5.http.ReadableByteChannelMock;
39  import org.apache.hc.core5.http.impl.BasicHttpTransportMetrics;
40  import org.apache.hc.core5.http.nio.SessionInputBuffer;
41  import org.junit.After;
42  import org.junit.Assert;
43  import org.junit.Test;
44  
45  /**
46   * Simple tests for {@link LengthDelimitedDecoder}.
47   */
48  public class TestIdentityDecoder {
49  
50      private File tmpfile;
51  
52      protected File createTempFile() throws IOException {
53          this.tmpfile = File.createTempFile("testFile", ".txt");
54          return this.tmpfile;
55      }
56  
57      @After
58      public void deleteTempFile() {
59          if (this.tmpfile != null && this.tmpfile.exists()) {
60              this.tmpfile.delete();
61          }
62      }
63  
64      @Test
65      public void testBasicDecoding() throws Exception {
66          final ReadableByteChannel channel = new ReadableByteChannelMock(
67                  new String[] {"stuff;", "more stuff"}, StandardCharsets.US_ASCII);
68  
69          final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII);
70          final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
71          final IdentityDecoder decoder = new IdentityDecoder(channel, inbuf, metrics);
72  
73          final ByteBuffer dst = ByteBuffer.allocate(1024);
74  
75          int bytesRead = decoder.read(dst);
76          Assert.assertEquals(6, bytesRead);
77          Assert.assertEquals("stuff;", CodecTestUtils.convert(dst));
78          Assert.assertFalse(decoder.isCompleted());
79          Assert.assertEquals(6, metrics.getBytesTransferred());
80  
81          dst.clear();
82          bytesRead = decoder.read(dst);
83          Assert.assertEquals(10, bytesRead);
84          Assert.assertEquals("more stuff", CodecTestUtils.convert(dst));
85          Assert.assertFalse(decoder.isCompleted());
86          Assert.assertEquals(16, metrics.getBytesTransferred());
87  
88          dst.clear();
89          bytesRead = decoder.read(dst);
90          Assert.assertEquals(-1, bytesRead);
91          Assert.assertTrue(decoder.isCompleted());
92          Assert.assertEquals(16, metrics.getBytesTransferred());
93  
94          dst.clear();
95          bytesRead = decoder.read(dst);
96          Assert.assertEquals(-1, bytesRead);
97          Assert.assertTrue(decoder.isCompleted());
98          Assert.assertEquals(16, metrics.getBytesTransferred());
99  
100         Assert.assertEquals("[identity; completed: true]", decoder.toString());
101     }
102 
103     @Test
104     public void testDecodingFromSessionBuffer() throws Exception {
105         final ReadableByteChannel channel = new ReadableByteChannelMock(
106                 new String[] {"stuff;", "more stuff"}, StandardCharsets.US_ASCII);
107 
108         final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII);
109         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
110 
111         inbuf.fill(channel);
112 
113         Assert.assertEquals(6, inbuf.length());
114 
115         final IdentityDecoder decoder = new IdentityDecoder(channel, inbuf, metrics);
116 
117         final ByteBuffer dst = ByteBuffer.allocate(1024);
118 
119         int bytesRead = decoder.read(dst);
120         Assert.assertEquals(6, bytesRead);
121         Assert.assertEquals("stuff;", CodecTestUtils.convert(dst));
122         Assert.assertFalse(decoder.isCompleted());
123         Assert.assertEquals(0, metrics.getBytesTransferred()); // doesn't count if from session buffer
124 
125         dst.clear();
126         bytesRead = decoder.read(dst);
127         Assert.assertEquals(10, bytesRead);
128         Assert.assertEquals("more stuff", CodecTestUtils.convert(dst));
129         Assert.assertFalse(decoder.isCompleted());
130         Assert.assertEquals(10, metrics.getBytesTransferred());
131 
132         dst.clear();
133         bytesRead = decoder.read(dst);
134         Assert.assertEquals(-1, bytesRead);
135         Assert.assertTrue(decoder.isCompleted());
136         Assert.assertEquals(10, metrics.getBytesTransferred());
137 
138         dst.clear();
139         bytesRead = decoder.read(dst);
140         Assert.assertEquals(-1, bytesRead);
141         Assert.assertTrue(decoder.isCompleted());
142         Assert.assertEquals(10, metrics.getBytesTransferred());
143 
144     }
145 
146     @Test
147     public void testBasicDecodingFile() throws Exception {
148         final ReadableByteChannel channel = new ReadableByteChannelMock(
149                 new String[] {"stuff; ", "more stuff; ", "a lot more stuff!"}, StandardCharsets.US_ASCII);
150 
151         final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII);
152         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
153         final IdentityDecoder decoder = new IdentityDecoder(
154                 channel, inbuf, metrics);
155 
156         createTempFile();
157         final RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw");
158         try {
159             final FileChannel fchannel = testfile.getChannel();
160             long pos = 0;
161             while (!decoder.isCompleted()) {
162                 final long bytesRead = decoder.transfer(fchannel, pos, 10);
163                 if (bytesRead > 0) {
164                     pos += bytesRead;
165                 }
166             }
167 
168             Assert.assertEquals(testfile.length(), metrics.getBytesTransferred());
169         } finally {
170             testfile.close();
171         }
172         Assert.assertEquals("stuff; more stuff; a lot more stuff!",
173             CodecTestUtils.readFromFile(this.tmpfile));
174     }
175 
176     @Test
177     public void testDecodingFileWithBufferedSessionData() throws Exception {
178         final ReadableByteChannel channel = new ReadableByteChannelMock(
179                 new String[] {"stuff; ", "more stuff; ", "a lot more stuff!"}, StandardCharsets.US_ASCII);
180 
181         final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII);
182         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
183         final IdentityDecoder decoder = new IdentityDecoder(
184                 channel, inbuf, metrics);
185 
186         final int i = inbuf.fill(channel);
187         Assert.assertEquals(7, i);
188 
189         createTempFile();
190         final RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw");
191         try {
192             final FileChannel fchannel = testfile.getChannel();
193             long pos = 0;
194             while (!decoder.isCompleted()) {
195                 final long bytesRead = decoder.transfer(fchannel, pos, 10);
196                 if (bytesRead > 0) {
197                     pos += bytesRead;
198                 }
199             }
200 
201             // count everything except the initial 7 bytes that went to the session buffer
202             Assert.assertEquals(testfile.length() - 7, metrics.getBytesTransferred());
203         } finally {
204             testfile.close();
205         }
206         Assert.assertEquals("stuff; more stuff; a lot more stuff!",
207             CodecTestUtils.readFromFile(this.tmpfile));
208     }
209 
210     @Test
211     public void testDecodingFileWithOffsetAndBufferedSessionData() throws Exception {
212         final ReadableByteChannel channel = new ReadableByteChannelMock(
213                 new String[] {"stuff; ", "more stuff; ", "a lot more stuff!"}, StandardCharsets.US_ASCII);
214 
215         final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII);
216         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
217         final IdentityDecoder decoder = new IdentityDecoder(
218                 channel, inbuf, metrics);
219 
220         final int i = inbuf.fill(channel);
221         Assert.assertEquals(7, i);
222 
223         final byte[] beginning = "beginning; ".getBytes(StandardCharsets.US_ASCII);
224 
225         createTempFile();
226         RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw");
227         try {
228             testfile.write(beginning);
229         } finally {
230             testfile.close();
231         }
232 
233         testfile = new RandomAccessFile(this.tmpfile, "rw");
234         try {
235             final FileChannel fchannel = testfile.getChannel();
236             long pos = beginning.length;
237             while (!decoder.isCompleted()) {
238                 if(testfile.length() < pos) {
239                     testfile.setLength(pos);
240                 }
241                 final long bytesRead = decoder.transfer(fchannel, pos, 10);
242                 if (bytesRead > 0) {
243                     pos += bytesRead;
244                 }
245             }
246 
247             // count everything except the initial 7 bytes that went to the session buffer
248             Assert.assertEquals(testfile.length() - 7 - beginning.length, metrics.getBytesTransferred());
249         } finally {
250             testfile.close();
251         }
252 
253         Assert.assertEquals("beginning; stuff; more stuff; a lot more stuff!",
254             CodecTestUtils.readFromFile(this.tmpfile));
255     }
256 
257     @Test
258     public void testWriteBeyondFileSize() throws Exception {
259         final ReadableByteChannel channel = new ReadableByteChannelMock(
260                 new String[] {"a"}, StandardCharsets.US_ASCII);
261 
262         final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII);
263         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
264         final IdentityDecoder decoder = new IdentityDecoder(
265                 channel, inbuf, metrics);
266 
267         createTempFile();
268         final RandomAccessFile testfile = new RandomAccessFile(this.tmpfile, "rw");
269         try {
270             Assert.assertEquals(0, testfile.length());
271             final FileChannel fchannel = testfile.getChannel();
272             try {
273                 decoder.transfer(fchannel, 5, 10);
274                 Assert.fail("expected IOException");
275             } catch(final IOException iox) {}
276         } finally {
277             testfile.close();
278         }
279     }
280 
281     @Test
282     public void testInvalidConstructor() {
283         final ReadableByteChannel channel = new ReadableByteChannelMock(
284                 new String[] {"stuff;", "more stuff"}, StandardCharsets.US_ASCII);
285 
286         final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII);
287         try {
288             new IdentityDecoder(null, null, null);
289             Assert.fail("IllegalArgumentException should have been thrown");
290         } catch (final IllegalArgumentException ex) {
291             // ignore
292         }
293         try {
294             new IdentityDecoder(channel, null, null);
295             Assert.fail("IllegalArgumentException should have been thrown");
296         } catch (final IllegalArgumentException ex) {
297             // ignore
298         }
299         try {
300             new IdentityDecoder(channel, inbuf, null);
301             Assert.fail("IllegalArgumentException should have been thrown");
302         } catch (final IllegalArgumentException ex) {
303             // ignore
304         }
305     }
306 
307     @Test
308     public void testInvalidInput() throws Exception {
309         final String s = "stuff";
310         final ReadableByteChannel channel = new ReadableByteChannelMock(
311                 new String[] {s}, StandardCharsets.US_ASCII);
312 
313         final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 256, 0, StandardCharsets.US_ASCII);
314         final BasicHttpTransportMetrics metrics = new BasicHttpTransportMetrics();
315         final IdentityDecoder decoder = new IdentityDecoder(channel, inbuf, metrics);
316 
317         try {
318             decoder.read(null);
319             Assert.fail("IllegalArgumentException should have been thrown");
320         } catch (final IllegalArgumentException ex) {
321             // expected
322         }
323     }
324 
325 }