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.integration;
28  
29  import java.io.IOException;
30  import java.util.concurrent.CancellationException;
31  import java.util.concurrent.CountDownLatch;
32  import java.util.concurrent.ExecutionException;
33  import java.util.concurrent.Future;
34  import java.util.concurrent.TimeUnit;
35  import java.util.concurrent.atomic.AtomicBoolean;
36  import java.util.concurrent.atomic.AtomicInteger;
37  
38  import org.apache.http.HttpConnection;
39  import org.apache.http.HttpException;
40  import org.apache.http.HttpHost;
41  import org.apache.http.HttpRequest;
42  import org.apache.http.HttpResponse;
43  import org.apache.http.client.methods.HttpGet;
44  import org.apache.http.concurrent.FutureCallback;
45  import org.apache.http.entity.ContentType;
46  import org.apache.http.impl.nio.client.HttpAsyncClients;
47  import org.apache.http.localserver.EchoHandler;
48  import org.apache.http.localserver.HttpAsyncTestBase;
49  import org.apache.http.localserver.RandomHandler;
50  import org.apache.http.nio.ContentDecoder;
51  import org.apache.http.nio.ContentEncoder;
52  import org.apache.http.nio.IOControl;
53  import org.apache.http.nio.client.methods.HttpAsyncMethods;
54  import org.apache.http.nio.entity.NStringEntity;
55  import org.apache.http.nio.protocol.BasicAsyncRequestConsumer;
56  import org.apache.http.nio.protocol.BasicAsyncRequestHandler;
57  import org.apache.http.nio.protocol.BasicAsyncRequestProducer;
58  import org.apache.http.nio.protocol.BasicAsyncResponseProducer;
59  import org.apache.http.nio.protocol.HttpAsyncExchange;
60  import org.apache.http.nio.protocol.HttpAsyncRequestConsumer;
61  import org.apache.http.nio.protocol.HttpAsyncRequestHandler;
62  import org.apache.http.nio.protocol.HttpAsyncRequestProducer;
63  import org.apache.http.nio.protocol.HttpAsyncResponseConsumer;
64  import org.apache.http.protocol.HttpContext;
65  import org.apache.http.protocol.HttpCoreContext;
66  import org.junit.Assert;
67  import org.junit.Ignore;
68  import org.junit.Test;
69  
70  public class TestHttpAsyncPrematureTermination extends HttpAsyncTestBase {
71  
72      @Test
73      public void testConnectionTerminatedProcessingRequest() throws Exception {
74          this.serverBootstrap.registerHandler("*", new HttpAsyncRequestHandler<HttpRequest>() {
75  
76              @Override
77              public HttpAsyncRequestConsumer<HttpRequest> processRequest(
78                      final HttpRequest request,
79                      final HttpContext context) throws HttpException, IOException {
80                  final HttpConnection conn = (HttpConnection) context.getAttribute(
81                          HttpCoreContext.HTTP_CONNECTION);
82                  conn.shutdown();
83                  return new BasicAsyncRequestConsumer();
84              }
85  
86              @Override
87              public void handle(
88                      final HttpRequest request,
89                      final HttpAsyncExchange httpExchange,
90                      final HttpContext context) throws HttpException, IOException {
91                  final HttpResponse response = httpExchange.getResponse();
92                  response.setEntity(new NStringEntity("all is well", ContentType.TEXT_PLAIN));
93                  httpExchange.submitResponse();
94              }
95  
96          });
97  
98          final HttpHost target = start();
99          final HttpGet httpget = new HttpGet("/");
100 
101         final CountDownLatch latch = new CountDownLatch(1);
102 
103         final FutureCallback<HttpResponse> callback = new FutureCallback<HttpResponse>() {
104 
105             @Override
106             public void cancelled() {
107                 latch.countDown();
108             }
109 
110             @Override
111             public void failed(final Exception ex) {
112                 latch.countDown();
113             }
114 
115             @Override
116             public void completed(final HttpResponse response) {
117                 Assert.fail();
118             }
119 
120         };
121 
122         this.httpclient.execute(target, httpget, callback);
123         Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
124     }
125 
126     @Test
127     public void testConnectionTerminatedHandlingRequest() throws Exception {
128         this.serverBootstrap.registerHandler("*", new HttpAsyncRequestHandler<HttpRequest>() {
129 
130             @Override
131             public HttpAsyncRequestConsumer<HttpRequest> processRequest(
132                     final HttpRequest request,
133                     final HttpContext context) throws HttpException, IOException {
134                 return new BasicAsyncRequestConsumer();
135             }
136 
137             @Override
138             public void handle(
139                     final HttpRequest request,
140                     final HttpAsyncExchange httpExchange,
141                     final HttpContext context) throws HttpException, IOException {
142                 final HttpConnection conn = (HttpConnection) context.getAttribute(
143                         HttpCoreContext.HTTP_CONNECTION);
144                 conn.shutdown();
145                 final HttpResponse response = httpExchange.getResponse();
146                 response.setEntity(new NStringEntity("all is well", ContentType.TEXT_PLAIN));
147                 httpExchange.submitResponse();
148             }
149 
150         });
151 
152         final HttpHost target = start();
153         final HttpGet httpget = new HttpGet("/");
154 
155         final CountDownLatch latch = new CountDownLatch(1);
156 
157         final FutureCallback<HttpResponse> callback = new FutureCallback<HttpResponse>() {
158 
159             @Override
160             public void cancelled() {
161                 latch.countDown();
162             }
163 
164             @Override
165             public void failed(final Exception ex) {
166                 latch.countDown();
167             }
168 
169             @Override
170             public void completed(final HttpResponse response) {
171                 Assert.fail();
172             }
173 
174         };
175 
176         this.httpclient.execute(target, httpget, callback);
177         Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
178     }
179 
180     @Test
181     public void testConnectionTerminatedSendingResponse() throws Exception {
182         this.serverBootstrap.registerHandler("*", new HttpAsyncRequestHandler<HttpRequest>() {
183 
184             @Override
185             public HttpAsyncRequestConsumer<HttpRequest> processRequest(
186                     final HttpRequest request,
187                     final HttpContext context) throws HttpException, IOException {
188                 return new BasicAsyncRequestConsumer();
189             }
190 
191             @Override
192             public void handle(
193                     final HttpRequest request,
194                     final HttpAsyncExchange httpExchange,
195                     final HttpContext context) throws HttpException, IOException {
196                 final HttpResponse response = httpExchange.getResponse();
197                 response.setEntity(new NStringEntity("all is well", ContentType.TEXT_PLAIN));
198                 httpExchange.submitResponse(new BasicAsyncResponseProducer(response) {
199 
200                     @Override
201                     public synchronized void produceContent(
202                             final ContentEncoder encoder,
203                             final IOControl ioControl) throws IOException {
204                         ioControl.shutdown();
205                     }
206 
207                 });
208             }
209 
210         });
211 
212         final HttpHost target = start();
213         final HttpGet httpget = new HttpGet("/");
214 
215         final CountDownLatch latch = new CountDownLatch(1);
216 
217         final FutureCallback<HttpResponse> callback = new FutureCallback<HttpResponse>() {
218 
219             @Override
220             public void cancelled() {
221                 latch.countDown();
222             }
223 
224             @Override
225             public void failed(final Exception ex) {
226                 latch.countDown();
227             }
228 
229             @Override
230             public void completed(final HttpResponse response) {
231                 Assert.fail();
232             }
233 
234         };
235 
236         this.httpclient.execute(target, httpget, callback);
237         Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
238     }
239 
240     @Test @Ignore(value = "Fails on some Windows platforms")
241     public void testConnectionRequestFailure() throws Exception {
242         this.httpclient = HttpAsyncClients.custom()
243                 .setConnectionManager(this.connMgr)
244                 .build();
245         this.httpclient.start();
246 
247         final HttpGet get = new HttpGet("http://0.0.0.0/");
248         final HttpAsyncRequestProducer producer = HttpAsyncMethods.create(get);
249 
250         final AtomicBoolean closed = new AtomicBoolean(false);
251         final AtomicBoolean cancelled = new AtomicBoolean(false);
252         final AtomicBoolean failed = new AtomicBoolean(false);
253 
254         final HttpAsyncResponseConsumer<?> consumer = new HttpAsyncResponseConsumer<Object>() {
255 
256             @Override
257             public void close() throws IOException {
258                 closed.set(true);
259             }
260 
261             @Override
262             public boolean cancel() {
263                 cancelled.set(true);
264                 return false;
265             }
266 
267             @Override
268             public void failed(final Exception ex) {
269                 failed.set(true);
270             }
271 
272             @Override
273             public void responseReceived(
274                     final HttpResponse response) throws IOException, HttpException {
275                 throw new IllegalStateException();
276             }
277 
278             @Override
279             public void consumeContent(
280                     final ContentDecoder decoder, final IOControl ioControl) throws IOException {
281                 throw new IllegalStateException();
282             }
283 
284             @Override
285             public void responseCompleted(final HttpContext context) {
286                 throw new IllegalStateException();
287             }
288 
289             @Override
290             public Exception getException() {
291                 return null;
292             }
293 
294             @Override
295             public String getResult() {
296                 return null;
297             }
298 
299             @Override
300             public boolean isDone() {
301                 return false;
302             }
303         };
304 
305         final Future<?> future = this.httpclient.execute(producer, consumer, null, null);
306         try {
307             future.get();
308             Assert.fail();
309         } catch (final ExecutionException e) {
310             final Throwable cause = e.getCause();
311             Assert.assertTrue("Unexpected cause: " + cause, cause instanceof IOException);
312         }
313         this.connMgr.shutdown(1000);
314 
315         Assert.assertTrue(closed.get());
316         Assert.assertFalse(cancelled.get());
317         Assert.assertTrue(failed.get());
318     }
319 
320     @Test
321     public void testConsumerIsDone() throws Exception {
322         this.serverBootstrap.registerHandler("/echo/*", new BasicAsyncRequestHandler(new EchoHandler()));
323         this.serverBootstrap.registerHandler("/random/*", new BasicAsyncRequestHandler(new RandomHandler()));
324 
325         final HttpHost target = start();
326 
327         final AtomicInteger producerClosed = new AtomicInteger(0);
328         final AtomicInteger consumerClosed = new AtomicInteger(0);
329 
330         final HttpAsyncRequestProducer producer = new BasicAsyncRequestProducer(target, new HttpGet("/")) {
331 
332             @Override
333             public synchronized void close() throws IOException {
334                 producerClosed.incrementAndGet();
335                 super.close();
336             }
337         };
338 
339         final HttpAsyncResponseConsumer<?> consumer = new HttpAsyncResponseConsumer<Object>() {
340 
341             @Override
342             public void close() throws IOException {
343                 consumerClosed.incrementAndGet();
344             }
345 
346             @Override
347             public boolean cancel() {
348                 return false;
349             }
350 
351             @Override
352             public void failed(final Exception ex) {
353             }
354 
355             @Override
356             public void responseReceived(
357                     final HttpResponse response) throws IOException, HttpException {
358             }
359 
360             @Override
361             public void consumeContent(
362                     final ContentDecoder decoder, final IOControl ioControl) throws IOException {
363             }
364 
365             @Override
366             public void responseCompleted(final HttpContext context) {
367             }
368 
369             @Override
370             public Exception getException() {
371                 return null;
372             }
373 
374             @Override
375             public String getResult() {
376                 return null;
377             }
378 
379             @Override
380             public boolean isDone() {
381                 return true; // cancels fetching the response-body
382             }
383         };
384 
385         final Future<?> future = this.httpclient.execute(producer, consumer, null, null);
386         try {
387             future.get();
388             Assert.fail("CancellationException expected");
389         } catch (final CancellationException expected) {
390         }
391 
392         connMgr.shutdown(1000);
393 
394         Assert.assertTrue(future.isCancelled());
395         Assert.assertTrue(future.isCancelled());
396 
397         Assert.assertEquals(1, producerClosed.get());
398         Assert.assertEquals(1, consumerClosed.get());
399     }
400 
401 }