hc-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ol...@apache.org
Subject [2/3] httpcomponents-core git commit: Improved handling of 1xx status messages by the classic transport; server expectation (expect-continue) handshake can now be implemented as a cross-cutting aspect by both the classic and asynchronous transports
Date Mon, 07 Aug 2017 19:13:22 GMT
http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/HttpService.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/HttpService.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/HttpService.java
index ae6057f..3d799b3 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/HttpService.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/HttpService.java
@@ -29,6 +29,7 @@ package org.apache.hc.core5.http.impl.io;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.apache.hc.core5.annotation.Contract;
 import org.apache.hc.core5.annotation.ThreadingBehavior;
@@ -36,7 +37,7 @@ import org.apache.hc.core5.http.ClassicHttpRequest;
 import org.apache.hc.core5.http.ClassicHttpResponse;
 import org.apache.hc.core5.http.ConnectionReuseStrategy;
 import org.apache.hc.core5.http.ContentType;
-import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HeaderElements;
 import org.apache.hc.core5.http.HttpEntity;
 import org.apache.hc.core5.http.HttpException;
 import org.apache.hc.core5.http.HttpHeaders;
@@ -50,10 +51,14 @@ import org.apache.hc.core5.http.ProtocolVersion;
 import org.apache.hc.core5.http.UnsupportedHttpVersionException;
 import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy;
 import org.apache.hc.core5.http.impl.Http1StreamListener;
-import org.apache.hc.core5.http.io.HttpExpectationVerifier;
 import org.apache.hc.core5.http.io.HttpRequestHandler;
 import org.apache.hc.core5.http.io.HttpServerConnection;
+import org.apache.hc.core5.http.io.HttpServerRequestHandler;
 import org.apache.hc.core5.http.io.entity.StringEntity;
+import org.apache.hc.core5.http.io.support.BasicHttpServerExpectationDecorator;
+import org.apache.hc.core5.http.io.support.BasicHttpServerRequestHandler;
+import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
+import org.apache.hc.core5.http.message.MessageSupport;
 import org.apache.hc.core5.http.protocol.HttpContext;
 import org.apache.hc.core5.http.protocol.HttpCoreContext;
 import org.apache.hc.core5.http.protocol.HttpProcessor;
@@ -72,9 +77,6 @@ import org.apache.hc.core5.util.Args;
  * {@code HttpService} uses {@link HttpRequestMapper} to map
  * matching request handler for a particular request URI of an incoming HTTP
  * request.
- * <p>
- * {@code HttpService} can use optional {@link HttpExpectationVerifier}
- * to ensure that incoming requests meet server's expectations.
  *
  * @since 4.0
  */
