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  package org.apache.http.nio.client.methods;
28  
29  import java.io.File;
30  import java.io.FileInputStream;
31  import java.io.FileNotFoundException;
32  import java.io.IOException;
33  import java.io.InputStream;
34  import java.net.URI;
35  import java.nio.charset.Charset;
36  import java.util.concurrent.Future;
37  
38  import org.apache.commons.io.FileUtils;
39  import org.apache.commons.io.IOUtils;
40  import org.apache.commons.io.LineIterator;
41  import org.apache.commons.io.output.FileWriterWithEncoding;
42  import org.apache.http.Consts;
43  import org.apache.http.localserver.HttpAsyncTestBase;
44  import org.apache.http.HttpEntity;
45  import org.apache.http.HttpEntityEnclosingRequest;
46  import org.apache.http.HttpException;
47  import org.apache.http.HttpHost;
48  import org.apache.http.HttpRequest;
49  import org.apache.http.HttpResponse;
50  import org.apache.http.HttpStatus;
51  import org.apache.http.client.methods.HttpPost;
52  import org.apache.http.entity.BasicHttpEntity;
53  import org.apache.http.entity.ContentType;
54  import org.apache.http.nio.entity.NFileEntity;
55  import org.apache.http.nio.entity.NStringEntity;
56  import org.apache.http.nio.protocol.BasicAsyncRequestHandler;
57  import org.apache.http.protocol.HttpContext;
58  import org.apache.http.protocol.HttpRequestHandler;
59  import org.junit.After;
60  import org.junit.AfterClass;
61  import org.junit.Assert;
62  import org.junit.BeforeClass;
63  import org.junit.Test;
64  
65  public class TestZeroCopy extends HttpAsyncTestBase {
66  
67      private static final String[] TEXT = {
68          "blah blah blah blah blah blah blah blah blah blah blah blah blah blah",
69          "yada yada yada yada yada yada yada yada yada yada yada yada yada yada",
70          "da da da da da da da da da da da da da da da da da da da da da da da da",
71          "nyet nyet nyet nyet nyet nyet nyet nyet nyet nyet nyet nyet nyet nyet"
72      };
73  
74      private static final Charset ASCII = Charset.forName("ascii");
75      private static File TEST_FILE;
76      private File tmpfile;
77  
78      @BeforeClass
79      public static void createSrcFile() throws Exception {
80          final File tmpdir = FileUtils.getTempDirectory();
81          TEST_FILE = new File(tmpdir, "src.test");
82          final FileWriterWithEncoding out = new FileWriterWithEncoding(TEST_FILE, ASCII);
83          try {
84              for (int i = 0; i < 500; i++) {
85                  for (final String line: TEXT) {
86                      out.write(line);
87                      out.write("\r\n");
88                  }
89              }
90          } finally {
91              out.close();
92          }
93      }
94  
95      @AfterClass
96      public static void deleteSrcFile() throws Exception {
97          if (TEST_FILE != null) {
98              TEST_FILE.delete();
99              TEST_FILE = null;
100         }
101     }
102 
103     @After
104     public void cleanUp() throws Exception {
105         if (this.tmpfile != null && this.tmpfile.exists()) {
106             this.tmpfile.delete();
107         }
108     }
109 
110     static class TestZeroCopyPost extends BaseZeroCopyRequestProducer {
111 
112         private final boolean forceChunking;
113 
114         protected TestZeroCopyPost(
115                 final String requestURI,
116                 final boolean forceChunking) throws FileNotFoundException {
117             super(URI.create(requestURI), TEST_FILE, ContentType.create("text/plain"));
118             this.forceChunking = forceChunking;
119         }
120 
121         @Override
122         protected HttpEntityEnclosingRequest createRequest(final URI requestURI, final HttpEntity entity) {
123             final HttpPost httppost = new HttpPost(requestURI);
124             if (this.forceChunking) {
125                 final BasicHttpEntity chunkedEntity = new BasicHttpEntity();
126                 chunkedEntity.setChunked(true);
127                 httppost.setEntity(chunkedEntity);
128             } else {
129                 httppost.setEntity(entity);
130             }
131             return httppost;
132         }
133 
134     }
135 
136     static class TestZeroCopyConsumer extends ZeroCopyConsumer<Integer> {
137 
138         public TestZeroCopyConsumer(final File file) throws FileNotFoundException {
139             super(file);
140         }
141 
142         @Override
143         protected Integer process(
144                 final HttpResponse response,
145                 final File file,
146                 final ContentType contentType) {
147             return response.getStatusLine().getStatusCode();
148         }
149 
150     }
151 
152     static class TestHandler implements HttpRequestHandler {
153 
154         private final boolean forceChunking;
155 
156         TestHandler(final boolean forceChunking) {
157             super();
158             this.forceChunking = forceChunking;
159         }
160 
161         @Override
162         public void handle(
163                 final HttpRequest request,
164                 final HttpResponse response,
165                 final HttpContext context) throws HttpException, IOException {
166             HttpEntity requestEntity = null;
167             if (request instanceof HttpEntityEnclosingRequest) {
168                 requestEntity = ((HttpEntityEnclosingRequest) request).getEntity();
169             }
170             if (requestEntity == null) {
171                 response.setEntity(new NStringEntity("Empty content"));
172                 return;
173             }
174 
175             boolean ok = true;
176 
177             final InputStream instream = requestEntity.getContent();
178             try {
179                 final ContentType contentType = ContentType.getOrDefault(requestEntity);
180                 Charset charset = contentType.getCharset();
181                 if (charset == null) {
182                     charset = Consts.ISO_8859_1;
183                 }
184                 final LineIterator it = IOUtils.lineIterator(instream, charset.name());
185                 int count = 0;
186                 while (it.hasNext()) {
187                     final String line = it.next();
188                     final int i = count % TEXT.length;
189                     final String expected = TEXT[i];
190                     if (!line.equals(expected)) {
191                         ok = false;
192                         break;
193                     }
194                     count++;
195                 }
196             } finally {
197                 instream.close();
198             }
199             if (ok) {
200                 final NFileEntity responseEntity = new NFileEntity(TEST_FILE,
201                         ContentType.create("text/plian"));
202                 if (this.forceChunking) {
203                     responseEntity.setChunked(true);
204                 }
205                 response.setEntity(responseEntity);
206             } else {
207                 response.setEntity(new NStringEntity("Invalid content"));
208             }
209         }
210     }
211 
212     @Test
213     public void testTwoWayZeroCopy() throws Exception {
214         this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(new TestHandler(false)));
215         final HttpHost target = start();
216 
217         final File tmpdir = FileUtils.getTempDirectory();
218         this.tmpfile = new File(tmpdir, "dst.test");
219         final TestZeroCopyPost httppost = new TestZeroCopyPost(target.toURI() + "/bounce", false);
220         final TestZeroCopyConsumer consumer = new TestZeroCopyConsumer(this.tmpfile);
221         final Future<Integer> future = this.httpclient.execute(httppost, consumer, null);
222         final Integer status = future.get();
223         Assert.assertNotNull(status);
224         Assert.assertEquals(HttpStatus.SC_OK, status.intValue());
225         final InputStream instream = new FileInputStream(this.tmpfile);
226         try {
227             final LineIterator it = IOUtils.lineIterator(instream, ASCII.name());
228             int count = 0;
229             while (it.hasNext()) {
230                 final String line = it.next();
231                 final int i = count % TEXT.length;
232                 final String expected = TEXT[i];
233                 Assert.assertEquals(expected, line);
234                 count++;
235             }
236         } finally {
237             instream.close();
238         }
239     }
240 
241     @Test
242     public void testZeroCopyFallback() throws Exception {
243         this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(new TestHandler(true)));
244         final HttpHost target = start();
245         final File tmpdir = FileUtils.getTempDirectory();
246         this.tmpfile = new File(tmpdir, "dst.test");
247         final TestZeroCopyPost httppost = new TestZeroCopyPost(target.toURI() + "/bounce", true);
248         final TestZeroCopyConsumer consumer = new TestZeroCopyConsumer(this.tmpfile);
249         final Future<Integer> future = this.httpclient.execute(httppost, consumer, null);
250         final Integer status = future.get();
251         Assert.assertNotNull(status);
252         Assert.assertEquals(HttpStatus.SC_OK, status.intValue());
253         final InputStream instream = new FileInputStream(this.tmpfile);
254         try {
255             final LineIterator it = IOUtils.lineIterator(instream, ASCII.name());
256             int count = 0;
257             while (it.hasNext()) {
258                 final String line = it.next();
259                 final int i = count % TEXT.length;
260                 final String expected = TEXT[i];
261                 Assert.assertEquals(expected, line);
262                 count++;
263             }
264         } finally {
265             instream.close();
266         }
267     }
268 
269 }