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:21 UTC

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

Repository: httpcomponents-core
Updated Branches:
  refs/heads/master c7d4ee28b -> 76c3a52e4


http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/76c3a52e/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestHttpService.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestHttpService.java b/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestHttpService.java
index b8a30c4..656c677 100644
--- a/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestHttpService.java
+++ b/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestHttpService.java
@@ -27,7 +27,9 @@
 
 package org.apache.hc.core5.http.impl.io;
 
+import java.io.IOException;
 import java.io.InputStream;
+import java.util.List;
 
 import org.apache.hc.core5.http.ClassicHttpRequest;
 import org.apache.hc.core5.http.ClassicHttpResponse;
@@ -36,12 +38,12 @@ import org.apache.hc.core5.http.HeaderElements;
 import org.apache.hc.core5.http.HttpException;
 import org.apache.hc.core5.http.HttpHeaders;
 import org.apache.hc.core5.http.HttpRequestMapper;
+import org.apache.hc.core5.http.HttpResponse;
 import org.apache.hc.core5.http.HttpResponseFactory;
 import org.apache.hc.core5.http.HttpStatus;
 import org.apache.hc.core5.http.MethodNotSupportedException;
 import org.apache.hc.core5.http.ProtocolException;
 import org.apache.hc.core5.http.UnsupportedHttpVersionException;
-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.entity.InputStreamEntity;
@@ -53,9 +55,12 @@ import org.apache.hc.core5.http.protocol.HttpProcessor;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatcher;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
 
 public class TestHttpService {
 
@@ -63,6 +68,8 @@ public class TestHttpService {
     private HttpProcessor httprocessor;
     @Mock
     private ConnectionReuseStrategy connReuseStrategy;
+    @Spy
+    private ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK);
     @Mock
     private HttpResponseFactory<ClassicHttpResponse> responseFactory;
     @Mock
@@ -79,9 +86,9 @@ public class TestHttpService {
         MockitoAnnotations.initMocks(this);
         httpservice = new HttpService(
                 httprocessor,
+                handlerResolver,
                 connReuseStrategy,
-                responseFactory,
-                handlerResolver);
+                responseFactory);
     }
 
     @Test
@@ -89,9 +96,9 @@ public class TestHttpService {
         try {
             new HttpService(
                     null,
+                    handlerResolver,
                     connReuseStrategy,
-                    responseFactory,
-                    handlerResolver);
+                    responseFactory);
             Assert.fail("IllegalArgumentException expected");
         } catch (final IllegalArgumentException expected) {
         }
