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