@@ -82,74 +84,81 @@ import org.apache.hc.core5.util.Args;
 public class HttpService {
 
     private final HttpProcessor processor;
-    private final HttpRequestMapper<HttpRequestHandler> handlerMapper;
+    private final HttpServerRequestHandler requestHandler;
     private final ConnectionReuseStrategy connReuseStrategy;
-    private final HttpResponseFactory<ClassicHttpResponse> responseFactory;
-    private final HttpExpectationVerifier expectationVerifier;
     private final Http1StreamListener streamListener;
 
     /**
      * Create a new HTTP service.
      *
      * @param processor the processor to use on requests and responses
-     * @param connReuseStrategy the connection reuse strategy. If {@code null}
-     *   {@link DefaultConnectionReuseStrategy#INSTANCE} will be used.
+     * @param handlerMapper  the handler mapper
      * @param responseFactory  the response factory. If {@code null}
      *   {@link DefaultClassicHttpResponseFactory#INSTANCE} will be used.
-     * @param handlerMapper  the handler mapper. May be null.
-     * @param expectationVerifier the expectation verifier. May be null.
-     *
-     * @since 4.3
+     * @param connReuseStrategy the connection reuse strategy. If {@code null}
+     *   {@link DefaultConnectionReuseStrategy#INSTANCE} will be used.
+     * @param streamListener message stream listener.
      */
     public HttpService(
             final HttpProcessor processor,
+            final HttpRequestMapper<HttpRequestHandler> handlerMapper,
             final ConnectionReuseStrategy connReuseStrategy,
             final HttpResponseFactory<ClassicHttpResponse> responseFactory,
-            final HttpRequestMapper<HttpRequestHandler> handlerMapper,
-            final HttpExpectationVerifier expectationVerifier,
             final Http1StreamListener streamListener) {
-        super();
-        this.processor =  Args.notNull(processor, "HTTP processor");
-        this.connReuseStrategy = connReuseStrategy != null ? connReuseStrategy :
-            DefaultConnectionReuseStrategy.INSTANCE;
-        this.responseFactory = responseFactory != null ? responseFactory :
-            DefaultClassicHttpResponseFactory.INSTANCE;
-        this.handlerMapper = handlerMapper;
-        this.expectationVerifier = expectationVerifier;
-        this.streamListener = streamListener;
+        this(processor,
+                new BasicHttpServerExpectationDecorator(new BasicHttpServerRequestHandler(handlerMapper, responseFactory)),
+                connReuseStrategy,
+                streamListener);
     }
 
     /**
      * Create a new HTTP service.
      *
      * @param processor the processor to use on requests and responses
+     * @param handlerMapper  the handler mapper
      * @param connReuseStrategy the connection reuse strategy. If {@code null}
      *   {@link DefaultConnectionReuseStrategy#INSTANCE} will be used.
      * @param responseFactory  the response factory. If {@code null}
      *   {@link DefaultClassicHttpResponseFactory#INSTANCE} will be used.
-     * @param handlerMapper  the handler mapper. May be null.
-     *
-     * @since 4.3
      */
     public HttpService(
             final HttpProcessor processor,
+            final HttpRequestMapper<HttpRequestHandler> handlerMapper,
             final ConnectionReuseStrategy connReuseStrategy,
-            final HttpResponseFactory<ClassicHttpResponse> responseFactory,
-            final HttpRequestMapper<HttpRequestHandler> handlerMapper) {
-        this(processor, connReuseStrategy, responseFactory, handlerMapper, null, null);
+            final HttpResponseFactory<ClassicHttpResponse> responseFactory) {
+        this(processor, handlerMapper, connReuseStrategy, responseFactory, null);
     }
 
     /**
      * Create a new HTTP service.
      *
      * @param processor the processor to use on requests and responses
-     * @param handlerMapper  the handler mapper. May be null.
+     * @param requestHandler  the request handler.
+     * @param connReuseStrategy the connection reuse strategy. If {@code null}
+     *   {@link DefaultConnectionReuseStrategy#INSTANCE} will be used.
+     * @param streamListener message stream listener.
+     */
+    public HttpService(
+            final HttpProcessor processor,
+            final HttpServerRequestHandler requestHandler,
+            final ConnectionReuseStrategy connReuseStrategy,
+            final Http1StreamListener streamListener) {
+        super();
+        this.processor =  Args.notNull(processor, "HTTP processor");
+        this.requestHandler =  Args.notNull(requestHandler, "Request handler");
+        this.connReuseStrategy = connReuseStrategy != null ? connReuseStrategy : DefaultConnectionReuseStrategy.INSTANCE;
+        this.streamListener = streamListener;
+    }
+
+    /**
+     * Create a new HTTP service.
      *
-     * @since 4.3
+     * @param processor the processor to use on requests and responses
+     * @param requestHandler  the request handler.
      */
     public HttpService(
-            final HttpProcessor processor, final HttpRequestMapper<HttpRequestHandler> handlerMapper) {
-        this(processor, null, null, handlerMapper);
+            final HttpProcessor processor, final HttpServerRequestHandler requestHandler) {
+        this(processor, requestHandler, null, null);
     }
 
     /**
@@ -166,96 +175,95 @@ public class HttpService {
             final HttpServerConnection conn,
             final HttpContext context) throws IOException, HttpException {
 
-        final ClassicHttpRequest request = conn.receiveRequestHeader();
-        if (streamListener != null) {
-            streamListener.onRequestHead(conn, request);
-        }
-        ClassicHttpResponse response = null;
+        final AtomicBoolean responseSubmitted = new AtomicBoolean(false);
         try {
-            try {
-                conn.receiveRequestEntity(request);
-                final ProtocolVersion transportVersion = request.getVersion();
-                if (transportVersion != null) {
-                    context.setProtocolVersion(transportVersion);
-                }
-                context.setAttribute(HttpCoreContext.SSL_SESSION, conn.getSSLSession());
-                context.setAttribute(HttpCoreContext.CONNECTION_ENDPOINT, conn.getEndpointDetails());
-                context.setAttribute(HttpCoreContext.HTTP_REQUEST, request);
-                this.processor.process(request, request.getEntity(), context);
+            final ClassicHttpRequest request = conn.receiveRequestHeader();
+            if (streamListener != null) {
+                streamListener.onRequestHead(conn, request);
+            }
+            conn.receiveRequestEntity(request);
+            final ProtocolVersion transportVersion = request.getVersion();
+            if (transportVersion != null) {
+                context.setProtocolVersion(transportVersion);
+            }
+            context.setAttribute(HttpCoreContext.SSL_SESSION, conn.getSSLSession());
+            context.setAttribute(HttpCoreContext.CONNECTION_ENDPOINT, conn.getEndpointDetails());
+            context.setAttribute(HttpCoreContext.HTTP_REQUEST, request);
+            this.processor.process(request, request.getEntity(), context);
 
-                final Header expect = request.getFirstHeader(HttpHeaders.EXPECT);
-                final boolean expectContinue = expect != null && "100-continue".equalsIgnoreCase(expect.getValue());
+            this.requestHandler.handle(request, new HttpServerRequestHandler.ResponseTrigger() {
 
-                if (expectContinue) {
-                    final ClassicHttpResponse ack = this.responseFactory.newHttpResponse(HttpStatus.SC_CONTINUE);
-                    if (this.expectationVerifier != null) {
-                        this.expectationVerifier.verify(request, ack, context);
+                @Override
+                public void sendInformation(final ClassicHttpResponse response) throws HttpException, IOException {
+                    if (responseSubmitted.get()) {
+                        throw new HttpException("Response already submitted");
+                    }
+                    if (response.getCode() >= HttpStatus.SC_SUCCESS) {
+                        throw new HttpException("Invalid intermediate response");
+                    }
+                    if (streamListener != null) {
+                        streamListener.onResponseHead(conn, response);
                     }
-                    if (ack.getCode() < HttpStatus.SC_SUCCESS) {
-                        // Send 1xx response indicating the server expectations
-                        // have been met
-                        conn.sendResponseHeader(ack);
+                    conn.sendResponseHeader(response);
+                    conn.flush();
+                }
+
+                @Override
+                public void submitResponse(final ClassicHttpResponse response) throws HttpException, IOException {
+                    try {
+                        context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response);
+                        processor.process(response, response.getEntity(), context);
+
+                        responseSubmitted.set(true);
+                        conn.sendResponseHeader(response);
                         if (streamListener != null) {
-                            streamListener.onResponseHead(conn, ack);
+                            streamListener.onResponseHead(conn, response);
+                        }
+                        if (MessageSupport.canResponseHaveBody(request.getMethod(), response)) {
+                            conn.sendResponseEntity(response);
+                        }
+                        // Make sure the request content is fully consumed
+                        final HttpEntity entity = request.getEntity();
+                        if (entity != null && entity.isStreaming()) {
+                            final InputStream instream = entity.getContent();
+                            if (instream != null) {
+                                instream.close();
+                            }
+                        }
+                        final boolean keepAlive = connReuseStrategy.keepAlive(request, response, context);
+                        if (streamListener != null) {
+                            streamListener.onExchangeComplete(conn, keepAlive);
+                        }
+                        if (!keepAlive) {
+                            conn.close();
                         }
                         conn.flush();
-                    } else {
-                        response = ack;
+                    } finally {
+                        response.close();
                     }
                 }
-                if (response == null) {
-                    response = this.responseFactory.newHttpResponse(HttpStatus.SC_OK);
-                    doService(request, response, context);
-                }
-            } catch (final HttpException ex) {
-                if (response != null) {
-                    response.close();
-                }
-                response = this.responseFactory.newHttpResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR);
-                handleException(ex, response);
-            }
-            context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response);
-            this.processor.process(response, response.getEntity(), context);
 
-            conn.sendResponseHeader(response);
-            if (streamListener != null) {
-                streamListener.onResponseHead(conn, response);
-            }
-            if (canResponseHaveBody(request, response)) {
-                conn.sendResponseEntity(response);
-            }
-            conn.flush();
+            }, context);
 
-            // Make sure the request content is fully consumed
-            final HttpEntity entity = request.getEntity();
-            if (entity != null && entity.isStreaming()) {
-                final InputStream instream = entity.getContent();
-                if (instream != null) {
-                    instream.close();
+        } catch (final HttpException ex) {
+            if (responseSubmitted.get()) {
+                throw ex;
+            } else {
+                try (final ClassicHttpResponse errorResponse = new BasicClassicHttpResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR)) {
+                    handleException(ex, errorResponse);
+                    errorResponse.setHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE);
+                    context.setAttribute(HttpCoreContext.HTTP_RESPONSE, errorResponse);
+                    this.processor.process(errorResponse, errorResponse.getEntity(), context);
+
+                    conn.sendResponseHeader(errorResponse);
+                    if (streamListener != null) {
+                        streamListener.onResponseHead(conn, errorResponse);
+                    }
+                    conn.sendResponseEntity(errorResponse);
+                    conn.close();
                 }
             }
-            final boolean keepAlive = this.connReuseStrategy.keepAlive(request, response, context);
-            if (streamListener != null) {
-                streamListener.onExchangeComplete(conn, keepAlive);
-            }
-            if (!keepAlive) {
-                conn.close();
-            }
-        } finally {
-            if (response != null) {
-                response.close();
-            }
-        }
-    }
-
-    private boolean canResponseHaveBody(final ClassicHttpRequest request, final ClassicHttpResponse response) {
-        if (request != null && "HEAD".equalsIgnoreCase(request.getMethod())) {
-            return false;
         }
-        final int status = response.getCode();
-        return status >= HttpStatus.SC_SUCCESS
-                && status != HttpStatus.SC_NO_CONTENT
-                && status != HttpStatus.SC_NOT_MODIFIED;
     }
 
     /**
@@ -291,36 +299,4 @@ public class HttpService {
         return code;
     }
 
-    /**
-     * The default implementation of this method attempts to resolve an
-     * {@link HttpRequestHandler} for the request URI of the given request
-     * and, if found, executes its
-     * {@link HttpRequestHandler#handle(ClassicHttpRequest, ClassicHttpResponse, HttpContext)}
-     * method.
-     * <p>
-     * Super-classes can override this method in order to provide a custom
-     * implementation of the request processing logic.
-     *
-     * @param request the HTTP request.
-     * @param response the HTTP response.
-     * @param context the execution context.
-     * @throws IOException in case of an I/O error.
-     * @throws HttpException in case of HTTP protocol violation or a processing
-     *   problem.
-     */
-    protected void doService(
-            final ClassicHttpRequest request,
-            final ClassicHttpResponse response,
-            final HttpContext context) throws HttpException, IOException {
-        HttpRequestHandler handler = null;
-        if (this.handlerMapper != null) {
-            handler = this.handlerMapper.resolve(request, context);
-        }
-        if (handler != null) {
-            handler.handle(request, response, context);
-        } else {
-            response.setCode(HttpStatus.SC_NOT_IMPLEMENTED);
-        }
-    }
-
 }

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamHandler.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamHandler.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamHandler.java
index c5394a2..fc87b11 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamHandler.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamHandler.java
@@ -48,7 +48,6 @@ import org.apache.hc.core5.http.impl.LazyEntityDetails;
 import org.apache.hc.core5.http.nio.AsyncClientExchangeHandler;
 import org.apache.hc.core5.http.nio.CapacityChannel;
 import org.apache.hc.core5.http.nio.DataStreamChannel;
