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/25 17:03:33 UTC

httpcomponents-core git commit: Fixed propagation of entity details of incoming HTTP/1.1 messages by non-blocking HTTP/1.1 stream duplexer

Repository: httpcomponents-core
Updated Branches:
  refs/heads/master bf041e342 -> c3472dfb1


Fixed propagation of entity details of incoming HTTP/1.1 messages by non-blocking HTTP/1.1 stream duplexer


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

Branch: refs/heads/master
Commit: c3472dfb1ad303f089a858cc084322e8762cb587
Parents: bf041e3
Author: Oleg Kalnichevski <ol...@apache.org>
Authored: Fri Aug 25 15:17:59 2017 +0200
Committer: Oleg Kalnichevski <ol...@apache.org>
Committed: Fri Aug 25 16:11:11 2017 +0200

----------------------------------------------------------------------
 .../impl/nio/ClientHttp2StreamHandler.java      |  4 +-
 .../impl/nio/ClientPushHttp2StreamHandler.java  |  4 +-
 .../impl/nio/ServerHttp2StreamHandler.java      |  4 +-
 .../core5/testing/nio/Http1IntegrationTest.java |  7 +-
 .../core5/http/impl/IncomingEntityDetails.java  | 90 ++++++++++++++++++++
 .../hc/core5/http/impl/LazyEntityDetails.java   | 84 ------------------
 .../impl/nio/AbstractHttp1StreamDuplexer.java   | 47 ++++++++--
 .../impl/nio/ClientHttp1StreamDuplexer.java     | 44 +++++-----
 .../http/impl/nio/ClientHttp1StreamHandler.java |  6 +-
 .../impl/nio/ServerHttp1StreamDuplexer.java     | 34 ++++----
 .../http/impl/nio/ServerHttp1StreamHandler.java |  6 +-
 11 files changed, 181 insertions(+), 149 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/c3472dfb/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 4bc0987..b3467c0 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
@@ -41,7 +41,7 @@ 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.impl.LazyEntityDetails;
+import org.apache.hc.core5.http.impl.IncomingEntityDetails;
 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;
@@ -199,7 +199,7 @@ class ClientHttp2StreamHandler implements Http2StreamHandler {
                     return;
                 }
 
-                final EntityDetails entityDetails = endStream ? null : new LazyEntityDetails(response);
+                final EntityDetails entityDetails = endStream ? null : new IncomingEntityDetails(response, -1);
                 context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response);
                 httpProcessor.process(response, entityDetails, context);
                 connMetrics.incrementResponseCount();

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/c3472dfb/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 abb1f94..6b52010 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
@@ -39,7 +39,7 @@ import org.apache.hc.core5.http.HttpResponse;
 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.impl.LazyEntityDetails;
+import org.apache.hc.core5.http.impl.IncomingEntityDetails;
 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;
@@ -126,7 +126,7 @@ class ClientPushHttp2StreamHandler implements Http2StreamHandler {
             Asserts.notNull(exchangeHandler, "Exchange handler");
 
             final HttpResponse response = DefaultH2ResponseConverter.INSTANCE.convert(headers);
-            final EntityDetails entityDetails = endStream ? null : new LazyEntityDetails(request);
+            final EntityDetails entityDetails = endStream ? null : new IncomingEntityDetails(request, -1);
 
             context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response);
             httpProcessor.process(response, entityDetails, context);

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/c3472dfb/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 52ab4a2..8349c9c 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
@@ -40,7 +40,7 @@ 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.impl.LazyEntityDetails;
+import org.apache.hc.core5.http.impl.IncomingEntityDetails;
 import org.apache.hc.core5.http.impl.nio.MessageState;
 import org.apache.hc.core5.http.nio.AsyncPushProducer;
 import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
