You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by ol...@apache.org on 2017/08/07 19:13:23 UTC

[3/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

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


Project: http://git-wip-us.apache.org/repos/asf/httpcomponents-core/repo
Commit: http://git-wip-us.apache.org/repos/asf/httpcomponents-core/commit/76c3a52e
Tree: http://git-wip-us.apache.org/repos/asf/httpcomponents-core/tree/76c3a52e
Diff: http://git-wip-us.apache.org/repos/asf/httpcomponents-core/diff/76c3a52e

Branch: refs/heads/master
Commit: 76c3a52e4da2ffdcbfcb58b190503e65fa56ba12
Parents: c7d4ee2
Author: Oleg Kalnichevski <ol...@apache.org>
Authored: Tue Aug 1 15:05:34 2017 +0200
Committer: Oleg Kalnichevski <ol...@apache.org>
Committed: Mon Aug 7 21:09:22 2017 +0200

----------------------------------------------------------------------
 .../http/examples/Http2FileServerExample.java   |   3 +-
 .../impl/nio/ClientHttp2StreamHandler.java      |   7 +-
 .../impl/nio/ClientPushHttp2StreamHandler.java  |   5 -
 .../impl/nio/ServerHttp2StreamHandler.java      |   7 +-
 .../impl/nio/bootstrap/H2ServerBootstrap.java   |  15 +-
 .../testing/classic/ClassicTestServer.java      |  17 +-
 .../hc/core5/testing/nio/Http1TestServer.java   |  26 +-
 .../hc/core5/testing/nio/Http2TestServer.java   |  42 ++-
 .../core5/testing/classic/TestClassicHttp.java  |  50 ++--
 .../hc/core5/testing/nio/EchoHandler.java       |   4 +-
 .../core5/testing/nio/Http1IntegrationTest.java |  57 ++--
 .../nio/Http1ServerAndRequesterTest.java        |   6 +-
 .../core5/testing/nio/Http2IntegrationTest.java |  51 ++--
 .../testing/nio/MultiLineResponseHandler.java   |   3 +-
 .../testing/nio/SingleLineResponseHandler.java  |   3 +-
 .../http/examples/AsyncFileServerExample.java   |   3 +-
 .../http/examples/AsyncReverseProxyExample.java |   4 +-
 .../impl/bootstrap/AsyncServerBootstrap.java    |  13 +-
 .../http/impl/bootstrap/HttpRequester.java      |  30 ++-
 .../http/impl/bootstrap/ServerBootstrap.java    |  13 +-
 .../impl/io/DefaultBHttpClientConnection.java   |   5 +
 .../core5/http/impl/io/HttpRequestExecutor.java | 107 ++++----
 .../hc/core5/http/impl/io/HttpService.java      | 262 +++++++++----------
 .../http/impl/nio/ClientHttp1StreamHandler.java |   7 +-
 .../http/impl/nio/ServerHttp1StreamHandler.java |   9 +-
 .../http/io/HttpClientResponseHandler.java      |  54 ++++
 .../core5/http/io/HttpExpectationVerifier.java  |  62 -----
 .../io/HttpResponseInformationCallback.java     |  44 ++++
 .../core5/http/io/HttpServerRequestHandler.java |  57 ++++
 .../hc/core5/http/io/ResponseHandler.java       |  54 ----
 .../BasicHttpServerExpectationDecorator.java    |  76 ++++++
 .../support/BasicHttpServerRequestHandler.java  |  78 ++++++
 .../http/nio/AsyncServerExchangeHandler.java    |   4 +-
 .../http/nio/AsyncServerRequestHandler.java     |  13 +-
 .../http/nio/AsyncServerResponseTrigger.java    |  49 ----
 .../hc/core5/http/nio/HttpContextAware.java     |  40 ---
 .../AbstractClassicServerExchangeHandler.java   |  19 +-
 .../support/AbstractServerExchangeHandler.java  |  71 ++---
 .../BasicAsyncServerExpectationDecorator.java   | 160 +++++++++++
 .../nio/support/BasicServerExchangeHandler.java |   3 +-
 ...aultAsyncResponseExchangeHandlerFactory.java |  25 +-
 .../ImmediateResponseExchangeHandler.java       |   4 +-
 .../http/impl/io/TestHttpRequestExecutor.java   |  64 ++++-
 .../hc/core5/http/impl/io/TestHttpService.java  | 205 +++++----------
 44 files changed, 1056 insertions(+), 775 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5-h2/src/examples/org/apache/hc/core5/http/examples/Http2FileServerExample.java
----------------------------------------------------------------------
diff --git a/httpcore5-h2/src/examples/org/apache/hc/core5/http/examples/Http2FileServerExample.java b/httpcore5-h2/src/examples/org/apache/hc/core5/http/examples/Http2FileServerExample.java
index 29ed426..1d61ca8 100644
--- a/httpcore5-h2/src/examples/org/apache/hc/core5/http/examples/Http2FileServerExample.java
+++ b/httpcore5-h2/src/examples/org/apache/hc/core5/http/examples/Http2FileServerExample.java
@@ -48,7 +48,6 @@ import org.apache.hc.core5.http.ProtocolException;
 import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer;
 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.nio.BasicRequestConsumer;
 import org.apache.hc.core5.http.nio.BasicResponseProducer;
 import org.apache.hc.core5.http.nio.entity.FileEntityProducer;
@@ -132,7 +131,7 @@ public class Http2FileServerExample {
                     @Override
                     public void handle(
                             final Message<HttpRequest, Void> message,
-                            final AsyncServerResponseTrigger responseTrigger,
+                            final ResponseTrigger responseTrigger,
                             final HttpContext context) throws HttpException, IOException {
                         HttpRequest request = message.getHead();
                         URI requestUri;

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttp2StreamHandler.java
----------------------------------------------------------------------
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttp2StreamHandler.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttp2StreamHandler.java
index 45cda1c..4bc0987 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttp2StreamHandler.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttp2StreamHandler.java
@@ -45,7 +45,6 @@ import org.apache.hc.core5.http.impl.LazyEntityDetails;
 import org.apache.hc.core5.http.impl.nio.MessageState;
 import org.apache.hc.core5.http.nio.AsyncClientExchangeHandler;
 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.protocol.HttpCoreContext;
 import org.apache.hc.core5.http.protocol.HttpProcessor;
@@ -127,10 +126,6 @@ class ClientHttp2StreamHandler implements Http2StreamHandler {
             context.setProtocolVersion(HttpVersion.HTTP_2);
             context.setAttribute(HttpCoreContext.HTTP_REQUEST, request);
 
-            if (exchangeHandler instanceof HttpContextAware) {
-                ((HttpContextAware) exchangeHandler).setContext(context);
-            }
-
             httpProcessor.process(request, entityDetails, context);
 
             final List<Header> headers = DefaultH2RequestConverter.INSTANCE.convert(request);
@@ -191,7 +186,7 @@ class ClientHttp2StreamHandler implements Http2StreamHandler {
                 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);
                 }
                 if (requestState == MessageState.ACK) {

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientPushHttp2StreamHandler.java
----------------------------------------------------------------------
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientPushHttp2StreamHandler.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientPushHttp2StreamHandler.java
index 106d88c..abb1f94 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientPushHttp2StreamHandler.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientPushHttp2StreamHandler.java
@@ -43,7 +43,6 @@ import org.apache.hc.core5.http.impl.LazyEntityDetails;
 import org.apache.hc.core5.http.impl.nio.MessageState;
 import org.apache.hc.core5.http.nio.AsyncPushConsumer;
 import org.apache.hc.core5.http.nio.HandlerFactory;
-import org.apache.hc.core5.http.nio.HttpContextAware;
 import org.apache.hc.core5.http.protocol.HttpCoreContext;
 import org.apache.hc.core5.http.protocol.HttpProcessor;
 import org.apache.hc.core5.http2.H2ConnectionException;
@@ -112,10 +111,6 @@ class ClientPushHttp2StreamHandler implements Http2StreamHandler {
             context.setProtocolVersion(HttpVersion.HTTP_2);
             context.setAttribute(HttpCoreContext.HTTP_REQUEST, request);
 
-            if (exchangeHandler instanceof HttpContextAware) {
-                ((HttpContextAware) exchangeHandler).setContext(context);
-            }
-
             httpProcessor.process(request, null, context);
             connMetrics.incrementRequestCount();
             this.requestState = MessageState.COMPLETE;

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttp2StreamHandler.java
----------------------------------------------------------------------
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttp2StreamHandler.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttp2StreamHandler.java
index bbd6a56..52ab4a2 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttp2StreamHandler.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttp2StreamHandler.java
@@ -46,7 +46,6 @@ import org.apache.hc.core5.http.nio.AsyncPushProducer;
 import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
 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.ResponseChannel;
 import org.apache.hc.core5.http.protocol.HttpCoreContext;
 import org.apache.hc.core5.http.protocol.HttpProcessor;
@@ -199,10 +198,6 @@ public class ServerHttp2StreamHandler implements Http2StreamHandler {
                 context.setProtocolVersion(HttpVersion.HTTP_2);
                 context.setAttribute(HttpCoreContext.HTTP_REQUEST, request);
 
-                if (exchangeHandler instanceof HttpContextAware) {
-                    ((HttpContextAware) exchangeHandler).setContext(context);
-                }
-
                 httpProcessor.process(request, requestEntityDetails, context);
                 connMetrics.incrementRequestCount();
                 receivedRequest = request;
@@ -226,7 +221,7 @@ public class ServerHttp2StreamHandler implements Http2StreamHandler {
                         commitPromise(promise, pushProducer);
                     }
 
-                });
+                }, context);
                 break;
             case BODY:
                 responseState = MessageState.COMPLETE;

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2ServerBootstrap.java
----------------------------------------------------------------------
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2ServerBootstrap.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2ServerBootstrap.java
index 0a25098..3e3ddcc 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2ServerBootstrap.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2ServerBootstrap.java
@@ -45,6 +45,7 @@ import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
 import org.apache.hc.core5.http.nio.AsyncServerRequestHandler;
 import org.apache.hc.core5.http.nio.HandlerFactory;
 import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
+import org.apache.hc.core5.http.nio.support.BasicAsyncServerExpectationDecorator;
 import org.apache.hc.core5.http.nio.support.BasicServerExchangeHandler;
 import org.apache.hc.core5.http.nio.support.DefaultAsyncResponseExchangeHandlerFactory;
 import org.apache.hc.core5.http.protocol.HttpProcessor;
@@ -279,16 +280,24 @@ public class H2ServerBootstrap {
         for (final HandlerEntry<Supplier<AsyncServerExchangeHandler>> entry: handlerList) {
             registry.register(entry.hostname, entry.uriPattern, entry.handler);
         }
-        final HandlerFactory<AsyncServerExchangeHandler> exchangeHandlerFactory = new DefaultAsyncResponseExchangeHandlerFactory(registry);
+        final HandlerFactory<AsyncServerExchangeHandler> handlerFactory = new DefaultAsyncResponseExchangeHandlerFactory(
+                registry, new Decorator<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler handler) {
+                return new BasicAsyncServerExpectationDecorator(handler);
+            }
+
+        });
         final ServerHttp2StreamMultiplexerFactory http2StreamHandlerFactory = new ServerHttp2StreamMultiplexerFactory(
                 httpProcessor != null ? httpProcessor : Http2Processors.server(),
-                exchangeHandlerFactory,
+                handlerFactory,
                 h2Config != null ? h2Config : H2Config.DEFAULT,
                 charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT,
                 http2StreamListener);
         final ServerHttp1StreamDuplexerFactory http1StreamHandlerFactory = new ServerHttp1StreamDuplexerFactory(
                 httpProcessor != null ? httpProcessor : HttpProcessors.server(),
-                exchangeHandlerFactory,
+                handlerFactory,
                 h1Config != null ? h1Config : H1Config.DEFAULT,
                 charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT,
                 DefaultConnectionReuseStrategy.INSTANCE,

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/classic/ClassicTestServer.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/classic/ClassicTestServer.java b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/classic/ClassicTestServer.java
index 84937dc..41bb3f8 100644
--- a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/classic/ClassicTestServer.java
+++ b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/classic/ClassicTestServer.java
@@ -34,6 +34,7 @@ import java.util.concurrent.atomic.AtomicReference;
 import javax.net.ServerSocketFactory;
 import javax.net.ssl.SSLContext;
 
+import org.apache.hc.core5.function.Decorator;
 import org.apache.hc.core5.http.URIScheme;
 import org.apache.hc.core5.http.config.CharCodingConfig;
 import org.apache.hc.core5.http.config.H1Config;
@@ -42,10 +43,11 @@ import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy;
 import org.apache.hc.core5.http.impl.HttpProcessors;
 import org.apache.hc.core5.http.impl.bootstrap.HttpServer;
 import org.apache.hc.core5.http.impl.io.DefaultBHttpServerConnectionFactory;
-import org.apache.hc.core5.http.impl.io.DefaultClassicHttpResponseFactory;
 import org.apache.hc.core5.http.impl.io.HttpService;
-import org.apache.hc.core5.http.io.HttpExpectationVerifier;
 import org.apache.hc.core5.http.io.HttpRequestHandler;
+import org.apache.hc.core5.http.io.HttpServerRequestHandler;
+import org.apache.hc.core5.http.io.support.BasicHttpServerExpectationDecorator;
+import org.apache.hc.core5.http.io.support.BasicHttpServerRequestHandler;
 import org.apache.hc.core5.http.protocol.HttpProcessor;
 import org.apache.hc.core5.http.protocol.RequestHandlerRegistry;
 import org.apache.hc.core5.io.ShutdownType;
@@ -100,14 +102,13 @@ public class ClassicTestServer {
         }
     }
 
-    public void start(final HttpProcessor httpProcessor, final HttpExpectationVerifier expectationVerifier) throws IOException {
+    public void start(final HttpProcessor httpProcessor, final Decorator<HttpServerRequestHandler> handlerDecorator) throws IOException {
         if (serverRef.get() == null) {
+            final HttpServerRequestHandler handler = new BasicHttpServerRequestHandler(registry);
             final HttpService httpService = new HttpService(
                     httpProcessor != null ? httpProcessor : HttpProcessors.server(),
+                    handlerDecorator != null ? handlerDecorator.decorate(handler) : new BasicHttpServerExpectationDecorator(handler),
                     DefaultConnectionReuseStrategy.INSTANCE,
-                    DefaultClassicHttpResponseFactory.INSTANCE,
-                    registry,
-                    expectationVerifier,
                     LoggingHttp1StreamListener.INSTANCE_CLIENT);
             final HttpServer server = new HttpServer(
                     0,
@@ -129,10 +130,6 @@ public class ClassicTestServer {
         }
     }
 
-    public void start(final HttpExpectationVerifier expectationVerifier) throws IOException {
-        start(null, expectationVerifier);
-    }
-
     public void start() throws IOException {
         start(null, null);
     }

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/Http1TestServer.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/Http1TestServer.java b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/Http1TestServer.java
index 9193a05..84bfe18 100644
--- a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/Http1TestServer.java
+++ b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/Http1TestServer.java
@@ -33,6 +33,7 @@ import java.util.concurrent.Future;
 
 import javax.net.ssl.SSLContext;
 
+import org.apache.hc.core5.function.Decorator;
 import org.apache.hc.core5.function.Supplier;
 import org.apache.hc.core5.http.config.CharCodingConfig;
 import org.apache.hc.core5.http.config.H1Config;
@@ -40,6 +41,7 @@ import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy;
 import org.apache.hc.core5.http.impl.HttpProcessors;
 import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
 import org.apache.hc.core5.http.nio.AsyncServerRequestHandler;
+import org.apache.hc.core5.http.nio.support.BasicAsyncServerExpectationDecorator;
 import org.apache.hc.core5.http.nio.support.BasicServerExchangeHandler;
 import org.apache.hc.core5.http.nio.support.DefaultAsyncResponseExchangeHandlerFactory;
 import org.apache.hc.core5.http.protocol.HttpProcessor;
@@ -87,18 +89,34 @@ public class Http1TestServer extends AsyncServer {
         return (InetSocketAddress) listener.getAddress();
     }
 
-    public InetSocketAddress start(final HttpProcessor httpProcessor, final H1Config h1Config) throws Exception {
+    public InetSocketAddress start(
+            final HttpProcessor httpProcessor,
+            final Decorator<AsyncServerExchangeHandler> exchangeHandlerDecorator,
+            final H1Config h1Config) throws Exception {
         return start(new InternalServerHttp1EventHandlerFactory(
-                httpProcessor,
-                new DefaultAsyncResponseExchangeHandlerFactory(registry),
+                httpProcessor != null ? httpProcessor : HttpProcessors.server(),
+                new DefaultAsyncResponseExchangeHandlerFactory(
+                        registry,
+                        exchangeHandlerDecorator != null ? exchangeHandlerDecorator : new Decorator<AsyncServerExchangeHandler>() {
+
+                            @Override
+                            public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler handler) {
+                                return new BasicAsyncServerExpectationDecorator(handler);
+                            }
+
+                        }),
                 h1Config,
                 CharCodingConfig.DEFAULT,
                 DefaultConnectionReuseStrategy.INSTANCE,
                 sslContext));
     }
 
+    public InetSocketAddress start(final HttpProcessor httpProcessor, final H1Config h1Config) throws Exception {
+        return start(httpProcessor, null, h1Config);
+    }
+
     public InetSocketAddress start() throws Exception {
-        return start(HttpProcessors.server(), H1Config.DEFAULT);
+        return start(null, null, H1Config.DEFAULT);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/Http2TestServer.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/Http2TestServer.java b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/Http2TestServer.java
index 171f9c0..4bb2443 100644
--- a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/Http2TestServer.java
+++ b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/Http2TestServer.java
@@ -33,12 +33,14 @@ import java.util.concurrent.Future;
 
 import javax.net.ssl.SSLContext;
 
+import org.apache.hc.core5.function.Decorator;
 import org.apache.hc.core5.function.Supplier;
 import org.apache.hc.core5.http.config.CharCodingConfig;
 import org.apache.hc.core5.http.config.H1Config;
 import org.apache.hc.core5.http.impl.HttpProcessors;
 import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
 import org.apache.hc.core5.http.nio.AsyncServerRequestHandler;
+import org.apache.hc.core5.http.nio.support.BasicAsyncServerExpectationDecorator;
 import org.apache.hc.core5.http.nio.support.BasicServerExchangeHandler;
 import org.apache.hc.core5.http.nio.support.DefaultAsyncResponseExchangeHandlerFactory;
 import org.apache.hc.core5.http.protocol.HttpProcessor;
@@ -86,10 +88,22 @@ public class Http2TestServer extends AsyncServer {
         execute(handlerFactory);
     }
 
-    public InetSocketAddress start(final HttpProcessor httpProcessor, final H2Config h2Config) throws Exception {
+    public InetSocketAddress start(
+            final HttpProcessor httpProcessor,
+            final Decorator<AsyncServerExchangeHandler> exchangeHandlerDecorator,
+            final H2Config h2Config) throws Exception {
         start(new InternalServerHttp2EventHandlerFactory(
-                httpProcessor,
-                new DefaultAsyncResponseExchangeHandlerFactory(registry),
+                httpProcessor != null ? httpProcessor : Http2Processors.server(),
+                new DefaultAsyncResponseExchangeHandlerFactory(
+                        registry,
+                        exchangeHandlerDecorator != null ? exchangeHandlerDecorator : new Decorator<AsyncServerExchangeHandler>() {
+
+                            @Override
+                            public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler handler) {
+                                return new BasicAsyncServerExpectationDecorator(handler);
+                            }
+
+                        }),
                 HttpVersionPolicy.FORCE_HTTP_2,
                 h2Config,
                 H1Config.DEFAULT,
@@ -100,10 +114,22 @@ public class Http2TestServer extends AsyncServer {
         return (InetSocketAddress) listener.getAddress();
     }
 
-    public InetSocketAddress start(final HttpProcessor httpProcessor, final H1Config h1Config) throws Exception {
+    public InetSocketAddress start(
+            final HttpProcessor httpProcessor,
+            final Decorator<AsyncServerExchangeHandler> exchangeHandlerDecorator,
+            final H1Config h1Config) throws Exception {
         start(new InternalServerHttp2EventHandlerFactory(
-                httpProcessor,
-                new DefaultAsyncResponseExchangeHandlerFactory(registry),
+                httpProcessor != null ? httpProcessor : HttpProcessors.server(),
+                new DefaultAsyncResponseExchangeHandlerFactory(
+                        registry,
+                        exchangeHandlerDecorator != null ? exchangeHandlerDecorator : new Decorator<AsyncServerExchangeHandler>() {
+
+                    @Override
+                    public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler handler) {
+                        return new BasicAsyncServerExpectationDecorator(handler);
+                    }
+
+                }),
                 HttpVersionPolicy.FORCE_HTTP_1,
                 H2Config.DEFAULT,
                 h1Config,
@@ -115,11 +141,11 @@ public class Http2TestServer extends AsyncServer {
     }
 
     public InetSocketAddress start(final H2Config h2Config) throws Exception {
-        return start(Http2Processors.server(), h2Config);
+        return start(null, null, h2Config);
     }
 
     public InetSocketAddress start(final H1Config h1Config) throws Exception {
-        return start(HttpProcessors.server(), h1Config);
+        return start(null, null, h1Config);
     }
 
     public InetSocketAddress start() throws Exception {

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/TestClassicHttp.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/TestClassicHttp.java b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/TestClassicHttp.java
index 8d45c2d..f8a3c90 100644
--- a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/TestClassicHttp.java
+++ b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/TestClassicHttp.java
@@ -39,6 +39,7 @@ import java.util.List;
 import java.util.Random;
 import java.util.concurrent.TimeUnit;
 
+import org.apache.hc.core5.function.Decorator;
 import org.apache.hc.core5.http.ClassicHttpRequest;
 import org.apache.hc.core5.http.ClassicHttpResponse;
 import org.apache.hc.core5.http.ContentType;
@@ -53,13 +54,15 @@ import org.apache.hc.core5.http.HttpRequestInterceptor;
 import org.apache.hc.core5.http.HttpStatus;
 import org.apache.hc.core5.http.HttpVersion;
 import org.apache.hc.core5.http.config.SocketConfig;
-import org.apache.hc.core5.http.io.HttpExpectationVerifier;
 import org.apache.hc.core5.http.io.HttpRequestHandler;
+import org.apache.hc.core5.http.io.HttpServerRequestHandler;
 import org.apache.hc.core5.http.io.entity.AbstractHttpEntity;
 import org.apache.hc.core5.http.io.entity.ByteArrayEntity;
 import org.apache.hc.core5.http.io.entity.EntityUtils;
 import org.apache.hc.core5.http.io.entity.StringEntity;
+import org.apache.hc.core5.http.io.support.BasicHttpServerExpectationDecorator;
 import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
+import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
 import org.apache.hc.core5.http.protocol.DefaultHttpProcessor;
 import org.apache.hc.core5.http.protocol.HttpContext;
 import org.apache.hc.core5.http.protocol.HttpCoreContext;
@@ -452,7 +455,6 @@ public class TestClassicHttp {
         }
     }
 
-
     /**
      * This test case executes a series of simple POST requests that do not
      * meet the target server expectations.
@@ -477,28 +479,34 @@ public class TestClassicHttp {
 
         });
 
-        this.server.start(new HttpExpectationVerifier() {
+        this.server.start(null, new Decorator<HttpServerRequestHandler>() {
 
             @Override
-            public void verify(
-                    final ClassicHttpRequest request,
-                    final ClassicHttpResponse response,
-                    final HttpContext context) throws HttpException {
-                final Header someheader = request.getFirstHeader("Secret");
-                if (someheader != null) {
-                    final int secretNumber;
-                    try {
-                        secretNumber = Integer.parseInt(someheader.getValue());
-                    } catch (final NumberFormatException ex) {
-                        response.setCode(HttpStatus.SC_BAD_REQUEST);
-                        return;
-                    }
-                    if (secretNumber >= 2) {
-                        response.setCode(HttpStatus.SC_EXPECTATION_FAILED);
-                        response.setEntity(
-                                new StringEntity("Wrong secret number", ContentType.TEXT_PLAIN));
+            public HttpServerRequestHandler decorate(final HttpServerRequestHandler handler) {
+                return new BasicHttpServerExpectationDecorator(handler) {
+
+                    @Override
+                    protected ClassicHttpResponse verify(final ClassicHttpRequest request, final HttpContext context) {
+                        final Header someheader = request.getFirstHeader("Secret");
+                        if (someheader != null) {
+                            final int secretNumber;
+                            try {
+                                secretNumber = Integer.parseInt(someheader.getValue());
+                            } catch (final NumberFormatException ex) {
+                                final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_BAD_REQUEST);
+                                response.setEntity(new StringEntity(ex.toString()));
+                                return response;
+                            }
+                            if (secretNumber >= 2) {
+                                final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_EXPECTATION_FAILED);
+                                response.setEntity(new StringEntity("Wrong secret number", ContentType.TEXT_PLAIN));
+                                return response;
+                            }
+                        }
+                        return null;
                     }
-                }
+
+                };
             }
 
         });

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/EchoHandler.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/EchoHandler.java b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/EchoHandler.java
index 5b82f0e..f7962cc 100644
--- a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/EchoHandler.java
+++ b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/EchoHandler.java
@@ -42,6 +42,7 @@ 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;
 
 public class EchoHandler implements AsyncServerExchangeHandler {
 
@@ -67,7 +68,8 @@ public class EchoHandler implements AsyncServerExchangeHandler {
     public void handleRequest(
             final HttpRequest request,
             final EntityDetails entityDetails,
-            final ResponseChannel responseChannel) throws HttpException, IOException {
+            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())) {

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http1IntegrationTest.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http1IntegrationTest.java b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http1IntegrationTest.java
index 825fb6f..2ad7661 100644
--- a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http1IntegrationTest.java
+++ b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http1IntegrationTest.java
@@ -58,6 +58,7 @@ import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
 import java.util.concurrent.atomic.AtomicReference;
 
+import org.apache.hc.core5.function.Decorator;
 import org.apache.hc.core5.function.Supplier;
 import org.apache.hc.core5.http.ConnectionReuseStrategy;
 import org.apache.hc.core5.http.ContentLengthStrategy;
@@ -91,7 +92,7 @@ import org.apache.hc.core5.http.nio.AsyncRequestConsumer;
 import org.apache.hc.core5.http.nio.AsyncRequestProducer;
 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.BasicRequestConsumer;
 import org.apache.hc.core5.http.nio.BasicRequestProducer;
 import org.apache.hc.core5.http.nio.BasicResponseConsumer;
@@ -113,6 +114,7 @@ import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer;
 import org.apache.hc.core5.http.nio.entity.StringAsyncEntityProducer;
 import org.apache.hc.core5.http.nio.support.AbstractClassicServerExchangeHandler;
 import org.apache.hc.core5.http.nio.support.AbstractServerExchangeHandler;
+import org.apache.hc.core5.http.nio.support.BasicAsyncServerExpectationDecorator;
 import org.apache.hc.core5.http.protocol.DefaultHttpProcessor;
 import org.apache.hc.core5.http.protocol.HttpContext;
 import org.apache.hc.core5.http.protocol.HttpProcessor;
@@ -697,9 +699,26 @@ public class Http1IntegrationTest extends InternalHttp1ServerTestBase {
                 return new MessageExchangeHandler<String>(new StringAsyncEntityConsumer()) {
 
                     @Override
-                    protected AsyncResponseProducer verify(
-                            final HttpRequest request,
+                    protected void handle(
+                            final Message<HttpRequest, String> request,
+                            final AsyncServerRequestHandler.ResponseTrigger responseTrigger,
                             final HttpContext context) throws IOException, HttpException {
+                        responseTrigger.submitResponse(new BasicResponseProducer(HttpStatus.SC_OK, "All is well"));
+
+                    }
+                };
+            }
+
+        });
+        final InetSocketAddress serverEndpoint = server.start(null, new Decorator<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler handler) {
+
+                return new BasicAsyncServerExpectationDecorator(handler) {
+
+                    @Override
+                    protected AsyncResponseProducer verify(final HttpRequest request, final HttpContext context) throws IOException, HttpException {
                         final Header h = request.getFirstHeader("password");
                         if (h != null && "secret".equals(h.getValue())) {
                             return null;
@@ -707,21 +726,10 @@ public class Http1IntegrationTest extends InternalHttp1ServerTestBase {
                             return new BasicResponseProducer(HttpStatus.SC_UNAUTHORIZED, "You shall not pass");
                         }
                     }
-
-                    @Override
-                    protected void handle(
-                            final Message<HttpRequest, String> request,
-                            final AsyncServerResponseTrigger responseTrigger,
-                            final HttpContext context) throws IOException, HttpException {
-                        responseTrigger.submitResponse(
-                                new BasicResponseProducer(HttpStatus.SC_OK, "All is well"));
-
-                    }
                 };
-            }
 
-        });
-        final InetSocketAddress serverEndpoint = server.start();
+            }
+        }, H1Config.DEFAULT);
 
         client.start();
         final Future<IOSession> sessionFuture = client.requestSession(
@@ -805,7 +813,8 @@ public class Http1IntegrationTest extends InternalHttp1ServerTestBase {
                     public void handleRequest(
                             final HttpRequest request,
                             final EntityDetails entityDetails,
-                            final ResponseChannel responseChannel) throws HttpException, IOException {
+                            final ResponseChannel responseChannel,
+                            final HttpContext context) throws HttpException, IOException {
 
                         Executors.newSingleThreadExecutor().execute(new Runnable() {
                             @Override
@@ -906,7 +915,8 @@ public class Http1IntegrationTest extends InternalHttp1ServerTestBase {
                     public void handleRequest(
                             final HttpRequest request,
                             final EntityDetails entityDetails,
-                            final ResponseChannel responseChannel) throws HttpException, IOException {
+                            final ResponseChannel responseChannel,
+                            final HttpContext context) throws HttpException, IOException {
                         final AsyncResponseProducer producer;
                         final Header h = request.getFirstHeader("password");
                         if (h != null && "secret".equals(h.getValue())) {
@@ -1356,7 +1366,7 @@ public class Http1IntegrationTest extends InternalHttp1ServerTestBase {
                             @Override
                             protected void handle(
                                     final Message<HttpRequest, String> request,
-                                    final AsyncServerResponseTrigger responseTrigger,
+                                    final AsyncServerRequestHandler.ResponseTrigger responseTrigger,
                                     final HttpContext context) throws IOException, HttpException {
                                 responseTrigger.submitResponse(
                                         new BasicResponseProducer(new StringAsyncEntityProducer("useful stuff")));
@@ -1440,7 +1450,7 @@ public class Http1IntegrationTest extends InternalHttp1ServerTestBase {
                     @Override
                     protected void handle(
                             final Message<HttpRequest, String> request,
-                            final AsyncServerResponseTrigger responseTrigger,
+                            final AsyncServerRequestHandler.ResponseTrigger responseTrigger,
                             final HttpContext context) throws IOException, HttpException {
                         throw new HttpException("Boom");
                     }
@@ -1499,7 +1509,7 @@ public class Http1IntegrationTest extends InternalHttp1ServerTestBase {
                     @Override
                     protected void handle(
                             final Message<HttpRequest, String> request,
-                            final AsyncServerResponseTrigger responseTrigger,
+                            final AsyncServerRequestHandler.ResponseTrigger responseTrigger,
                             final HttpContext context) throws IOException, HttpException {
                         final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_NO_CONTENT);
                         responseTrigger.submitResponse(new BasicResponseProducer(response));
@@ -1586,7 +1596,7 @@ public class Http1IntegrationTest extends InternalHttp1ServerTestBase {
                     @Override
                     protected void handle(
                             final Message<HttpRequest, String> requestMessage,
-                            final AsyncServerResponseTrigger responseTrigger,
+                            final AsyncServerRequestHandler.ResponseTrigger responseTrigger,
                             final HttpContext context) throws HttpException, IOException {
                         responseTrigger.submitResponse(new BasicResponseProducer(
                                 HttpStatus.SC_OK,
@@ -1648,7 +1658,8 @@ public class Http1IntegrationTest extends InternalHttp1ServerTestBase {
                     public void handleRequest(
                             final HttpRequest request,
                             final EntityDetails entityDetails,
-                            final ResponseChannel responseChannel) throws HttpException, IOException {
+                            final ResponseChannel responseChannel,
+                            final HttpContext context) throws HttpException, IOException {
                         final String requestUri = request.getRequestUri();
                         if (requestUri.endsWith("boom")) {
                             throw new ProtocolException("Boom!!!");

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http1ServerAndRequesterTest.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http1ServerAndRequesterTest.java b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http1ServerAndRequesterTest.java
index ba736e4..96a975c 100644
--- a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http1ServerAndRequesterTest.java
+++ b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http1ServerAndRequesterTest.java
@@ -57,6 +57,7 @@ import org.apache.hc.core5.http.nio.BasicResponseConsumer;
 import org.apache.hc.core5.http.nio.ResponseChannel;
 import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer;
 import org.apache.hc.core5.http.nio.entity.StringAsyncEntityProducer;
+import org.apache.hc.core5.http.protocol.HttpContext;
 import org.apache.hc.core5.io.ShutdownType;
 import org.apache.hc.core5.reactor.ExceptionEvent;
 import org.apache.hc.core5.reactor.IOReactorConfig;
@@ -102,7 +103,8 @@ public class Http1ServerAndRequesterTest {
                                 public void handleRequest(
                                         final HttpRequest request,
                                         final EntityDetails entityDetails,
-                                        final ResponseChannel responseChannel) throws HttpException, IOException {
+                                        final ResponseChannel responseChannel,
+                                        final HttpContext context) throws HttpException, IOException {
                                     super.handleRequest(request, entityDetails, new ResponseChannel() {
 
                                         @Override
@@ -125,7 +127,7 @@ public class Http1ServerAndRequesterTest {
                                             responseChannel.pushPromise(promise, pushProducer);
                                         }
 
-                                    });
+                                    }, context);
                                 }
                             };
                         }

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http2IntegrationTest.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http2IntegrationTest.java b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http2IntegrationTest.java
index 3b105c0..86a95ac 100644
--- a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http2IntegrationTest.java
+++ b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http2IntegrationTest.java
@@ -61,6 +61,7 @@ import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 
 import org.apache.hc.core5.function.Callback;
+import org.apache.hc.core5.function.Decorator;
 import org.apache.hc.core5.function.Supplier;
 import org.apache.hc.core5.http.ContentType;
 import org.apache.hc.core5.http.EndpointDetails;
@@ -82,7 +83,7 @@ import org.apache.hc.core5.http.nio.AsyncPushConsumer;
 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.BasicPushProducer;
 import org.apache.hc.core5.http.nio.BasicRequestConsumer;
 import org.apache.hc.core5.http.nio.BasicRequestProducer;
@@ -102,6 +103,7 @@ import org.apache.hc.core5.http.nio.entity.StringAsyncEntityProducer;
 import org.apache.hc.core5.http.nio.support.AbstractAsyncPushHandler;
 import org.apache.hc.core5.http.nio.support.AbstractClassicServerExchangeHandler;
 import org.apache.hc.core5.http.nio.support.AbstractServerExchangeHandler;
+import org.apache.hc.core5.http.nio.support.BasicAsyncServerExpectationDecorator;
 import org.apache.hc.core5.http.protocol.HttpContext;
 import org.apache.hc.core5.http2.H2Error;
 import org.apache.hc.core5.http2.H2StreamResetException;
@@ -576,7 +578,7 @@ public class Http2IntegrationTest extends InternalHttp2ServerTestBase {
                     @Override
                     protected void handle(
                             final Message<HttpRequest, Void> request,
-                            final AsyncServerResponseTrigger responseTrigger,
+                            final AsyncServerRequestHandler.ResponseTrigger responseTrigger,
                             final HttpContext context) throws IOException, HttpException {
                         responseTrigger.pushPromise(
                                 new BasicHttpRequest("GET", createRequestURI(serverEndpoint, "/stuff")),
@@ -656,7 +658,7 @@ public class Http2IntegrationTest extends InternalHttp2ServerTestBase {
                     @Override
                     protected void handle(
                             final Message<HttpRequest, Void> request,
-                            final AsyncServerResponseTrigger responseTrigger,
+                            final AsyncServerRequestHandler.ResponseTrigger responseTrigger,
                             final HttpContext context) throws IOException, HttpException {
 
                         responseTrigger.pushPromise(
@@ -760,12 +762,29 @@ public class Http2IntegrationTest extends InternalHttp2ServerTestBase {
 
             @Override
             public AsyncServerExchangeHandler get() {
-                return new MessageExchangeHandler<Void>(new NoopEntityConsumer()) {
+                return new MessageExchangeHandler<String>(new StringAsyncEntityConsumer()) {
 
                     @Override
-                    protected AsyncResponseProducer verify(
-                            final HttpRequest request,
+                    protected void handle(
+                            final Message<HttpRequest, String> request,
+                            final AsyncServerRequestHandler.ResponseTrigger responseTrigger,
                             final HttpContext context) throws IOException, HttpException {
+                        responseTrigger.submitResponse(new BasicResponseProducer(HttpStatus.SC_OK, "All is well"));
+
+                    }
+                };
+            }
+
+        });
+        final InetSocketAddress serverEndpoint = server.start(null, new Decorator<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler handler) {
+
+                return new BasicAsyncServerExpectationDecorator(handler) {
+
+                    @Override
+                    protected AsyncResponseProducer verify(final HttpRequest request, final HttpContext context) throws IOException, HttpException {
                         final Header h = request.getFirstHeader("password");
                         if (h != null && "secret".equals(h.getValue())) {
                             return null;
@@ -773,21 +792,10 @@ public class Http2IntegrationTest extends InternalHttp2ServerTestBase {
                             return new BasicResponseProducer(HttpStatus.SC_UNAUTHORIZED, "You shall not pass");
                         }
                     }
-
-                    @Override
-                    protected void handle(
-                            final Message<HttpRequest, Void> request,
-                            final AsyncServerResponseTrigger responseTrigger,
-                            final HttpContext context) throws IOException, HttpException {
-                        responseTrigger.submitResponse(
-                                new BasicResponseProducer(HttpStatus.SC_OK, "All is well"));
-
-                    }
                 };
-            }
 
-        });
-        final InetSocketAddress serverEndpoint = server.start();
+            }
+        }, H2Config.DEFAULT);
 
         client.start();
         final Future<ClientSessionEndpoint> connectFuture = client.connect(
@@ -846,7 +854,8 @@ public class Http2IntegrationTest extends InternalHttp2ServerTestBase {
                     public void handleRequest(
                             final HttpRequest request,
                             final EntityDetails entityDetails,
-                            final ResponseChannel responseChannel) throws HttpException, IOException {
+                            final ResponseChannel responseChannel,
+                            final HttpContext context) throws HttpException, IOException {
                         final AsyncResponseProducer producer;
                         final Header h = request.getFirstHeader("password");
                         if (h != null && "secret".equals(h.getValue())) {
@@ -917,7 +926,7 @@ public class Http2IntegrationTest extends InternalHttp2ServerTestBase {
                     @Override
                     protected void handle(
                             final Message<HttpRequest, String> requestMessage,
-                            final AsyncServerResponseTrigger responseTrigger,
+                            final AsyncServerRequestHandler.ResponseTrigger responseTrigger,
                             final HttpContext context) throws HttpException, IOException {
                         responseTrigger.submitResponse(new BasicResponseProducer(
                                 HttpStatus.SC_OK,

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/MultiLineResponseHandler.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/MultiLineResponseHandler.java b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/MultiLineResponseHandler.java
index 4e025d4..1bbaaf0 100644
--- a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/MultiLineResponseHandler.java
+++ b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/MultiLineResponseHandler.java
@@ -36,7 +36,6 @@ import org.apache.hc.core5.http.Message;
 import org.apache.hc.core5.http.message.BasicHttpResponse;
 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.nio.BasicRequestConsumer;
 import org.apache.hc.core5.http.nio.BasicResponseProducer;
 import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer;
@@ -58,7 +57,7 @@ public class MultiLineResponseHandler extends BasicServerExchangeHandler<Message
                   @Override
                   public void handle(
                           final Message<HttpRequest, String> requestMessage,
-                          final AsyncServerResponseTrigger responseTrigger,
+                          final ResponseTrigger responseTrigger,
                           final HttpContext context) throws HttpException, IOException {
                       final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK);
                       responseTrigger.submitResponse(new BasicResponseProducer(

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/SingleLineResponseHandler.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/SingleLineResponseHandler.java b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/SingleLineResponseHandler.java
index af8d453..e0cbe2c 100644
--- a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/SingleLineResponseHandler.java
+++ b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/SingleLineResponseHandler.java
@@ -34,7 +34,6 @@ import org.apache.hc.core5.http.HttpStatus;
 import org.apache.hc.core5.http.Message;
 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.nio.BasicRequestConsumer;
 import org.apache.hc.core5.http.nio.BasicResponseProducer;
 import org.apache.hc.core5.http.nio.entity.BasicAsyncEntityProducer;
@@ -57,7 +56,7 @@ public class SingleLineResponseHandler extends BasicServerExchangeHandler<Messag
                   @Override
                   public void handle(
                           final Message<HttpRequest, String> requestMessage,
-                          final AsyncServerResponseTrigger responseTrigger,
+                          final ResponseTrigger responseTrigger,
                           final HttpContext context) throws HttpException, IOException {
                       responseTrigger.submitResponse(new BasicResponseProducer(
                               HttpStatus.SC_OK, new BasicAsyncEntityProducer(message)));

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/examples/org/apache/hc/core5/http/examples/AsyncFileServerExample.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/examples/org/apache/hc/core5/http/examples/AsyncFileServerExample.java b/httpcore5/src/examples/org/apache/hc/core5/http/examples/AsyncFileServerExample.java
index ed0c077..9a00868 100644
--- a/httpcore5/src/examples/org/apache/hc/core5/http/examples/AsyncFileServerExample.java
+++ b/httpcore5/src/examples/org/apache/hc/core5/http/examples/AsyncFileServerExample.java
@@ -46,7 +46,6 @@ import org.apache.hc.core5.http.impl.bootstrap.AsyncServerBootstrap;
 import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer;
 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.nio.BasicRequestConsumer;
 import org.apache.hc.core5.http.nio.BasicResponseProducer;
 import org.apache.hc.core5.http.nio.entity.FileEntityProducer;
@@ -94,7 +93,7 @@ public class AsyncFileServerExample {
                     @Override
                     public void handle(
                             final Message<HttpRequest, Void> message,
-                            final AsyncServerResponseTrigger responseTrigger,
+                            final ResponseTrigger responseTrigger,
                             final HttpContext context) throws HttpException, IOException {
                         HttpRequest request = message.getHead();
                         URI requestUri;

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/examples/org/apache/hc/core5/http/examples/AsyncReverseProxyExample.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/examples/org/apache/hc/core5/http/examples/AsyncReverseProxyExample.java b/httpcore5/src/examples/org/apache/hc/core5/http/examples/AsyncReverseProxyExample.java
index e66efcb..d0e7006 100644
--- a/httpcore5/src/examples/org/apache/hc/core5/http/examples/AsyncReverseProxyExample.java
+++ b/httpcore5/src/examples/org/apache/hc/core5/http/examples/AsyncReverseProxyExample.java
@@ -72,6 +72,7 @@ import org.apache.hc.core5.http.nio.CapacityChannel;
 import org.apache.hc.core5.http.nio.DataStreamChannel;
 import org.apache.hc.core5.http.nio.RequestChannel;
 import org.apache.hc.core5.http.nio.ResponseChannel;
+import org.apache.hc.core5.http.protocol.HttpContext;
 import org.apache.hc.core5.io.ShutdownType;
 import org.apache.hc.core5.pool.ConnPoolListener;
 import org.apache.hc.core5.pool.ConnPoolStats;
@@ -264,7 +265,8 @@ public class AsyncReverseProxyExample {
         public void handleRequest(
                 final HttpRequest incomingRequest,
                 final EntityDetails entityDetails,
-                final ResponseChannel responseChannel) throws HttpException, IOException {
+                final ResponseChannel responseChannel,
+                final HttpContext context) throws HttpException, IOException {
 
             synchronized (exchangeState) {
                 System.out.println("[client->proxy] " + exchangeState.id + " " +

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/AsyncServerBootstrap.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/AsyncServerBootstrap.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/AsyncServerBootstrap.java
index 8e5be10..d58d9ae 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/AsyncServerBootstrap.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/AsyncServerBootstrap.java
@@ -44,8 +44,10 @@ import org.apache.hc.core5.http.impl.nio.ServerHttp1IOEventHandlerFactory;
 import org.apache.hc.core5.http.impl.nio.ServerHttp1StreamDuplexerFactory;
 import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
 import org.apache.hc.core5.http.nio.AsyncServerRequestHandler;
+import org.apache.hc.core5.http.nio.HandlerFactory;
 import org.apache.hc.core5.http.nio.ssl.BasicServerTlsStrategy;
 import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
+import org.apache.hc.core5.http.nio.support.BasicAsyncServerExpectationDecorator;
 import org.apache.hc.core5.http.nio.support.BasicServerExchangeHandler;
 import org.apache.hc.core5.http.nio.support.DefaultAsyncResponseExchangeHandlerFactory;
 import org.apache.hc.core5.http.protocol.HttpProcessor;
@@ -255,9 +257,18 @@ public class AsyncServerBootstrap {
         for (final HandlerEntry<Supplier<AsyncServerExchangeHandler>> entry: handlerList) {
             registry.register(entry.hostname, entry.uriPattern, entry.handler);
         }
+        final HandlerFactory<AsyncServerExchangeHandler> handlerFactory = new DefaultAsyncResponseExchangeHandlerFactory(
+                registry, new Decorator<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler handler) {
+                return new BasicAsyncServerExpectationDecorator(handler);
+            }
+
+        });
         final ServerHttp1StreamDuplexerFactory streamHandlerFactory = new ServerHttp1StreamDuplexerFactory(
                 httpProcessor != null ? httpProcessor : HttpProcessors.server(),
-                new DefaultAsyncResponseExchangeHandlerFactory(registry),
+                handlerFactory,
                 h1Config != null ? h1Config : H1Config.DEFAULT,
                 charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT,
                 connStrategy != null ? connStrategy : DefaultConnectionReuseStrategy.INSTANCE,

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/HttpRequester.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/HttpRequester.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/HttpRequester.java
index 039e98b..22ec655 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/HttpRequester.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/HttpRequester.java
@@ -55,8 +55,9 @@ import org.apache.hc.core5.http.impl.io.HttpRequestExecutor;
 import org.apache.hc.core5.http.io.EofSensorInputStream;
 import org.apache.hc.core5.http.io.EofSensorWatcher;
 import org.apache.hc.core5.http.io.HttpClientConnection;
+import org.apache.hc.core5.http.io.HttpClientResponseHandler;
 import org.apache.hc.core5.http.io.HttpConnectionFactory;
-import org.apache.hc.core5.http.io.ResponseHandler;
+import org.apache.hc.core5.http.io.HttpResponseInformationCallback;
 import org.apache.hc.core5.http.io.entity.EntityUtils;
 import org.apache.hc.core5.http.io.entity.HttpEntityWrapper;
 import org.apache.hc.core5.http.protocol.HttpContext;
@@ -100,6 +101,7 @@ public class HttpRequester implements GracefullyCloseable {
     public ClassicHttpResponse execute(
             final HttpClientConnection connection,
             final ClassicHttpRequest request,
+            final HttpResponseInformationCallback informationCallback,
             final HttpContext context) throws HttpException, IOException {
         Args.notNull(connection, "HTTP connection");
         Args.notNull(request, "HTTP request");
@@ -108,11 +110,18 @@ public class HttpRequester implements GracefullyCloseable {
             throw new ConnectionClosedException("Connection is closed");
         }
         requestExecutor.preProcess(request, httpProcessor, context);
-        final ClassicHttpResponse response = requestExecutor.execute(request, connection, context);
+        final ClassicHttpResponse response = requestExecutor.execute(request, connection, informationCallback, context);
         requestExecutor.postProcess(response, httpProcessor, context);
         return response;
     }
 
+    public ClassicHttpResponse execute(
+            final HttpClientConnection connection,
+            final ClassicHttpRequest request,
+            final HttpContext context) throws HttpException, IOException {
+        return execute(connection, request, null, context);
+    }
+
     public boolean keepAlive(
             final HttpClientConnection connection,
             final ClassicHttpRequest request,
@@ -129,7 +138,7 @@ public class HttpRequester implements GracefullyCloseable {
             final HttpClientConnection connection,
             final ClassicHttpRequest request,
             final HttpContext context,
-            final ResponseHandler<T> responseHandler) throws HttpException, IOException {
+            final HttpClientResponseHandler<T> responseHandler) throws HttpException, IOException {
         try (final ClassicHttpResponse response = execute(connection, request, context)) {
             final T result = responseHandler.handleResponse(response);
             EntityUtils.consume(response.getEntity());
@@ -188,6 +197,7 @@ public class HttpRequester implements GracefullyCloseable {
     public ClassicHttpResponse execute(
             final HttpHost targetHost,
             final ClassicHttpRequest request,
+            final HttpResponseInformationCallback informationCallback,
             final Timeout connectTimeout,
             final HttpContext context) throws HttpException, IOException {
         Args.notNull(targetHost, "HTTP host");
@@ -212,7 +222,7 @@ public class HttpRequester implements GracefullyCloseable {
                 connection = connectFactory.createConnection(socket);
                 poolEntry.assignConnection(connection);
             }
-            final ClassicHttpResponse response = execute(connection, request, context);
+            final ClassicHttpResponse response = execute(connection, request, informationCallback, context);
             final HttpEntity entity = response.getEntity();
             if (entity != null) {
                 response.setEntity(new HttpEntityWrapper(entity) {
@@ -302,13 +312,21 @@ public class HttpRequester implements GracefullyCloseable {
         }
     }
 
+    public ClassicHttpResponse execute(
+            final HttpHost targetHost,
+            final ClassicHttpRequest request,
+            final Timeout connectTimeout,
+            final HttpContext context) throws HttpException, IOException {
+        return execute(targetHost, request, null, connectTimeout, context);
+    }
+
     public <T> T  execute(
             final HttpHost targetHost,
             final ClassicHttpRequest request,
             final Timeout connectTimeout,
             final HttpContext context,
-            final ResponseHandler<T> responseHandler) throws HttpException, IOException {
-        try (final ClassicHttpResponse response = execute(targetHost, request, connectTimeout, context)) {
+            final HttpClientResponseHandler<T> responseHandler) throws HttpException, IOException {
+        try (final ClassicHttpResponse response = execute(targetHost, request, null, connectTimeout, context)) {
             final T result = responseHandler.handleResponse(response);
             EntityUtils.consume(response.getEntity());
             return result;

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/ServerBootstrap.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/ServerBootstrap.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/ServerBootstrap.java
index 4bdbba7..c9a1452 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/ServerBootstrap.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/ServerBootstrap.java
@@ -50,7 +50,6 @@ import org.apache.hc.core5.http.impl.io.DefaultBHttpServerConnectionFactory;
 import org.apache.hc.core5.http.impl.io.DefaultClassicHttpResponseFactory;
 import org.apache.hc.core5.http.impl.io.HttpService;
 import org.apache.hc.core5.http.io.HttpConnectionFactory;
-import org.apache.hc.core5.http.io.HttpExpectationVerifier;
 import org.apache.hc.core5.http.io.HttpRequestHandler;
 import org.apache.hc.core5.http.protocol.HttpProcessor;
 import org.apache.hc.core5.http.protocol.RequestHandlerRegistry;
@@ -74,7 +73,6 @@ public class ServerBootstrap {
     private HttpProcessor httpProcessor;
     private ConnectionReuseStrategy connStrategy;
     private HttpResponseFactory<ClassicHttpResponse> responseFactory;
-    private HttpExpectationVerifier expectationVerifier;
     private ServerSocketFactory serverSocketFactory;
     private SSLContext sslContext;
     private SSLServerSetupHandler sslSetupHandler;
@@ -203,14 +201,6 @@ public class ServerBootstrap {
     }
 
     /**
-     * Assigns {@link HttpExpectationVerifier} instance.
-     */
-    public final ServerBootstrap setExpectationVerifier(final HttpExpectationVerifier expectationVerifier) {
-        this.expectationVerifier = expectationVerifier;
-        return this;
-    }
-
-    /**
      * Assigns {@link HttpConnectionFactory} instance.
      */
     public final ServerBootstrap setConnectionFactory(
@@ -286,8 +276,7 @@ public class ServerBootstrap {
         }
 
         final HttpService httpService = new HttpService(
-                httpProcessorCopy, connStrategyCopy, responseFactoryCopy, requestHandlerRegistry,
-                this.expectationVerifier, this.streamListener);
+                httpProcessorCopy, requestHandlerRegistry, connStrategyCopy, responseFactoryCopy, this.streamListener);
 
         ServerSocketFactory serverSocketFactoryCopy = this.serverSocketFactory;
         if (serverSocketFactoryCopy == null) {

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/DefaultBHttpClientConnection.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/DefaultBHttpClientConnection.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/DefaultBHttpClientConnection.java
index 00da305..9036caa 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/DefaultBHttpClientConnection.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/DefaultBHttpClientConnection.java
@@ -41,6 +41,7 @@ import org.apache.hc.core5.http.HttpException;
 import org.apache.hc.core5.http.HttpStatus;
 import org.apache.hc.core5.http.HttpVersion;
 import org.apache.hc.core5.http.LengthRequiredException;
+import org.apache.hc.core5.http.ProtocolException;
 import org.apache.hc.core5.http.ProtocolVersion;
 import org.apache.hc.core5.http.UnsupportedHttpVersionException;
 import org.apache.hc.core5.http.config.H1Config;
@@ -189,6 +190,10 @@ public class DefaultBHttpClientConnection extends BHttpConnectionBase
         }
         this.version = transportVersion;
         onResponseReceived(response);
+        final int status = response.getCode();
+        if (status < HttpStatus.SC_INFORMATIONAL) {
+            throw new ProtocolException("Invalid response: " + status);
+        }
         if (response.getCode() >= HttpStatus.SC_SUCCESS) {
             incrementResponseCount();
         }

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/HttpRequestExecutor.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/HttpRequestExecutor.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/HttpRequestExecutor.java
index d6bc0da..ac36432 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/HttpRequestExecutor.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/HttpRequestExecutor.java
@@ -39,11 +39,12 @@ import org.apache.hc.core5.http.HttpEntity;
 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.ProtocolException;
 import org.apache.hc.core5.http.ProtocolVersion;
 import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy;
 import org.apache.hc.core5.http.impl.Http1StreamListener;
 import org.apache.hc.core5.http.io.HttpClientConnection;
+import org.apache.hc.core5.http.io.HttpResponseInformationCallback;
+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;
@@ -95,34 +96,13 @@ public class HttpRequestExecutor {
     }
 
     /**
-     * Decide whether a response comes with an entity.
-     * The implementation in this class is based on RFC 2616.
-     * <p>
-     * Derived executors can override this method to handle
-     * methods and response codes not specified in RFC 2616.
-     * </p>
-     *
-     * @param request   the request, to obtain the executed method
-     * @param response  the response, to obtain the status code
-     */
-    protected boolean canResponseHaveBody(final ClassicHttpRequest request,
-                                          final ClassicHttpResponse response) {
-
-        if ("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;
-    }
-
-    /**
      * Sends the request and obtain a response.
      *
      * @param request   the request to execute.
      * @param conn      the connection over which to execute the request.
-     *
+     * @param informationCallback   callback to execute upon receipt of information status (1xx).
+     *                              May be null.
+     * @param context the context
      * @return  the response to the request.
      *
      * @throws IOException in case of an I/O error.
@@ -132,13 +112,12 @@ public class HttpRequestExecutor {
     public ClassicHttpResponse execute(
             final ClassicHttpRequest request,
             final HttpClientConnection conn,
+            final HttpResponseInformationCallback informationCallback,
             final HttpContext context) throws IOException, HttpException {
         Args.notNull(request, "HTTP request");
         Args.notNull(conn, "Client connection");
         Args.notNull(context, "HTTP context");
         try {
-            ClassicHttpResponse response = null;
-
             context.setAttribute(HttpCoreContext.SSL_SESSION, conn.getSSLSession());
             context.setAttribute(HttpCoreContext.CONNECTION_ENDPOINT, conn.getEndpointDetails());
 
@@ -146,50 +125,61 @@ public class HttpRequestExecutor {
             if (streamListener != null) {
                 streamListener.onRequestHead(conn, request);
             }
+            boolean expectContinue = false;
             final HttpEntity entity = request.getEntity();
             if (entity != null) {
                 final Header expect = request.getFirstHeader(HttpHeaders.EXPECT);
-                final boolean expectContinue = expect != null && "100-continue".equalsIgnoreCase(expect.getValue());
+                expectContinue = expect != null && "100-continue".equalsIgnoreCase(expect.getValue());
+                if (!expectContinue) {
+                    conn.sendRequestEntity(request);
+                }
+            }
+            conn.flush();
+            ClassicHttpResponse response = null;
+            while (response == null) {
                 if (expectContinue) {
-
-                    conn.flush();
-                    // Don't wait for a 100-continue response forever. On timeout, send the entity.
                     if (conn.isDataAvailable(this.waitForContinue)) {
                         response = conn.receiveResponseHeader();
                         if (streamListener != null) {
                             streamListener.onResponseHead(conn, response);
                         }
                         final int status = response.getCode();
-                        if (status < HttpStatus.SC_SUCCESS) {
-                            if (status != HttpStatus.SC_CONTINUE) {
-                                throw new ProtocolException("Unexpected response: " + response.getCode());
-                            }
+                        if (status == HttpStatus.SC_CONTINUE) {
                             // discard 100-continue
                             response = null;
                             conn.sendRequestEntity(request);
-                        } else {
-                            if (canResponseHaveBody(request, response)) {
-                                conn.receiveResponseEntity(response);
+                        } else if (status < HttpStatus.SC_SUCCESS) {
+                            if (informationCallback != null) {
+                                informationCallback.execute(response, conn, context);
                             }
+                            response = null;
+                            continue;
+                        } else if (status >= HttpStatus.SC_CLIENT_ERROR){
                             conn.terminateRequest(request);
+                        } else {
+                            conn.sendRequestEntity(request);
                         }
                     } else {
                         conn.sendRequestEntity(request);
                     }
+                    conn.flush();
+                    expectContinue = false;
                 } else {
-                    conn.sendRequestEntity(request);
+                    response = conn.receiveResponseHeader();
+                    if (streamListener != null) {
+                        streamListener.onResponseHead(conn, response);
+                    }
+                    final int status = response.getCode();
+                    if (status < HttpStatus.SC_SUCCESS) {
+                        if (informationCallback != null && status != HttpStatus.SC_CONTINUE) {
+                            informationCallback.execute(response, conn, context);
+                        }
+                        response = null;
+                    }
                 }
             }
-            conn.flush();
-
-            while (response == null || response.getCode() < HttpStatus.SC_OK) {
-                response = conn.receiveResponseHeader();
-                if (streamListener != null) {
-                    streamListener.onResponseHead(conn, response);
-                }
-                if (canResponseHaveBody(request, response)) {
-                    conn.receiveResponseEntity(response);
-                }
+            if (MessageSupport.canResponseHaveBody(request.getMethod(), response)) {
+                conn.receiveResponseEntity(response);
             }
             return response;
 
@@ -199,6 +189,25 @@ public class HttpRequestExecutor {
         }
     }
 
+    /**
+     * Sends the request and obtain a response.
+     *
+     * @param request   the request to execute.
+     * @param conn      the connection over which to execute the request.
+     * @param context the context
+     * @return  the response to the request.
+     *
+     * @throws IOException in case of an I/O error.
+     * @throws HttpException in case of HTTP protocol violation or a processing
+     *   problem.
+     */
+    public ClassicHttpResponse execute(
+            final ClassicHttpRequest request,
+            final HttpClientConnection conn,
+            final HttpContext context) throws IOException, HttpException {
+        return execute(request, conn, null, context);
+    }
+
     private static void closeConnection(final HttpClientConnection conn) {
         try {
             conn.close();