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 2016/09/27 17:50:56 UTC

svn commit: r1762528 - in /httpcomponents/httpcore/trunk/httpcore5-h2/src: main/java/org/apache/hc/core5/http2/impl/nio/ main/java/org/apache/hc/core5/http2/nio/ main/java/org/apache/hc/core5/http2/nio/entity/ test/java/org/apache/hc/core5/http2/integr...

Author: olegk
Date: Tue Sep 27 17:50:56 2016
New Revision: 1762528

URL: http://svn.apache.org/viewvc?rev=1762528&view=rev
Log:
RFC 7230: Expect-continue handshake for HTTP/2 streams

Added:
    httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/ExpectationChannel.java
      - copied, changed from r1762527, httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/AsyncExchangeHandler.java
Modified:
    httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttp2StreamHandler.java
    httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/MessageState.java
    httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttp2StreamHandler.java
    httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerPushHttp2StreamHandler.java
    httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/AbstractAsyncExchangeHandler.java
    httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/AbstractClassicExchangeHandler.java
    httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/AsyncExchangeHandler.java
    httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/entity/AbstractClassicEntityProducer.java
    httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/entity/SharedOutputBuffer.java
    httpcomponents/httpcore/trunk/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/integration/Http2IntegrationTest.java

Modified: httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttp2StreamHandler.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttp2StreamHandler.java?rev=1762528&r1=1762527&r2=1762528&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttp2StreamHandler.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttp2StreamHandler.java Tue Sep 27 17:50:56 2016
@@ -35,26 +35,27 @@ import org.apache.hc.core5.concurrent.Fu
 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.HttpVersion;
 import org.apache.hc.core5.http.ProtocolException;
 import org.apache.hc.core5.http.impl.BasicHttpConnectionMetrics;
 import org.apache.hc.core5.http.protocol.HttpContext;
 import org.apache.hc.core5.http.protocol.HttpCoreContext;
 import org.apache.hc.core5.http.protocol.HttpProcessor;
-import org.apache.hc.core5.http2.H2ConnectionException;
-import org.apache.hc.core5.http2.H2Error;
 import org.apache.hc.core5.http2.impl.DefaultH2RequestConverter;
 import org.apache.hc.core5.http2.impl.DefaultH2ResponseConverter;
 import org.apache.hc.core5.http2.impl.IncomingEntityDetails;
-import org.apache.hc.core5.http2.nio.AsyncPushProducer;
 import org.apache.hc.core5.http2.nio.AsyncRequestProducer;
 import org.apache.hc.core5.http2.nio.AsyncResponseConsumer;