@@ -102,7 +109,6 @@ public class TestHttpService {
         final HttpCoreContext context = HttpCoreContext.create();
         final ClassicHttpRequest request = new BasicClassicHttpRequest("GET", "/");
         Mockito.when(conn.receiveRequestHeader()).thenReturn(request);
-        final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
         Mockito.when(responseFactory.newHttpResponse(200)).thenReturn(response);
         Mockito.when(connReuseStrategy.keepAlive(request, response, context)).thenReturn(Boolean.FALSE);
 
@@ -119,6 +125,7 @@ public class TestHttpService {
         Mockito.verify(conn).sendResponseEntity(response);
         Mockito.verify(conn).flush();
         Mockito.verify(conn).close();
+        Mockito.verify(response).close();
     }
 
     @Test
@@ -130,25 +137,34 @@ public class TestHttpService {
         request.setEntity(entity);
 
         Mockito.when(conn.receiveRequestHeader()).thenReturn(request);
-        final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
         Mockito.when(responseFactory.newHttpResponse(200)).thenReturn(response);
-        Mockito.when(connReuseStrategy.keepAlive(request, response, context)).thenReturn(Boolean.FALSE);
+        Mockito.when(connReuseStrategy.keepAlive(Mockito.eq(request), Mockito.argThat(new ArgumentMatcher<HttpResponse>() {
+
+            @Override
+            public boolean matches(final HttpResponse errorResponse) {
+                return errorResponse.getCode() == HttpStatus.SC_NOT_IMPLEMENTED;
+            }
+
+        }), Mockito.eq(context))).thenReturn(Boolean.TRUE);
 
         httpservice.handleRequest(conn, context);
+        final ArgumentCaptor<ClassicHttpResponse> responseCaptor = ArgumentCaptor.forClass(ClassicHttpResponse.class);
+        Mockito.verify(conn).sendResponseHeader(responseCaptor.capture());
+        final ClassicHttpResponse response = responseCaptor.getValue();
+        Assert.assertNotNull(response);
 
         Assert.assertEquals(HttpStatus.SC_NOT_IMPLEMENTED, response.getCode());
 
         Assert.assertSame(request, context.getRequest());
-        Assert.assertSame(response, context.getResponse());
 
-        Mockito.verify(conn).receiveRequestEntity(request);
         Mockito.verify(httprocessor).process(request, request.getEntity(), context);
         Mockito.verify(instream).close();
         Mockito.verify(httprocessor).process(response, response.getEntity(), context);
         Mockito.verify(conn).sendResponseHeader(response);
         Mockito.verify(conn).sendResponseEntity(response);
         Mockito.verify(conn).flush();
-        Mockito.verify(conn).close();
+        Mockito.verify(conn, Mockito.never()).close();
+        Mockito.verify(response).close();
     }
 
     @Test
@@ -161,116 +177,38 @@ public class TestHttpService {
         request.setEntity(entity);
 
         Mockito.when(conn.receiveRequestHeader()).thenReturn(request);
-        final ClassicHttpResponse resp100 = new BasicClassicHttpResponse(100, "Continue");
-        Mockito.when(responseFactory.newHttpResponse(100)).thenReturn(resp100);
-        final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
         Mockito.when(responseFactory.newHttpResponse(200)).thenReturn(response);
-        Mockito.when(connReuseStrategy.keepAlive(request, response, context)).thenReturn(Boolean.FALSE);
-
-        httpservice.handleRequest(conn, context);
-
-        Assert.assertEquals(HttpStatus.SC_NOT_IMPLEMENTED, response.getCode());
-
-        Assert.assertSame(request, context.getRequest());
-        Assert.assertSame(response, context.getResponse());
-
-        Mockito.verify(conn).sendResponseHeader(resp100);
-        Mockito.verify(conn).receiveRequestEntity(request);
-        Mockito.verify(httprocessor).process(request, request.getEntity(), context);
-        Mockito.verify(instream).close();
-        Mockito.verify(httprocessor).process(response, response.getEntity(), context);
-        Mockito.verify(conn).sendResponseHeader(response);
-        Mockito.verify(conn).sendResponseEntity(response);
-        Mockito.verify(conn, Mockito.times(2)).flush();
-        Mockito.verify(conn).close();
-    }
-
-    @Test
-    public void testExecutionEntityEnclosingRequestCustomExpectationVerifier() throws Exception {
-        final HttpExpectationVerifier expectationVerifier = new HttpExpectationVerifier() {
+        Mockito.when(connReuseStrategy.keepAlive(Mockito.eq(request), Mockito.argThat(new ArgumentMatcher<HttpResponse>() {
 
             @Override
-            public void verify(
-                    final ClassicHttpRequest request,
-                    final ClassicHttpResponse response,
-                    final HttpContext context) throws HttpException {
-                response.setCode(HttpStatus.SC_UNAUTHORIZED);
+            public boolean matches(final HttpResponse errorResponse) {
+                return errorResponse.getCode() == HttpStatus.SC_NOT_IMPLEMENTED;
             }
 
-        };
-
-        final HttpService httpservice = new HttpService(
-                httprocessor,
-                connReuseStrategy,
-                responseFactory,
-                handlerResolver,
-                expectationVerifier,
-                null);
-        final HttpCoreContext context = HttpCoreContext.create();
-        final ClassicHttpRequest request = new BasicClassicHttpRequest("POST", "/");
-        request.addHeader(HttpHeaders.EXPECT, HeaderElements.CONTINUE);
-        final InputStream instream = Mockito.mock(InputStream.class);
-        final InputStreamEntity entity = new InputStreamEntity(instream, -1);
-        request.setEntity(entity);
-
-        Mockito.when(conn.receiveRequestHeader()).thenReturn(request);
-        final ClassicHttpResponse response = new BasicClassicHttpResponse(100, "Continue");
-        Mockito.when(responseFactory.newHttpResponse(100)).thenReturn(response);
-        Mockito.when(connReuseStrategy.keepAlive(request, response, context)).thenReturn(Boolean.FALSE);
-
-        httpservice.handleRequest(conn, context);
-
-        Assert.assertSame(request, context.getRequest());
-        Assert.assertSame(response, context.getResponse());
-
-        Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode());
-
-        Mockito.verify(conn).sendResponseHeader(response);
-        Mockito.verify(httprocessor).process(response, response.getEntity(), context);
-        Mockito.verify(conn).sendResponseHeader(response);
-        Mockito.verify(conn).sendResponseEntity(response);
-        Mockito.verify(conn).flush();
-        Mockito.verify(conn).receiveRequestEntity(request);
-    }
-
-    @Test
-    public void testExecutionExceptionInCustomExpectationVerifier() throws Exception {
-        final HttpExpectationVerifier expectationVerifier = Mockito.mock(HttpExpectationVerifier.class);
-        final HttpService httpservice = new HttpService(
-                httprocessor,
-                connReuseStrategy,
-                responseFactory,
-                handlerResolver,
-                expectationVerifier,
-                null);
-        final HttpCoreContext context = HttpCoreContext.create();
-        final ClassicHttpRequest request = new BasicClassicHttpRequest("POST", "/");
-        request.addHeader(HttpHeaders.EXPECT, "100-continue");
-        final InputStream instream = Mockito.mock(InputStream.class);
-        final InputStreamEntity entity = new InputStreamEntity(instream, -1);
-        request.setEntity(entity);
-
-        Mockito.when(conn.receiveRequestHeader()).thenReturn(request);
-        final ClassicHttpResponse resp100 = new BasicClassicHttpResponse(100, "Continue");
-        Mockito.when(responseFactory.newHttpResponse(100)).thenReturn(resp100);
-        final ClassicHttpResponse response = new BasicClassicHttpResponse(500, "Oppsie");
-        Mockito.when(responseFactory.newHttpResponse(500)).thenReturn(response);
-        Mockito.doThrow(new HttpException("Oopsie")).when(expectationVerifier).verify(request, resp100, context);
-        Mockito.when(connReuseStrategy.keepAlive(request, response, context)).thenReturn(Boolean.FALSE);
+        }), Mockito.eq(context))).thenReturn(Boolean.TRUE);
 
         httpservice.handleRequest(conn, context);
+        final ArgumentCaptor<ClassicHttpResponse> responseCaptor = ArgumentCaptor.forClass(ClassicHttpResponse.class);
+        Mockito.verify(conn, Mockito.times(2)).sendResponseHeader(responseCaptor.capture());
+        final List<ClassicHttpResponse> responses = responseCaptor.getAllValues();
+        Assert.assertNotNull(responses);
+        Assert.assertEquals(2, responses.size());
+        final ClassicHttpResponse ack = responses.get(0);
+        final ClassicHttpResponse response = responses.get(1);
+
+        Assert.assertEquals(HttpStatus.SC_CONTINUE, ack.getCode());
+        Assert.assertEquals(HttpStatus.SC_NOT_IMPLEMENTED, response.getCode());
 
         Assert.assertSame(request, context.getRequest());
-        Assert.assertSame(response, context.getResponse());
-
-        Assert.assertEquals(HttpStatus.SC_INTERNAL_SERVER_ERROR, response.getCode());
 
-        Mockito.verify(conn).sendResponseHeader(response);
+        Mockito.verify(httprocessor).process(request, request.getEntity(), context);
+        Mockito.verify(instream).close();
         Mockito.verify(httprocessor).process(response, response.getEntity(), context);
         Mockito.verify(conn).sendResponseHeader(response);
         Mockito.verify(conn).sendResponseEntity(response);
-        Mockito.verify(conn).flush();
-        Mockito.verify(conn).receiveRequestEntity(request);
+        Mockito.verify(conn, Mockito.times(2)).flush();
+        Mockito.verify(conn, Mockito.never()).close();
+        Mockito.verify(response).close();
     }
 
     @Test
@@ -279,27 +217,24 @@ public class TestHttpService {
         final ClassicHttpRequest request = new BasicClassicHttpRequest("whatever", "/");
 
         Mockito.when(conn.receiveRequestHeader()).thenReturn(request);
-        final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
         Mockito.when(responseFactory.newHttpResponse(200)).thenReturn(response);
-        final ClassicHttpResponse error = new BasicClassicHttpResponse(500, "Oppsie");
-        Mockito.when(responseFactory.newHttpResponse(500)).thenReturn(error);
         Mockito.when(handlerResolver.resolve(request, context)).thenReturn(requestHandler);
         Mockito.doThrow(new MethodNotSupportedException("whatever")).when(
                 requestHandler).handle(request, response, context);
-        Mockito.when(connReuseStrategy.keepAlive(request, error, context)).thenReturn(Boolean.FALSE);
 
         httpservice.handleRequest(conn, context);
+        final ArgumentCaptor<ClassicHttpResponse> responseCaptor = ArgumentCaptor.forClass(ClassicHttpResponse.class);
+        Mockito.verify(conn).sendResponseHeader(responseCaptor.capture());
+        final ClassicHttpResponse error = responseCaptor.getValue();
+        Assert.assertNotNull(error);
 
         Assert.assertSame(request, context.getRequest());
-        Assert.assertSame(error, context.getResponse());
 
         Assert.assertEquals(HttpStatus.SC_NOT_IMPLEMENTED, error.getCode());
 
-        Mockito.verify(conn).sendResponseHeader(error);
         Mockito.verify(httprocessor).process(error, error.getEntity(), context);
         Mockito.verify(conn).sendResponseHeader(error);
         Mockito.verify(conn).sendResponseEntity(error);
-        Mockito.verify(conn).flush();
         Mockito.verify(conn).close();
     }
 
@@ -309,27 +244,24 @@ public class TestHttpService {
         final ClassicHttpRequest request = new BasicClassicHttpRequest("whatever", "/");
 
         Mockito.when(conn.receiveRequestHeader()).thenReturn(request);
-        final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
         Mockito.when(responseFactory.newHttpResponse(200)).thenReturn(response);
-        final ClassicHttpResponse error = new BasicClassicHttpResponse(500, "Oppsie");
-        Mockito.when(responseFactory.newHttpResponse(500)).thenReturn(error);
         Mockito.when(handlerResolver.resolve(request, context)).thenReturn(requestHandler);
         Mockito.doThrow(new UnsupportedHttpVersionException()).when(
                 requestHandler).handle(request, response, context);
-        Mockito.when(connReuseStrategy.keepAlive(request, error, context)).thenReturn(Boolean.FALSE);
 
         httpservice.handleRequest(conn, context);
+        final ArgumentCaptor<ClassicHttpResponse> responseCaptor = ArgumentCaptor.forClass(ClassicHttpResponse.class);
+        Mockito.verify(conn).sendResponseHeader(responseCaptor.capture());
+        final ClassicHttpResponse error = responseCaptor.getValue();
+        Assert.assertNotNull(error);
 
         Assert.assertSame(request, context.getRequest());
-        Assert.assertSame(error, context.getResponse());
 
         Assert.assertEquals(HttpStatus.SC_HTTP_VERSION_NOT_SUPPORTED, error.getCode());
 
-        Mockito.verify(conn).sendResponseHeader(error);
         Mockito.verify(httprocessor).process(error, error.getEntity(), context);
         Mockito.verify(conn).sendResponseHeader(error);
         Mockito.verify(conn).sendResponseEntity(error);
-        Mockito.verify(conn).flush();
         Mockito.verify(conn).close();
     }
 
@@ -339,27 +271,24 @@ public class TestHttpService {
         final ClassicHttpRequest request = new BasicClassicHttpRequest("whatever", "/");
 
         Mockito.when(conn.receiveRequestHeader()).thenReturn(request);
-        final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
         Mockito.when(responseFactory.newHttpResponse(200)).thenReturn(response);
-        final ClassicHttpResponse error = new BasicClassicHttpResponse(500, "Oppsie");
-        Mockito.when(responseFactory.newHttpResponse(500)).thenReturn(error);
         Mockito.when(handlerResolver.resolve(request, context)).thenReturn(requestHandler);
         Mockito.doThrow(new ProtocolException("oh, this world is wrong")).when(
                 requestHandler).handle(request, response, context);
-        Mockito.when(connReuseStrategy.keepAlive(request, error, context)).thenReturn(Boolean.FALSE);
 
         httpservice.handleRequest(conn, context);
+        final ArgumentCaptor<ClassicHttpResponse> responseCaptor = ArgumentCaptor.forClass(ClassicHttpResponse.class);
+        Mockito.verify(conn).sendResponseHeader(responseCaptor.capture());
+        final ClassicHttpResponse error = responseCaptor.getValue();
+        Assert.assertNotNull(error);
 
         Assert.assertSame(request, context.getRequest());
-        Assert.assertSame(error, context.getResponse());
 
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, error.getCode());
 
-        Mockito.verify(conn).sendResponseHeader(error);
         Mockito.verify(httprocessor).process(error, error.getEntity(), context);
         Mockito.verify(conn).sendResponseHeader(error);
         Mockito.verify(conn).sendResponseEntity(error);
-        Mockito.verify(conn).flush();
         Mockito.verify(conn).close();
     }
 
