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  
33  import org.apache.http.ConnectionReuseStrategy;
34  import org.apache.http.HttpEntity;
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.MethodNotSupportedException;
43  import org.apache.http.ProtocolException;
44  import org.apache.http.UnsupportedHttpVersionException;
45  import org.apache.http.annotation.Immutable;
46  import org.apache.http.concurrent.Cancellable;
47  import org.apache.http.entity.ContentType;
48  import org.apache.http.impl.DefaultConnectionReuseStrategy;
49  import org.apache.http.impl.DefaultHttpResponseFactory;
50  import org.apache.http.nio.ContentDecoder;
51  import org.apache.http.nio.ContentEncoder;
52  import org.apache.http.nio.NHttpConnection;
53  import org.apache.http.nio.NHttpServerConnection;
54  import org.apache.http.nio.NHttpServerEventHandler;
55  import org.apache.http.nio.entity.NStringEntity;
56  import org.apache.http.params.HttpParams;
57  import org.apache.http.protocol.BasicHttpContext;
58  import org.apache.http.protocol.HttpContext;
59  import org.apache.http.protocol.HttpCoreContext;
60  import org.apache.http.protocol.HttpProcessor;
61  import org.apache.http.util.Args;
62  import org.apache.http.util.Asserts;
63  
64  /**
65   * <tt>HttpAsyncService</tt> is a fully asynchronous HTTP server side protocol
66   * handler based on the non-blocking (NIO) I/O model.
67   * <tt>HttpAsyncServerProtocolHandler</tt> translates individual events fired
68   * through the {@link NHttpServerEventHandler} interface into logically related
69   * HTTP message exchanges.
70   * <p/>
71   * Upon receiving an incoming request <tt>HttpAsyncService</tt> verifies
72   * the message for compliance with the server expectations using
73   * {@link HttpAsyncExpectationVerifier}, if provided, and then
74   * {@link HttpAsyncRequestHandlerMapper} is used to map the request
75   * to a particular {@link HttpAsyncRequestHandler} intended to handle
76   * the request with the given URI. The protocol handler uses the selected
77   * {@link HttpAsyncRequestHandler} instance to process the incoming request
78   * and to generate an outgoing response.
79   * <p/>
80   * <tt>HttpAsyncService</tt> relies on {@link HttpProcessor} to generate
81   * mandatory protocol headers for all outgoing messages and apply common,
82   * cross-cutting message transformations to all incoming and outgoing messages,
83   * whereas individual {@link HttpAsyncRequestHandler}s are expected
84   * to implement application specific content generation and processing.
85   * <p/>
86   * Individual {@link HttpAsyncRequestHandler}s do not have to submit a response
87   * immediately. They can defer transmission of an HTTP response back to
88   * the client without blocking the I/O thread by delegating the process of
89   * request handling to another service or a worker thread. HTTP response can
90   * be submitted as a later a later point of time once response content becomes
91   * available.
92   *
93   * @since 4.2
94   */
95  @SuppressWarnings("deprecation")
96  @Immutable // provided injected dependencies are immutable
97  public class HttpAsyncService implements NHttpServerEventHandler {
98  
99      static final String HTTP_EXCHANGE_STATE = "http.nio.http-exchange-state";
100 
101     private final HttpProcessor httpProcessor;
102     private final ConnectionReuseStrategy connStrategy;
103     private final HttpResponseFactory responseFactory;
104     private final HttpAsyncRequestHandlerMapper handlerMapper;
105     private final HttpAsyncExpectationVerifier expectationVerifier;
106 
107     /**
108      * Creates new instance of <tt>HttpAsyncServerProtocolHandler</tt>.
109      *
110      * @param httpProcessor HTTP protocol processor (required).
111      * @param connStrategy Connection re-use strategy (required).
112      * @param responseFactory HTTP response factory (required).
113      * @param handlerResolver Request handler resolver.
114      * @param expectationVerifier Request expectation verifier (optional).
115      * @param params HTTP parameters (required).
116      *
117      * @deprecated (4.3) use {@link HttpAsyncService#HttpAsyncService(HttpProcessor,
118      *  ConnectionReuseStrategy, HttpResponseFactory, HttpAsyncRequestHandlerMapper,
119      *    HttpAsyncExpectationVerifier)}
120      */
121     @Deprecated
122     public HttpAsyncService(
123             final HttpProcessor httpProcessor,
124             final ConnectionReuseStrategy connStrategy,
125             final HttpResponseFactory responseFactory,
126             final HttpAsyncRequestHandlerResolver handlerResolver,
127             final HttpAsyncExpectationVerifier expectationVerifier,
128             final HttpParams params) {
129         this(httpProcessor,
130              connStrategy,
131              responseFactory,
132              new HttpAsyncRequestHandlerResolverAdapter(handlerResolver),
133              expectationVerifier);
134     }
135 
136     /**
137      * Creates new instance of <tt>HttpAsyncServerProtocolHandler</tt>.
138      *
139      * @param httpProcessor HTTP protocol processor (required).
140      * @param connStrategy Connection re-use strategy (required).
141      * @param handlerResolver Request handler resolver.
142      * @param params HTTP parameters (required).
143      *
144      * @deprecated (4.3) use {@link HttpAsyncService#HttpAsyncService(HttpProcessor,
145      *   ConnectionReuseStrategy, HttpResponseFactory, HttpAsyncRequestHandlerMapper,
146      *   HttpAsyncExpectationVerifier)}
147      */
148     @Deprecated
149     public HttpAsyncService(
150             final HttpProcessor httpProcessor,
151             final ConnectionReuseStrategy connStrategy,
152             final HttpAsyncRequestHandlerResolver handlerResolver,
153             final HttpParams params) {
154         this(httpProcessor,
155              connStrategy,
156              DefaultHttpResponseFactory.INSTANCE,
157              new HttpAsyncRequestHandlerResolverAdapter(handlerResolver),
158              null);
159     }
160 
161     /**
162      * Creates new instance of <tt>HttpAsyncServerProtocolHandler</tt>.
163      *
164      * @param httpProcessor HTTP protocol processor.
165      * @param connStrategy Connection re-use strategy. If <code>null</code>
166      *   {@link DefaultConnectionReuseStrategy#INSTANCE} will be used.
167      * @param responseFactory HTTP response factory. If <code>null</code>
168      *   {@link DefaultHttpResponseFactory#INSTANCE} will be used.
169      * @param handlerMapper Request handler mapper.
170      * @param expectationVerifier Request expectation verifier. May be <code>null</code>.
171      *
172      * @since 4.3
173      */
174     public HttpAsyncService(
175             final HttpProcessor httpProcessor,
176             final ConnectionReuseStrategy connStrategy,
177             final HttpResponseFactory responseFactory,
178             final HttpAsyncRequestHandlerMapper handlerMapper,
179             final HttpAsyncExpectationVerifier expectationVerifier) {
180         super();
181         this.httpProcessor = Args.notNull(httpProcessor, "HTTP processor");
182         this.connStrategy = connStrategy != null ? connStrategy :
183             DefaultConnectionReuseStrategy.INSTANCE;
184         this.responseFactory = responseFactory != null ? responseFactory :
185             DefaultHttpResponseFactory.INSTANCE;
186         this.handlerMapper = handlerMapper;
187         this.expectationVerifier = expectationVerifier;
188     }
189 
190     /**
191      * Creates new instance of <tt>HttpAsyncServerProtocolHandler</tt>.
192      *
193      * @param httpProcessor HTTP protocol processor.
194      * @param handlerMapper Request handler mapper.
195      *
196      * @since 4.3
197      */
198     public HttpAsyncService(
199             final HttpProcessor httpProcessor,
200             final HttpAsyncRequestHandlerMapper handlerMapper) {
201         this(httpProcessor, null, null, handlerMapper, null);
202     }
203 
204     public void connected(final NHttpServerConnection conn) {
205         final State state = new State();
206         conn.getContext().setAttribute(HTTP_EXCHANGE_STATE, state);
207     }
208 
209     public void closed(final NHttpServerConnection conn) {
210         final State state = getState(conn);
211         if (state != null) {
212             state.setTerminated();
213             closeHandlers(state);
214             final Cancellable cancellable = state.getCancellable();
215             if (cancellable != null) {
216                 cancellable.cancel();
217             }
218             state.reset();
219         }
220     }
221 
222     public void exception(
223             final NHttpServerConnection conn, final Exception cause) {
224         final State state = getState(conn);
225         if (state == null) {
226             shutdownConnection(conn);
227             log(cause);
228             return;
229         }
230         state.setTerminated();
231         closeHandlers(state, cause);
232         final Cancellable cancellable = state.getCancellable();
233         if (cancellable != null) {
234             cancellable.cancel();
235         }
236         if (conn.isResponseSubmitted()
237                 || state.getResponseState().compareTo(MessageState.INIT) > 0) {
238             // There is not much that we can do if a response
239             // has already been submitted
240             shutdownConnection(conn);
241         } else {
242             final HttpContext context = state.getContext();
243             final HttpAsyncResponseProducer responseProducer = handleException(
244                     cause, context);
245             state.setResponseProducer(responseProducer);
246             try {
247                 final HttpResponse response = responseProducer.generateResponse();
248                 state.setResponse(response);
249                 commitFinalResponse(conn, state);
250             } catch (final Exception ex) {
251                 shutdownConnection(conn);
252                 closeHandlers(state);
253                 if (ex instanceof RuntimeException) {
254                     throw (RuntimeException) ex;
255                 } else {
256                     log(ex);
257                 }
258             }
259         }
260     }
261 
262     public void requestReceived(
263             final NHttpServerConnection conn) throws IOException, HttpException {
264         final State state = ensureNotNull(getState(conn));
265         if (state.getResponseState() != MessageState.READY) {
266             throw new ProtocolException("Out of sequence request message detected (pipelining is not supported)");
267         }
268         final HttpRequest request = conn.getHttpRequest();
269         final HttpContext context = state.getContext();
270 
271         context.setAttribute(HttpCoreContext.HTTP_REQUEST, request);
272         context.setAttribute(HttpCoreContext.HTTP_CONNECTION, conn);
273         this.httpProcessor.process(request, context);
274 
275         state.setRequest(request);
276         final HttpAsyncRequestHandler<Object> requestHandler = getRequestHandler(request);
277         state.setRequestHandler(requestHandler);
278         final HttpAsyncRequestConsumer<Object> consumer = requestHandler.processRequest(request, context);
279         state.setRequestConsumer(consumer);
280 
281         consumer.requestReceived(request);
282 
283         if (request instanceof HttpEntityEnclosingRequest) {
284             if (((HttpEntityEnclosingRequest) request).expectContinue()) {
285                 state.setRequestState(MessageState.ACK_EXPECTED);
286                 final HttpResponse ack = this.responseFactory.newHttpResponse(HttpVersion.HTTP_1_1,
287                         HttpStatus.SC_CONTINUE, context);
288                 if (this.expectationVerifier != null) {
289                     conn.suspendInput();
290                     final HttpAsyncExchange httpex = new Exchange(
291                             request, ack, state, conn);
292                     this.expectationVerifier.verify(httpex, context);
293                 } else {
294                     conn.submitResponse(ack);
295                     state.setRequestState(MessageState.BODY_STREAM);
296                 }
297             } else {
298                 state.setRequestState(MessageState.BODY_STREAM);
299             }
300         } else {
301             // No request content is expected.
302             // Process request right away
303             processRequest(conn, state);
304         }
305     }
306 
307     public void inputReady(
308             final NHttpServerConnection conn,
309             final ContentDecoder decoder) throws IOException, HttpException {
310         final State state = ensureNotNull(getState(conn));
311         final HttpAsyncRequestConsumer<?> consumer = ensureNotNull(state.getRequestConsumer());
312         consumer.consumeContent(decoder, conn);
313         state.setRequestState(MessageState.BODY_STREAM);
314         if (decoder.isCompleted()) {
315             processRequest(conn, state);
316         }
317     }
318 
319     public void responseReady(
320             final NHttpServerConnection conn) throws IOException, HttpException {
321         final State state = ensureNotNull(getState(conn));
322         if (state.getResponse() != null) {
323             return;
324         }
325         final HttpAsyncResponseProducer responseProducer = state.getResponseProducer();
326         if (responseProducer == null) {
327             return;
328         }
329         final HttpContext context = state.getContext();
330         final HttpResponse response = responseProducer.generateResponse();
331         final int status = response.getStatusLine().getStatusCode();
332         if (state.getRequestState() == MessageState.ACK_EXPECTED) {
333             if (status == 100) {
334                 try {
335                     // Make sure 100 response has no entity
336                     response.setEntity(null);
337                     conn.requestInput();
338                     state.setRequestState(MessageState.BODY_STREAM);
339                     conn.submitResponse(response);
340                     responseProducer.responseCompleted(context);
341                 } finally {
342                     state.setResponseProducer(null);
343                     responseProducer.close();
344                 }
345             } else if (status >= 400) {
346                 conn.resetInput();
347                 state.setRequestState(MessageState.COMPLETED);
348                 state.setResponse(response);
349                 commitFinalResponse(conn, state);
350             } else {
351                 throw new HttpException("Invalid response: " + response.getStatusLine());
352             }
353         } else {
354             if (status >= 200) {
355                 state.setResponse(response);
356                 commitFinalResponse(conn, state);
357             } else {
358                 throw new HttpException("Invalid response: " + response.getStatusLine());
359             }
360         }
361     }
362 
363     public void outputReady(
364             final NHttpServerConnection conn,
365             final ContentEncoder encoder) throws IOException {
366         final State state = ensureNotNull(getState(conn));
367         final HttpAsyncResponseProducer responseProducer = state.getResponseProducer();
368         final HttpContext context = state.getContext();
369         final HttpResponse response = state.getResponse();
370 
371         responseProducer.produceContent(encoder, conn);
372         state.setResponseState(MessageState.BODY_STREAM);
373         if (encoder.isCompleted()) {
374             try {
375                 responseProducer.responseCompleted(context);
376                 state.reset();
377             } finally {
378                 responseProducer.close();
379             }
380             if (!this.connStrategy.keepAlive(response, context)) {
381                 conn.close();
382             } else {
383                 conn.requestInput();
384             }
385         }
386     }
387 
388     public void endOfInput(final NHttpServerConnection conn) throws IOException {
389         // Closing connection in an orderly manner and
390         // waiting for output buffer to get flushed.
391         // Do not want to wait indefinitely, though, in case
392         // the opposite end is not reading
393         if (conn.getSocketTimeout() <= 0) {
394             conn.setSocketTimeout(1000);
395         }
396         conn.close();
397     }
398 
399     public void timeout(final NHttpServerConnection conn) throws IOException {
400         final State state = getState(conn);
401         if (state != null) {
402             closeHandlers(state, new SocketTimeoutException());
403         }
404         if (conn.getStatus() == NHttpConnection.ACTIVE) {
405             conn.close();
406             if (conn.getStatus() == NHttpConnection.CLOSING) {
407                 // Give the connection some grace time to
408                 // close itself nicely
409                 conn.setSocketTimeout(250);
410             }
411         } else {
412             conn.shutdown();
413         }
414     }
415 
416     private State getState(final NHttpConnection conn) {
417         return (State) conn.getContext().getAttribute(HTTP_EXCHANGE_STATE);
418     }
419 
420     private State ensureNotNull(final State state) {
421         Asserts.notNull(state, "HTTP exchange state");
422         return state;
423     }
424 
425     private HttpAsyncRequestConsumer<Object> ensureNotNull(final HttpAsyncRequestConsumer<Object> requestConsumer) {
426         Asserts.notNull(requestConsumer, "Request consumer");
427         return requestConsumer;
428     }
429 
430     /**
431      * This method can be used to log I/O exception thrown while closing
432      * {@link java.io.Closeable} objects (such as
433      * {@link org.apache.http.HttpConnection}).
434      *
435      * @param ex I/O exception thrown by {@link java.io.Closeable#close()}
436      */
437     protected void log(final Exception ex) {
438     }
439 
440     private void closeConnection(final NHttpConnection conn) {
441         try {
442             conn.close();
443         } catch (final IOException ex) {
444             log(ex);
445         }
446     }
447 
448     private void shutdownConnection(final NHttpConnection conn) {
449         try {
450             conn.shutdown();
451         } catch (final IOException ex) {
452             log(ex);
453         }
454     }
455 
456     private void closeHandlers(final State state, final Exception ex) {
457         final HttpAsyncRequestConsumer<Object> consumer = state.getRequestConsumer();
458         if (consumer != null) {
459             try {
460                 consumer.failed(ex);
461             } finally {
462                 try {
463                     consumer.close();
464                 } catch (final IOException ioex) {
465                     log(ioex);
466                 }
467             }
468         }
469         final HttpAsyncResponseProducer producer = state.getResponseProducer();
470         if (producer != null) {
471             try {
472                 producer.failed(ex);
473             } finally {
474                 try {
475                     producer.close();
476                 } catch (final IOException ioex) {
477                     log(ioex);
478                 }
479             }
480         }
481     }
482 
483     private void closeHandlers(final State state) {
484         final HttpAsyncRequestConsumer<Object> consumer = state.getRequestConsumer();
485         if (consumer != null) {
486             try {
487                 consumer.close();
488             } catch (final IOException ioex) {
489                 log(ioex);
490             }
491         }
492         final HttpAsyncResponseProducer producer = state.getResponseProducer();
493         if (producer != null) {
494             try {
495                 producer.close();
496             } catch (final IOException ioex) {
497                 log(ioex);
498             }
499         }
500     }
501 
502     protected HttpAsyncResponseProducer handleException(
503             final Exception ex, final HttpContext context) {
504         final int code;
505         if (ex instanceof MethodNotSupportedException) {
506             code = HttpStatus.SC_NOT_IMPLEMENTED;
507         } else if (ex instanceof UnsupportedHttpVersionException) {
508             code = HttpStatus.SC_HTTP_VERSION_NOT_SUPPORTED;
509         } else if (ex instanceof ProtocolException) {
510             code = HttpStatus.SC_BAD_REQUEST;
511         } else {
512             code = HttpStatus.SC_INTERNAL_SERVER_ERROR;
513         }
514         String message = ex.getMessage();
515         if (message == null) {
516             message = ex.toString();
517         }
518         final HttpResponse response = this.responseFactory.newHttpResponse(HttpVersion.HTTP_1_1,
519                 code, context);
520         return new ErrorResponseProducer(response,
521                 new NStringEntity(message, ContentType.DEFAULT_TEXT), false);
522     }
523 
524     private boolean canResponseHaveBody(final HttpRequest request, final HttpResponse response) {
525         if (request != null && "HEAD".equalsIgnoreCase(request.getRequestLine().getMethod())) {
526             return false;
527         }
528         final int status = response.getStatusLine().getStatusCode();
529         return status >= HttpStatus.SC_OK
530             && status != HttpStatus.SC_NO_CONTENT
531             && status != HttpStatus.SC_NOT_MODIFIED
532             && status != HttpStatus.SC_RESET_CONTENT;
533     }
534 
535     private void processRequest(
536             final NHttpServerConnection conn,
537             final State state) throws IOException, HttpException {
538         final HttpContext context = state.getContext();
539 
540         state.setRequestState(MessageState.COMPLETED);
541         state.setResponseState(MessageState.INIT);
542 
543         final Object result;
544         final HttpAsyncRequestConsumer<?> consumer = state.getRequestConsumer();
545         try {
546             consumer.requestCompleted(context);
547             result = consumer.getResult();
548         } finally {
549             consumer.close();
550         }
551         if (result != null) {
552             final HttpResponse response = this.responseFactory.newHttpResponse(HttpVersion.HTTP_1_1,
553                     HttpStatus.SC_OK, context);
554             final HttpRequest request = state.getRequest();
555             final Exchange httpexchange = new Exchange(request, response, state, conn);
556             final HttpAsyncRequestHandler<Object> handler = state.getRequestHandler();
557             handler.handle(result, httpexchange, context);
558         } else {
559             final Exception exception = consumer.getException();
560             final HttpAsyncResponseProducer responseProducer = handleException(
561                     exception != null ? exception : new HttpException("Internal failure processing request"),
562                     context);
563             state.setResponseProducer(responseProducer);
564             conn.requestOutput();
565         }
566     }
567 
568     private void commitFinalResponse(
569             final NHttpServerConnection conn,
570             final State state) throws IOException, HttpException {
571         final HttpContext context = state.getContext();
572         final HttpRequest request = state.getRequest();
573         final HttpResponse response = state.getResponse();
574 
575         context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response);
576         this.httpProcessor.process(response, context);
577 
578         HttpEntity entity = response.getEntity();
579         if (entity != null && !canResponseHaveBody(request, response)) {
580             response.setEntity(null);
581             entity = null;
582         }
583 
584         conn.submitResponse(response);
585 
586         if (entity == null) {
587             final HttpAsyncResponseProducer responseProducer = state.getResponseProducer();
588             try {
589                 responseProducer.responseCompleted(context);
590                 state.reset();
591             } finally {
592                 responseProducer.close();
593             }
594             if (!this.connStrategy.keepAlive(response, context)) {
595                 conn.close();
596             } else {
597                 // Ready to process new request
598                 conn.requestInput();
599             }
600         } else {
601             state.setResponseState(MessageState.BODY_STREAM);
602         }
603     }
604 
605     @SuppressWarnings("unchecked")
606     private HttpAsyncRequestHandler<Object> getRequestHandler(final HttpRequest request) {
607         HttpAsyncRequestHandler<Object> handler = null;
608         if (this.handlerMapper != null) {
609             handler = (HttpAsyncRequestHandler<Object>) this.handlerMapper.lookup(request);
610         }
611         if (handler == null) {
612             handler = new NullRequestHandler();
613         }
614         return handler;
615     }
616 
617     static class State {
618 
619         private volatile BasicHttpContext context;
620         private volatile boolean terminated;
621         private volatile HttpAsyncRequestHandler<Object> requestHandler;
622         private volatile MessageState requestState;
623         private volatile MessageState responseState;
624         private volatile HttpAsyncRequestConsumer<Object> requestConsumer;
625         private volatile HttpAsyncResponseProducer responseProducer;
626         private volatile HttpRequest request;
627         private volatile HttpResponse response;
628         private volatile Cancellable cancellable;
629 
630         State() {
631             super();
632             this.context = new BasicHttpContext();
633             this.requestState = MessageState.READY;
634             this.responseState = MessageState.READY;
635         }
636 
637         public HttpContext getContext() {
638             return this.context;
639         }
640 
641         public boolean isTerminated() {
642             return this.terminated;
643         }
644 
645         public void setTerminated() {
646             this.terminated = true;
647         }
648 
649         public HttpAsyncRequestHandler<Object> getRequestHandler() {
650             return this.requestHandler;
651         }
652 
653         public void setRequestHandler(final HttpAsyncRequestHandler<Object> requestHandler) {
654             this.requestHandler = requestHandler;
655         }
656 
657         public MessageState getRequestState() {
658             return this.requestState;
659         }
660 
661         public void setRequestState(final MessageState state) {
662             this.requestState = state;
663         }
664 
665         public MessageState getResponseState() {
666             return this.responseState;
667         }
668 
669         public void setResponseState(final MessageState state) {
670             this.responseState = state;
671         }
672 
673         public HttpAsyncRequestConsumer<Object> getRequestConsumer() {
674             return this.requestConsumer;
675         }
676 
677         public void setRequestConsumer(final HttpAsyncRequestConsumer<Object> requestConsumer) {
678             this.requestConsumer = requestConsumer;
679         }
680 
681         public HttpAsyncResponseProducer getResponseProducer() {
682             return this.responseProducer;
683         }
684 
685         public void setResponseProducer(final HttpAsyncResponseProducer responseProducer) {
686             this.responseProducer = responseProducer;
687         }
688 
689         public HttpRequest getRequest() {
690             return this.request;
691         }
692 
693         public void setRequest(final HttpRequest request) {
694             this.request = request;
695         }
696 
697         public HttpResponse getResponse() {
698             return this.response;
699         }
700 
701         public void setResponse(final HttpResponse response) {
702             this.response = response;
703         }
704 
705         public Cancellable getCancellable() {
706             return this.cancellable;
707         }
708 
709         public void setCancellable(final Cancellable cancellable) {
710             this.cancellable = cancellable;
711         }
712 
713         public void reset() {
714             this.context = new BasicHttpContext();
715             this.responseState = MessageState.READY;
716             this.requestState = MessageState.READY;
717             this.requestHandler = null;
718             this.requestConsumer = null;
719             this.responseProducer = null;
720             this.request = null;
721             this.response = null;
722             this.cancellable = null;
723         }
724 
725         @Override
726         public String toString() {
727             final StringBuilder buf = new StringBuilder();
728             buf.append("request state: ");
729             buf.append(this.requestState);
730             buf.append("; request: ");
731             if (this.request != null) {
732                 buf.append(this.request.getRequestLine());
733             }
734             buf.append("; response state: ");
735             buf.append(this.responseState);
736             buf.append("; response: ");
737             if (this.response != null) {
738                 buf.append(this.response.getStatusLine());
739             }
740             buf.append(";");
741             return buf.toString();
742         }
743 
744     }
745 
746     static class Exchange implements HttpAsyncExchange {
747 
748         private final HttpRequest request;
749         private final HttpResponse response;
750         private final State state;
751         private final NHttpServerConnection conn;
752 
753         private volatile boolean completed;
754 
755         public Exchange(
756                 final HttpRequest request,
757                 final HttpResponse response,
758                 final State state,
759                 final NHttpServerConnection conn) {
760             super();
761             this.request = request;
762             this.response = response;
763             this.state = state;
764             this.conn = conn;
765         }
766 
767         public HttpRequest getRequest() {
768             return this.request;
769         }
770 
771         public HttpResponse getResponse() {
772             return this.response;
773         }
774 
775         public void setCallback(final Cancellable cancellable) {
776             synchronized (this) {
777                 Asserts.check(!this.completed, "Response already submitted");
778                 if (this.state.isTerminated() && cancellable != null) {
779                     cancellable.cancel();
780                 } else {
781                     this.state.setCancellable(cancellable);
782                     this.conn.requestInput();
783                 }
784             }
785         }
786 
787         public void submitResponse(final HttpAsyncResponseProducer responseProducer) {
788             Args.notNull(responseProducer, "Response producer");
789             synchronized (this) {
790                 Asserts.check(!this.completed, "Response already submitted");
791                 this.completed = true;
792                 if (!this.state.isTerminated()) {
793                     this.state.setResponseProducer(responseProducer);
794                     this.state.setCancellable(null);
795                     this.conn.requestOutput();
796                 } else {
797                     try {
798                         responseProducer.close();
799                     } catch (final IOException ex) {
800                     }
801                 }
802             }
803         }
804 
805         public void submitResponse() {
806             submitResponse(new BasicAsyncResponseProducer(this.response));
807         }
808 
809         public boolean isCompleted() {
810             return this.completed;
811         }
812 
813         public void setTimeout(final int timeout) {
814             this.conn.setSocketTimeout(timeout);
815         }
816 
817         public int getTimeout() {
818             return this.conn.getSocketTimeout();
819         }
820 
821     }
822 
823     /**
824      * Adaptor class to transition from HttpAsyncRequestHandlerResolver to HttpAsyncRequestHandlerMapper.
825      */
826     @Deprecated
827     private static class HttpAsyncRequestHandlerResolverAdapter implements HttpAsyncRequestHandlerMapper {
828 
829         private final HttpAsyncRequestHandlerResolver resolver;
830 
831         public HttpAsyncRequestHandlerResolverAdapter(final HttpAsyncRequestHandlerResolver resolver) {
832             this.resolver = resolver;
833         }
834 
835         public HttpAsyncRequestHandler<?> lookup(final HttpRequest request) {
836             return resolver.lookup(request.getRequestLine().getUri());
837         }
838 
839     }
840 
841 }