+import org.apache.hc.core5.http2.nio.DataStreamChannel;
 
 class ClientHttp2StreamHandler<T> implements Http2StreamHandler {
 
-    private final Http2StreamChannel internalOutputChannel;
+    private final Http2StreamChannel outputChannel;
+    private final DataStreamChannel dataChannel;
     private final HttpProcessor httpProcessor;
     private final BasicHttpConnectionMetrics connMetrics;
     private final AsyncRequestProducer requestProducer;
@@ -74,23 +75,8 @@ class ClientHttp2StreamHandler<T> implem
             final AsyncResponseConsumer<T> responseConsumer,
             final HttpContext context,
             final FutureCallback<T> resultCallback) {
-        this.internalOutputChannel = new Http2StreamChannel() {
-
-            @Override
-            public void submit(final List<Header> headers, final boolean endStream) throws HttpException, IOException {
-                outputChannel.submit(headers, endStream);
-                requestState = endStream ? MessageState.COMPLETE : MessageState.BODY;
-            }
-
-            @Override
-            public void push(final List<Header> headers, final AsyncPushProducer pushProducer) throws IOException {
-                throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Push not allowed");
-            }
-
-            @Override
-            public void update(final int increment) throws IOException {
-                outputChannel.update(increment);
-            }
+        this.outputChannel = outputChannel;
+        this.dataChannel = new DataStreamChannel() {
 
             @Override
             public void requestOutput() {
@@ -149,16 +135,26 @@ class ClientHttp2StreamHandler<T> implem
                 context.setAttribute(HttpCoreContext.HTTP_REQUEST, request);
                 context.setAttribute(HttpCoreContext.HTTP_CONNECTION, this);
                 httpProcessor.process(request, entityDetails, context);
+                connMetrics.incrementRequestCount();
 
                 final List<Header> headers = DefaultH2RequestConverter.INSTANCE.convert(request);
-                internalOutputChannel.submit(headers, entityDetails == null);
-                connMetrics.incrementRequestCount();
-                if (entityDetails != null) {
-                    requestProducer.dataStart(internalOutputChannel);
+                outputChannel.submit(headers, entityDetails == null);
+
+                if (entityDetails == null) {
+                    requestState = MessageState.COMPLETE;
+                } else {
+                    final Header h = request.getFirstHeader(HttpHeaders.EXPECT);
+                    final boolean expectContinue = h != null && "100-continue".equalsIgnoreCase(h.getValue());
+                    if (expectContinue) {
+                        requestState = MessageState.ACK;
+                    } else {
+                        requestState = MessageState.BODY;
+                        requestProducer.dataStart(dataChannel);
+                    }
                 }
                 break;
             case BODY:
-                requestProducer.produce(internalOutputChannel);
+                requestProducer.produce(dataChannel);
                 break;
         }
     }
@@ -176,6 +172,14 @@ class ClientHttp2StreamHandler<T> implem
         final HttpResponse response = DefaultH2ResponseConverter.INSTANCE.convert(headers);
         final EntityDetails entityDetails = endStream ? null : new IncomingEntityDetails(response);
 
+        if (response.getCode() < 200) {
+            if (response.getCode() == HttpStatus.SC_CONTINUE && requestState == MessageState.ACK) {
+                requestState = MessageState.BODY;
+                requestProducer.dataStart(dataChannel);
+            }
+            return;
+        }
+
         context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response);
         httpProcessor.process(response, entityDetails, context);
         connMetrics.incrementResponseCount();
@@ -214,7 +218,7 @@ class ClientHttp2StreamHandler<T> implem
 
     @Override
     public void updateInputCapacity() throws IOException {
-        responseConsumer.updateCapacity(internalOutputChannel);
+        responseConsumer.updateCapacity(outputChannel);
     }
 
     @Override

Modified: httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/MessageState.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/MessageState.java?rev=1762528&r1=1762527&r2=1762528&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/MessageState.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/MessageState.java Tue Sep 27 17:50:56 2016
@@ -26,4 +26,4 @@
  */
 package org.apache.hc.core5.http2.impl.nio;
 
-enum MessageState { IDLE, HEADERS, BODY, COMPLETE }
\ No newline at end of file
+enum MessageState { IDLE, HEADERS, ACK, BODY, COMPLETE }
\ No newline at end of file

Modified: httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttp2StreamHandler.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttp2StreamHandler.java?rev=1762528&r1=1762527&r2=1762528&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttp2StreamHandler.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttp2StreamHandler.java Tue Sep 27 17:50:56 2016
@@ -34,11 +34,14 @@ import java.util.concurrent.atomic.Atomi
 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.HttpVersion;
 import org.apache.hc.core5.http.ProtocolException;
 import org.apache.hc.core5.http.impl.BasicHttpConnectionMetrics;
+import org.apache.hc.core5.http.message.BasicHttpResponse;
 import org.apache.hc.core5.http.protocol.HttpCoreContext;
 import org.apache.hc.core5.http.protocol.HttpProcessor;
 import org.apache.hc.core5.http2.H2ConnectionException;
@@ -48,19 +51,25 @@ import org.apache.hc.core5.http2.impl.De
 import org.apache.hc.core5.http2.impl.IncomingEntityDetails;
 import org.apache.hc.core5.http2.nio.AsyncExchangeHandler;
 import org.apache.hc.core5.http2.nio.AsyncPushProducer;
+import org.apache.hc.core5.http2.nio.DataStreamChannel;
+import org.apache.hc.core5.http2.nio.ExpectationChannel;
 import org.apache.hc.core5.http2.nio.HandlerFactory;
 import org.apache.hc.core5.http2.nio.ResponseChannel;
 import org.apache.hc.core5.util.Asserts;
 
 public class ServerHttp2StreamHandler implements Http2StreamHandler {
 
-    private final Http2StreamChannel internalOutputChannel;
+    private final Http2StreamChannel outputChannel;
+    private final DataStreamChannel dataChannel;
     private final HttpProcessor httpProcessor;
     private final BasicHttpConnectionMetrics connMetrics;
     private final HandlerFactory<AsyncExchangeHandler> exchangeHandlerFactory;
     private final HttpCoreContext context;
+    private final AtomicBoolean responseCommitted;
     private final AtomicBoolean done;
 
+    private volatile HttpRequest request;
+    private volatile EntityDetails requestEntityDetails;
     private volatile AsyncExchangeHandler exchangeHandler;
     private volatile MessageState requestState;
     private volatile MessageState responseState;
@@ -70,23 +79,8 @@ public class ServerHttp2StreamHandler im
             final HttpProcessor httpProcessor,
             final BasicHttpConnectionMetrics connMetrics,
             final HandlerFactory<AsyncExchangeHandler> exchangeHandlerFactory) {
-        this.internalOutputChannel = new Http2StreamChannel() {
-
-            @Override
-            public void submit(final List<Header> headers, final boolean endStream) throws HttpException, IOException {
-                outputChannel.submit(headers, endStream);
-                responseState = endStream ? MessageState.COMPLETE : MessageState.BODY;
-            }
-
-            @Override
-            public void push(final List<Header> headers, final AsyncPushProducer responseProducer) throws HttpException, IOException {
-                outputChannel.push(headers, responseProducer);
-            }
-
-            @Override
-            public void update(final int increment) throws IOException {
-                outputChannel.update(increment);
-            }
+        this.outputChannel = outputChannel;
+        this.dataChannel = new DataStreamChannel() {
 
             @Override
             public void requestOutput() {
@@ -115,11 +109,53 @@ public class ServerHttp2StreamHandler im
         this.connMetrics = connMetrics;
         this.exchangeHandlerFactory = exchangeHandlerFactory;
         this.context = HttpCoreContext.create();
+        this.responseCommitted = new AtomicBoolean(false);
         this.done = new AtomicBoolean(false);
         this.requestState = MessageState.HEADERS;
         this.responseState = MessageState.IDLE;
     }
 
+    private void commitResponse(
+            final HttpResponse response,
+            final EntityDetails responseEntityDetails) throws HttpException, IOException {
+        if (responseCommitted.compareAndSet(false, true)) {
+
+            context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response);
+            httpProcessor.process(response, responseEntityDetails, context);
+
+            final List<Header> responseHeaders = DefaultH2ResponseConverter.INSTANCE.convert(response);
+
+            outputChannel.submit(responseHeaders, responseEntityDetails == null);
+            connMetrics.incrementResponseCount();
+            if (responseEntityDetails == null) {
+                responseState = MessageState.COMPLETE;
+            } else {
+                responseState = MessageState.BODY;
+                exchangeHandler.produce(dataChannel);
+            }
+        } else {
+            throw new H2ConnectionException(H2Error.INTERNAL_ERROR, "Response already committed");
+        }
+    }
+
+    private void commitPromise(
+            final HttpRequest promise,
+            final AsyncPushProducer pushProducer) throws HttpException, IOException {
+
+        httpProcessor.process(promise, null, context);
+
+        final List<Header> promiseHeaders = DefaultH2RequestConverter.INSTANCE.convert(promise);
+        outputChannel.push(promiseHeaders, pushProducer);
+        connMetrics.incrementRequestCount();
+    }
+
+    private void commitContinue() throws IOException, HttpException {
+        final HttpResponse ack = new BasicHttpResponse(HttpStatus.SC_CONTINUE);
+        final List<Header> responseHeaders = DefaultH2ResponseConverter.INSTANCE.convert(ack);
+
+        outputChannel.submit(responseHeaders, false);
+    }
+
     @Override
     public void consumePromise(final List<Header> headers) throws HttpException, IOException {
         throw new ProtocolException("Unexpected message promise");
@@ -146,52 +182,68 @@ public class ServerHttp2StreamHandler im
             throw new H2ConnectionException(H2Error.INTERNAL_ERROR,
                     "Unable to handle " + request.getMethod() + " " + request.getPath());
         }
-        exchangeHandler.handleRequest(request, requestEntityDetails, new ResponseChannel() {
 
-            private final AtomicBoolean responseCommitted = new AtomicBoolean(false);
+        boolean expectContinue = false;
+        if (requestEntityDetails != null) {
+            final Header h = request.getFirstHeader(HttpHeaders.EXPECT);
+            if ("100-continue".equalsIgnoreCase(h.getValue())) {
+                expectContinue = true;
+            }
+        }
 
-            @Override
-            public void sendResponse(
-                    final HttpResponse response, final EntityDetails responseEntityDetails) throws HttpException, IOException {
-                if (responseCommitted.compareAndSet(false, true)) {
-
-                    context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response);
-                    httpProcessor.process(response, responseEntityDetails, context);
-
-                    final List<Header> responseHeaders = DefaultH2ResponseConverter.INSTANCE.convert(response);
-
-                    internalOutputChannel.submit(responseHeaders, responseEntityDetails == null);
-                    connMetrics.incrementResponseCount();
-                    if (responseEntityDetails != null) {
-                        exchangeHandler.produce(internalOutputChannel);
-                    }
-                } else {
-                    throw new H2ConnectionException(H2Error.INTERNAL_ERROR, "Response already committed");
+        if (expectContinue) {
+            exchangeHandler.verify(request, requestEntityDetails, new ExpectationChannel() {
+
+                @Override
+                public void sendResponse(
+                        final HttpResponse response, final EntityDetails responseEntityDetails) throws HttpException, IOException {
+                    commitResponse(response, responseEntityDetails);
                 }
-            }
 
-            @Override
-            public void pushPromise(final HttpRequest promise, final AsyncPushProducer pushProducer) throws HttpException, IOException {
+                @Override
+                public void sendContinue() throws HttpException, IOException {
+                    commitContinue();
+                    exchangeHandler.handleRequest(request, requestEntityDetails, new ResponseChannel() {
+
+                        @Override
+                        public void sendResponse(
+                                final HttpResponse response, final EntityDetails responseEntityDetails) throws HttpException, IOException {
+                            commitResponse(response, responseEntityDetails);
+                        }
+
+                        @Override
+                        public void pushPromise(
+                                final HttpRequest promise, final AsyncPushProducer pushProducer) throws HttpException, IOException {
+                            commitPromise(promise, pushProducer);
+                        }
 
-                httpProcessor.process(promise, null, context);
+                    });
+                }
 
-                final List<Header> promiseHeaders = DefaultH2RequestConverter.INSTANCE.convert(promise);
-                internalOutputChannel.push(promiseHeaders, pushProducer);
-                connMetrics.incrementRequestCount();
-            }
+            });
+        } else {
+            exchangeHandler.handleRequest(request, requestEntityDetails, new ResponseChannel() {
+
+                @Override
+                public void sendResponse(
+                        final HttpResponse response, final EntityDetails responseEntityDetails) throws HttpException, IOException {
+                    commitResponse(response, responseEntityDetails);
+                }
 
-            @Override
-            public String toString() {
-                return internalOutputChannel.toString();
-            }
+                @Override
+                public void pushPromise(
+                        final HttpRequest promise, final AsyncPushProducer pushProducer) throws HttpException, IOException {
+                    commitPromise(promise, pushProducer);
+                }
 
-        });
+            });
+        }
     }
 
     @Override
     public void updateInputCapacity() throws IOException {
         Asserts.notNull(exchangeHandler, "Exchange handler");
-        exchangeHandler.updateCapacity(internalOutputChannel);
+        exchangeHandler.updateCapacity(outputChannel);
     }
 
     @Override
@@ -218,7 +270,7 @@ public class ServerHttp2StreamHandler im
     public void produceOutput() throws HttpException, IOException {
         if (responseState == MessageState.BODY) {
             Asserts.notNull(exchangeHandler, "Exchange handler");
-            exchangeHandler.produce(internalOutputChannel);
+            exchangeHandler.produce(dataChannel);
         }
     }
 

Modified: httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerPushHttp2StreamHandler.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerPushHttp2StreamHandler.java?rev=1762528&r1=1762527&r2=1762528&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerPushHttp2StreamHandler.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerPushHttp2StreamHandler.java Tue Sep 27 17:50:56 2016
@@ -41,16 +41,16 @@ import org.apache.hc.core5.http.Protocol
 import org.apache.hc.core5.http.impl.BasicHttpConnectionMetrics;
 import org.apache.hc.core5.http.protocol.HttpCoreContext;
 import org.apache.hc.core5.http.protocol.HttpProcessor;
-import org.apache.hc.core5.http2.H2ConnectionException;
-import org.apache.hc.core5.http2.H2Error;
 import org.apache.hc.core5.http2.impl.DefaultH2RequestConverter;
 import org.apache.hc.core5.http2.impl.DefaultH2ResponseConverter;
 import org.apache.hc.core5.http2.nio.AsyncPushProducer;
+import org.apache.hc.core5.http2.nio.DataStreamChannel;
 import org.apache.hc.core5.http2.nio.ResponseChannel;
 
 class ServerPushHttp2StreamHandler implements Http2StreamHandler {
 
-    private final Http2StreamChannel internalOutputChannel;
+    private final Http2StreamChannel outputChannel;
+    private final DataStreamChannel dataChannel;
     private final HttpProcessor httpProcessor;
     private final BasicHttpConnectionMetrics connMetrics;
     private final AsyncPushProducer pushProducer;
@@ -65,23 +65,8 @@ class ServerPushHttp2StreamHandler imple
             final HttpProcessor httpProcessor,
             final BasicHttpConnectionMetrics connMetrics,
             final AsyncPushProducer pushProducer) {
-        this.internalOutputChannel = new Http2StreamChannel() {
-
-            @Override
-            public void submit(final List<Header> headers, final boolean endStream) throws HttpException, IOException {
-                outputChannel.submit(headers, endStream);
-                responseState = endStream ? MessageState.COMPLETE : MessageState.BODY;
-            }
-
-            @Override
-            public void push(final List<Header> headers, final AsyncPushProducer pushProducer) throws IOException {
-                throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Push not allowed");
-            }
-
-            @Override
-            public void update(final int increment) throws IOException {
-                outputChannel.update(increment);
-            }
+        this.outputChannel = outputChannel;
+        this.dataChannel = new DataStreamChannel() {
 
             @Override
             public void requestOutput() {
@@ -166,7 +151,8 @@ class ServerPushHttp2StreamHandler imple
                             httpProcessor.process(response, entityDetails, context);
 
                             final List<Header> headers = DefaultH2ResponseConverter.INSTANCE.convert(response);
-                            internalOutputChannel.submit(headers, entityDetails == null);
+                            outputChannel.submit(headers, entityDetails == null);
+                            responseState = entityDetails == null ? MessageState.COMPLETE : MessageState.BODY;
                             connMetrics.incrementResponseCount();
                         }
                     }
@@ -182,14 +168,14 @@ class ServerPushHttp2StreamHandler imple
 
                         final List<Header> headers = DefaultH2RequestConverter.INSTANCE.convert(promise);
 
-                        internalOutputChannel.push(headers, pushProducer);
+                        outputChannel.push(headers, pushProducer);
                         connMetrics.incrementRequestCount();
                     }
 
                 });
                 break;
             case BODY:
-                pushProducer.produce(internalOutputChannel);
+                pushProducer.produce(dataChannel);
                 break;
         }
     }