@@ -368,7 +297,6 @@ public class TestHttpService {
         final HttpCoreContext context = HttpCoreContext.create();
         final ClassicHttpRequest request = new BasicClassicHttpRequest("GET", "/");
         Mockito.when(conn.receiveRequestHeader()).thenReturn(request);
-        final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
         Mockito.when(responseFactory.newHttpResponse(200)).thenReturn(response);
         Mockito.when(handlerResolver.resolve(request, context)).thenReturn(requestHandler);
         Mockito.when(connReuseStrategy.keepAlive(request, response, context)).thenReturn(Boolean.TRUE);
@@ -386,6 +314,7 @@ public class TestHttpService {
         Mockito.verify(conn).sendResponseEntity(response);
         Mockito.verify(conn).flush();
         Mockito.verify(conn, Mockito.never()).close();
+        Mockito.verify(response).close();
     }
 
     @Test
@@ -394,9 +323,17 @@ public class TestHttpService {
         final ClassicHttpRequest request = new BasicClassicHttpRequest("GET", "/");
 
         Mockito.when(conn.receiveRequestHeader()).thenReturn(request);
-        final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_NO_CONTENT, "No Content");
         Mockito.when(responseFactory.newHttpResponse(200)).thenReturn(response);
-        Mockito.when(handlerResolver.resolve(request, context)).thenReturn(requestHandler);
+        Mockito.when(handlerResolver.resolve(request, context)).thenReturn(new HttpRequestHandler() {
+
+            @Override
+            public void handle(
+                    final ClassicHttpRequest request,
+                    final ClassicHttpResponse response,
+                    final HttpContext context) throws HttpException, IOException {
+                response.setCode(HttpStatus.SC_NO_CONTENT);
+            }
+        });
         Mockito.when(connReuseStrategy.keepAlive(request, response, context)).thenReturn(Boolean.TRUE);
 
         httpservice.handleRequest(conn, context);
@@ -404,12 +341,12 @@ public class TestHttpService {
         Assert.assertSame(request, context.getRequest());
 
         Mockito.verify(httprocessor).process(response, response.getEntity(), context);
-        Mockito.verify(requestHandler).handle(request, response, context);
 
         Mockito.verify(conn).sendResponseHeader(response);
         Mockito.verify(conn, Mockito.never()).sendResponseEntity(Mockito.<ClassicHttpResponse>any());
         Mockito.verify(conn).flush();
         Mockito.verify(conn, Mockito.never()).close();
+        Mockito.verify(response).close();
     }
 
     @Test
@@ -418,7 +355,6 @@ public class TestHttpService {
         final ClassicHttpRequest request = new BasicClassicHttpRequest("HEAD", "/");
 
         Mockito.when(conn.receiveRequestHeader()).thenReturn(request);
-        final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
         Mockito.when(responseFactory.newHttpResponse(200)).thenReturn(response);
         Mockito.when(handlerResolver.resolve(request, context)).thenReturn(requestHandler);
         Mockito.when(connReuseStrategy.keepAlive(request, response, context)).thenReturn(Boolean.TRUE);
@@ -434,6 +370,7 @@ public class TestHttpService {
         Mockito.verify(conn, Mockito.never()).sendResponseEntity(Mockito.<ClassicHttpResponse>any());
         Mockito.verify(conn).flush();
         Mockito.verify(conn, Mockito.never()).close();
+        Mockito.verify(response).close();
     }
 
 }


[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

Posted by ol...@apache.org.
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();


[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

Posted by ol...@apache.org.
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