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.nio.protocol;
29  
30  import java.util.concurrent.ExecutionException;
31  import java.util.concurrent.Future;
32  
33  import org.apache.http.ConnectionClosedException;
34  import org.apache.http.ConnectionReuseStrategy;
35  import org.apache.http.HttpHost;
36  import org.apache.http.concurrent.FutureCallback;
37  import org.apache.http.impl.nio.pool.BasicNIOPoolEntry;
38  import org.apache.http.nio.NHttpClientConnection;
39  import org.apache.http.nio.protocol.HttpAsyncRequester.ConnRequestCallback;
40  import org.apache.http.pool.ConnPool;
41  import org.apache.http.pool.PoolEntry;
42  import org.apache.http.protocol.BasicHttpContext;
43  import org.apache.http.protocol.HttpContext;
44  import org.apache.http.protocol.HttpProcessor;
45  import org.junit.After;
46  import org.junit.Assert;
47  import org.junit.Before;
48  import org.junit.Test;
49  import org.mockito.ArgumentCaptor;
50  import org.mockito.Matchers;
51  import org.mockito.Mockito;
52  
53  public class TestHttpAsyncRequester {
54  
55      private HttpProcessor httpProcessor;
56      private ConnectionReuseStrategy reuseStrategy;
57      private HttpAsyncRequester requester;
58      private HttpContext exchangeContext;
59      private HttpContext connContext;
60      private HttpAsyncRequestProducer requestProducer;
61      private HttpAsyncResponseConsumer<Object> responseConsumer;
62      private NHttpClientConnection conn;
63      private FutureCallback<Object> callback;
64      private ConnPool<HttpHost, PoolEntry<HttpHost, NHttpClientConnection>> connPool;
65  
66      @SuppressWarnings("unchecked")
67      @Before
68      public void setUp() throws Exception {
69          this.httpProcessor = Mockito.mock(HttpProcessor.class);
70          this.reuseStrategy = Mockito.mock(ConnectionReuseStrategy.class);
71          this.requester = new HttpAsyncRequester(this.httpProcessor, this.reuseStrategy);
72          this.exchangeContext = new BasicHttpContext();
73          this.requestProducer = Mockito.mock(HttpAsyncRequestProducer.class);
74          this.responseConsumer = Mockito.mock(HttpAsyncResponseConsumer.class);
75          this.conn = Mockito.mock(NHttpClientConnection.class);
76          this.callback = Mockito.mock(FutureCallback.class);
77          this.connContext = new BasicHttpContext();
78          this.connPool = Mockito.mock(ConnPool.class);
79  
80          Mockito.when(this.conn.getContext()).thenReturn(this.connContext);
81      }
82  
83      @After
84      public void tearDown() throws Exception {
85      }
86  
87      @Test
88      public void testInvalidExecution() throws Exception {
89          try {
90              this.requester.execute(
91                      null,
92                      this.responseConsumer,
93                      this.conn);
94              Assert.fail("IllegalArgumentException expected");
95          } catch (final IllegalArgumentException ex) {
96          }
97          try {
98              this.requester.execute(
99                      this.requestProducer,
100                     null,
101                     this.conn);
102             Assert.fail("IllegalArgumentException expected");
103         } catch (final IllegalArgumentException ex) {
104         }
105         try {
106             this.requester.execute(
107                     this.requestProducer,
108                     this.responseConsumer,
109                     (NHttpClientConnection) null);
110             Assert.fail("IllegalArgumentException expected");
111         } catch (final IllegalArgumentException ex) {
112         }
113         try {
114             this.requester.execute(
115                     this.requestProducer,
116                     this.responseConsumer,
117                     this.conn,
118                     null);
119             Assert.fail("IllegalArgumentException expected");
120         } catch (final IllegalArgumentException ex) {
121         }
122 
123         try {
124             this.requester.execute(
125                     null,
126                     this.responseConsumer,
127                     this.connPool);
128             Assert.fail("IllegalArgumentException expected");
129         } catch (final IllegalArgumentException ex) {
130         }
131         try {
132             this.requester.execute(
133                     this.requestProducer,
134                     null,
135                     this.connPool);
136             Assert.fail("IllegalArgumentException expected");
137         } catch (final IllegalArgumentException ex) {
138         }
139         try {
140             this.requester.execute(
141                     this.requestProducer,
142                     this.responseConsumer,
143                     (ConnPool<HttpHost, PoolEntry<HttpHost, NHttpClientConnection>>) null);
144             Assert.fail("IllegalArgumentException expected");
145         } catch (final IllegalArgumentException ex) {
146         }
147         try {
148             this.requester.execute(
149                     this.requestProducer,
150                     this.responseConsumer,
151                     this.connPool,
152                     null);
153             Assert.fail("IllegalArgumentException expected");
154         } catch (final IllegalArgumentException ex) {
155         }
156     }
157 
158     @Test
159     public void testSimpleExecute() throws Exception {
160         Mockito.when(this.conn.isOpen()).thenReturn(Boolean.TRUE);
161         final Future<Object> future = this.requester.execute(
162                 this.requestProducer,
163                 this.responseConsumer,
164                 this.conn, this.exchangeContext, null);
165         Assert.assertNotNull(future);
166         Assert.assertNotNull(this.connContext.getAttribute(HttpAsyncRequestExecutor.HTTP_HANDLER));
167         Mockito.verify(this.conn).requestOutput();
168     }
169 
170     @Test
171     public void testExecuteConnectionClosedUnexpectedly() throws Exception {
172         Mockito.when(this.conn.isOpen()).thenReturn(false);
173         final Future<Object> future = this.requester.execute(
174                 this.requestProducer,
175                 this.responseConsumer,
176                 this.conn, this.exchangeContext, null);
177         Assert.assertNotNull(future);
178         Mockito.verify(this.requestProducer).failed(Matchers.any(ConnectionClosedException.class));
179         Mockito.verify(this.responseConsumer).failed(Matchers.any(ConnectionClosedException.class));
180         Mockito.verify(this.requestProducer, Mockito.atLeastOnce()).close();
181         Mockito.verify(this.responseConsumer, Mockito.atLeastOnce()).close();
182         Assert.assertTrue(future.isDone());
183         Assert.assertNotNull(future.isDone());
184         try {
185             future.get();
186         } catch (final ExecutionException ex) {
187             final Throwable cause =  ex.getCause();
188             Assert.assertNotNull(cause);
189             Assert.assertTrue(cause instanceof ConnectionClosedException);
190         }
191 
192     }
193 
194     @SuppressWarnings({ "rawtypes", "unchecked" })
195     @Test
196     public void testPooledConnectionRequestFailed() throws Exception {
197         final HttpHost host = new HttpHost("somehost");
198         Mockito.when(this.requestProducer.getTarget()).thenReturn(host);
199 
200         final Future<Object> future = this.requester.execute(
201                 this.requestProducer,
202                 this.responseConsumer,
203                 this.connPool, this.exchangeContext, this.callback);
204         Assert.assertNotNull(future);
205         final ArgumentCaptor<FutureCallback> argCaptor = ArgumentCaptor.forClass(FutureCallback.class);
206         Mockito.verify(this.connPool).lease(
207                 Matchers.eq(host), Matchers.isNull(), argCaptor.capture());
208         final ConnRequestCallback connRequestCallback = (ConnRequestCallback) argCaptor.getValue();
209 
210         final Exception oppsie = new Exception();
211         connRequestCallback.failed(oppsie);
212         Mockito.verify(this.responseConsumer).failed(oppsie);
213         Mockito.verify(this.callback).failed(oppsie);
214         Mockito.verify(this.responseConsumer).close();
215         Mockito.verify(this.requestProducer).close();
216     }
217 
218     @SuppressWarnings({ "rawtypes", "unchecked" })
219     @Test
220     public void testPooledConnectionRequestCancelled() throws Exception {
221         final HttpHost host = new HttpHost("somehost");
222         Mockito.when(this.requestProducer.getTarget()).thenReturn(host);
223 
224         final Future<Object> future = this.requester.execute(
225                 this.requestProducer,
226                 this.responseConsumer,
227                 this.connPool, this.exchangeContext, this.callback);
228         Assert.assertNotNull(future);
229         final ArgumentCaptor<FutureCallback> argCaptor = ArgumentCaptor.forClass(FutureCallback.class);
230         Mockito.verify(this.connPool).lease(
231                 Matchers.eq(host), Matchers.isNull(), argCaptor.capture());
232         final ConnRequestCallback connRequestCallback = (ConnRequestCallback) argCaptor.getValue();
233 
234         connRequestCallback.cancelled();
235         Mockito.verify(this.responseConsumer).cancel();
236         Mockito.verify(this.callback).cancelled();
237         Mockito.verify(this.responseConsumer).close();
238         Mockito.verify(this.requestProducer).close();
239     }
240 
241     @SuppressWarnings({ "rawtypes", "unchecked" })
242     @Test
243     public void testPooledConnectionAutoReleaseOnRequestCancel() throws Exception {
244         final HttpHost host = new HttpHost("somehost");
245         Mockito.when(this.requestProducer.getTarget()).thenReturn(host);
246 
247         final Future<Object> future = this.requester.execute(
248                 this.requestProducer,
249                 this.responseConsumer,
250                 this.connPool, this.exchangeContext, this.callback);
251         Assert.assertNotNull(future);
252         final ArgumentCaptor<FutureCallback> argCaptor = ArgumentCaptor.forClass(FutureCallback.class);
253         Mockito.verify(this.connPool).lease(
254                 Matchers.eq(host), Matchers.isNull(), argCaptor.capture());
255         final ConnRequestCallback connRequestCallback = (ConnRequestCallback) argCaptor.getValue();
256 
257         future.cancel(true);
258         final BasicNIOPoolEntry entry = new BasicNIOPoolEntry("id", host, this.conn);
259         connRequestCallback.completed(entry);
260         Mockito.verify(this.connPool).release(entry, true);
261         Mockito.verify(this.conn, Mockito.never()).requestOutput();
262     }
263 
264 
265     @SuppressWarnings({ "rawtypes", "unchecked" })
266     @Test
267     public void testPooledRequestExecutionSucceeded() throws Exception {
268         final HttpHost host = new HttpHost("somehost");
269         Mockito.when(this.requestProducer.getTarget()).thenReturn(host);
270         Mockito.when(this.conn.isOpen()).thenReturn(true);
271 
272         final Future<Object> future = this.requester.execute(
273                 this.requestProducer,
274                 this.responseConsumer,
275                 this.connPool, this.exchangeContext, this.callback);
276         Assert.assertNotNull(future);
277         final ArgumentCaptor<FutureCallback> argCaptor = ArgumentCaptor.forClass(FutureCallback.class);
278         Mockito.verify(this.connPool).lease(
279                 Matchers.eq(host), Matchers.isNull(), argCaptor.capture());
280         final ConnRequestCallback connRequestCallback = (ConnRequestCallback) argCaptor.getValue();
281 
282         final BasicNIOPoolEntry entry = new BasicNIOPoolEntry("id", host, this.conn);
283         connRequestCallback.completed(entry);
284         final BasicAsyncClientExchangeHandler exchangeHandler = (BasicAsyncClientExchangeHandler) this.connContext.getAttribute(
285                 HttpAsyncRequestExecutor.HTTP_HANDLER);
286         Assert.assertNotNull(exchangeHandler);
287         Mockito.verify(this.conn).requestOutput();
288 
289         final Object result = new Object();
290         Mockito.when(this.responseConsumer.getResult()).thenReturn(result);
291         exchangeHandler.responseCompleted();
292         Mockito.verify(this.callback).completed(result);
293         Mockito.verify(this.responseConsumer).close();
294         Mockito.verify(this.requestProducer).close();
295         Mockito.verify(this.connPool).release(entry, true);
296     }
297 
298     @SuppressWarnings({ "rawtypes", "unchecked" })
299     @Test
300     public void testPooledRequestExecutionFailed() throws Exception {
301         final HttpHost host = new HttpHost("somehost");
302         Mockito.when(this.requestProducer.getTarget()).thenReturn(host);
303         Mockito.when(this.conn.isOpen()).thenReturn(true);
304 
305         final Future<Object> future = this.requester.execute(
306                 this.requestProducer,
307                 this.responseConsumer,
308                 this.connPool, this.exchangeContext, this.callback);
309         Assert.assertNotNull(future);
310         final ArgumentCaptor<FutureCallback> argCaptor = ArgumentCaptor.forClass(FutureCallback.class);
311         Mockito.verify(this.connPool).lease(
312                 Matchers.eq(host), Matchers.isNull(), argCaptor.capture());
313         final ConnRequestCallback connRequestCallback = (ConnRequestCallback) argCaptor.getValue();
314 
315         final BasicNIOPoolEntry entry = new BasicNIOPoolEntry("id", host, this.conn);
316         connRequestCallback.completed(entry);
317         final BasicAsyncClientExchangeHandler exchangeHandler = (BasicAsyncClientExchangeHandler) this.connContext.getAttribute(
318                 HttpAsyncRequestExecutor.HTTP_HANDLER);
319         Assert.assertNotNull(exchangeHandler);
320         Mockito.verify(this.conn).requestOutput();
321 
322         final Exception oppsie = new Exception();
323         exchangeHandler.failed(oppsie);
324         Mockito.verify(this.responseConsumer).failed(oppsie);
325         Mockito.verify(this.callback).failed(oppsie);
326         Mockito.verify(this.responseConsumer).close();
327         Mockito.verify(this.requestProducer).close();
328         Mockito.verify(this.connPool).release(entry, false);
329     }
330 
331     @SuppressWarnings({ "rawtypes", "unchecked" })
332     @Test
333     public void testPooledRequestExecutionCancelled() throws Exception {
334         final HttpHost host = new HttpHost("somehost");
335         Mockito.when(this.requestProducer.getTarget()).thenReturn(host);
336         Mockito.when(this.conn.isOpen()).thenReturn(true);
337 
338         final Future<Object> future = this.requester.execute(
339                 this.requestProducer,
340                 this.responseConsumer,
341                 this.connPool, this.exchangeContext, this.callback);
342         Assert.assertNotNull(future);
343         final ArgumentCaptor<FutureCallback> argCaptor = ArgumentCaptor.forClass(FutureCallback.class);
344         Mockito.verify(this.connPool).lease(
345                 Matchers.eq(host), Matchers.isNull(), argCaptor.capture());
346         final ConnRequestCallback connRequestCallback = (ConnRequestCallback) argCaptor.getValue();
347 
348         final BasicNIOPoolEntry entry = new BasicNIOPoolEntry("id", host, this.conn);
349         connRequestCallback.completed(entry);
350         final BasicAsyncClientExchangeHandler exchangeHandler = (BasicAsyncClientExchangeHandler) this.connContext.getAttribute(
351                 HttpAsyncRequestExecutor.HTTP_HANDLER);
352         Assert.assertNotNull(exchangeHandler);
353         Mockito.verify(this.conn).requestOutput();
354 
355         exchangeHandler.cancel();
356         Mockito.verify(this.responseConsumer).cancel();
357         Mockito.verify(this.callback).cancelled();
358         Mockito.verify(this.responseConsumer).close();
359         Mockito.verify(this.requestProducer).close();
360         Mockito.verify(this.connPool).release(entry, false);
361     }
362 
363 }