Modified: httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/AbstractAsyncExchangeHandler.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/AbstractAsyncExchangeHandler.java?rev=1762528&r1=1762527&r2=1762528&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/AbstractAsyncExchangeHandler.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/AbstractAsyncExchangeHandler.java Tue Sep 27 17:50:56 2016
@@ -72,6 +72,14 @@ public abstract class AbstractAsyncExcha
     protected abstract void handle(Message<HttpRequest, T> request, AsyncResponseTrigger responseTrigger) throws IOException, HttpException;
 
     @Override
+    public void verify(
+            final HttpRequest request,
+            final EntityDetails entityDetails,
+            final ExpectationChannel expectationChannel) throws HttpException, IOException {
+        expectationChannel.sendContinue();
+    }
+
+    @Override
     public final void handleRequest(
             final HttpRequest request,
             final EntityDetails entityDetails,

Modified: httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/AbstractClassicExchangeHandler.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/AbstractClassicExchangeHandler.java?rev=1762528&r1=1762527&r2=1762528&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/AbstractClassicExchangeHandler.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/AbstractClassicExchangeHandler.java Tue Sep 27 17:50:56 2016
@@ -85,6 +85,14 @@ public abstract class AbstractClassicExc
             HttpResponse response, OutputStream responseStream) throws IOException, HttpException;
 
     @Override