@@ -182,7 +182,7 @@ public class ServerHttp2StreamHandler implements Http2StreamHandler {
                 requestState = endStream ? MessageState.COMPLETE : MessageState.BODY;
 
                 final HttpRequest request = DefaultH2RequestConverter.INSTANCE.convert(headers);
-                final EntityDetails requestEntityDetails = endStream ? null : new LazyEntityDetails(request);
+                final EntityDetails requestEntityDetails = endStream ? null : new IncomingEntityDetails(request, -1);
 
                 final AsyncServerExchangeHandler handler;
                 try {

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/c3472dfb/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 2ad7661..fd6ac50 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
@@ -1402,16 +1402,15 @@ public class Http1IntegrationTest extends InternalHttp1ServerTestBase {
                         streamListener) {
 
                     @Override
-                    protected ContentEncoder handleOutgoingMessage(
-                            final HttpResponse response,
+                    protected ContentEncoder createContentEncoder(
+                            final long len,
                             final WritableByteChannel channel,
                             final SessionOutputBuffer buffer,
                             final BasicHttpTransportMetrics metrics) throws HttpException {
-                        final long len = outgoingContentStrategy.determineLength(response);
                         if (len == ContentLengthStrategy.CHUNKED) {
                             return new BrokenChunkEncoder(channel, buffer, metrics);
                         } else {
-                            return super.handleOutgoingMessage(response, channel, buffer, metrics);
+                            return super.createContentEncoder(len, channel, buffer, metrics);
                         }
                     }
 

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/c3472dfb/httpcore5/src/main/java/org/apache/hc/core5/http/impl/IncomingEntityDetails.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/IncomingEntityDetails.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/IncomingEntityDetails.java
new file mode 100644
index 0000000..010132c
--- /dev/null
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/IncomingEntityDetails.java
@@ -0,0 +1,90 @@
+/*
+ * ====================================================================
+ * 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.impl;
+
+import java.util.Collections;
+import java.util.Set;
+
+import org.apache.hc.core5.http.EntityDetails;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpHeaders;
+import org.apache.hc.core5.http.MessageHeaders;
+import org.apache.hc.core5.http.message.MessageSupport;
+import org.apache.hc.core5.util.Args;
+
+/**
+ * HTTP message entity details.
+ *
+ * @since 5.0
+ */
+public class IncomingEntityDetails implements EntityDetails {
+
+    private final MessageHeaders message;
+    private final long contentLength;
+
+    public IncomingEntityDetails(final MessageHeaders message, final long contentLength) {
+        this.message = Args.notNull(message, "Message");
+        this.contentLength = contentLength;
+    }
+
+    public IncomingEntityDetails(final MessageHeaders message) {
+        this(message, -1);
+    }
+
+    @Override
+    public long getContentLength() {
+        return contentLength;
+    }
+
+    @Override
+    public String getContentType() {
+        final Header h = message.getFirstHeader(HttpHeaders.CONTENT_TYPE);
+        return h != null ? h.getValue() : null;
+    }
+
+    @Override
+    public String getContentEncoding() {
+        final Header h = message.getFirstHeader(HttpHeaders.CONTENT_ENCODING);
+        return h != null ? h.getValue() : null;
+    }
+
+    @Override
+    public boolean isChunked() {
+        return contentLength < 0;
+    }
+
+    @Override
+    public Set<String> getTrailerNames() {
+        final Header h = message.getFirstHeader(HttpHeaders.TRAILER);
+        if (h == null) {
+            return Collections.emptySet();
+        }
+        return MessageSupport.parseTokens(h);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/c3472dfb/httpcore5/src/main/java/org/apache/hc/core5/http/impl/LazyEntityDetails.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/LazyEntityDetails.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/LazyEntityDetails.java
deleted file mode 100644
index 2babd44..0000000
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/LazyEntityDetails.java
+++ /dev/null
@@ -1,84 +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.impl;
-
-import java.util.Collections;
-import java.util.Set;
-
-import org.apache.hc.core5.http.EntityDetails;
-import org.apache.hc.core5.http.Header;
-import org.apache.hc.core5.http.HttpHeaders;
-import org.apache.hc.core5.http.MessageHeaders;
-import org.apache.hc.core5.http.message.MessageSupport;
-import org.apache.hc.core5.util.Args;
-
-/**
- * HTTP message entity details.
- *
- * @since 5.0
- */
-public class LazyEntityDetails implements EntityDetails {
-
-    private final MessageHeaders message;
-
-    public LazyEntityDetails(final MessageHeaders message) {
-        this.message = Args.notNull(message, "Message");
-    }
-
-    @Override
-    public long getContentLength() {
-        return -1;
-    }
-
-    @Override
-    public String getContentType() {
-        final Header h = message.getFirstHeader(HttpHeaders.CONTENT_TYPE);
-        return h != null ? h.getValue() : null;
-    }
-
-    @Override
-    public String getContentEncoding() {
-        final Header h = message.getFirstHeader(HttpHeaders.CONTENT_TYPE);
-        return h != null ? h.getValue() : null;
-    }
-
-    @Override
-    public boolean isChunked() {
-        return false;
-    }
-
-    @Override
-    public Set<String> getTrailerNames() {
-        final Header h = message.getFirstHeader(HttpHeaders.TRAILER);
-        if (h == null) {
-            return Collections.emptySet();
-        }
-        return MessageSupport.parseTokens(h);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/c3472dfb/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/AbstractHttp1StreamDuplexer.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/AbstractHttp1StreamDuplexer.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/AbstractHttp1StreamDuplexer.java
index f5e2018..e6aa31f 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/AbstractHttp1StreamDuplexer.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/AbstractHttp1StreamDuplexer.java
@@ -44,7 +44,9 @@ import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLSession;
 
 import org.apache.hc.core5.http.ConnectionClosedException;
+import org.apache.hc.core5.http.ContentLengthStrategy;
 import org.apache.hc.core5.http.EndpointDetails;
+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.HttpMessage;
@@ -56,6 +58,8 @@ import org.apache.hc.core5.http.impl.BasicEndpointDetails;
 import org.apache.hc.core5.http.impl.BasicHttpConnectionMetrics;
 import org.apache.hc.core5.http.impl.BasicHttpTransportMetrics;
 import org.apache.hc.core5.http.impl.CharCodingSupport;
+import org.apache.hc.core5.http.impl.DefaultContentLengthStrategy;
+import org.apache.hc.core5.http.impl.IncomingEntityDetails;
 import org.apache.hc.core5.http.nio.AsyncClientExchangeHandler;
 import org.apache.hc.core5.http.nio.CapacityChannel;
 import org.apache.hc.core5.http.nio.ContentDecoder;
@@ -93,6 +97,8 @@ abstract class AbstractHttp1StreamDuplexer<IncomingMessage extends HttpMessage,
     private final BasicHttpConnectionMetrics connMetrics;
     private final NHttpMessageParser<IncomingMessage> incomingMessageParser;
     private final NHttpMessageWriter<OutgoingMessage> outgoingMessageWriter;
+    private final ContentLengthStrategy incomingContentStrategy;
+    private final ContentLengthStrategy outgoingContentStrategy;
     private final ByteBuffer contentBuffer;
     private final Lock outputLock;
     private final AtomicInteger outputRequests;
@@ -109,7 +115,9 @@ abstract class AbstractHttp1StreamDuplexer<IncomingMessage extends HttpMessage,
             final H1Config h1Config,
             final CharCodingConfig charCodingConfig,
             final NHttpMessageParser<IncomingMessage> incomingMessageParser,
-            final NHttpMessageWriter<OutgoingMessage> outgoingMessageWriter) {
+            final NHttpMessageWriter<OutgoingMessage> outgoingMessageWriter,
+            final ContentLengthStrategy incomingContentStrategy,
+            final ContentLengthStrategy outgoingContentStrategy) {
         this.ioSession = Args.notNull(ioSession, "I/O session");
         this.h1Config = h1Config != null ? h1Config : H1Config.DEFAULT;
         final int bufferSize = this.h1Config.getBufferSize();
@@ -123,6 +131,10 @@ abstract class AbstractHttp1StreamDuplexer<IncomingMessage extends HttpMessage,
         this.connMetrics = new BasicHttpConnectionMetrics(inTransportMetrics, outTransportMetrics);
         this.incomingMessageParser = incomingMessageParser;
         this.outgoingMessageWriter = outgoingMessageWriter;
+        this.incomingContentStrategy = incomingContentStrategy != null ? incomingContentStrategy :
+                DefaultContentLengthStrategy.INSTANCE;
+        this.outgoingContentStrategy = outgoingContentStrategy != null ? outgoingContentStrategy :
+                DefaultContentLengthStrategy.INSTANCE;
         this.contentBuffer = ByteBuffer.allocate(this.h1Config.getBufferSize());
         this.outputLock = new ReentrantLock();
         this.outputRequests = new AtomicInteger(0);
@@ -161,16 +173,20 @@ abstract class AbstractHttp1StreamDuplexer<IncomingMessage extends HttpMessage,
 
     abstract void updateOutputMetrics(OutgoingMessage outgoingMessage, BasicHttpConnectionMetrics connMetrics);
 
-    abstract void consumeHeader(IncomingMessage messageHead, boolean endStream) throws HttpException, IOException;
+    abstract void consumeHeader(IncomingMessage messageHead, EntityDetails entityDetails) throws HttpException, IOException;
 
-    abstract ContentDecoder handleIncomingMessage(
-            IncomingMessage incomingMessage,
+    abstract boolean handleIncomingMessage(IncomingMessage incomingMessage) throws HttpException;
+
+    abstract boolean handleOutgoingMessage(OutgoingMessage outgoingMessage) throws HttpException;
+
+    abstract ContentDecoder createContentDecoder(
+            long contentLength,
             ReadableByteChannel channel,
             SessionInputBuffer buffer,
             BasicHttpTransportMetrics metrics) throws HttpException;
 
-    abstract ContentEncoder handleOutgoingMessage(
-            OutgoingMessage outgoingMessage,
+    abstract ContentEncoder createContentEncoder(
+            long contentLength,
             WritableByteChannel channel,
             SessionOutputBuffer buffer,
             BasicHttpTransportMetrics metrics) throws HttpException;
@@ -253,8 +269,15 @@ abstract class AbstractHttp1StreamDuplexer<IncomingMessage extends HttpMessage,
                         this.version = messageHead.getVersion();
 
                         updateInputMetrics(messageHead, connMetrics);
-                        final ContentDecoder contentDecoder = handleIncomingMessage(messageHead, ioSession.channel(), inbuf, inTransportMetrics);
-                        consumeHeader(messageHead, contentDecoder == null);
+                        final ContentDecoder contentDecoder;
+                        if (handleIncomingMessage(messageHead)) {
+                            final long len = incomingContentStrategy.determineLength(messageHead);
+                            contentDecoder = createContentDecoder(len, ioSession.channel(), inbuf, inTransportMetrics);
+                            consumeHeader(messageHead, contentDecoder != null ? new IncomingEntityDetails(messageHead, len) : null);
+                        } else {
+                            consumeHeader(messageHead, null);
+                            contentDecoder = null;
+                        }
                         if (contentDecoder != null) {
                             incomingMessage = new Message<>(messageHead, contentDecoder);
                             break;
@@ -436,7 +459,13 @@ abstract class AbstractHttp1StreamDuplexer<IncomingMessage extends HttpMessage,
             outgoingMessageWriter.write(messageHead, outbuf);
             updateOutputMetrics(messageHead, connMetrics);
             if (!endStream) {
-                final ContentEncoder contentEncoder = handleOutgoingMessage(messageHead, ioSession.channel(), outbuf, outTransportMetrics);
+                final ContentEncoder contentEncoder;
+                if (handleOutgoingMessage(messageHead)) {
+                    final long len = outgoingContentStrategy.determineLength(messageHead);
+                    contentEncoder = createContentEncoder(len, ioSession.channel(), outbuf, outTransportMetrics);
+                } else {
+                    contentEncoder = null;
+                }
                 if (contentEncoder != null) {
                     outgoingMessage = new Message<>(messageHead, contentEncoder);
                 }

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/c3472dfb/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamDuplexer.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamDuplexer.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamDuplexer.java
index cf81b1d..2adfd3b 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamDuplexer.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamDuplexer.java
@@ -38,6 +38,7 @@ import java.util.concurrent.ConcurrentLinkedQueue;
 import org.apache.hc.core5.http.ConnectionClosedException;
 import org.apache.hc.core5.http.ConnectionReuseStrategy;
 import org.apache.hc.core5.http.ContentLengthStrategy;
+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.HttpRequest;
@@ -48,7 +49,6 @@ import org.apache.hc.core5.http.config.H1Config;
 import org.apache.hc.core5.http.impl.BasicHttpConnectionMetrics;
 import org.apache.hc.core5.http.impl.BasicHttpTransportMetrics;
 import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy;
-import org.apache.hc.core5.http.impl.DefaultContentLengthStrategy;
 import org.apache.hc.core5.http.impl.Http1StreamListener;
 import org.apache.hc.core5.http.message.MessageSupport;
 import org.apache.hc.core5.http.nio.AsyncClientExchangeHandler;
@@ -73,8 +73,6 @@ public class ClientHttp1StreamDuplexer extends AbstractHttp1StreamDuplexer<HttpR
     private final HttpProcessor httpProcessor;
     private final ConnectionReuseStrategy connectionReuseStrategy;
     private final H1Config h1Config;
-    private final ContentLengthStrategy incomingContentStrategy;
-    private final ContentLengthStrategy outgoingContentStrategy;
     private final Http1StreamListener streamListener;
     private final Queue<ClientHttp1StreamHandler> pipeline;
     private final Http1StreamChannel<HttpRequest> outputChannel;
@@ -93,15 +91,11 @@ public class ClientHttp1StreamDuplexer extends AbstractHttp1StreamDuplexer<HttpR
             final ContentLengthStrategy incomingContentStrategy,
             final ContentLengthStrategy outgoingContentStrategy,
             final Http1StreamListener streamListener) {
-        super(ioSession, h1Config, charCodingConfig, incomingMessageParser, outgoingMessageWriter);
+        super(ioSession, h1Config, charCodingConfig, incomingMessageParser, outgoingMessageWriter, incomingContentStrategy, outgoingContentStrategy);
         this.httpProcessor = Args.notNull(httpProcessor, "HTTP processor");
         this.h1Config = h1Config != null ? h1Config : H1Config.DEFAULT;
         this.connectionReuseStrategy = connectionReuseStrategy != null ? connectionReuseStrategy :
                 DefaultConnectionReuseStrategy.INSTANCE;
-        this.incomingContentStrategy = incomingContentStrategy != null ? incomingContentStrategy :
-                DefaultContentLengthStrategy.INSTANCE;
-        this.outgoingContentStrategy = outgoingContentStrategy != null ? outgoingContentStrategy :
-                DefaultContentLengthStrategy.INSTANCE;
         this.streamListener = streamListener;
         this.pipeline = new ConcurrentLinkedQueue<>();
         this.outputChannel = new Http1StreamChannel<HttpRequest>() {
@@ -255,11 +249,7 @@ public class ClientHttp1StreamDuplexer extends AbstractHttp1StreamDuplexer<HttpR
     }
 
     @Override
-    protected ContentDecoder handleIncomingMessage(
-            final HttpResponse response,
-            final ReadableByteChannel channel,
-            final SessionInputBuffer buffer,
-            final BasicHttpTransportMetrics metrics) throws HttpException {
+    protected boolean handleIncomingMessage(final HttpResponse response) throws HttpException {
 
         if (incoming == null) {
             incoming = pipeline.poll();
@@ -267,10 +257,16 @@ public class ClientHttp1StreamDuplexer extends AbstractHttp1StreamDuplexer<HttpR
         if (incoming == null) {
             throw new HttpException("Unexpected response");
         }
-        if (!MessageSupport.canResponseHaveBody(incoming.getRequestMethod(), response)) {
-            return null;
-        }
-        final long len = incomingContentStrategy.determineLength(response);
+        return MessageSupport.canResponseHaveBody(incoming.getRequestMethod(), response);
+    }
+
+    @Override
+    protected ContentDecoder createContentDecoder(
+            final long len,
+            final ReadableByteChannel channel,
+            final SessionInputBuffer buffer,
+            final BasicHttpTransportMetrics metrics) throws HttpException {
+
         if (len >= 0) {
             return new LengthDelimitedDecoder(channel, buffer, metrics, len);
         } else if (len == ContentLengthStrategy.CHUNKED) {
@@ -281,12 +277,16 @@ public class ClientHttp1StreamDuplexer extends AbstractHttp1StreamDuplexer<HttpR
     }
 
     @Override
-    protected ContentEncoder handleOutgoingMessage(
-            final HttpRequest request,
+    protected boolean handleOutgoingMessage(final HttpRequest request) throws HttpException {
+        return true;
+    }
+
+    @Override
+    protected ContentEncoder createContentEncoder(
+            final long len,
             final WritableByteChannel channel,
             final SessionOutputBuffer buffer,
             final BasicHttpTransportMetrics metrics) throws HttpException {
-        final long len = outgoingContentStrategy.determineLength(request);
         if (len >= 0) {
             return new LengthDelimitedEncoder(channel, buffer, metrics, len, h1Config.getChunkSizeHint());
         } else if (len == ContentLengthStrategy.CHUNKED) {
@@ -351,12 +351,12 @@ public class ClientHttp1StreamDuplexer extends AbstractHttp1StreamDuplexer<HttpR
     }
 
     @Override
-    void consumeHeader(final HttpResponse response, final boolean endStream) throws HttpException, IOException {
+    void consumeHeader(final HttpResponse response, final EntityDetails entityDetails) throws HttpException, IOException {
         if (streamListener != null) {
             streamListener.onResponseHead(this, response);
         }
         Asserts.notNull(incoming, "Response stream handler");
-        incoming.consumeHeader(response, endStream);
+        incoming.consumeHeader(response, entityDetails);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/c3472dfb/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 fc87b11..a390445 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
@@ -44,7 +44,6 @@ 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;
-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;
@@ -196,7 +195,7 @@ class ClientHttp1StreamHandler implements ResourceHolder {
         }
     }
 
-    void consumeHeader(final HttpResponse response, final boolean endStream) throws HttpException, IOException {
+    void consumeHeader(final HttpResponse response, final EntityDetails entityDetails) throws HttpException, IOException {
         if (done.get() || responseState != MessageState.HEADERS) {
             throw new ProtocolException("Unexpected message head");
         }
@@ -237,12 +236,11 @@ class ClientHttp1StreamHandler implements ResourceHolder {
             }
         }
 
-        final EntityDetails entityDetails = endStream ? null : new LazyEntityDetails(response);
         context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response);
         httpProcessor.process(response, entityDetails, context);
 
         exchangeHandler.consumeResponse(response, entityDetails);
-        if (endStream) {
+        if (entityDetails == null) {
             if (!keepAlive) {
                 outputChannel.close();
             }

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/c3472dfb/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamDuplexer.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamDuplexer.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamDuplexer.java
index 957ccd6..c400cd0 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamDuplexer.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamDuplexer.java
@@ -38,6 +38,7 @@ import java.util.concurrent.ConcurrentLinkedQueue;
 import org.apache.hc.core5.http.ConnectionClosedException;
 import org.apache.hc.core5.http.ConnectionReuseStrategy;
 import org.apache.hc.core5.http.ContentLengthStrategy;
+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.HttpRequest;
@@ -47,7 +48,6 @@ import org.apache.hc.core5.http.config.H1Config;
 import org.apache.hc.core5.http.impl.BasicHttpConnectionMetrics;
 import org.apache.hc.core5.http.impl.BasicHttpTransportMetrics;
 import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy;
-import org.apache.hc.core5.http.impl.DefaultContentLengthStrategy;
 import org.apache.hc.core5.http.impl.Http1StreamListener;
 import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
 import org.apache.hc.core5.http.nio.CapacityChannel;
@@ -74,8 +74,6 @@ public class ServerHttp1StreamDuplexer extends AbstractHttp1StreamDuplexer<HttpR
     private final HandlerFactory<AsyncServerExchangeHandler> exchangeHandlerFactory;
     private final H1Config h1Config;
     private final ConnectionReuseStrategy connectionReuseStrategy;
-    private final ContentLengthStrategy incomingContentStrategy;
-    private final ContentLengthStrategy outgoingContentStrategy;
     private final Http1StreamListener streamListener;
     private final Queue<ServerHttp1StreamHandler> pipeline;
     private final Http1StreamChannel<HttpResponse> outputChannel;
@@ -96,17 +94,13 @@ public class ServerHttp1StreamDuplexer extends AbstractHttp1StreamDuplexer<HttpR
             final ContentLengthStrategy incomingContentStrategy,
             final ContentLengthStrategy outgoingContentStrategy,
             final Http1StreamListener streamListener) {
-        super(ioSession, h1Config, charCodingConfig, incomingMessageParser, outgoingMessageWriter);
+        super(ioSession, h1Config, charCodingConfig, incomingMessageParser, outgoingMessageWriter, incomingContentStrategy, outgoingContentStrategy);
         this.httpProcessor = Args.notNull(httpProcessor, "HTTP processor");
         this.exchangeHandlerFactory = Args.notNull(exchangeHandlerFactory, "Exchange handler factory");
         this.scheme = scheme;
         this.h1Config = h1Config != null ? h1Config : H1Config.DEFAULT;
         this.connectionReuseStrategy = connectionReuseStrategy != null ? connectionReuseStrategy :
                 DefaultConnectionReuseStrategy.INSTANCE;
-        this.incomingContentStrategy = incomingContentStrategy != null ? incomingContentStrategy :
-                DefaultContentLengthStrategy.INSTANCE;
-        this.outgoingContentStrategy = outgoingContentStrategy != null ? outgoingContentStrategy :
-                DefaultContentLengthStrategy.INSTANCE;
         this.streamListener = streamListener;
         this.pipeline = new ConcurrentLinkedQueue<>();
         this.outputChannel = new Http1StreamChannel<HttpResponse>() {
@@ -255,12 +249,16 @@ public class ServerHttp1StreamDuplexer extends AbstractHttp1StreamDuplexer<HttpR
     }
 
     @Override
-    protected ContentDecoder handleIncomingMessage(
-            final HttpRequest request,
+    protected boolean handleIncomingMessage(final HttpRequest request) throws HttpException {
+        return true;
+    }
+
+    @Override
+    protected ContentDecoder createContentDecoder(
+            final long len,
             final ReadableByteChannel channel,
             final SessionInputBuffer buffer,
             final BasicHttpTransportMetrics metrics) throws HttpException {
-        final long len = incomingContentStrategy.determineLength(request);
         if (len >= 0) {
             return new LengthDelimitedDecoder(channel, buffer, metrics, len);
         } else if (len == ContentLengthStrategy.CHUNKED) {
@@ -271,12 +269,16 @@ public class ServerHttp1StreamDuplexer extends AbstractHttp1StreamDuplexer<HttpR
     }
 
     @Override
-    protected ContentEncoder handleOutgoingMessage(
-            final HttpResponse response,
+    protected boolean handleOutgoingMessage(final HttpResponse response) throws HttpException {
+        return true;
+    }
+
+    @Override
+    protected ContentEncoder createContentEncoder(
+            final long len,
             final WritableByteChannel channel,
             final SessionOutputBuffer buffer,
             final BasicHttpTransportMetrics metrics) throws HttpException {
-        final long len = outgoingContentStrategy.determineLength(response);
         if (len >= 0) {
             return new LengthDelimitedEncoder(channel, buffer, metrics, len, h1Config.getChunkSizeHint());
         } else if (len == ContentLengthStrategy.CHUNKED) {
@@ -298,7 +300,7 @@ public class ServerHttp1StreamDuplexer extends AbstractHttp1StreamDuplexer<HttpR
     }
 
     @Override
-    void consumeHeader(final HttpRequest request, final boolean endStream) throws HttpException, IOException {
+    void consumeHeader(final HttpRequest request, final EntityDetails entityDetails) throws HttpException, IOException {
         if (streamListener != null) {
             streamListener.onRequestHead(this, request);
         }
@@ -324,7 +326,7 @@ public class ServerHttp1StreamDuplexer extends AbstractHttp1StreamDuplexer<HttpR
             pipeline.add(streamHandler);
         }
         request.setScheme(scheme);
-        streamHandler.consumeHeader(request, endStream);
+        streamHandler.consumeHeader(request, entityDetails);
         incoming = streamHandler;
     }
 

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/c3472dfb/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 9c8c6a3..413e8b9 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
@@ -45,7 +45,6 @@ import org.apache.hc.core5.http.NotImplementedException;
 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.impl.LazyEntityDetails;
 import org.apache.hc.core5.http.nio.AsyncPushProducer;
 import org.apache.hc.core5.http.nio.AsyncResponseProducer;
 import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
@@ -228,12 +227,12 @@ class ServerHttp1StreamHandler implements ResourceHolder {
         return code;
     }
 
-    void consumeHeader(final HttpRequest request, final boolean requestEndStream) throws HttpException, IOException {
+    void consumeHeader(final HttpRequest request, final EntityDetails requestEntityDetails) throws HttpException, IOException {
         if (done.get() || requestState != MessageState.HEADERS) {
             throw new ProtocolException("Unexpected message head");
         }
         receivedRequest = request;
-        requestState = requestEndStream ? MessageState.COMPLETE : MessageState.BODY;
+        requestState = requestEntityDetails == null ? MessageState.COMPLETE : MessageState.BODY;
 
         AsyncServerExchangeHandler handler;
         try {
@@ -256,7 +255,6 @@ class ServerHttp1StreamHandler implements ResourceHolder {
         context.setProtocolVersion(transportVersion);
         context.setAttribute(HttpCoreContext.HTTP_REQUEST, request);
 
-        final EntityDetails requestEntityDetails = requestEndStream ? null : new LazyEntityDetails(request);
         final ResponseChannel responseChannel = new ResponseChannel() {
 
             @Override