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.io.IOException;
31  import java.net.SocketTimeoutException;
32  import java.util.Queue;
33  
34  import org.apache.http.ConnectionReuseStrategy;
35  import org.apache.http.HttpEntityEnclosingRequest;
36  import org.apache.http.HttpException;
37  import org.apache.http.HttpRequest;
38  import org.apache.http.HttpResponse;
39  import org.apache.http.HttpResponseFactory;
40  import org.apache.http.HttpStatus;
41  import org.apache.http.HttpVersion;
42  import org.apache.http.concurrent.Cancellable;
43  import org.apache.http.impl.DefaultHttpResponseFactory;
44  import org.apache.http.message.BasicHttpEntityEnclosingRequest;
45  import org.apache.http.message.BasicHttpRequest;
46  import org.apache.http.message.BasicHttpResponse;
47  import org.apache.http.nio.ContentDecoder;
48  import org.apache.http.nio.ContentEncoder;
49  import org.apache.http.nio.NHttpConnection;
50  import org.apache.http.nio.NHttpServerConnection;
51  import org.apache.http.nio.entity.NStringEntity;
52  import org.apache.http.nio.protocol.HttpAsyncService.Incoming;
53  import org.apache.http.nio.protocol.HttpAsyncService.Outgoing;
54  import org.apache.http.nio.protocol.HttpAsyncService.PipelineEntry;
55  import org.apache.http.nio.protocol.HttpAsyncService.State;
56  import org.apache.http.nio.reactor.SessionBufferStatus;
57  import org.apache.http.protocol.BasicHttpContext;
58  import org.apache.http.protocol.HTTP;
59  import org.apache.http.protocol.HttpContext;
60  import org.apache.http.protocol.HttpCoreContext;
61  import org.apache.http.protocol.HttpProcessor;
62  import org.junit.After;
63  import org.junit.Assert;
64  import org.junit.Before;
65  import org.junit.Test;
66  import org.mockito.ArgumentCaptor;
67  import org.mockito.ArgumentMatcher;
68  import org.mockito.Matchers;
69  import org.mockito.Mockito;
70  
71  public class TestHttpAsyncService {
72  
73      private UriHttpAsyncRequestHandlerMapper handlerResolver;
74      private HttpAsyncService protocolHandler;
75      private HttpProcessor httpProcessor;
76      private ConnectionReuseStrategy reuseStrategy;
77      private HttpResponseFactory responseFactory;
78      private HttpContext connContext;
79      private NHttpServerConnection conn;
80      private HttpAsyncRequestHandler<Object> requestHandler;
81      private HttpAsyncRequestConsumer<Object> requestConsumer;
82      private HttpAsyncResponseProducer responseProducer;
83      private ContentEncoder encoder;
84      private ContentDecoder decoder;
85      private Cancellable cancellable;
86  
87      @Before
88      public void setUp() throws Exception {
89          this.requestHandler = Mockito.mock(HttpAsyncRequestHandler.class);
90          this.requestConsumer = Mockito.mock(HttpAsyncRequestConsumer.class);
91          this.responseProducer = Mockito.mock(HttpAsyncResponseProducer.class);
92          this.handlerResolver = new UriHttpAsyncRequestHandlerMapper();
93          this.handlerResolver.register("/", this.requestHandler);
94          this.httpProcessor = Mockito.mock(HttpProcessor.class);
95          this.reuseStrategy = Mockito.mock(ConnectionReuseStrategy.class);
96          this.responseFactory = DefaultHttpResponseFactory.INSTANCE;
97          this.protocolHandler = new HttpAsyncService(
98                  this.httpProcessor, this.reuseStrategy, this.responseFactory, this.handlerResolver, null);
99          this.connContext = new BasicHttpContext();
100         this.conn = Mockito.mock(NHttpServerConnection.class);
101         this.encoder = Mockito.mock(ContentEncoder.class);
102         this.decoder = Mockito.mock(ContentDecoder.class);
103         this.cancellable = Mockito.mock(Cancellable.class);
104 
105         Mockito.when(this.conn.getContext()).thenReturn(this.connContext);
106     }
107 
108     @After
109     public void tearDown() throws Exception {
110     }
111 
112     @Test(expected=IllegalArgumentException.class)
113     public void testInvalidConstruction() throws Exception {
114         new HttpAsyncService(null, this.reuseStrategy, this.responseFactory, this.handlerResolver, null);
115     }
116 
117     @Test
118     public void testConnected() throws Exception {
119         this.protocolHandler.connected(this.conn);
120 
121         final State state = (State) this.connContext.getAttribute(
122                 HttpAsyncService.HTTP_EXCHANGE_STATE);
123         Assert.assertNotNull(state);
124         Assert.assertEquals(MessageState.READY, state.getRequestState());
125         Assert.assertEquals(MessageState.READY, state.getResponseState());
126         Assert.assertEquals("[incoming READY; outgoing READY]", state.toString());
127     }
128 
129     @Test
130     public void testClosed() throws Exception {
131         final State state = new State();
132         state.setRequestState(MessageState.COMPLETED);
133         state.setResponseState(MessageState.COMPLETED);
134         final HttpContext exchangeContext = new BasicHttpContext();
135 
136         final HttpRequest request = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
137         final Incoming incoming = new Incoming(
138                 request, this.requestHandler, this.requestConsumer, exchangeContext);
139         state.setIncoming(incoming);
140         final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
141         final Outgoing outgoing = new Outgoing(
142                 request, response, this.responseProducer, exchangeContext);
143         state.setOutgoing(outgoing);
144         state.setCancellable(this.cancellable);
145         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
146 
147         this.protocolHandler.closed(this.conn);
148 
149         Mockito.verify(this.requestConsumer).close();
150         Mockito.verify(this.responseProducer).close();
151         Mockito.verify(this.cancellable).cancel();
152     }
153 
154     @Test
155     public void testHttpExceptionHandling() throws Exception {
156         final State state = new State();
157         state.setRequestState(MessageState.READY);
158         state.setResponseState(MessageState.READY);
159         final HttpContext exchangeContext = new BasicHttpContext();
160         final HttpRequest request = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
161         final Incoming incoming = new Incoming(
162                 request, this.requestHandler, this.requestConsumer, exchangeContext);
163         state.setIncoming(incoming);
164         state.setCancellable(this.cancellable);
165         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
166         Mockito.when(this.conn.getHttpRequest()).thenReturn(request);
167 
168         final HttpException httpex = new HttpException();
169         this.protocolHandler.exception(this.conn, httpex);
170 
171         Assert.assertEquals(MessageState.READY, state.getRequestState());
172         Assert.assertEquals(MessageState.BODY_STREAM, state.getResponseState());
173         final Outgoing outgoing = state.getOutgoing();
174         Assert.assertNotNull(outgoing);
175         Assert.assertNotNull(outgoing.getProducer());
176         Assert.assertNotNull(outgoing.getResponse());
177         Assert.assertEquals(500, outgoing.getResponse().getStatusLine().getStatusCode());
178 
179         Mockito.verify(this.requestConsumer).failed(httpex);
180         Mockito.verify(this.requestConsumer).close();
181         Mockito.verify(this.cancellable).cancel();
182         Mockito.verify(this.conn, Mockito.never()).shutdown();
183         Mockito.verify(this.conn, Mockito.never()).close();
184     }
185 
186     @Test
187     public void testExceptionHandlingNoState() throws Exception {
188         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, null);
189 
190         final Exception ex = new Exception("Oopsie");
191         this.protocolHandler.exception(conn, ex);
192 
193         Mockito.verify(conn).getContext();
194         Mockito.verify(conn).shutdown();
195         Mockito.verifyNoMoreInteractions(conn);
196     }
197 
198     @Test
199     public void testExceptionHandlingRuntimeException() throws Exception {
200         final State state = new State();
201         state.setRequestState(MessageState.READY);
202         state.setResponseState(MessageState.READY);
203         final HttpContext exchangeContext = new BasicHttpContext();
204         final HttpRequest request = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
205         final Incoming incoming = new Incoming(
206                 request, this.requestHandler, this.requestConsumer, exchangeContext);
207         state.setIncoming(incoming);
208         final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
209         final Outgoing outgoing = new Outgoing(
210                 request, response, this.responseProducer, exchangeContext);
211         state.setOutgoing(outgoing);
212         state.setCancellable(this.cancellable);
213         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
214         Mockito.when(this.conn.getHttpRequest()).thenReturn(request);
215 
216         Mockito.doThrow(new RuntimeException()).when(this.httpProcessor).process(
217                 Matchers.any(HttpResponse.class), Matchers.any(HttpContext.class));
218         final HttpException httpex = new HttpException();
219         try {
220             this.protocolHandler.exception(this.conn, httpex);
221             Assert.fail("RuntimeException expected");
222         } catch (final RuntimeException ex) {
223             Mockito.verify(this.conn).shutdown();
224             Mockito.verify(this.requestConsumer).failed(httpex);
225             Mockito.verify(this.requestConsumer, Mockito.atLeastOnce()).close();
226             Mockito.verify(this.responseProducer).failed(httpex);
227             Mockito.verify(this.responseProducer, Mockito.atLeastOnce()).close();
228             Mockito.verify(this.cancellable).cancel();
229         }
230     }
231 
232     @Test
233     public void testHttpExceptionHandlingIOException() throws Exception {
234         final State state = new State();
235         state.setRequestState(MessageState.READY);
236         state.setResponseState(MessageState.READY);
237         final HttpContext exchangeContext = new BasicHttpContext();
238         final HttpRequest request = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
239         final Incoming incoming = new Incoming(
240                 request, this.requestHandler, this.requestConsumer, exchangeContext);
241         state.setIncoming(incoming);
242         final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
243         final Outgoing outgoing = new Outgoing(
244                 request, response, this.responseProducer, exchangeContext);
245         state.setOutgoing(outgoing);
246         state.setCancellable(this.cancellable);
247         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
248         Mockito.when(this.conn.getHttpRequest()).thenReturn(request);
249 
250         Mockito.doThrow(new IOException()).when(this.httpProcessor).process(
251                 Matchers.any(HttpResponse.class), Matchers.any(HttpContext.class));
252         final HttpException httpex = new HttpException();
253 
254         this.protocolHandler.exception(this.conn, httpex);
255 
256         Mockito.verify(this.conn).shutdown();
257         Mockito.verify(this.requestConsumer).failed(httpex);
258         Mockito.verify(this.requestConsumer, Mockito.atLeastOnce()).close();
259         Mockito.verify(this.responseProducer).failed(httpex);
260         Mockito.verify(this.responseProducer, Mockito.atLeastOnce()).close();
261         Mockito.verify(this.cancellable).cancel();
262     }
263 
264     @Test
265     public void testHttpExceptionHandlingResponseSubmitted() throws Exception {
266         final State state = new State();
267         state.setRequestState(MessageState.READY);
268         state.setResponseState(MessageState.READY);
269         final HttpContext exchangeContext = new BasicHttpContext();
270         final HttpRequest request = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
271         final Incoming incoming = new Incoming(
272                 request, this.requestHandler, this.requestConsumer, exchangeContext);
273         state.setIncoming(incoming);
274         final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
275         final Outgoing outgoing = new Outgoing(
276                 request, response, this.responseProducer, exchangeContext);
277         state.setOutgoing(outgoing);
278         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
279         Mockito.when(this.conn.isResponseSubmitted()).thenReturn(Boolean.TRUE);
280 
281         final HttpException httpex = new HttpException();
282         this.protocolHandler.exception(this.conn, httpex);
283 
284         Assert.assertEquals(MessageState.READY, state.getRequestState());
285         Assert.assertEquals(MessageState.READY, state.getResponseState());
286         Mockito.verify(this.conn).close();
287         Mockito.verify(this.requestConsumer).failed(httpex);
288         Mockito.verify(this.requestConsumer).close();
289         Mockito.verify(this.responseProducer).failed(httpex);
290         Mockito.verify(this.responseProducer).close();
291     }
292 
293     @Test
294     public void testBasicRequest() throws Exception {
295         final State state = new State();
296         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
297 
298         final HttpRequest request = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
299         Mockito.when(this.conn.getHttpRequest()).thenReturn(request);
300         Mockito.when(this.requestHandler.processRequest(
301                 Matchers.eq(request), Matchers.any(HttpContext.class))).thenReturn(this.requestConsumer);
302         Mockito.when(this.requestConsumer.getException()).thenReturn(null);
303         final Object data = new Object();
304         Mockito.when(this.requestConsumer.getResult()).thenReturn(data);
305 
306         this.protocolHandler.requestReceived(this.conn);
307 
308         Assert.assertEquals(MessageState.READY, state.getRequestState());
309         Assert.assertEquals(MessageState.READY, state.getResponseState());
310 
311         final Incoming incoming = state.getIncoming();
312         Assert.assertNull(incoming);
313 
314         final ArgumentCaptor<HttpContext> argumentCaptor = ArgumentCaptor.forClass(HttpContext.class);
315         Mockito.verify(this.httpProcessor).process(Matchers.eq(request), argumentCaptor.capture());
316         final HttpContext exchangeContext = argumentCaptor.getValue();
317         Assert.assertNotNull(exchangeContext);
318 
319         Assert.assertSame(request, exchangeContext.getAttribute(HttpCoreContext.HTTP_REQUEST));
320         Assert.assertSame(this.conn, exchangeContext.getAttribute(HttpCoreContext.HTTP_CONNECTION));
321 
322         Mockito.verify(this.requestConsumer).requestReceived(request);
323         Mockito.verify(this.requestConsumer).requestCompleted(exchangeContext);
324         Mockito.verify(this.conn).requestOutput();
325 
326         final PipelineEntry entry = state.getPipeline().poll();
327         Assert.assertNotNull(entry);
328         Assert.assertSame(request, entry.getRequest());
329         Assert.assertSame(requestHandler, entry.getHandler());
330         Assert.assertNotNull(entry.getResult());
331         Assert.assertNull(entry.getException());
332     }
333 
334     @Test
335     public void testRequestPipelineIfResponseInitiated() throws Exception {
336         final State state = new State();
337         state.setRequestState(MessageState.READY);
338         state.setResponseState(MessageState.INIT);
339         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
340 
341         final HttpRequest request = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
342         Mockito.when(this.conn.getHttpRequest()).thenReturn(request);
343         Mockito.when(this.requestHandler.processRequest(
344                 Matchers.eq(request), Matchers.any(HttpContext.class))).thenReturn(this.requestConsumer);
345         Mockito.when(this.requestConsumer.getException()).thenReturn(null);
346         final Object data = new Object();
347         Mockito.when(this.requestConsumer.getResult()).thenReturn(data);
348 
349         this.protocolHandler.requestReceived(this.conn);
350 
351         Assert.assertEquals(MessageState.READY, state.getRequestState());
352         Assert.assertEquals(MessageState.INIT, state.getResponseState());
353 
354         final Incoming incoming = state.getIncoming();
355         Assert.assertNull(incoming);
356 
357         Mockito.verify(this.requestConsumer).requestReceived(request);
358         Mockito.verify(this.requestConsumer).requestCompleted(Matchers.<HttpContext>any());
359         Mockito.verify(this.requestHandler, Mockito.never()).handle(
360                 Matchers.any(),
361                 Matchers.any(HttpAsyncExchange.class),
362                 Matchers.any(HttpContext.class));
363 
364         Assert.assertFalse(state.getPipeline().isEmpty());
365         final PipelineEntry entry = state.getPipeline().remove();
366         Assert.assertSame(request, entry.getRequest());
367         Assert.assertSame(data, entry.getResult());
368     }
369 
370     @Test
371     public void testRequestPipelineIfPipelineNotEmpty() throws Exception {
372         final State state = new State();
373         state.setRequestState(MessageState.READY);
374         state.setResponseState(MessageState.READY);
375 
376         final Queue<PipelineEntry> pipeline = state.getPipeline();
377 
378         final HttpContext exchangeContext = new BasicHttpContext();
379         final HttpRequest pipelinedRequest = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
380         final PipelineEntry entry = new PipelineEntry(pipelinedRequest, pipelinedRequest,
381                 null, requestHandler, exchangeContext);
382         pipeline.add(entry);
383 
384         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
385 
386         final HttpRequest request = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
387         Mockito.when(this.conn.getHttpRequest()).thenReturn(request);
388         Mockito.when(this.requestHandler.processRequest(
389                 Matchers.eq(request), Matchers.any(HttpContext.class))).thenReturn(this.requestConsumer);
390         Mockito.when(this.requestConsumer.getException()).thenReturn(null);
391         final Object data = new Object();
392         Mockito.when(this.requestConsumer.getResult()).thenReturn(data);
393 
394         this.protocolHandler.requestReceived(this.conn);
395 
396         Assert.assertEquals(MessageState.READY, state.getRequestState());
397         Assert.assertEquals(MessageState.READY, state.getResponseState());
398 
399         final Incoming incoming = state.getIncoming();
400         Assert.assertNull(incoming);
401 
402         Mockito.verify(this.requestConsumer).requestReceived(request);
403         Mockito.verify(this.requestConsumer).requestCompleted(Matchers.<HttpContext>any());
404         Mockito.verify(this.requestHandler, Mockito.never()).handle(
405                 Matchers.any(),
406                 Matchers.any(HttpAsyncExchange.class),
407                 Matchers.any(HttpContext.class));
408 
409         Assert.assertFalse(state.getPipeline().isEmpty());
410         final PipelineEntry entry1 = state.getPipeline().remove();
411         Assert.assertSame(entry, entry1);
412         final PipelineEntry entry2 = state.getPipeline().remove();
413         Assert.assertSame(request, entry2.getRequest());
414         Assert.assertSame(data, entry2.getResult());
415     }
416 
417     @Test
418     public void testRequestNoMatchingHandler() throws Exception {
419         final State state = new State();
420         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
421 
422         final HttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest("POST",
423                 "/stuff", HttpVersion.HTTP_1_1);
424         request.setEntity(new NStringEntity("stuff"));
425         Mockito.when(this.conn.getHttpRequest()).thenReturn(request);
426         Mockito.when(this.requestHandler.processRequest(
427                 Matchers.eq(request), Matchers.any(HttpContext.class))).thenReturn(this.requestConsumer);
428 
429         this.protocolHandler.requestReceived(this.conn);
430 
431         Assert.assertEquals(MessageState.BODY_STREAM, state.getRequestState());
432         Assert.assertEquals(MessageState.READY, state.getResponseState());
433 
434         final Incoming incoming = state.getIncoming();
435         Assert.assertNotNull(incoming);
436         Assert.assertSame(request, incoming.getRequest());
437         Assert.assertTrue(incoming.getHandler() instanceof NullRequestHandler);
438     }
439 
440     @Test
441     public void testEntityEnclosingRequest() throws Exception {
442         final State state = new State();
443         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
444 
445         final HttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest("POST", "/",
446                 HttpVersion.HTTP_1_1);
447         Mockito.when(this.conn.getHttpRequest()).thenReturn(request);
448         Mockito.when(this.requestHandler.processRequest(
449                 Matchers.eq(request), Matchers.any(HttpContext.class))).thenReturn(this.requestConsumer);
450 
451         this.protocolHandler.requestReceived(this.conn);
452 
453         Assert.assertEquals(MessageState.BODY_STREAM, state.getRequestState());
454         Assert.assertEquals(MessageState.READY, state.getResponseState());
455 
456         final Incoming incoming = state.getIncoming();
457         Assert.assertNotNull(incoming);
458         Assert.assertSame(request, incoming.getRequest());
459         Assert.assertSame(this.requestHandler, incoming.getHandler());
460         Assert.assertSame(this.requestConsumer, incoming.getConsumer());
461 
462         final HttpContext exchangeContext = incoming.getContext();
463         Assert.assertNotNull(exchangeContext);
464 
465         Assert.assertSame(request, exchangeContext.getAttribute(HttpCoreContext.HTTP_REQUEST));
466         Assert.assertSame(this.conn, exchangeContext.getAttribute(HttpCoreContext.HTTP_CONNECTION));
467 
468         Mockito.verify(this.httpProcessor).process(request, exchangeContext);
469         Mockito.verify(this.requestConsumer).requestReceived(request);
470         Mockito.verify(this.conn, Mockito.never()).suspendInput();
471     }
472 
473     @Test
474     public void testEntityEnclosingRequestContinueWithoutVerification() throws Exception {
475         final State state = new State();
476         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
477 
478         final HttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest("POST", "/",
479                 HttpVersion.HTTP_1_1);
480         request.addHeader(HTTP.EXPECT_DIRECTIVE, HTTP.EXPECT_CONTINUE);
481         Mockito.when(this.conn.getHttpRequest()).thenReturn(request);
482         Mockito.when(this.requestHandler.processRequest(
483                 Matchers.eq(request), Matchers.any(HttpContext.class))).thenReturn(this.requestConsumer);
484 
485         this.protocolHandler.requestReceived(this.conn);
486 
487         Assert.assertEquals(MessageState.BODY_STREAM, state.getRequestState());
488         Assert.assertEquals(MessageState.READY, state.getResponseState());
489 
490         final Incoming incoming = state.getIncoming();
491         Assert.assertNotNull(incoming);
492         Assert.assertSame(request, incoming.getRequest());
493         Assert.assertSame(this.requestHandler, incoming.getHandler());
494         Assert.assertSame(this.requestConsumer, incoming.getConsumer());
495 
496         final HttpContext exchangeContext = incoming.getContext();
497         Assert.assertNotNull(exchangeContext);
498 
499         Assert.assertSame(request, exchangeContext.getAttribute(HttpCoreContext.HTTP_REQUEST));
500         Assert.assertSame(this.conn, exchangeContext.getAttribute(HttpCoreContext.HTTP_CONNECTION));
501 
502         Mockito.verify(this.httpProcessor).process(request, exchangeContext);
503         Mockito.verify(this.requestConsumer).requestReceived(request);
504         Mockito.verify(this.conn, Mockito.never()).suspendInput();
505         Mockito.verify(this.conn).submitResponse(Matchers.argThat(new ArgumentMatcher<HttpResponse>() {
506 
507             @Override
508             public boolean matches(final Object argument) {
509                 final int status = ((HttpResponse) argument).getStatusLine().getStatusCode();
510                 return status == 100;
511             }
512 
513         }));
514     }
515 
516     @Test
517     public void testEntityEnclosingRequestExpectationVerification() throws Exception {
518         final HttpAsyncExpectationVerifier expectationVerifier = Mockito.mock(HttpAsyncExpectationVerifier.class);
519         this.protocolHandler = new HttpAsyncService(
520                 this.httpProcessor, this.reuseStrategy, this.responseFactory,
521                 this.handlerResolver, expectationVerifier);
522 
523         final State state = new State();
524         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
525 
526         final HttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest("POST", "/",
527                 HttpVersion.HTTP_1_1);
528         request.addHeader(HTTP.EXPECT_DIRECTIVE, HTTP.EXPECT_CONTINUE);
529         Mockito.when(this.conn.getHttpRequest()).thenReturn(request);
530         Mockito.when(this.requestHandler.processRequest(
531                 Matchers.eq(request), Matchers.any(HttpContext.class))).thenReturn(this.requestConsumer);
532 
533         this.protocolHandler.requestReceived(this.conn);
534 
535         Assert.assertEquals(MessageState.ACK_EXPECTED, state.getRequestState());
536         Assert.assertEquals(MessageState.READY, state.getResponseState());
537 
538         final Incoming incoming = state.getIncoming();
539         Assert.assertNotNull(incoming);
540         Assert.assertSame(request, incoming.getRequest());
541         Assert.assertSame(this.requestHandler, incoming.getHandler());
542         Assert.assertSame(this.requestConsumer, incoming.getConsumer());
543 
544         final HttpContext exchangeContext = incoming.getContext();
545         Assert.assertNotNull(exchangeContext);
546 
547         Assert.assertSame(request, exchangeContext.getAttribute(HttpCoreContext.HTTP_REQUEST));
548         Assert.assertSame(this.conn, exchangeContext.getAttribute(HttpCoreContext.HTTP_CONNECTION));
549 
550         Mockito.verify(this.httpProcessor).process(request, exchangeContext);
551         Mockito.verify(this.requestConsumer).requestReceived(request);
552         Mockito.verify(this.conn).suspendInput();
553         Mockito.verify(expectationVerifier).verify(
554                 Matchers.any(HttpAsyncExchange.class),
555                 Matchers.eq(exchangeContext));
556     }
557 
558     @Test
559     public void testRequestExpectationFailed() throws Exception {
560         final State state = new State();
561         state.setRequestState(MessageState.ACK_EXPECTED);
562         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
563 
564         final HttpContext exchangeContext = new BasicHttpContext();
565         final HttpAsyncExchange httpexchanage = protocolHandler.new HttpAsyncExchangeImpl(
566                 new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1),
567                 new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK"),
568                 state, this.conn, exchangeContext);
569         Assert.assertFalse(httpexchanage.isCompleted());
570         httpexchanage.submitResponse(this.responseProducer);
571         Assert.assertTrue(httpexchanage.isCompleted());
572 
573         Assert.assertEquals(MessageState.ACK_EXPECTED, state.getRequestState());
574         Assert.assertEquals(MessageState.READY, state.getResponseState());
575         final Outgoing outgoing = state.getOutgoing();
576         Assert.assertNotNull(outgoing);
577         Assert.assertSame(this.responseProducer, outgoing.getProducer());
578 
579         Mockito.verify(this.conn).requestOutput();
580 
581         try {
582             httpexchanage.submitResponse();
583             Assert.fail("IllegalStateException expected");
584         } catch (final IllegalStateException ex) {
585         }
586     }
587 
588     @Test(expected=IllegalArgumentException.class)
589     public void testRequestExpectationFailedInvalidResponseProducer() throws Exception {
590         final State state = new State();
591         state.setRequestState(MessageState.ACK_EXPECTED);
592         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
593 
594         final HttpContext exchangeContext = new BasicHttpContext();
595         final HttpAsyncExchange httpexchanage = protocolHandler.new HttpAsyncExchangeImpl(
596                 new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1),
597                 new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK"),
598                 state, this.conn, exchangeContext);
599         httpexchanage.submitResponse(null);
600     }
601 
602     @Test
603     public void testRequestExpectationNoHandshakeIfResponseInitiated() throws Exception {
604         final State state = new State();
605         state.setRequestState(MessageState.READY);
606         state.setResponseState(MessageState.INIT);
607         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
608 
609         final HttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest("POST", "/",
610                 HttpVersion.HTTP_1_1);
611         request.addHeader(HTTP.EXPECT_DIRECTIVE, HTTP.EXPECT_CONTINUE);
612 
613         Mockito.when(this.conn.getHttpRequest()).thenReturn(request);
614         Mockito.when(this.requestHandler.processRequest(
615                 Matchers.eq(request), Matchers.any(HttpContext.class))).thenReturn(this.requestConsumer);
616 
617         this.protocolHandler.requestReceived(this.conn);
618 
619         Mockito.verify(this.requestConsumer).requestReceived(request);
620 
621         Assert.assertEquals(MessageState.BODY_STREAM, state.getRequestState());
622         Assert.assertEquals(MessageState.INIT, state.getResponseState());
623     }
624 
625     @Test
626     public void testRequestExpectationNoHandshakeIfPipelineNotEmpty() throws Exception {
627         final State state = new State();
628         state.setRequestState(MessageState.READY);
629         state.setResponseState(MessageState.READY);
630 
631         final Queue<PipelineEntry> pipeline = state.getPipeline();
632 
633         final HttpContext exchangeContext = new BasicHttpContext();
634         final HttpRequest pipelinedRequest = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
635         final PipelineEntry entry = new PipelineEntry(pipelinedRequest, pipelinedRequest,
636                 null, requestHandler, exchangeContext);
637         pipeline.add(entry);
638 
639         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
640 
641         final HttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest("POST", "/",
642                 HttpVersion.HTTP_1_1);
643         request.addHeader(HTTP.EXPECT_DIRECTIVE, HTTP.EXPECT_CONTINUE);
644 
645         Mockito.when(this.conn.getHttpRequest()).thenReturn(request);
646         Mockito.when(this.requestHandler.processRequest(
647                 Matchers.eq(request), Matchers.any(HttpContext.class))).thenReturn(this.requestConsumer);
648 
649         this.protocolHandler.requestReceived(this.conn);
650 
651         Mockito.verify(this.requestConsumer).requestReceived(request);
652 
653         Assert.assertEquals(MessageState.BODY_STREAM, state.getRequestState());
654         Assert.assertEquals(MessageState.READY, state.getResponseState());
655     }
656 
657     @Test
658     public void testRequestExpectationNoHandshakeIfMoreInputAvailable() throws Exception {
659         final State state = new State();
660         state.setRequestState(MessageState.READY);
661         state.setResponseState(MessageState.READY);
662 
663         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
664 
665         this.conn = Mockito.mock(NHttpServerConnection.class,
666                 Mockito.withSettings().extraInterfaces(SessionBufferStatus.class));
667 
668         final HttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest("POST", "/",
669                 HttpVersion.HTTP_1_1);
670         request.addHeader(HTTP.EXPECT_DIRECTIVE, HTTP.EXPECT_CONTINUE);
671 
672         Mockito.when(this.conn.getContext()).thenReturn(this.connContext);
673         Mockito.when(this.conn.getHttpRequest()).thenReturn(request);
674         Mockito.when(this.requestHandler.processRequest(
675                 Matchers.eq(request), Matchers.any(HttpContext.class))).thenReturn(this.requestConsumer);
676         Mockito.when(((SessionBufferStatus) this.conn).hasBufferedInput()).thenReturn(Boolean.TRUE);
677 
678         this.protocolHandler.requestReceived(this.conn);
679 
680         Mockito.verify(this.requestConsumer).requestReceived(request);
681 
682         Assert.assertEquals(MessageState.BODY_STREAM, state.getRequestState());
683         Assert.assertEquals(MessageState.READY, state.getResponseState());
684     }
685 
686     @Test
687     public void testRequestContinue() throws Exception {
688         final State state = new State();
689         state.setRequestState(MessageState.ACK_EXPECTED);
690         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
691 
692         final HttpContext exchangeContext = new BasicHttpContext();
693         final HttpAsyncExchange httpexchanage = protocolHandler.new HttpAsyncExchangeImpl(
694                 new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1),
695                 new BasicHttpResponse(HttpVersion.HTTP_1_1, 100, "Continue"),
696                 state, this.conn, exchangeContext);
697         Assert.assertFalse(httpexchanage.isCompleted());
698         httpexchanage.submitResponse();
699         Assert.assertTrue(httpexchanage.isCompleted());
700 
701         final Outgoing outgoing = state.getOutgoing();
702         Assert.assertNotNull(outgoing);
703         final HttpAsyncResponseProducer responseProducer = outgoing.getProducer();
704         Assert.assertNotNull(responseProducer);
705         Assert.assertEquals(MessageState.ACK_EXPECTED, state.getRequestState());
706         Assert.assertEquals(MessageState.READY, state.getResponseState());
707         final HttpResponse response = responseProducer.generateResponse();
708         Assert.assertEquals(HttpStatus.SC_CONTINUE, response.getStatusLine().getStatusCode());
709 
710         Mockito.verify(this.conn).requestOutput();
711 
712         try {
713             httpexchanage.submitResponse(this.responseProducer);
714             Assert.fail("IllegalStateException expected");
715         } catch (final IllegalStateException ex) {
716         }
717     }
718 
719     @Test
720     public void testRequestContent() throws Exception {
721         final State state = new State();
722         final HttpContext exchangeContext = new BasicHttpContext();
723         final HttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest("POST", "/",
724                 HttpVersion.HTTP_1_1);
725         state.setRequestState(MessageState.BODY_STREAM);
726         final Incoming incoming = new Incoming(
727                 request, this.requestHandler, this.requestConsumer, exchangeContext);
728         state.setIncoming(incoming);
729         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
730         Mockito.when(this.decoder.isCompleted()).thenReturn(Boolean.FALSE);
731 
732         this.protocolHandler.inputReady(conn, this.decoder);
733 
734         Assert.assertEquals(MessageState.BODY_STREAM, state.getRequestState());
735         Assert.assertEquals(MessageState.READY, state.getResponseState());
736 
737         Mockito.verify(this.requestConsumer).consumeContent(this.decoder, this.conn);
738         Mockito.verify(this.conn, Mockito.never()).suspendInput();
739     }
740 
741     @Test
742     public void testRequestContentCompleted() throws Exception {
743         final State state = new State();
744         final HttpContext exchangeContext = new BasicHttpContext();
745         final HttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest("POST", "/",
746                 HttpVersion.HTTP_1_1);
747         state.setRequestState(MessageState.BODY_STREAM);
748         final Incoming incoming = new Incoming(
749                 request, this.requestHandler, this.requestConsumer, exchangeContext);
750         state.setIncoming(incoming);
751         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
752         Mockito.when(this.decoder.isCompleted()).thenReturn(Boolean.TRUE);
753         Mockito.when(this.requestConsumer.getException()).thenReturn(null);
754         final Object data = new Object();
755         Mockito.when(this.requestConsumer.getResult()).thenReturn(data);
756 
757         this.protocolHandler.inputReady(conn, this.decoder);
758 
759         Assert.assertEquals(MessageState.READY, state.getRequestState());
760         Assert.assertEquals(MessageState.READY, state.getResponseState());
761 
762         Mockito.verify(this.requestConsumer).consumeContent(this.decoder, this.conn);
763         Mockito.verify(this.requestConsumer).requestCompleted(exchangeContext);
764         Mockito.verify(this.conn).requestOutput();
765 
766         final PipelineEntry entry = state.getPipeline().poll();
767         Assert.assertNotNull(entry);
768         Assert.assertSame(request, entry.getRequest());
769         Assert.assertSame(requestHandler, entry.getHandler());
770         Assert.assertNotNull(entry.getResult());
771         Assert.assertNull(entry.getException());
772     }
773 
774     @Test
775     public void testRequestCompletedWithException() throws Exception {
776         final State state = new State();
777         final HttpContext exchangeContext = new BasicHttpContext();
778         final HttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest("POST", "/",
779                 HttpVersion.HTTP_1_1);
780         state.setRequestState(MessageState.BODY_STREAM);
781         final Incoming incoming = new Incoming(
782                 request, this.requestHandler, this.requestConsumer, exchangeContext);
783         state.setIncoming(incoming);
784         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
785         Mockito.when(this.decoder.isCompleted()).thenReturn(Boolean.TRUE);
786         Mockito.when(this.requestConsumer.getException()).thenReturn(new HttpException());
787         Mockito.when(this.requestConsumer.getResult()).thenReturn(null);
788 
789         this.protocolHandler.inputReady(conn, this.decoder);
790 
791         Assert.assertEquals(MessageState.READY, state.getRequestState());
792         Assert.assertEquals(MessageState.READY, state.getResponseState());
793 
794         Mockito.verify(this.requestConsumer).consumeContent(this.decoder, this.conn);
795         Mockito.verify(this.requestConsumer).requestCompleted(exchangeContext);
796         Mockito.verify(this.conn).requestOutput();
797 
798         final PipelineEntry entry = state.getPipeline().poll();
799         Assert.assertNotNull(entry);
800         Assert.assertSame(request, entry.getRequest());
801         Assert.assertSame(requestHandler, entry.getHandler());
802         Assert.assertNull(entry.getResult());
803         Assert.assertNotNull(entry.getException());
804     }
805 
806     @Test
807     public void testBasicResponse() throws Exception {
808         final State state = new State();
809         final HttpContext exchangeContext = new BasicHttpContext();
810         final HttpRequest request = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
811         final Incoming incoming = new Incoming(
812                 request, this.requestHandler, this.requestConsumer, exchangeContext);
813         state.setIncoming(incoming);
814         state.setRequestState(MessageState.COMPLETED);
815         final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
816         final Outgoing outgoing = new Outgoing(
817                 request, response, this.responseProducer, exchangeContext);
818         state.setOutgoing(outgoing);
819         state.setResponseState(MessageState.INIT);
820         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
821 
822         Mockito.when(this.responseProducer.generateResponse()).thenReturn(response);
823         Mockito.when(this.reuseStrategy.keepAlive(response, exchangeContext)).thenReturn(Boolean.TRUE);
824 
825         this.protocolHandler.responseReady(this.conn);
826 
827         Assert.assertEquals(MessageState.READY, state.getResponseState());
828 
829         Mockito.verify(this.httpProcessor).process(response, exchangeContext);
830         Mockito.verify(this.conn).submitResponse(response);
831         Mockito.verify(this.responseProducer).responseCompleted(exchangeContext);
832         Mockito.verify(this.conn).requestInput();
833         Mockito.verify(this.conn, Mockito.never()).close();
834     }
835 
836     @Test
837     public void testBasicResponseWithPipelining() throws Exception {
838         final State state = new State();
839         final HttpContext exchangeContext = new BasicHttpContext();
840         final HttpRequest request = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
841         final Incoming incoming = new Incoming(
842                 request, this.requestHandler, this.requestConsumer, exchangeContext);
843         state.setIncoming(incoming);
844         state.setRequestState(MessageState.COMPLETED);
845         final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
846         final Outgoing outgoing = new Outgoing(
847                 request, response, this.responseProducer, exchangeContext);
848         response.setEntity(new NStringEntity("stuff"));
849         state.setOutgoing(outgoing);
850 
851         final Queue<PipelineEntry> pipeline = state.getPipeline();
852 
853         final HttpContext exchangeContext2 = new BasicHttpContext();
854         final HttpRequest pipelinedRequest = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
855         final PipelineEntry entry = new PipelineEntry(pipelinedRequest, pipelinedRequest,
856                 null, requestHandler, exchangeContext2);
857         pipeline.add(entry);
858 
859         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
860 
861         Mockito.when(this.responseProducer.generateResponse()).thenReturn(response);
862         Mockito.when(this.reuseStrategy.keepAlive(response, exchangeContext)).thenReturn(Boolean.TRUE);
863 
864         this.protocolHandler.responseReady(this.conn);
865 
866         Assert.assertEquals(MessageState.BODY_STREAM, state.getResponseState());
867 
868         Mockito.verify(this.httpProcessor).process(response, exchangeContext);
869         Mockito.verify(this.conn).suspendOutput();
870         Mockito.verify(this.conn).submitResponse(response);
871         Mockito.verify(this.conn, Mockito.never()).close();
872     }
873 
874     @Test
875     public void testBasicResponseNoKeepAlive() throws Exception {
876         final State state = new State();
877         final HttpContext exchangeContext = new BasicHttpContext();
878         final HttpRequest request = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
879         final Incoming incoming = new Incoming(
880                 request, this.requestHandler, this.requestConsumer, exchangeContext);
881         state.setIncoming(incoming);
882         state.setRequestState(MessageState.COMPLETED);
883         final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
884         final Outgoing outgoing = new Outgoing(
885                 request, response, this.responseProducer, exchangeContext);
886         state.setOutgoing(outgoing);
887         state.setResponseState(MessageState.INIT);
888         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
889 
890         Mockito.when(this.responseProducer.generateResponse()).thenReturn(response);
891         Mockito.when(this.reuseStrategy.keepAlive(response, exchangeContext)).thenReturn(Boolean.FALSE);
892 
893         this.protocolHandler.responseReady(this.conn);
894 
895         Assert.assertEquals(MessageState.READY, state.getResponseState());
896 
897         Mockito.verify(this.httpProcessor).process(response, exchangeContext);
898         Mockito.verify(this.conn).submitResponse(response);
899         Mockito.verify(this.responseProducer).responseCompleted(exchangeContext);
900         Mockito.verify(this.conn).close();
901     }
902 
903     @Test
904     public void testEntityEnclosingResponse() throws Exception {
905         final State state = new State();
906         final HttpContext exchangeContext = new BasicHttpContext();
907         final HttpRequest request = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
908         final Incoming incoming = new Incoming(
909                 request, this.requestHandler, this.requestConsumer, exchangeContext);
910         state.setIncoming(incoming);
911         state.setRequestState(MessageState.COMPLETED);
912         final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
913         final Outgoing outgoing = new Outgoing(
914                 request, response, this.responseProducer, exchangeContext);
915         state.setOutgoing(outgoing);
916         state.setResponseState(MessageState.INIT);
917         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
918 
919         response.setEntity(new NStringEntity("stuff"));
920         Mockito.when(this.responseProducer.generateResponse()).thenReturn(response);
921 
922         this.protocolHandler.responseReady(this.conn);
923 
924         Assert.assertEquals(MessageState.COMPLETED, state.getRequestState());
925         Assert.assertEquals(MessageState.BODY_STREAM, state.getResponseState());
926         Assert.assertEquals("[incoming COMPLETED GET / HTTP/1.1; outgoing BODY_STREAM HTTP/1.1 200 OK]",
927                 state.toString());
928 
929         Mockito.verify(this.httpProcessor).process(response, exchangeContext);
930         Mockito.verify(this.conn).submitResponse(response);
931         Mockito.verify(this.responseProducer, Mockito.never()).responseCompleted(exchangeContext);
932     }
933 
934     @Test
935     public void testResponseToHead() throws Exception {
936         final State state = new State();
937         final HttpContext exchangeContext = new BasicHttpContext();
938         final HttpRequest request = new BasicHttpRequest("HEAD", "/", HttpVersion.HTTP_1_1);
939         final Incoming incoming = new Incoming(
940                 request, this.requestHandler, this.requestConsumer, exchangeContext);
941         state.setIncoming(incoming);
942         state.setRequestState(MessageState.COMPLETED);
943         final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
944         final Outgoing outgoing = new Outgoing(
945                 request, response, this.responseProducer, exchangeContext);
946         state.setOutgoing(outgoing);
947         state.setResponseState(MessageState.INIT);
948         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
949 
950         response.setEntity(new NStringEntity("stuff"));
951         Mockito.when(this.responseProducer.generateResponse()).thenReturn(response);
952         Mockito.when(this.reuseStrategy.keepAlive(response, exchangeContext)).thenReturn(Boolean.TRUE);
953 
954         this.protocolHandler.responseReady(this.conn);
955 
956         Assert.assertEquals(MessageState.READY, state.getResponseState());
957 
958         Mockito.verify(this.httpProcessor).process(response, exchangeContext);
959         Mockito.verify(this.conn).submitResponse(response);
960         Mockito.verify(this.responseProducer).responseCompleted(exchangeContext);
961         Mockito.verify(this.conn).requestInput();
962         Mockito.verify(this.conn, Mockito.never()).close();
963     }
964 
965     @Test
966     public void testResponseNotModified() throws Exception {
967         final State state = new State();
968         final HttpContext exchangeContext = new BasicHttpContext();
969         final HttpRequest request = new BasicHttpRequest("HEAD", "/", HttpVersion.HTTP_1_1);
970         final Incoming incoming = new Incoming(
971                 request, this.requestHandler, this.requestConsumer, exchangeContext);
972         state.setIncoming(incoming);
973         state.setRequestState(MessageState.COMPLETED);
974         final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1,
975                 HttpStatus.SC_NOT_MODIFIED, "Not modified");
976         final Outgoing outgoing = new Outgoing(
977                 request, response, this.responseProducer, exchangeContext);
978         state.setOutgoing(outgoing);
979         state.setResponseState(MessageState.INIT);
980         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
981 
982         response.setEntity(new NStringEntity("stuff"));
983         Mockito.when(this.responseProducer.generateResponse()).thenReturn(response);
984         Mockito.when(this.reuseStrategy.keepAlive(response, exchangeContext)).thenReturn(Boolean.TRUE);
985 
986         this.protocolHandler.responseReady(this.conn);
987 
988         Assert.assertEquals(MessageState.READY, state.getResponseState());
989 
990         Mockito.verify(this.httpProcessor).process(response, exchangeContext);
991         Mockito.verify(this.conn).submitResponse(response);
992         Mockito.verify(this.responseProducer).responseCompleted(exchangeContext);
993         Mockito.verify(this.conn).requestInput();
994         Mockito.verify(this.conn, Mockito.never()).close();
995     }
996 
997     @Test
998     public void testResponseContinue() throws Exception {
999         final State state = new State();
1000         final HttpContext exchangeContext = new BasicHttpContext();
1001         final HttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest("POST", "/",
1002                 HttpVersion.HTTP_1_1);
1003         final Incoming incoming = new Incoming(
1004                 request, this.requestHandler, this.requestConsumer, exchangeContext);
1005         state.setIncoming(incoming);
1006         state.setRequestState(MessageState.ACK_EXPECTED);
1007         final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1,
1008                 HttpStatus.SC_CONTINUE, "Continue");
1009         final Outgoing outgoing = new Outgoing(
1010                 request, response, this.responseProducer, exchangeContext);
1011         state.setOutgoing(outgoing);
1012         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
1013 
1014         Mockito.when(this.responseProducer.generateResponse()).thenReturn(response);
1015 
1016         this.protocolHandler.responseReady(this.conn);
1017 
1018         Assert.assertEquals(MessageState.BODY_STREAM, state.getRequestState());
1019         Assert.assertEquals(MessageState.READY, state.getResponseState());
1020 
1021         Mockito.verify(this.conn).requestInput();
1022         Mockito.verify(this.conn).submitResponse(Matchers.argThat(new ArgumentMatcher<HttpResponse>() {
1023 
1024             @Override
1025             public boolean matches(final Object argument) {
1026                 final int status = ((HttpResponse) argument).getStatusLine().getStatusCode();
1027                 return status == 100;
1028             }
1029 
1030         }));
1031     }
1032 
1033     @Test
1034     public void testResponseFailedExpectation() throws Exception {
1035         final State state = new State();
1036         final HttpContext exchangeContext = new BasicHttpContext();
1037         final HttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest("POST", "/",
1038                 HttpVersion.HTTP_1_1);
1039         final Incoming incoming = new Incoming(
1040                 request, this.requestHandler, this.requestConsumer, exchangeContext);
1041         state.setIncoming(incoming);
1042         state.setRequestState(MessageState.ACK_EXPECTED);
1043         final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1,
1044                 417, "Expectation failed");
1045         final Outgoing outgoing = new Outgoing(
1046                 request, response, this.responseProducer, exchangeContext);
1047         state.setOutgoing(outgoing);
1048         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
1049 
1050         response.setEntity(new NStringEntity("stuff"));
1051         Mockito.when(this.responseProducer.generateResponse()).thenReturn(response);
1052 
1053         this.protocolHandler.responseReady(this.conn);
1054 
1055         Assert.assertEquals(MessageState.READY, state.getRequestState());
1056         Assert.assertEquals(MessageState.BODY_STREAM, state.getResponseState());
1057 
1058         Mockito.verify(this.conn).resetInput();
1059         Mockito.verify(this.httpProcessor).process(response, exchangeContext);
1060         Mockito.verify(this.conn).submitResponse(response);
1061         Mockito.verify(this.responseProducer, Mockito.never()).responseCompleted(exchangeContext);
1062     }
1063 
1064     @Test
1065     public void testResponsePipelinedEmpty() throws Exception {
1066         final State state = new State();
1067 
1068         state.setRequestState(MessageState.READY);
1069         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
1070 
1071         this.protocolHandler.responseReady(this.conn);
1072 
1073         Assert.assertEquals(MessageState.READY, state.getRequestState());
1074         Assert.assertEquals(MessageState.READY, state.getResponseState());
1075         Assert.assertNull(state.getOutgoing());
1076 
1077         Mockito.verify(conn).suspendOutput();
1078         Mockito.verifyNoMoreInteractions(requestHandler);
1079     }
1080 
1081     @Test
1082     public void testResponseHandlePipelinedRequest() throws Exception {
1083         final State state = new State();
1084         final Queue<PipelineEntry> pipeline = state.getPipeline();
1085 
1086         final HttpContext exchangeContext = new BasicHttpContext();
1087         final HttpRequest request = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
1088         final PipelineEntry entry = new PipelineEntry(request, request, null, requestHandler, exchangeContext);
1089         pipeline.add(entry);
1090 
1091         state.setRequestState(MessageState.READY);
1092         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
1093 
1094         this.protocolHandler.responseReady(this.conn);
1095 
1096         Assert.assertEquals(MessageState.READY, state.getRequestState());
1097         Assert.assertEquals(MessageState.INIT, state.getResponseState());
1098         Assert.assertNull(state.getOutgoing());
1099 
1100         final ArgumentCaptor<HttpAsyncExchange> argCaptor = ArgumentCaptor.forClass(HttpAsyncExchange.class);
1101         Mockito.verify(this.requestHandler).handle(Matchers.same(request),
1102                 argCaptor.capture(), Matchers.same(exchangeContext));
1103         final HttpAsyncExchange exchange = argCaptor.getValue();
1104 
1105         Assert.assertNotNull(exchange);
1106         Assert.assertSame(request, exchange.getRequest());
1107         Assert.assertNotNull(exchange.getResponse());
1108         Assert.assertEquals(200, exchange.getResponse().getStatusLine().getStatusCode());
1109     }
1110 
1111     @Test
1112     public void testResponseHandleFailedPipelinedRequest() throws Exception {
1113         final State state = new State();
1114         final Queue<PipelineEntry> pipeline = state.getPipeline();
1115 
1116         final HttpContext exchangeContext = new BasicHttpContext();
1117         final HttpRequest request = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
1118         final Exception ex = new Exception("Opppsie");
1119         final PipelineEntry entry = new PipelineEntry(request, null, ex, requestHandler, exchangeContext);
1120         pipeline.add(entry);
1121 
1122         state.setRequestState(MessageState.READY);
1123         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
1124 
1125         this.protocolHandler.responseReady(this.conn);
1126 
1127         Assert.assertEquals(MessageState.READY, state.getRequestState());
1128         Assert.assertEquals(MessageState.BODY_STREAM, state.getResponseState());
1129 
1130         final Outgoing outgoing = state.getOutgoing();
1131         Assert.assertNotNull(outgoing.getProducer());
1132         final HttpResponse response = outgoing.getResponse();
1133         Assert.assertNotNull(response);
1134         Assert.assertEquals(500, response.getStatusLine().getStatusCode());
1135 
1136         Mockito.verify(this.requestHandler, Mockito.never()).handle(Matchers.<HttpRequest>any(),
1137                 Matchers.<HttpAsyncExchange>any(), Matchers.<HttpContext>any());
1138         Mockito.verify(this.conn).submitResponse(Matchers.same(response));
1139     }
1140 
1141     @Test(expected=HttpException.class)
1142     public void testInvalidResponseStatus() throws Exception {
1143         final State state = new State();
1144         final HttpContext exchangeContext = new BasicHttpContext();
1145         final HttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest("POST", "/",
1146                 HttpVersion.HTTP_1_1);
1147         final Incoming incoming = new Incoming(
1148                 request, this.requestHandler, this.requestConsumer, exchangeContext);
1149         state.setIncoming(incoming);
1150         state.setRequestState(MessageState.COMPLETED);
1151         state.setResponseState(MessageState.INIT);
1152         final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 112, "Something stupid");
1153         final Outgoing outgoing = new Outgoing(
1154                 request, response, this.responseProducer, exchangeContext);
1155         state.setOutgoing(outgoing);
1156         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
1157 
1158         Mockito.when(this.responseProducer.generateResponse()).thenReturn(response);
1159         Mockito.when(this.conn.isResponseSubmitted()).thenReturn(Boolean.FALSE);
1160 
1161         this.protocolHandler.responseReady(this.conn);
1162     }
1163 
1164     @Test(expected=HttpException.class)
1165     public void testInvalidResponseStatusToExpection() throws Exception {
1166         final State state = new State();
1167         final HttpContext exchangeContext = new BasicHttpContext();
1168         final HttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest("POST", "/",
1169                 HttpVersion.HTTP_1_1);
1170         final Incoming incoming = new Incoming(
1171                 request, this.requestHandler, this.requestConsumer, exchangeContext);
1172         state.setIncoming(incoming);
1173         state.setRequestState(MessageState.ACK_EXPECTED);
1174         state.setResponseState(MessageState.READY);
1175         final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
1176         response.setEntity(new NStringEntity("stuff"));
1177         final Outgoing outgoing = new Outgoing(
1178                 request, response, this.responseProducer, exchangeContext);
1179         state.setOutgoing(outgoing);
1180         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
1181 
1182         Mockito.when(this.responseProducer.generateResponse()).thenReturn(response);
1183         Mockito.when(this.conn.isResponseSubmitted()).thenReturn(Boolean.FALSE);
1184 
1185         this.protocolHandler.responseReady(this.conn);
1186     }
1187 
1188     @Test
1189     public void testResponseTrigger() throws Exception {
1190         final State state = new State();
1191         state.setRequestState(MessageState.COMPLETED);
1192         state.setResponseState(MessageState.READY);
1193         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
1194 
1195         final HttpContext exchangeContext = new BasicHttpContext();
1196         final HttpAsyncExchange httpexchanage = protocolHandler.new HttpAsyncExchangeImpl(
1197                 new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1),
1198                 new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK"),
1199                 state, this.conn, exchangeContext);
1200         Assert.assertFalse(httpexchanage.isCompleted());
1201         httpexchanage.submitResponse(this.responseProducer);
1202         Assert.assertTrue(httpexchanage.isCompleted());
1203 
1204         Assert.assertEquals(MessageState.COMPLETED, state.getRequestState());
1205         Assert.assertEquals(MessageState.READY, state.getResponseState());
1206         final Outgoing outgoing = state.getOutgoing();
1207         Assert.assertNotNull(outgoing);
1208         Assert.assertSame(this.responseProducer, outgoing.getProducer());
1209 
1210         Mockito.verify(this.conn).requestOutput();
1211 
1212         try {
1213             httpexchanage.submitResponse(Mockito.mock(HttpAsyncResponseProducer.class));
1214             Assert.fail("IllegalStateException expected");
1215         } catch (final IllegalStateException ex) {
1216         }
1217     }
1218 
1219     @Test(expected=IllegalArgumentException.class)
1220     public void testResponseTriggerInvalidResponseProducer() throws Exception {
1221         final State state = new State();
1222         state.setRequestState(MessageState.ACK_EXPECTED);
1223         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
1224 
1225         final HttpContext exchangeContext = new BasicHttpContext();
1226         final HttpAsyncExchange httpexchanage = protocolHandler.new HttpAsyncExchangeImpl(
1227                 new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1),
1228                 new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK"),
1229                 state, this.conn, exchangeContext);
1230         httpexchanage.submitResponse(null);
1231     }
1232 
1233     @Test
1234     public void testResponseContent() throws Exception {
1235         final State state = new State();
1236         state.setRequestState(MessageState.COMPLETED);
1237         state.setResponseState(MessageState.BODY_STREAM);
1238         final HttpContext exchangeContext = new BasicHttpContext();
1239         final HttpRequest request = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
1240         final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
1241         response.setEntity(new NStringEntity("stuff"));
1242         final Outgoing outgoing = new Outgoing(
1243                 request, response, this.responseProducer, exchangeContext);
1244         state.setOutgoing(outgoing);
1245         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
1246         Mockito.when(this.encoder.isCompleted()).thenReturn(Boolean.FALSE);
1247 
1248         this.protocolHandler.outputReady(conn, this.encoder);
1249 
1250         Assert.assertEquals(MessageState.COMPLETED, state.getRequestState());
1251         Assert.assertEquals(MessageState.BODY_STREAM, state.getResponseState());
1252 
1253         Mockito.verify(this.responseProducer).produceContent(this.encoder, this.conn);
1254         Mockito.verify(this.conn, Mockito.never()).requestInput();
1255         Mockito.verify(this.conn, Mockito.never()).close();
1256     }
1257 
1258     @Test
1259     public void testResponseContentCompleted() throws Exception {
1260         final State state = new State();
1261         state.setRequestState(MessageState.COMPLETED);
1262         state.setResponseState(MessageState.BODY_STREAM);
1263         final HttpContext exchangeContext = new BasicHttpContext();
1264         final HttpRequest request = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
1265         final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
1266         response.setEntity(new NStringEntity("stuff"));
1267         final Outgoing outgoing = new Outgoing(
1268                 request, response, this.responseProducer, exchangeContext);
1269         state.setOutgoing(outgoing);
1270         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
1271         Mockito.when(this.encoder.isCompleted()).thenReturn(true);
1272         Mockito.when(this.reuseStrategy.keepAlive(response, exchangeContext)).thenReturn(Boolean.TRUE);
1273 
1274         this.protocolHandler.outputReady(conn, this.encoder);
1275 
1276         Assert.assertEquals(MessageState.READY, state.getResponseState());
1277 
1278         Mockito.verify(this.responseProducer).produceContent(this.encoder, this.conn);
1279         Mockito.verify(this.responseProducer).responseCompleted(exchangeContext);
1280         Mockito.verify(this.conn).requestInput();
1281         Mockito.verify(this.conn, Mockito.never()).close();
1282     }
1283 
1284     @Test
1285     public void testResponseContentCompletedNoKeepAlive() throws Exception {
1286         final State state = new State();
1287         state.setRequestState(MessageState.COMPLETED);
1288         state.setResponseState(MessageState.BODY_STREAM);
1289         final HttpContext exchangeContext = new BasicHttpContext();
1290         final HttpRequest request = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
1291         final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
1292         response.setEntity(new NStringEntity("stuff"));
1293         final Outgoing outgoing = new Outgoing(
1294                 request, response, this.responseProducer, exchangeContext);
1295         state.setOutgoing(outgoing);
1296         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
1297         Mockito.when(this.encoder.isCompleted()).thenReturn(true);
1298         Mockito.when(this.reuseStrategy.keepAlive(response, exchangeContext)).thenReturn(Boolean.FALSE);
1299 
1300         this.protocolHandler.outputReady(conn, this.encoder);
1301 
1302         Assert.assertEquals(MessageState.READY, state.getResponseState());
1303 
1304         Mockito.verify(this.responseProducer).produceContent(this.encoder, this.conn);
1305         Mockito.verify(this.responseProducer).responseCompleted(exchangeContext);
1306         Mockito.verify(this.conn, Mockito.never()).requestInput();
1307         Mockito.verify(this.conn).close();
1308     }
1309 
1310     @Test
1311     public void testEndOfInput() throws Exception {
1312 
1313         Mockito.when(this.conn.getSocketTimeout()).thenReturn(1000);
1314 
1315         this.protocolHandler.endOfInput(this.conn);
1316 
1317         Mockito.verify(this.conn, Mockito.never()).setSocketTimeout(Matchers.anyInt());
1318         Mockito.verify(this.conn).close();
1319     }
1320 
1321     @Test
1322     public void testEndOfInputNoTimeout() throws Exception {
1323 
1324         Mockito.when(this.conn.getSocketTimeout()).thenReturn(0);
1325 
1326         this.protocolHandler.endOfInput(this.conn);
1327 
1328         Mockito.verify(this.conn).setSocketTimeout(1000);
1329         Mockito.verify(this.conn).close();
1330     }
1331 
1332     @Test
1333     public void testTimeoutActiveConnection() throws Exception {
1334         final State state = new State();
1335         final HttpContext exchangeContext = new BasicHttpContext();
1336         final HttpRequest request = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
1337         final Incoming incoming = new Incoming(
1338                 request, this.requestHandler, this.requestConsumer, exchangeContext);
1339         state.setIncoming(incoming);
1340         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
1341         Mockito.when(this.conn.getHttpRequest()).thenReturn(request);
1342         Mockito.when(this.conn.getStatus()).thenReturn(NHttpConnection.ACTIVE, NHttpConnection.CLOSED);
1343 
1344         this.protocolHandler.timeout(this.conn);
1345 
1346         Mockito.verify(this.conn, Mockito.atLeastOnce()).close();
1347         Mockito.verify(this.conn, Mockito.never()).setSocketTimeout(Matchers.anyInt());
1348     }
1349 
1350     @Test
1351     public void testTimeoutActiveConnectionBufferedData() throws Exception {
1352         final State state = new State();
1353         final HttpContext exchangeContext = new BasicHttpContext();
1354         final HttpRequest request = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
1355         final Incoming incoming = new Incoming(
1356                 request, this.requestHandler, this.requestConsumer, exchangeContext);
1357         state.setIncoming(incoming);
1358         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
1359         Mockito.when(this.conn.getHttpRequest()).thenReturn(request);
1360         Mockito.when(this.conn.getStatus()).thenReturn(NHttpConnection.ACTIVE, NHttpConnection.CLOSING);
1361 
1362         this.protocolHandler.timeout(this.conn);
1363 
1364         Mockito.verify(this.conn).close();
1365         Mockito.verify(this.conn).setSocketTimeout(250);
1366     }
1367 
1368     @Test
1369     public void testTimeoutClosingConnection() throws Exception {
1370         final State state = new State();
1371         final HttpContext exchangeContext = new BasicHttpContext();
1372         final HttpRequest request = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
1373         final Incoming incoming = new Incoming(
1374                 request, this.requestHandler, this.requestConsumer, exchangeContext);
1375         state.setIncoming(incoming);
1376         final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
1377         final Outgoing outgoing = new Outgoing(
1378                 request, response, this.responseProducer, exchangeContext);
1379         state.setOutgoing(outgoing);
1380         this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
1381         Mockito.when(this.conn.getStatus()).thenReturn(NHttpConnection.CLOSING);
1382 
1383         this.protocolHandler.timeout(this.conn);
1384 
1385         Mockito.verify(this.conn).shutdown();
1386         Mockito.verify(this.requestConsumer).failed(Matchers.any(SocketTimeoutException.class));
1387         Mockito.verify(this.requestConsumer).close();
1388         Mockito.verify(this.responseProducer).failed(Matchers.any(SocketTimeoutException.class));
1389         Mockito.verify(this.responseProducer).close();
1390     }
1391 
1392 }