+    public void verify(
+            final HttpRequest request,
+            final EntityDetails entityDetails,
+            final ExpectationChannel expectationChannel) throws HttpException, IOException {
+        expectationChannel.sendContinue();
+    }
+
+    @Override
     public final void handleRequest(
             final HttpRequest request,
             final EntityDetails entityDetails,

Modified: httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/AsyncExchangeHandler.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/AsyncExchangeHandler.java?rev=1762528&r1=1762527&r2=1762528&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/AsyncExchangeHandler.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/AsyncExchangeHandler.java Tue Sep 27 17:50:56 2016
@@ -39,6 +39,8 @@ import org.apache.hc.core5.http.HttpRequ
  */
 public interface AsyncExchangeHandler extends AsyncDataConsumer, AsyncDataProducer {
 
+    void verify(HttpRequest request, EntityDetails entityDetails, ExpectationChannel expectationChannel) throws HttpException, IOException;
+
     void handleRequest(HttpRequest request, EntityDetails entityDetails, ResponseChannel responseChannel) throws HttpException, IOException;
 
     void failed(Exception cause);

Copied: httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/ExpectationChannel.java (from r1762527, httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/AsyncExchangeHandler.java)
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/ExpectationChannel.java?p2=httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/ExpectationChannel.java&p1=httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/AsyncExchangeHandler.java&r1=1762527&r2=1762528&rev=1762528&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/AsyncExchangeHandler.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/ExpectationChannel.java Tue Sep 27 17:50:56 2016
@@ -24,23 +24,29 @@
  * <http://www.apache.org/>.
  *
  */
+
 package org.apache.hc.core5.http2.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.EntityDetails;
 import org.apache.hc.core5.http.HttpException;
-import org.apache.hc.core5.http.HttpRequest;
+import org.apache.hc.core5.http.HttpResponse;
 
 /**
- * Abstract asynchronous message exchange handler that acts as a request consumer and a response producer.
+ * Abstract expect-continue handshake channel.
+ * <p>
+ * Implementations are expected to be thread-safe.
  *
  * @since 5.0
  */
-public interface AsyncExchangeHandler extends AsyncDataConsumer, AsyncDataProducer {
+@Contract(threading = ThreadingBehavior.SAFE)
+public interface ExpectationChannel {
 
-    void handleRequest(HttpRequest request, EntityDetails entityDetails, ResponseChannel responseChannel) throws HttpException, IOException;
+    void sendResponse(HttpResponse response, EntityDetails entityDetails) throws HttpException, IOException;
 
-    void failed(Exception cause);
+    void sendContinue() throws HttpException, IOException;
 
 }

Modified: httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/entity/AbstractClassicEntityProducer.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/entity/AbstractClassicEntityProducer.java?rev=1762528&r1=1762527&r2=1762528&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/entity/AbstractClassicEntityProducer.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/entity/AbstractClassicEntityProducer.java Tue Sep 27 17:50:56 2016
@@ -78,6 +78,7 @@ public abstract class AbstractClassicEnt
 
             });
         }