-import org.apache.hc.core5.http.nio.HttpContextAware;
 import org.apache.hc.core5.http.nio.RequestChannel;
 import org.apache.hc.core5.http.nio.ResourceHolder;
 import org.apache.hc.core5.http.protocol.HttpCoreContext;
@@ -150,10 +149,6 @@ class ClientHttp1StreamHandler implements ResourceHolder {
             context.setProtocolVersion(transportVersion);
             context.setAttribute(HttpCoreContext.HTTP_REQUEST, request);
 
-            if (exchangeHandler instanceof HttpContextAware) {
-                ((HttpContextAware) exchangeHandler).setContext(context);
-            }
-
             httpProcessor.process(request, entityDetails, context);
 
             final boolean endStream = entityDetails == null;
@@ -214,7 +209,7 @@ class ClientHttp1StreamHandler implements ResourceHolder {
         if (status < HttpStatus.SC_INFORMATIONAL) {
             throw new ProtocolException("Invalid response: " + status);
         }
-        if (status < HttpStatus.SC_SUCCESS) {
+        if (status > HttpStatus.SC_CONTINUE && status < HttpStatus.SC_SUCCESS) {
             exchangeHandler.consumeInformation(response);
         } else {
             if (!connectionReuseStrategy.keepAlive(committedRequest, response, context)) {

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamHandler.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamHandler.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamHandler.java
index 2dd95e7..9c8c6a3 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamHandler.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamHandler.java
@@ -53,7 +53,6 @@ import org.apache.hc.core5.http.nio.BasicResponseProducer;
 import org.apache.hc.core5.http.nio.CapacityChannel;
 import org.apache.hc.core5.http.nio.DataStreamChannel;
 import org.apache.hc.core5.http.nio.HandlerFactory;
-import org.apache.hc.core5.http.nio.HttpContextAware;
 import org.apache.hc.core5.http.nio.ResourceHolder;
 import org.apache.hc.core5.http.nio.ResponseChannel;
 import org.apache.hc.core5.http.nio.support.ImmediateResponseExchangeHandler;
@@ -257,10 +256,6 @@ class ServerHttp1StreamHandler implements ResourceHolder {
         context.setProtocolVersion(transportVersion);
         context.setAttribute(HttpCoreContext.HTTP_REQUEST, request);
 
-        if (exchangeHandler instanceof HttpContextAware) {
-            ((HttpContextAware) exchangeHandler).setContext(context);
-        }
-
         final EntityDetails requestEntityDetails = requestEndStream ? null : new LazyEntityDetails(request);
         final ResponseChannel responseChannel = new ResponseChannel() {
 
@@ -285,12 +280,12 @@ class ServerHttp1StreamHandler implements ResourceHolder {
         };
         try {
             httpProcessor.process(request, requestEntityDetails, context);
-            exchangeHandler.handleRequest(request, requestEntityDetails, responseChannel);
+            exchangeHandler.handleRequest(request, requestEntityDetails, responseChannel, context);
         } catch (final HttpException ex) {
             if (!responseCommitted.get()) {
                 final AsyncResponseProducer responseProducer = handleException(ex);
                 exchangeHandler = new ImmediateResponseExchangeHandler(responseProducer);
-                exchangeHandler.handleRequest(request, requestEntityDetails, responseChannel);
+                exchangeHandler.handleRequest(request, requestEntityDetails, responseChannel, context);
             } else {
                 throw ex;
             }

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpClientResponseHandler.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpClientResponseHandler.java b/httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpClientResponseHandler.java
new file mode 100644
index 0000000..1db8c98
--- /dev/null
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpClientResponseHandler.java
@@ -0,0 +1,54 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.core5.http.io;
+
+import java.io.IOException;
+
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.HttpException;
+
+/**
+ * Handler that encapsulates the process of generating a response object
+ * from a {@link ClassicHttpResponse}.
+ *
+ *
+ * @since 4.0
+ */
+public interface HttpClientResponseHandler<T> {
+
+    /**
+     * Processes an {@link ClassicHttpResponse} and returns some value
+     * corresponding to that response.
+     *
+     * @param response The response to process
+     * @return A value determined by the response
+     *
+     * @throws IOException in case of a problem or the connection was aborted
+     */
+    T handleResponse(ClassicHttpResponse response) throws HttpException, IOException;
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpExpectationVerifier.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpExpectationVerifier.java b/httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpExpectationVerifier.java
deleted file mode 100644
index 7dc079a..0000000
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpExpectationVerifier.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package org.apache.hc.core5.http.io;
-
-import org.apache.hc.core5.http.ClassicHttpRequest;
-import org.apache.hc.core5.http.ClassicHttpResponse;
-import org.apache.hc.core5.http.HttpException;
-import org.apache.hc.core5.http.protocol.HttpContext;
-
-/**
- * Defines an interface to verify whether an incoming HTTP request meets
- * the target server's expectations.
- *
- * @since 4.0
- */
-public interface HttpExpectationVerifier {
-
-    /**
-     * Verifies whether the given request meets the server's expectations.
-     * <p>
-     * If the request fails to meet particular criteria, this method can
-     * trigger a terminal response back to the client by setting the status
-     * code of the response object to a value greater or equal to
-     * {@code 200}. In this case the client will not have to transmit
-     * the request body. If the request meets expectations this method can
-     * terminate without modifying the response object. Per default the status
-     * code of the response object will be set to {@code 100}.
-     *
-     * @param request the HTTP request.
-     * @param response the HTTP response.
-     * @param context the HTTP context.
-     * @throws HttpException in case of an HTTP protocol violation.
-     */
-    void verify(ClassicHttpRequest request, ClassicHttpResponse response, HttpContext context)
-            throws HttpException;
-
-}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpResponseInformationCallback.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpResponseInformationCallback.java b/httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpResponseInformationCallback.java
new file mode 100644
index 0000000..76e5c97
--- /dev/null
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpResponseInformationCallback.java
@@ -0,0 +1,44 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.hc.core5.http.io;
+
+import org.apache.hc.core5.http.HttpConnection;
+import org.apache.hc.core5.http.HttpException;
+import org.apache.hc.core5.http.HttpResponse;
+import org.apache.hc.core5.http.protocol.HttpContext;
+
+/**
+ * Information message callback.
+ *
+ * @since 5.0
+ */
+public interface HttpResponseInformationCallback {
+
+    void execute(HttpResponse response, HttpConnection connection, HttpContext context) throws HttpException;
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpServerRequestHandler.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpServerRequestHandler.java b/httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpServerRequestHandler.java
new file mode 100644
index 0000000..648b781
--- /dev/null
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpServerRequestHandler.java
@@ -0,0 +1,57 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.core5.http.io;
+
+import java.io.IOException;
+
+import org.apache.hc.core5.annotation.Contract;
+import org.apache.hc.core5.annotation.ThreadingBehavior;
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.HttpException;
+import org.apache.hc.core5.http.protocol.HttpContext;
+
+/**
+ * @since 5.0
+ */
+@Contract(threading = ThreadingBehavior.STATELESS)
+public interface HttpServerRequestHandler {
+
+    interface ResponseTrigger {
+
+        void sendInformation(ClassicHttpResponse response) throws HttpException, IOException;
+
+        void submitResponse(ClassicHttpResponse response) throws HttpException, IOException;
+
+    }
+
+    void handle(
+            ClassicHttpRequest request,
+            ResponseTrigger responseTrigger,
+            HttpContext context) throws HttpException, IOException;
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/io/ResponseHandler.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/io/ResponseHandler.java b/httpcore5/src/main/java/org/apache/hc/core5/http/io/ResponseHandler.java
deleted file mode 100644
index 2782930..0000000
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/io/ResponseHandler.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-package org.apache.hc.core5.http.io;
-
-import java.io.IOException;
-
-import org.apache.hc.core5.http.ClassicHttpResponse;
-import org.apache.hc.core5.http.HttpException;
-
-/**
- * Handler that encapsulates the process of generating a response object
- * from a {@link ClassicHttpResponse}.
- *
- *
- * @since 4.0
- */
-public interface ResponseHandler<T> {
-
-    /**
-     * Processes an {@link ClassicHttpResponse} and returns some value
-     * corresponding to that response.
-     *
-     * @param response The response to process
-     * @return A value determined by the response
-     *
-     * @throws IOException in case of a problem or the connection was aborted
-     */
-    T handleResponse(ClassicHttpResponse response) throws HttpException, IOException;
-
-}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/io/support/BasicHttpServerExpectationDecorator.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/io/support/BasicHttpServerExpectationDecorator.java b/httpcore5/src/main/java/org/apache/hc/core5/http/io/support/BasicHttpServerExpectationDecorator.java
new file mode 100644
index 0000000..1920a4c
--- /dev/null
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/io/support/BasicHttpServerExpectationDecorator.java
@@ -0,0 +1,76 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.hc.core5.http.io.support;
+
+import java.io.IOException;
+
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpException;
+import org.apache.hc.core5.http.HttpHeaders;
+import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.io.HttpServerRequestHandler;
+import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
+import org.apache.hc.core5.http.protocol.HttpContext;
+import org.apache.hc.core5.util.Args;
+
+/**
+ * @since 5.0
+ */
+public class BasicHttpServerExpectationDecorator implements HttpServerRequestHandler {
+
+    private final HttpServerRequestHandler requestHandler;
+
+    public BasicHttpServerExpectationDecorator(final HttpServerRequestHandler requestHandler) {
+        this.requestHandler = Args.notNull(requestHandler, "Request handler");
+    }
+
+    protected ClassicHttpResponse verify(final ClassicHttpRequest request, final HttpContext context) {
+        return null;
+    }
+
+    @Override
+    public final void handle(
+            final ClassicHttpRequest request,
+            final ResponseTrigger responseTrigger,
+            final HttpContext context) throws HttpException, IOException {
+        final Header expect = request.getFirstHeader(HttpHeaders.EXPECT);
+        if (expect != null && "100-continue".equalsIgnoreCase(expect.getValue())) {
+            final ClassicHttpResponse response = verify(request, context);
+            if (response == null) {
+                responseTrigger.sendInformation(new BasicClassicHttpResponse(HttpStatus.SC_CONTINUE));
+            } else {
+                responseTrigger.submitResponse(response);
+                return;
+            }
+        }
+        requestHandler.handle(request, responseTrigger, context);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/io/support/BasicHttpServerRequestHandler.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/io/support/BasicHttpServerRequestHandler.java b/httpcore5/src/main/java/org/apache/hc/core5/http/io/support/BasicHttpServerRequestHandler.java
new file mode 100644
index 0000000..1fa9a47
--- /dev/null
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/io/support/BasicHttpServerRequestHandler.java
@@ -0,0 +1,78 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.hc.core5.http.io.support;
+
+import java.io.IOException;
+
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.HttpException;
+import org.apache.hc.core5.http.HttpRequestMapper;
+import org.apache.hc.core5.http.HttpResponseFactory;
+import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.impl.io.DefaultClassicHttpResponseFactory;
+import org.apache.hc.core5.http.io.HttpRequestHandler;
+import org.apache.hc.core5.http.io.HttpServerRequestHandler;
+import org.apache.hc.core5.http.protocol.HttpContext;
+import org.apache.hc.core5.util.Args;
+
+/**
+ * @since 5.0
+ */
+public class BasicHttpServerRequestHandler implements HttpServerRequestHandler {
+
+    private final HttpRequestMapper<HttpRequestHandler> handlerMapper;
+    private final HttpResponseFactory<ClassicHttpResponse> responseFactory;
+
+    public BasicHttpServerRequestHandler(
+            final HttpRequestMapper<HttpRequestHandler> handlerMapper,
+            final HttpResponseFactory<ClassicHttpResponse> responseFactory) {
+        this.handlerMapper = Args.notNull(handlerMapper, "Handler mapper");
+        this.responseFactory = responseFactory != null ? responseFactory : DefaultClassicHttpResponseFactory.INSTANCE;
+    }
+
+    public BasicHttpServerRequestHandler(final HttpRequestMapper<HttpRequestHandler> handlerMapper) {
+        this(handlerMapper, null);
+    }
+
+    @Override
+    public void handle(
+            final ClassicHttpRequest request,
+            final ResponseTrigger responseTrigger,
+            final HttpContext context) throws HttpException, IOException {
+        final ClassicHttpResponse response = responseFactory.newHttpResponse(HttpStatus.SC_OK);
+        final HttpRequestHandler handler = handlerMapper != null ? handlerMapper.resolve(request, context) : null;
+        if (handler != null) {
+            handler.handle(request, response, context);
+        } else {
+            response.setCode(HttpStatus.SC_NOT_IMPLEMENTED);
+        }
+        responseTrigger.submitResponse(response);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncServerExchangeHandler.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncServerExchangeHandler.java b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncServerExchangeHandler.java
index 4f0758e..3c75820 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncServerExchangeHandler.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncServerExchangeHandler.java
@@ -31,6 +31,7 @@ import java.io.IOException;
 import org.apache.hc.core5.http.EntityDetails;
 import org.apache.hc.core5.http.HttpException;
 import org.apache.hc.core5.http.HttpRequest;
+import org.apache.hc.core5.http.protocol.HttpContext;
 
 /**
  * Abstract asynchronous server side message exchange handler that acts as a request consumer
@@ -43,6 +44,7 @@ public interface AsyncServerExchangeHandler extends AsyncDataExchangeHandler {
     void handleRequest(
             HttpRequest request,
             EntityDetails entityDetails,
-            ResponseChannel responseChannel) throws HttpException, IOException;
+            ResponseChannel responseChannel,
+            HttpContext context) throws HttpException, IOException;
 
 }

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncServerRequestHandler.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncServerRequestHandler.java b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncServerRequestHandler.java
index a59471e..8ae9e29 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncServerRequestHandler.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncServerRequestHandler.java
@@ -32,6 +32,7 @@ import org.apache.hc.core5.annotation.Contract;
 import org.apache.hc.core5.annotation.ThreadingBehavior;
 import org.apache.hc.core5.http.HttpException;
 import org.apache.hc.core5.http.HttpRequest;
+import org.apache.hc.core5.http.HttpResponse;
 import org.apache.hc.core5.http.protocol.HttpContext;
 
 /**
@@ -40,8 +41,18 @@ import org.apache.hc.core5.http.protocol.HttpContext;
 @Contract(threading = ThreadingBehavior.STATELESS)
 public interface AsyncServerRequestHandler<T> {
 
+    interface ResponseTrigger {
+
+        void sendInformation(HttpResponse response) throws HttpException, IOException;
+
+        void submitResponse(AsyncResponseProducer responseProducer) throws HttpException, IOException;
+
+        void pushPromise(HttpRequest promise, AsyncPushProducer responseProducer) throws HttpException, IOException;
+
+    }
+
     AsyncRequestConsumer<T> prepare(HttpRequest request, HttpContext context) throws HttpException;
 
-    void handle(T requestMessage, AsyncServerResponseTrigger responseTrigger, HttpContext context) throws HttpException, IOException;
+    void handle(T requestMessage, ResponseTrigger responseTrigger, HttpContext context) throws HttpException, IOException;
 
 }

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncServerResponseTrigger.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncServerResponseTrigger.java b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncServerResponseTrigger.java
deleted file mode 100644
index db2e42a..0000000
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncServerResponseTrigger.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-package org.apache.hc.core5.http.nio;
-
-import java.io.IOException;
-
-import org.apache.hc.core5.annotation.Contract;
-import org.apache.hc.core5.annotation.ThreadingBehavior;
-import org.apache.hc.core5.http.HttpException;
-import org.apache.hc.core5.http.HttpRequest;
-import org.apache.hc.core5.http.HttpResponse;
-
-/**
- * @since 5.0
- */
-@Contract(threading = ThreadingBehavior.SAFE)
-public interface AsyncServerResponseTrigger {
-
-    void sendInformation(HttpResponse response) throws HttpException, IOException;
-
-    void submitResponse(AsyncResponseProducer responseProducer) throws HttpException, IOException;
-
-    void pushPromise(HttpRequest promise, AsyncPushProducer responseProducer) throws HttpException, IOException;
-
-}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/nio/HttpContextAware.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/HttpContextAware.java b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/HttpContextAware.java
deleted file mode 100644
index 3e920f1..0000000
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/HttpContextAware.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-package org.apache.hc.core5.http.nio;
-
-import org.apache.hc.core5.http.protocol.HttpContext;
-
-/**
- * {@link HttpContext} aware protocol handler.
- *
- * @since 5.0
- */
-public interface HttpContextAware {
-
-    void setContext(HttpContext context);
-
-}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AbstractClassicServerExchangeHandler.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AbstractClassicServerExchangeHandler.java b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AbstractClassicServerExchangeHandler.java
index 75a9101..233a9e6 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AbstractClassicServerExchangeHandler.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AbstractClassicServerExchangeHandler.java
@@ -47,7 +47,6 @@ import org.apache.hc.core5.http.HttpStatus;
 import org.apache.hc.core5.http.ProtocolVersion;
 import org.apache.hc.core5.http.message.BasicHttpResponse;
 import org.apache.hc.core5.http.message.HttpResponseWrapper;
-import org.apache.hc.core5.http.nio.HttpContextAware;
 import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
 import org.apache.hc.core5.http.nio.CapacityChannel;
 import org.apache.hc.core5.http.nio.DataStreamChannel;
@@ -63,7 +62,7 @@ import org.apache.hc.core5.util.Asserts;
 /**
  * @since 5.0
  */
-public abstract class AbstractClassicServerExchangeHandler implements HttpContextAware, AsyncServerExchangeHandler {
+public abstract class AbstractClassicServerExchangeHandler implements AsyncServerExchangeHandler {
 
     private enum State { IDLE, ACTIVE, COMPLETED }
 
@@ -72,7 +71,6 @@ public abstract class AbstractClassicServerExchangeHandler implements HttpContex
     private final AtomicReference<State> state;
     private final AtomicReference<Exception> exception;
 
-    private volatile HttpContext context;
     private volatile SharedInputBuffer inputBuffer;
     private volatile SharedOutputBuffer outputBuffer;
 
@@ -87,11 +85,6 @@ public abstract class AbstractClassicServerExchangeHandler implements HttpContex
         return exception.get();
     }
 
-    @Override
-    public void setContext(final HttpContext context) {
-        this.context = context;
-    }
-
     protected abstract void handle(
             HttpRequest request, InputStream requestStream,
             HttpResponse response, OutputStream responseStream,
@@ -101,14 +94,8 @@ public abstract class AbstractClassicServerExchangeHandler implements HttpContex
     public final void handleRequest(
             final HttpRequest request,
             final EntityDetails entityDetails,
-            final ResponseChannel responseChannel) throws HttpException, IOException {
-
-        if (entityDetails != null) {
-            final Header h = request.getFirstHeader(HttpHeaders.EXPECT);
-            if (h != null && "100-continue".equalsIgnoreCase(h.getValue())) {
-                responseChannel.sendInformation(new BasicHttpResponse(HttpStatus.SC_CONTINUE));
-            }
-        }
+            final ResponseChannel responseChannel,
+            final HttpContext context) throws HttpException, IOException {
         final AtomicBoolean responseCommitted = new AtomicBoolean(false);
 
         final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK);

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AbstractServerExchangeHandler.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AbstractServerExchangeHandler.java b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AbstractServerExchangeHandler.java
index 9a09237..88b13d4 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AbstractServerExchangeHandler.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AbstractServerExchangeHandler.java
@@ -35,20 +35,17 @@ import org.apache.hc.core5.concurrent.FutureCallback;
 import org.apache.hc.core5.http.EntityDetails;
 import org.apache.hc.core5.http.Header;
 import org.apache.hc.core5.http.HttpException;
-import org.apache.hc.core5.http.HttpHeaders;
 import org.apache.hc.core5.http.HttpRequest;
 import org.apache.hc.core5.http.HttpResponse;
 import org.apache.hc.core5.http.HttpStatus;
-import org.apache.hc.core5.http.message.BasicHttpResponse;
 import org.apache.hc.core5.http.nio.AsyncPushProducer;
 import org.apache.hc.core5.http.nio.AsyncRequestConsumer;
 import org.apache.hc.core5.http.nio.AsyncResponseProducer;
 import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
-import org.apache.hc.core5.http.nio.AsyncServerResponseTrigger;
+import org.apache.hc.core5.http.nio.AsyncServerRequestHandler;
 import org.apache.hc.core5.http.nio.BasicResponseProducer;
 import org.apache.hc.core5.http.nio.CapacityChannel;
 import org.apache.hc.core5.http.nio.DataStreamChannel;
-import org.apache.hc.core5.http.nio.HttpContextAware;
 import org.apache.hc.core5.http.nio.ResponseChannel;
 import org.apache.hc.core5.http.protocol.HttpContext;
 import org.apache.hc.core5.util.Asserts;
@@ -56,66 +53,38 @@ import org.apache.hc.core5.util.Asserts;
 /**
  * @since 5.0
  */
-public abstract class AbstractServerExchangeHandler<T> implements HttpContextAware, AsyncServerExchangeHandler {
+public abstract class AbstractServerExchangeHandler<T> implements AsyncServerExchangeHandler {
 
     private final AtomicReference<AsyncRequestConsumer<T>> requestConsumerRef;
     private final AtomicReference<AsyncResponseProducer> responseProducerRef;
 
-    private volatile HttpContext context;
-    private volatile boolean expectationFailed;
-
     public AbstractServerExchangeHandler() {
         this.requestConsumerRef = new AtomicReference<>(null);
         this.responseProducerRef = new AtomicReference<>(null);
     }
 
-    protected AsyncResponseProducer verify(
-            final HttpRequest request,
-            final HttpContext context) throws IOException, HttpException {
-        return null;
-    }
-
     protected abstract AsyncRequestConsumer<T> supplyConsumer(
             HttpRequest request,
             HttpContext context) throws HttpException;
 
     protected abstract void handle(
             T requestMessage,
-            AsyncServerResponseTrigger responseTrigger,
+            AsyncServerRequestHandler.ResponseTrigger responseTrigger,
             HttpContext context) throws HttpException, IOException;
 
     @Override
-    public void setContext(final HttpContext context) {
-        this.context = context;
-    }
-
-    @Override
     public final void handleRequest(
             final HttpRequest request,
             final EntityDetails entityDetails,
-            final ResponseChannel responseChannel) throws HttpException, IOException {
+            final ResponseChannel responseChannel,
+            final HttpContext context) throws HttpException, IOException {
 
         final AsyncRequestConsumer<T> requestConsumer = supplyConsumer(request, context);
         if (requestConsumer == null) {
             throw new HttpException("Unable to handle request");
         }
         requestConsumerRef.set(requestConsumer);
-
-        if (entityDetails != null) {
-            final Header h = request.getFirstHeader(HttpHeaders.EXPECT);
-            if (h != null && "100-continue".equalsIgnoreCase(h.getValue())) {
-                final AsyncResponseProducer producer = verify(request, context);
-                if (producer != null) {
-                    expectationFailed = true;
-                    responseProducerRef.set(producer);
-                    producer.sendResponse(responseChannel);
-                    return;
-                } else {
-                    responseChannel.sendInformation(new BasicHttpResponse(HttpStatus.SC_CONTINUE));
-                }
-            }
-        }
-        final AsyncServerResponseTrigger responseTrigger = new AsyncServerResponseTrigger() {
+        final AsyncServerRequestHandler.ResponseTrigger responseTrigger = new AsyncServerRequestHandler.ResponseTrigger() {
 
             @Override
             public void sendInformation(final HttpResponse response) throws HttpException, IOException {
@@ -176,33 +145,23 @@ public abstract class AbstractServerExchangeHandler<T> implements HttpContextAwa
 
     @Override
     public final void updateCapacity(final CapacityChannel capacityChannel) throws IOException {
-        if (!expectationFailed) {
-            final AsyncRequestConsumer<T> requestConsumer = requestConsumerRef.get();
-            Asserts.notNull(requestConsumer, "Data consumer");
-            requestConsumer.updateCapacity(capacityChannel);
-        } else {
-            capacityChannel.update(Integer.MAX_VALUE);
-        }
+        final AsyncRequestConsumer<T> requestConsumer = requestConsumerRef.get();
+        Asserts.notNull(requestConsumer, "Data consumer");
+        requestConsumer.updateCapacity(capacityChannel);
     }
 
     @Override
     public final int consume(final ByteBuffer src) throws IOException {
-        if (!expectationFailed) {
-            final AsyncRequestConsumer<T> requestConsumer = requestConsumerRef.get();
-            Asserts.notNull(requestConsumer, "Data consumer");
-            return requestConsumer.consume(src);
-        } else {
-            return Integer.MAX_VALUE;
-        }
+        final AsyncRequestConsumer<T> requestConsumer = requestConsumerRef.get();
+        Asserts.notNull(requestConsumer, "Data consumer");
+        return requestConsumer.consume(src);
     }
 
     @Override
     public final void streamEnd(final List<? extends Header> trailers) throws HttpException, IOException {
-        if (!expectationFailed) {
-            final AsyncRequestConsumer<T> requestConsumer = requestConsumerRef.get();
-            Asserts.notNull(requestConsumer, "Data consumer");
-            requestConsumer.streamEnd(trailers);
-        }
+        final AsyncRequestConsumer<T> requestConsumer = requestConsumerRef.get();
+        Asserts.notNull(requestConsumer, "Data consumer");
+        requestConsumer.streamEnd(trailers);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/BasicAsyncServerExpectationDecorator.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/BasicAsyncServerExpectationDecorator.java b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/BasicAsyncServerExpectationDecorator.java
new file mode 100644
index 0000000..59dcfd7
--- /dev/null
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/BasicAsyncServerExpectationDecorator.java
@@ -0,0 +1,160 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.core5.http.nio.support;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.hc.core5.http.EntityDetails;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpException;
+import org.apache.hc.core5.http.HttpHeaders;
+import org.apache.hc.core5.http.HttpRequest;
+import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.message.BasicHttpResponse;
+import org.apache.hc.core5.http.nio.AsyncResponseProducer;
+import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
+import org.apache.hc.core5.http.nio.CapacityChannel;
+import org.apache.hc.core5.http.nio.DataStreamChannel;
+import org.apache.hc.core5.http.nio.ResponseChannel;
+import org.apache.hc.core5.http.protocol.HttpContext;
+import org.apache.hc.core5.util.Args;
+
+/**
+ * @since 5.0
+ */
+public class BasicAsyncServerExpectationDecorator implements AsyncServerExchangeHandler {
+
+    private final AsyncServerExchangeHandler handler;
+    private final AtomicReference<AsyncResponseProducer> responseProducerRef;
+
+    public BasicAsyncServerExpectationDecorator(final AsyncServerExchangeHandler handler) {
+        this.handler = Args.notNull(handler, "Handler");
+        this.responseProducerRef = new AtomicReference<>(null);
+    }
+
+    protected AsyncResponseProducer verify(
+            final HttpRequest request,
+            final HttpContext context) throws IOException, HttpException {
+        return null;
+    }
+
+    @Override
+    public final void handleRequest(
+            final HttpRequest request,
+            final EntityDetails entityDetails,
+            final ResponseChannel responseChannel,
+            final HttpContext context) throws HttpException, IOException {
+        if (entityDetails != null) {
+            final Header h = request.getFirstHeader(HttpHeaders.EXPECT);
+            if (h != null && "100-continue".equalsIgnoreCase(h.getValue())) {
+                final AsyncResponseProducer producer = verify(request, context);
+                if (producer != null) {
+                    responseProducerRef.set(producer);
+                    producer.sendResponse(responseChannel);
+                    return;
+                } else {
+                    responseChannel.sendInformation(new BasicHttpResponse(HttpStatus.SC_CONTINUE));
+                }
+            }
+        }
+        handler.handleRequest(request, entityDetails, responseChannel, context);
+    }
+
+    @Override
+    public final void updateCapacity(final CapacityChannel capacityChannel) throws IOException {
+        final AsyncResponseProducer responseProducer = responseProducerRef.get();
+        if (responseProducer == null) {
+            handler.updateCapacity(capacityChannel);
+        } else {
+            capacityChannel.update(Integer.MAX_VALUE);
+        }
+    }
+
+    @Override
+    public final int consume(final ByteBuffer src) throws IOException {
+        final AsyncResponseProducer responseProducer = responseProducerRef.get();
+        if (responseProducer == null) {
+            return handler.consume(src);
+        } else {
+            return Integer.MAX_VALUE;
+        }
+    }
+
+    @Override
+    public final void streamEnd(final List<? extends Header> trailers) throws HttpException, IOException {
+        final AsyncResponseProducer responseProducer = responseProducerRef.get();
+        if (responseProducer == null) {
+            handler.streamEnd(trailers);
+        }
+    }
+
+    @Override
+    public final int available() {
+        final AsyncResponseProducer responseProducer = responseProducerRef.get();
+        if (responseProducer == null) {
+            return handler.available();
+        } else {
+            return responseProducer.available();
+        }
+    }
+
+    @Override
+    public final void produce(final DataStreamChannel channel) throws IOException {
+        final AsyncResponseProducer responseProducer = responseProducerRef.get();
+        if (responseProducer == null) {
+            handler.produce(channel);
+        } else {
+            responseProducer.produce(channel);
+        }
+    }
+
+    @Override
+    public final void failed(final Exception cause) {
+        try {
+            handler.failed(cause);
+            final AsyncResponseProducer dataProducer = responseProducerRef.getAndSet(null);
+            if (dataProducer != null) {
+                dataProducer.failed(cause);
+            }
+        } finally {
+            releaseResources();
+        }
+    }
+
+    @Override
+    public final void releaseResources() {
+        handler.releaseResources();
+        final AsyncResponseProducer dataProducer = responseProducerRef.getAndSet(null);
+        if (dataProducer != null) {
+            dataProducer.releaseResources();
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/BasicServerExchangeHandler.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/BasicServerExchangeHandler.java b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/BasicServerExchangeHandler.java
index e6ae855..555cc65 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/BasicServerExchangeHandler.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/BasicServerExchangeHandler.java
@@ -32,7 +32,6 @@ import org.apache.hc.core5.http.HttpException;
 import org.apache.hc.core5.http.HttpRequest;
 import org.apache.hc.core5.http.nio.AsyncRequestConsumer;
 import org.apache.hc.core5.http.nio.AsyncServerRequestHandler;
-import org.apache.hc.core5.http.nio.AsyncServerResponseTrigger;
 import org.apache.hc.core5.http.protocol.HttpContext;
 import org.apache.hc.core5.util.Args;
 
@@ -58,7 +57,7 @@ public class BasicServerExchangeHandler<T> extends AbstractServerExchangeHandler
     @Override
     protected void handle(
             final T requestMessage,
-            final AsyncServerResponseTrigger responseTrigger,
+            final AsyncServerRequestHandler.ResponseTrigger responseTrigger,
             final HttpContext context) throws HttpException, IOException {
         requestHandler.handle(requestMessage, responseTrigger, context);
     }

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/DefaultAsyncResponseExchangeHandlerFactory.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/DefaultAsyncResponseExchangeHandlerFactory.java b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/DefaultAsyncResponseExchangeHandlerFactory.java
index 3e39cbd..28d0ee5 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/DefaultAsyncResponseExchangeHandlerFactory.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/DefaultAsyncResponseExchangeHandlerFactory.java
@@ -26,6 +26,7 @@
  */
 package org.apache.hc.core5.http.nio.support;
 
+import org.apache.hc.core5.function.Decorator;
 import org.apache.hc.core5.function.Supplier;
 import org.apache.hc.core5.http.HttpException;
 import org.apache.hc.core5.http.HttpRequest;
@@ -43,13 +44,20 @@ import org.apache.hc.core5.util.Args;
 public final class DefaultAsyncResponseExchangeHandlerFactory implements HandlerFactory<AsyncServerExchangeHandler> {
 
     private final HttpRequestMapper<Supplier<AsyncServerExchangeHandler>> mapper;
+    private final Decorator<AsyncServerExchangeHandler> decorator;
 
-    public DefaultAsyncResponseExchangeHandlerFactory(final HttpRequestMapper<Supplier<AsyncServerExchangeHandler>> mapper) {
+    public DefaultAsyncResponseExchangeHandlerFactory(
+            final HttpRequestMapper<Supplier<AsyncServerExchangeHandler>> mapper,
+            final Decorator<AsyncServerExchangeHandler> decorator) {
         this.mapper = Args.notNull(mapper, "Request handler mapper");
+        this.decorator = decorator;
     }
 
-    @Override
-    public AsyncServerExchangeHandler create(final HttpRequest request, final HttpContext context) throws HttpException {
+    public DefaultAsyncResponseExchangeHandlerFactory(final HttpRequestMapper<Supplier<AsyncServerExchangeHandler>> mapper) {
+        this(mapper, null);
+    }
+
+    private AsyncServerExchangeHandler createHandler(final HttpRequest request, final HttpContext context) throws HttpException {
         try {
             final Supplier<AsyncServerExchangeHandler> supplier = mapper.resolve(request, context);
             if (supplier != null) {
@@ -61,4 +69,15 @@ public final class DefaultAsyncResponseExchangeHandlerFactory implements Handler
             return new ImmediateResponseExchangeHandler(HttpStatus.SC_MISDIRECTED_REQUEST, "Not authoritative");
         }
     }
+
+    @Override
+    public AsyncServerExchangeHandler create(final HttpRequest request, final HttpContext context) throws HttpException {
+        final AsyncServerExchangeHandler handler = createHandler(request, context);
+        if (handler != null) {
+            return decorator != null ? decorator.decorate(handler) : handler;
+        } else {
+            return null;
+        }
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/ImmediateResponseExchangeHandler.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/ImmediateResponseExchangeHandler.java b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/ImmediateResponseExchangeHandler.java
index 81e94fb..66f2fe1 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/ImmediateResponseExchangeHandler.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/ImmediateResponseExchangeHandler.java
@@ -44,6 +44,7 @@ import org.apache.hc.core5.http.nio.CapacityChannel;
 import org.apache.hc.core5.http.nio.DataStreamChannel;
 import org.apache.hc.core5.http.nio.ResponseChannel;
 import org.apache.hc.core5.http.nio.entity.BasicAsyncEntityProducer;
+import org.apache.hc.core5.http.protocol.HttpContext;
 import org.apache.hc.core5.util.Args;
 
 /**
@@ -69,7 +70,8 @@ public final class ImmediateResponseExchangeHandler implements AsyncServerExchan
     public void handleRequest(
             final HttpRequest request,
             final EntityDetails entityDetails,
-            final ResponseChannel responseChannel) throws HttpException, IOException {
+            final ResponseChannel responseChannel,
+            final HttpContext context) throws HttpException, IOException {
         responseProducer.sendResponse(responseChannel);
     }
 

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestHttpRequestExecutor.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestHttpRequestExecutor.java b/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestHttpRequestExecutor.java
index 656bad5..d60ba6d 100644
--- a/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestHttpRequestExecutor.java
+++ b/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestHttpRequestExecutor.java
@@ -28,20 +28,23 @@
 package org.apache.hc.core5.http.impl.io;
 
 import java.io.IOException;
+import java.util.List;
 
 import org.apache.hc.core5.http.ClassicHttpRequest;
 import org.apache.hc.core5.http.ClassicHttpResponse;
 import org.apache.hc.core5.http.HeaderElements;
 import org.apache.hc.core5.http.HttpEntity;
 import org.apache.hc.core5.http.HttpHeaders;
-import org.apache.hc.core5.http.ProtocolException;
+import org.apache.hc.core5.http.HttpResponse;
 import org.apache.hc.core5.http.io.HttpClientConnection;
+import org.apache.hc.core5.http.io.HttpResponseInformationCallback;
 import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
 import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
 import org.apache.hc.core5.http.protocol.HttpCoreContext;
 import org.apache.hc.core5.http.protocol.HttpProcessor;
 import org.junit.Assert;
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mockito;
 
 public class TestHttpRequestExecutor {
@@ -152,17 +155,31 @@ public class TestHttpRequestExecutor {
         Mockito.verify(httprocessor).process(request, request.getEntity(), context);
 
         Mockito.when(conn.receiveResponseHeader()).thenReturn(
-                new BasicClassicHttpResponse(100, "OK"),
-                new BasicClassicHttpResponse(101, "OK"),
-                new BasicClassicHttpResponse(102, "OK"),
+                new BasicClassicHttpResponse(100, "Continue"),
+                new BasicClassicHttpResponse(110, "Huh?"),
+                new BasicClassicHttpResponse(111, "Huh?"),
                 new BasicClassicHttpResponse(200, "OK"));
 
-        final ClassicHttpResponse response = executor.execute(request, conn, context);
+        final HttpResponseInformationCallback callback = Mockito.mock(HttpResponseInformationCallback.class);
+
+        final ClassicHttpResponse response = executor.execute(request, conn, callback, context);
         Mockito.verify(conn).sendRequestHeader(request);
         Mockito.verify(conn).flush();
         Mockito.verify(conn, Mockito.times(4)).receiveResponseHeader();
         Mockito.verify(conn, Mockito.times(1)).receiveResponseEntity(response);
 
+        final ArgumentCaptor<HttpResponse> responseCaptor = ArgumentCaptor.forClass(HttpResponse.class);
+        Mockito.verify(callback, Mockito.times(2)).execute(responseCaptor.capture(), Mockito.eq(conn), Mockito.eq(context));
+        final List<HttpResponse> infos = responseCaptor.getAllValues();
+        Assert.assertNotNull(infos);
+        Assert.assertEquals(2, infos.size());
+        final HttpResponse info1 = infos.get(0);
+        Assert.assertNotNull(info1);
+        Assert.assertEquals(110, info1.getCode());
+        final HttpResponse info2 = infos.get(1);
+        Assert.assertNotNull(info2);
+        Assert.assertEquals(111, info2.getCode());
+
         executor.postProcess(response, httprocessor, context);
         Mockito.verify(httprocessor).process(response, response.getEntity(), context);
 
@@ -313,7 +330,7 @@ public class TestHttpRequestExecutor {
     }
 
     @Test
-    public void testExecutionEntityEnclosingRequestUnsupportedIntermediateResponse() throws Exception {
+    public void testExecutionEntityEnclosingRequestWithExpectContinueMultiple1xxResponses() throws Exception {
         final HttpProcessor httprocessor = Mockito.mock(HttpProcessor.class);
         final HttpClientConnection conn = Mockito.mock(HttpClientConnection.class);
         final HttpRequestExecutor executor = new HttpRequestExecutor();
@@ -328,15 +345,36 @@ public class TestHttpRequestExecutor {
         Mockito.verify(httprocessor).process(request, request.getEntity(), context);
 
         Mockito.when(conn.receiveResponseHeader()).thenReturn(
-                new BasicClassicHttpResponse(101, "OK"));
+                new BasicClassicHttpResponse(110, "Huh?"),
+                new BasicClassicHttpResponse(100, "Continue"),
+                new BasicClassicHttpResponse(111, "Huh?"),
+                new BasicClassicHttpResponse(200, "OK"));
         Mockito.when(conn.isDataAvailable(Mockito.anyInt())).thenReturn(Boolean.TRUE);
 
-        try {
-            executor.execute(request, conn, context);
-            Assert.fail("ProtocolException should have been thrown");
-        } catch (final ProtocolException ex) {
-            Mockito.verify(conn).close();
-        }
+        final HttpResponseInformationCallback callback = Mockito.mock(HttpResponseInformationCallback.class);
+
+        final ClassicHttpResponse response = executor.execute(request, conn, callback, context);
+        Mockito.verify(conn).sendRequestHeader(request);
+        Mockito.verify(conn).sendRequestEntity(request);
+        Mockito.verify(conn, Mockito.times(2)).flush();
+        Mockito.verify(conn, Mockito.times(2)).isDataAvailable(3000);
+        Mockito.verify(conn, Mockito.times(4)).receiveResponseHeader();
+        Mockito.verify(conn).receiveResponseEntity(response);
+
+        final ArgumentCaptor<HttpResponse> responseCaptor = ArgumentCaptor.forClass(HttpResponse.class);
+        Mockito.verify(callback, Mockito.times(2)).execute(responseCaptor.capture(), Mockito.eq(conn), Mockito.eq(context));
+        final List<HttpResponse> infos = responseCaptor.getAllValues();
+        Assert.assertNotNull(infos);
+        Assert.assertEquals(2, infos.size());
+        final HttpResponse info1 = infos.get(0);
+        Assert.assertNotNull(info1);
+        Assert.assertEquals(110, info1.getCode());
+        final HttpResponse info2 = infos.get(1);
+        Assert.assertNotNull(info2);
+        Assert.assertEquals(111, info2.getCode());
+
+        executor.postProcess(response, httprocessor, context);
+        Mockito.verify(httprocessor).process(response, response.getEntity(), context);
     }
 
     @Test


Mime
View raw message