+        buffer.flush(channel);
     }
 
     @Override

Modified: httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/entity/SharedOutputBuffer.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/entity/SharedOutputBuffer.java?rev=1762528&r1=1762527&r2=1762528&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/entity/SharedOutputBuffer.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/entity/SharedOutputBuffer.java Tue Sep 27 17:50:56 2016
@@ -47,7 +47,7 @@ public final class SharedOutputBuffer ex
 
     public SharedOutputBuffer(final ReentrantLock lock, final int buffersize) {
         super(lock, buffersize);
-        this.hasCapacity = true;
+        this.hasCapacity = false;
     }
 
     public SharedOutputBuffer(final int buffersize) {

Modified: httpcomponents/httpcore/trunk/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/integration/Http2IntegrationTest.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/integration/Http2IntegrationTest.java?rev=1762528&r1=1762527&r2=1762528&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/integration/Http2IntegrationTest.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/integration/Http2IntegrationTest.java Tue Sep 27 17:50:56 2016
@@ -78,6 +78,7 @@ import org.apache.hc.core5.http2.nio.Bas
 import org.apache.hc.core5.http2.nio.BasicResponseProducer;
 import org.apache.hc.core5.http2.nio.CapacityChannel;
 import org.apache.hc.core5.http2.nio.DataStreamChannel;
+import org.apache.hc.core5.http2.nio.ExpectationChannel;
 import org.apache.hc.core5.http2.nio.ResponseChannel;
 import org.apache.hc.core5.http2.nio.StreamChannel;
 import org.apache.hc.core5.http2.nio.Supplier;
@@ -350,6 +351,14 @@ public class Http2IntegrationTest extend
         }
 
         @Override
+        public void verify(
+                final HttpRequest request,
+                final EntityDetails entityDetails,
+                final ExpectationChannel expectationChannel) throws HttpException, IOException {
+            expectationChannel.sendContinue();
+        }
+
+        @Override
         public void handleRequest(
                 final HttpRequest request,
                 final EntityDetails entityDetails,