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/11/13 14:07:18 UTC

svn commit: r1769497 [1/2] - in /httpcomponents/httpcore/trunk: httpcore5-ab/src/main/java/org/apache/hc/core5/http/benchmark/ httpcore5-h2/src/main/java/org/apache/hc/core5/http2/hpack/ httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ ht...

Author: olegk
Date: Sun Nov 13 14:07:18 2016
New Revision: 1769497

URL: http://svn.apache.org/viewvc?rev=1769497&view=rev
Log:
RFC 7230, RFC 7540: full support for message trailers

Added:
    httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/entity/DigestingEntityConsumer.java
      - copied, changed from r1769496, httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/entity/NoopEntityConsumer.java
    httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/entity/DigestingEntityProducer.java   (with props)
    httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/http/nio/entity/TestAbstractBinAsyncEntityConsumer.java   (with props)
    httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/http/nio/entity/TestAbstractCharAsyncEntityConsumer.java   (with props)
    httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/http/nio/entity/TestDigestingEntityConsumer.java
      - copied, changed from r1769496, httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncDataConsumer.java
    httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/http/nio/entity/TestDigestingEntityProducer.java   (with props)
Removed:
    httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/TrailerSupplier.java
Modified:
    httpcomponents/httpcore/trunk/httpcore5-ab/src/main/java/org/apache/hc/core5/http/benchmark/BenchmarkConnection.java
    httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/hpack/HPackEncoder.java
    httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/AbstractHttp2StreamMultiplexer.java
    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/Http2StreamListener.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/test/java/org/apache/hc/core5/http2/impl/nio/entity/TestSharedOutputBuffer.java
    httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/http2/InternalHttp2StreamListener.java
    httpcomponents/httpcore/trunk/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/http/EchoHandler.java
    httpcomponents/httpcore/trunk/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/http/Http1IntegrationTest.java
    httpcomponents/httpcore/trunk/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/http2/Http2IntegrationTest.java
    httpcomponents/httpcore/trunk/httpcore5/src/examples/org/apache/hc/core5/http/examples/AsyncReverseProxyExample.java
    httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/HttpEntity.java
    httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/IncomingHttpEntity.java
    httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/BHttpConnectionBase.java
    httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/ChunkedOutputStream.java
    httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/AbstractClassicServerExchangeHandler.java
    httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/AbstractContentDecoder.java
    httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/AbstractContentEncoder.java
    httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/AbstractHttp1StreamDuplexer.java
    httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ChunkDecoder.java
    httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ChunkEncoder.java
    httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamDuplexer.java
    httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamHandler.java
    httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/LengthDelimitedEncoder.java
    httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamDuplexer.java
    httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamHandler.java
    httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/entity/AbstractClassicEntityConsumer.java
    httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/AbstractHttpEntity.java
    httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/HttpEntityWithTrailers.java
    httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/HttpEntityWrapper.java
    httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncDataConsumer.java
    httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/BasicRequestConsumer.java
    httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/BasicResponseConsumer.java
    httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/ContentDecoder.java
    httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/ContentEncoder.java
    httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/DataStreamChannel.java
    httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/entity/AbstractBinAsyncEntityConsumer.java
    httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/entity/AbstractCharAsyncEntityConsumer.java
    httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/entity/NoopEntityConsumer.java
    httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AbstractServerExchangeHandler.java
    httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/BasicAsyncPushHandler.java
    httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/BasicClientExchangeHandler.java
    httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/ImmediateResponseExchangeHandler.java
    httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/util/TextUtils.java
    httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestChunkCoding.java
    httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/http/impl/nio/ContentDecoderMock.java
    httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/http/impl/nio/TestChunkDecoder.java
    httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/http/impl/nio/TestChunkEncoder.java
    httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/http/nio/BasicDataStreamChannel.java
    httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/http/nio/entity/TestAbstractBinAsyncEntityProducer.java
    httpcomponents/httpcore/trunk/httpcore5/src/test/java/org/apache/hc/core5/util/TestTextUtils.java

Modified: httpcomponents/httpcore/trunk/httpcore5-ab/src/main/java/org/apache/hc/core5/http/benchmark/BenchmarkConnection.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5-ab/src/main/java/org/apache/hc/core5/http/benchmark/BenchmarkConnection.java?rev=1769497&r1=1769496&r2=1769497&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5-ab/src/main/java/org/apache/hc/core5/http/benchmark/BenchmarkConnection.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5-ab/src/main/java/org/apache/hc/core5/http/benchmark/BenchmarkConnection.java Sun Nov 13 14:07:18 2016
@@ -28,11 +28,13 @@ package org.apache.hc.core5.http.benchma
 
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.util.List;
 
-import org.apache.hc.core5.http.TrailerSupplier;
+import org.apache.hc.core5.http.Header;
 import org.apache.hc.core5.http.impl.io.DefaultBHttpClientConnection;
 import org.apache.hc.core5.http.io.SessionInputBuffer;
 import org.apache.hc.core5.http.io.SessionOutputBuffer;
+import org.apache.hc.core5.http.nio.Supplier;
 
 class BenchmarkConnection extends DefaultBHttpClientConnection {
 
@@ -47,7 +49,7 @@ class BenchmarkConnection extends Defaul
     protected OutputStream createContentOutputStream(final long len,
                                                      final SessionOutputBuffer outbuffer,
                                                      final OutputStream outputStream,
-                                                     final TrailerSupplier trailers) {
+                                                     final Supplier<List<? extends Header>> trailers) {
         return new CountingOutputStream(
                 super.createContentOutputStream(len, outbuffer, outputStream, trailers),
                 this.stats);

Modified: httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/hpack/HPackEncoder.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/hpack/HPackEncoder.java?rev=1769497&r1=1769496&r2=1769497&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/hpack/HPackEncoder.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/hpack/HPackEncoder.java Sun Nov 13 14:07:18 2016
@@ -295,7 +295,7 @@ public final class HPackEncoder {
     }
 
     void encodeHeaders(
-            final ByteArrayBuffer dst, final List<Header> headers,
+            final ByteArrayBuffer dst, final List<? extends Header> headers,
             final boolean noIndexing, final boolean useHuffman) throws CharacterCodingException {
         for (int i = 0; i < headers.size(); i++) {
             encodeHeader(dst, headers.get(i), noIndexing, useHuffman);
@@ -317,7 +317,7 @@ public final class HPackEncoder {
     }
 
     public void encodeHeaders(
-            final ByteArrayBuffer dst, final List<Header> headers) throws CharacterCodingException {
+            final ByteArrayBuffer dst, final List<? extends Header> headers) throws CharacterCodingException {
         Args.notNull(dst, "ByteArrayBuffer");
         Args.notEmpty(headers, "Header list");
         encodeHeaders(dst, headers, false, true);

Modified: httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/AbstractHttp2StreamMultiplexer.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/AbstractHttp2StreamMultiplexer.java?rev=1769497&r1=1769496&r2=1769497&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/AbstractHttp2StreamMultiplexer.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/AbstractHttp2StreamMultiplexer.java Sun Nov 13 14:07:18 2016
@@ -215,10 +215,7 @@ abstract class AbstractHttp2StreamMultip
     }
 
     private void commitHeaders(
-            final int streamId, final List<Header> headers, final boolean endStream) throws IOException {
-        if (headers == null || headers.isEmpty()) {
-            throw new H2ConnectionException(H2Error.INTERNAL_ERROR, "Message headers are missing");
-        }
+            final int streamId, final List<? extends Header> headers, final boolean endStream) throws IOException {
         if (streamListener != null) {
             streamListener.onHeaderOutput(headers);
         }
@@ -1178,6 +1175,9 @@ abstract class AbstractHttp2StreamMultip
         public void submit(final List<Header> headers, final boolean endStream) throws IOException {
             outputLock.lock();
             try {
+                if (headers == null || headers.isEmpty()) {
+                    throw new H2ConnectionException(H2Error.INTERNAL_ERROR, "Message headers are missing");
+                }
                 if (localEndStream) {
                     return;
                 }
@@ -1193,7 +1193,7 @@ abstract class AbstractHttp2StreamMultip
         @Override
         public void push(final List<Header> headers, final AsyncPushProducer pushProducer) throws HttpException, IOException {
             if (mode == Mode.CLIENT) {
-                throw new H2ConnectionException(H2Error.INTERNAL_ERROR, "Illegal attempt to pushPromise a response");
+                throw new H2ConnectionException(H2Error.INTERNAL_ERROR, "Illegal attempt to push a response");
             }
             final int promisedStreamId = generateStreamId();
             final Http2StreamChannelImpl channel = new Http2StreamChannelImpl(
@@ -1239,15 +1239,19 @@ abstract class AbstractHttp2StreamMultip
         }
 
         @Override
-        public void endStream(final List<Header> trailers) throws IOException {
+        public void endStream(final List<? extends Header> trailers) throws IOException {
             outputLock.lock();
             try {
                 if (localEndStream) {
                     return;
                 }
                 localEndStream = true;
-                final RawFrame frame = frameFactory.createData(id, null, true);
-                commitFrameInternal(frame);
+                if (trailers != null && !trailers.isEmpty()) {
+                    commitHeaders(id, trailers, true);
+                } else {
+                    final RawFrame frame = frameFactory.createData(id, null, true);
+                    commitFrameInternal(frame);
+                }
             } finally {
                 outputLock.unlock();
             }

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=1769497&r1=1769496&r2=1769497&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 Sun Nov 13 14:07:18 2016
@@ -92,7 +92,7 @@ class ClientHttp2StreamHandler implement
             }
 
             @Override
-            public void endStream(final List<Header> trailers) throws IOException {
+            public void endStream(final List<? extends Header> trailers) throws IOException {
                 outputChannel.endStream(trailers);
                 requestState = MessageState.COMPLETE;
             }
@@ -181,34 +181,44 @@ class ClientHttp2StreamHandler implement
 
     @Override
     public void consumeHeader(final List<Header> headers, final boolean endStream) throws HttpException, IOException {
-        if (done.get() || responseState != MessageState.HEADERS) {
+        if (done.get()) {
             throw new ProtocolException("Unexpected message headers");
         }
-        final HttpResponse response = DefaultH2ResponseConverter.INSTANCE.convert(headers);
-        final int status = response.getCode();
-        if (status < HttpStatus.SC_INFORMATIONAL) {
-            throw new ProtocolException("Invalid response: " + status);
-        }
-        if (status < HttpStatus.SC_SUCCESS) {
-            exchangeHandler.consumeInformation(response);
-        }
-        if (requestState == MessageState.ACK) {
-            if (status == HttpStatus.SC_CONTINUE || status >= HttpStatus.SC_SUCCESS) {
-                requestState = MessageState.BODY;
-                exchangeHandler.produce(dataChannel);
-            }
-        }
-        if (status < HttpStatus.SC_SUCCESS) {
-            return;
-        }
+        switch (responseState) {
+            case HEADERS:
+                final HttpResponse response = DefaultH2ResponseConverter.INSTANCE.convert(headers);
+                final int status = response.getCode();
+                if (status < HttpStatus.SC_INFORMATIONAL) {
+                    throw new ProtocolException("Invalid response: " + status);
+                }
+                if (status < HttpStatus.SC_SUCCESS) {
+                    exchangeHandler.consumeInformation(response);
+                }
+                if (requestState == MessageState.ACK) {
+                    if (status == HttpStatus.SC_CONTINUE || status >= HttpStatus.SC_SUCCESS) {
+                        requestState = MessageState.BODY;
+                        exchangeHandler.produce(dataChannel);
+                    }
+                }
+                if (status < HttpStatus.SC_SUCCESS) {
+                    return;
+                }
 
-        final EntityDetails entityDetails = endStream ? null : new LazyEntityDetails(response);
-        context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response);
-        httpProcessor.process(response, entityDetails, context);
-        connMetrics.incrementResponseCount();
+                final EntityDetails entityDetails = endStream ? null : new LazyEntityDetails(response);
+                context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response);
+                httpProcessor.process(response, entityDetails, context);
+                connMetrics.incrementResponseCount();
 
-        exchangeHandler.consumeResponse(response, entityDetails);
-        responseState = endStream ? MessageState.COMPLETE : MessageState.BODY;
+                exchangeHandler.consumeResponse(response, entityDetails);
+                responseState = endStream ? MessageState.COMPLETE : MessageState.BODY;
+                break;
+            case BODY:
+                responseState = MessageState.COMPLETE;
+                exchangeHandler.streamEnd(headers);
+                break;
+            default:
+                throw new ProtocolException("Unexpected message headers");
+        }
     }
 
     @Override

Modified: httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/Http2StreamListener.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/Http2StreamListener.java?rev=1769497&r1=1769496&r2=1769497&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/Http2StreamListener.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/Http2StreamListener.java Sun Nov 13 14:07:18 2016
@@ -38,9 +38,9 @@ import org.apache.hc.core5.http2.frame.R
  */
 public interface Http2StreamListener {
 
-    void onHeaderInput(List<Header> headers);
+    void onHeaderInput(List<? extends Header> headers);
 
-    void onHeaderOutput(List<Header> headers);
+    void onHeaderOutput(List<? extends Header> headers);
 
     void onFrameInput(RawFrame frame);
 

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=1769497&r1=1769496&r2=1769497&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 Sun Nov 13 14:07:18 2016
@@ -94,7 +94,7 @@ public class ServerHttp2StreamHandler im
             }
 
             @Override
-            public void endStream(final List<Header> trailers) throws IOException {
+            public void endStream(final List<? extends Header> trailers) throws IOException {
                 outputChannel.endStream(trailers);
                 responseState = MessageState.COMPLETE;
             }
@@ -172,54 +172,64 @@ public class ServerHttp2StreamHandler im
     }
 
     @Override
-    public void consumeHeader(final List<Header> requestHeaders, final boolean requestEndStream) throws HttpException, IOException {
-        if (done.get() || requestState != MessageState.HEADERS) {
+    public void consumeHeader(final List<Header> headers, final boolean endStream) throws HttpException, IOException {
+        if (done.get()) {
             throw new ProtocolException("Unexpected message headers");
         }
-        requestState = requestEndStream ? MessageState.COMPLETE : MessageState.BODY;
-
-        final HttpRequest request = DefaultH2RequestConverter.INSTANCE.convert(requestHeaders);
-        final EntityDetails requestEntityDetails = requestEndStream ? null : new LazyEntityDetails(request);
-
-        final AsyncServerExchangeHandler handler;
-        try {
-            handler = exchangeHandlerFactory != null ? exchangeHandlerFactory.create(request) : null;
-        } catch (ProtocolException ex) {
-            throw new H2StreamResetException(H2Error.PROTOCOL_ERROR, ex.getMessage());
-        }
-        if (handler == null) {
-            throw new H2StreamResetException(H2Error.REFUSED_STREAM, "Stream refused");
+        switch (requestState) {
+            case HEADERS:
+                requestState = endStream ? MessageState.COMPLETE : MessageState.BODY;
+
+                final HttpRequest request = DefaultH2RequestConverter.INSTANCE.convert(headers);
+                final EntityDetails requestEntityDetails = endStream ? null : new LazyEntityDetails(request);
+
+                final AsyncServerExchangeHandler handler;
+                try {
+                    handler = exchangeHandlerFactory != null ? exchangeHandlerFactory.create(request) : null;
+                } catch (ProtocolException ex) {
+                    throw new H2StreamResetException(H2Error.PROTOCOL_ERROR, ex.getMessage());
+                }
+                if (handler == null) {
+                    throw new H2StreamResetException(H2Error.REFUSED_STREAM, "Stream refused");
+                }
+                exchangeHandler = handler;
+
+                context.setProtocolVersion(HttpVersion.HTTP_2);
+                context.setAttribute(HttpCoreContext.HTTP_REQUEST, request);
+                context.setAttribute(HttpCoreContext.HTTP_CONNECTION, connection);
+                exchangeHandler.setContext(context);
+
+                httpProcessor.process(request, requestEntityDetails, context);
+                connMetrics.incrementRequestCount();
+
+                exchangeHandler.handleRequest(request, requestEntityDetails, new ResponseChannel() {
+
+                    @Override
+                    public void sendInformation(final HttpResponse response) throws HttpException, IOException {
+                        commitInformation(response);
+                    }
+
+                    @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);
+                    }
+
+                });
+                break;
+            case BODY:
+                responseState = MessageState.COMPLETE;
+                exchangeHandler.streamEnd(headers);
+                break;
+            default:
+                throw new ProtocolException("Unexpected message headers");
         }
-        exchangeHandler = handler;
-
-        context.setProtocolVersion(HttpVersion.HTTP_2);
-        context.setAttribute(HttpCoreContext.HTTP_REQUEST, request);
-        context.setAttribute(HttpCoreContext.HTTP_CONNECTION, connection);
-        exchangeHandler.setContext(context);
-
-        httpProcessor.process(request, requestEntityDetails, context);
-        connMetrics.incrementRequestCount();
-
-        exchangeHandler.handleRequest(request, requestEntityDetails, new ResponseChannel() {
-
-            @Override
-            public void sendInformation(final HttpResponse response) throws HttpException, IOException {
-                commitInformation(response);
-            }
-
-            @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);
-            }
-
-        });
     }
 
     @Override

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=1769497&r1=1769496&r2=1769497&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 Sun Nov 13 14:07:18 2016
@@ -88,7 +88,7 @@ class ServerPushHttp2StreamHandler imple
             }
 
             @Override
-            public void endStream(final List<Header> trailers) throws IOException {
+            public void endStream(final List<? extends Header> trailers) throws IOException {
                 outputChannel.endStream(trailers);
                 responseState = MessageState.COMPLETE;
             }

Modified: httpcomponents/httpcore/trunk/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/impl/nio/entity/TestSharedOutputBuffer.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/impl/nio/entity/TestSharedOutputBuffer.java?rev=1769497&r1=1769496&r2=1769497&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/impl/nio/entity/TestSharedOutputBuffer.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/impl/nio/entity/TestSharedOutputBuffer.java Sun Nov 13 14:07:18 2016
@@ -69,7 +69,7 @@ public class TestSharedOutputBuffer {
         }
 
         @Override
-        public synchronized void endStream(final List<Header> trailers) throws IOException {
+        public synchronized void endStream(final List<? extends Header> trailers) throws IOException {
             channel.close();
             notifyAll();
         }

Modified: httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/http2/InternalHttp2StreamListener.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/http2/InternalHttp2StreamListener.java?rev=1769497&r1=1769496&r2=1769497&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/http2/InternalHttp2StreamListener.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/http2/InternalHttp2StreamListener.java Sun Nov 13 14:07:18 2016
@@ -82,7 +82,7 @@ class InternalHttp2StreamListener implem
     }
 
     @Override
-    public void onHeaderInput(final List<Header> headers) {
+    public void onHeaderInput(final List<? extends Header> headers) {
         if (headerLog.isDebugEnabled()) {
             for (int i = 0; i < headers.size(); i++) {
                 headerLog.debug(id + " << " + headers.get(i));
@@ -91,7 +91,7 @@ class InternalHttp2StreamListener implem
     }
 
     @Override
-    public void onHeaderOutput(final List<Header> headers) {
+    public void onHeaderOutput(final List<? extends Header> headers) {
         if (headerLog.isDebugEnabled()) {
             for (int i = 0; i < headers.size(); i++) {
                 headerLog.debug(id + " >> " + headers.get(i));

Modified: httpcomponents/httpcore/trunk/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/http/EchoHandler.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/http/EchoHandler.java?rev=1769497&r1=1769496&r2=1769497&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/http/EchoHandler.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/http/EchoHandler.java Sun Nov 13 14:07:18 2016
@@ -111,7 +111,7 @@ public class EchoHandler implements Asyn
     }
 
     @Override
-    public void streamEnd(final List<Header> trailers) throws HttpException, IOException {
+    public void streamEnd(final List<? extends Header> trailers) throws HttpException, IOException {
         endStream = true;
         if (buffer.position() == 0) {
             if (outputDataChannel != null) {

Modified: httpcomponents/httpcore/trunk/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/http/Http1IntegrationTest.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/http/Http1IntegrationTest.java?rev=1769497&r1=1769496&r2=1769497&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/http/Http1IntegrationTest.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/http/Http1IntegrationTest.java Sun Nov 13 14:07:18 2016
@@ -44,8 +44,11 @@ import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Locale;
+import java.util.Map;
 import java.util.Queue;
 import java.util.Random;
 import java.util.StringTokenizer;
@@ -87,9 +90,11 @@ import org.apache.hc.core5.http.io.entit
 import org.apache.hc.core5.http.message.BasicHttpRequest;
 import org.apache.hc.core5.http.message.BasicHttpResponse;
 import org.apache.hc.core5.http.nio.AsyncEntityProducer;
+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.BasicRequestConsumer;
 import org.apache.hc.core5.http.nio.BasicRequestProducer;
 import org.apache.hc.core5.http.nio.BasicResponseConsumer;
 import org.apache.hc.core5.http.nio.BasicResponseProducer;
@@ -103,8 +108,11 @@ import org.apache.hc.core5.http.nio.Resp
 import org.apache.hc.core5.http.nio.SessionOutputBuffer;
 import org.apache.hc.core5.http.nio.Supplier;
 import org.apache.hc.core5.http.nio.entity.BasicAsyncEntityProducer;
+import org.apache.hc.core5.http.nio.entity.DigestingEntityConsumer;
+import org.apache.hc.core5.http.nio.entity.DigestingEntityProducer;
 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.AbstractServerExchangeHandler;
 import org.apache.hc.core5.http.nio.support.ResponseTrigger;
 import org.apache.hc.core5.http.protocol.DefaultHttpProcessor;
 import org.apache.hc.core5.http.protocol.HttpContext;
@@ -118,6 +126,7 @@ import org.apache.hc.core5.reactor.IOSes
 import org.apache.hc.core5.reactor.SessionRequest;
 import org.apache.hc.core5.testing.ProtocolScheme;
 import org.apache.hc.core5.util.CharArrayBuffer;
+import org.apache.hc.core5.util.TextUtils;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
@@ -819,7 +828,7 @@ public class Http1IntegrationTest extend
                     }
 
                     @Override
-                    public void streamEnd(final List<Header> trailers) throws HttpException, IOException {
+                    public void streamEnd(final List<? extends Header> trailers) throws HttpException, IOException {
                     }
 
                     @Override
@@ -914,7 +923,7 @@ public class Http1IntegrationTest extend
                     }
 
                     @Override
-                    public void streamEnd(final List<Header> trailers) throws HttpException, IOException {
+                    public void streamEnd(final List<? extends Header> trailers) throws HttpException, IOException {
                     }
 
                     @Override
@@ -1296,8 +1305,8 @@ public class Http1IntegrationTest extend
         }
 
         @Override
-        public void complete() throws IOException {
-            super.complete();
+        public void complete(final List<? extends Header> trailers) throws IOException {
+            super.complete(trailers);
         }
 
         @Override
@@ -1551,4 +1560,64 @@ public class Http1IntegrationTest extend
         Assert.assertEquals("Host header is absent", result2.getBody());
     }
 
+    @Test
+    public void testMessageWithTrailers() throws Exception {
+        server.register("/hello", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new AbstractServerExchangeHandler<Message<HttpRequest, String>>() {
+
+                    @Override
+                    protected AsyncRequestConsumer<Message<HttpRequest, String>> supplyConsumer(
+                            final HttpRequest request, final HttpContext context) throws HttpException {
+                        return new BasicRequestConsumer<>(new StringAsyncEntityConsumer());
+                    }
+
+                    @Override
+                    protected void handle(
+                            final Message<HttpRequest, String> requestMessage,
+                            final ResponseTrigger responseTrigger,
+                            final HttpContext context) throws HttpException, IOException {
+                        responseTrigger.submitResponse(new BasicResponseProducer(
+                                HttpStatus.SC_OK,
+                                new DigestingEntityProducer("MD5",
+                                        new StringAsyncEntityProducer("Hello back with some trailers"))));
+                    }
+                };
+            }
+
+        });
+        final InetSocketAddress serverEndpoint = server.start();
+
+        client.start();
+
+        final Future<ClientEndpoint> connectFuture = client.connect(
+                "localhost", serverEndpoint.getPort(), TIMEOUT, TimeUnit.SECONDS);
+        final ClientEndpoint streamEndpoint = connectFuture.get();
+
+        final HttpRequest request1 = new BasicHttpRequest("GET", createRequestURI(serverEndpoint, "/hello"));
+        final DigestingEntityConsumer<String> entityConsumer = new DigestingEntityConsumer<>("MD5", new StringAsyncEntityConsumer());
+        final Future<Message<HttpResponse, String>> future1 = streamEndpoint.execute(
+                new BasicRequestProducer(request1, null),
+                new BasicResponseConsumer<>(entityConsumer), null);
+        final Message<HttpResponse, String> result1 = future1.get(TIMEOUT, TimeUnit.SECONDS);
+        Assert.assertNotNull(result1);
+        final HttpResponse response1 = result1.getHead();
+        Assert.assertNotNull(response1);
+        Assert.assertEquals(200, response1.getCode());
+        Assert.assertEquals("Hello back with some trailers", result1.getBody());
+
+        final List<Header> trailers = entityConsumer.getTrailers();
+        Assert.assertNotNull(trailers);
+        Assert.assertEquals(2, trailers.size());
+        final Map<String, String> map = new HashMap<>();
+        for (Header header: trailers) {
+            map.put(header.getName().toLowerCase(Locale.ROOT), header.getValue());
+        }
+        final String digest = TextUtils.toHexString(entityConsumer.getDigest());
+        Assert.assertEquals("MD5", map.get("digest-algo"));
+        Assert.assertEquals(digest, map.get("digest"));
+    }
+
 }

Modified: httpcomponents/httpcore/trunk/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/http2/Http2IntegrationTest.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/http2/Http2IntegrationTest.java?rev=1769497&r1=1769496&r2=1769497&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/http2/Http2IntegrationTest.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/http2/Http2IntegrationTest.java Sun Nov 13 14:07:18 2016
@@ -43,8 +43,11 @@ import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Locale;
+import java.util.Map;
 import java.util.Queue;
 import java.util.StringTokenizer;
 import java.util.concurrent.BlockingQueue;
@@ -69,9 +72,11 @@ import org.apache.hc.core5.http.impl.nio
 import org.apache.hc.core5.http.io.entity.ContentType;
 import org.apache.hc.core5.http.message.BasicHttpRequest;
 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.BasicPushProducer;
+import org.apache.hc.core5.http.nio.BasicRequestConsumer;
 import org.apache.hc.core5.http.nio.BasicRequestProducer;
 import org.apache.hc.core5.http.nio.BasicResponseConsumer;
 import org.apache.hc.core5.http.nio.BasicResponseProducer;
@@ -80,8 +85,12 @@ import org.apache.hc.core5.http.nio.Data
 import org.apache.hc.core5.http.nio.ResponseChannel;
 import org.apache.hc.core5.http.nio.Supplier;
 import org.apache.hc.core5.http.nio.entity.BasicAsyncEntityProducer;
+import org.apache.hc.core5.http.nio.entity.DigestingEntityConsumer;
+import org.apache.hc.core5.http.nio.entity.DigestingEntityProducer;
 import org.apache.hc.core5.http.nio.entity.NoopEntityConsumer;
 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.AbstractServerExchangeHandler;
 import org.apache.hc.core5.http.nio.support.BasicAsyncPushHandler;
 import org.apache.hc.core5.http.nio.support.ResponseTrigger;
 import org.apache.hc.core5.http.protocol.HttpContext;
@@ -95,6 +104,7 @@ import org.apache.hc.core5.testing.nio.h
 import org.apache.hc.core5.testing.nio.http.MultiLineEntityProducer;
 import org.apache.hc.core5.testing.nio.http.MultiLineResponseHandler;
 import org.apache.hc.core5.testing.nio.http.SingleLineResponseHandler;
+import org.apache.hc.core5.util.TextUtils;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
@@ -765,7 +775,7 @@ public class Http2IntegrationTest extend
                     }
 
                     @Override
-                    public void streamEnd(final List<Header> trailers) throws HttpException, IOException {
+                    public void streamEnd(final List<? extends Header> trailers) throws HttpException, IOException {
                     }
 
                     @Override
@@ -826,4 +836,64 @@ public class Http2IntegrationTest extend
         Assert.assertNotNull("You shall not pass", result1.getBody());
     }
 
+    @Test
+    public void testMessageWithTrailers() throws Exception {
+        server.register("/hello", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new AbstractServerExchangeHandler<Message<HttpRequest, String>>() {
+
+                    @Override
+                    protected AsyncRequestConsumer<Message<HttpRequest, String>> supplyConsumer(
+                            final HttpRequest request, final HttpContext context) throws HttpException {
+                        return new BasicRequestConsumer<>(new StringAsyncEntityConsumer());
+                    }
+
+                    @Override
+                    protected void handle(
+                            final Message<HttpRequest, String> requestMessage,
+                            final ResponseTrigger responseTrigger,
+                            final HttpContext context) throws HttpException, IOException {
+                        responseTrigger.submitResponse(new BasicResponseProducer(
+                                HttpStatus.SC_OK,
+                                new DigestingEntityProducer("MD5",
+                                        new StringAsyncEntityProducer("Hello back with some trailers"))));
+                    }
+                };
+            }
+
+        });
+        final InetSocketAddress serverEndpoint = server.start();
+
+        client.start();
+
+        final Future<ClientEndpoint> connectFuture = client.connect(
+                "localhost", serverEndpoint.getPort(), TIMEOUT, TimeUnit.SECONDS);
+        final ClientEndpoint streamEndpoint = connectFuture.get();
+
+        final HttpRequest request1 = new BasicHttpRequest("GET", createRequestURI(serverEndpoint, "/hello"));
+        final DigestingEntityConsumer<String> entityConsumer = new DigestingEntityConsumer<>("MD5", new StringAsyncEntityConsumer());
+        final Future<Message<HttpResponse, String>> future1 = streamEndpoint.execute(
+                new BasicRequestProducer(request1, null),
+                new BasicResponseConsumer<>(entityConsumer), null);
+        final Message<HttpResponse, String> result1 = future1.get(TIMEOUT, TimeUnit.SECONDS);
+        Assert.assertNotNull(result1);
+        final HttpResponse response1 = result1.getHead();
+        Assert.assertNotNull(response1);
+        Assert.assertEquals(200, response1.getCode());
+        Assert.assertEquals("Hello back with some trailers", result1.getBody());
+
+        final List<Header> trailers = entityConsumer.getTrailers();
+        Assert.assertNotNull(trailers);
+        Assert.assertEquals(2, trailers.size());
+        final Map<String, String> map = new HashMap<>();
+        for (Header header: trailers) {
+            map.put(header.getName().toLowerCase(Locale.ROOT), header.getValue());
+        }
+        final String digest = TextUtils.toHexString(entityConsumer.getDigest());
+        Assert.assertEquals("MD5", map.get("digest-algo"));
+        Assert.assertEquals(digest, map.get("digest"));
+    }
+
 }

Modified: httpcomponents/httpcore/trunk/httpcore5/src/examples/org/apache/hc/core5/http/examples/AsyncReverseProxyExample.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/examples/org/apache/hc/core5/http/examples/AsyncReverseProxyExample.java?rev=1769497&r1=1769496&r2=1769497&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/examples/org/apache/hc/core5/http/examples/AsyncReverseProxyExample.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/examples/org/apache/hc/core5/http/examples/AsyncReverseProxyExample.java Sun Nov 13 14:07:18 2016
@@ -390,7 +390,7 @@ public class AsyncReverseProxyExample {
         }
 
         @Override
-        public void streamEnd(final List<Header> trailers) throws HttpException, IOException {
+        public void streamEnd(final List<? extends Header> trailers) throws HttpException, IOException {
             synchronized (exchangeState) {
                 System.out.println("[client->proxy] " + exchangeState.id + " end of input");
                 exchangeState.inputEnd = true;
@@ -615,7 +615,7 @@ public class AsyncReverseProxyExample {
         }
 
         @Override
-        public void streamEnd(final List<Header> trailers) throws HttpException, IOException {
+        public void streamEnd(final List<? extends Header> trailers) throws HttpException, IOException {
             synchronized (exchangeState) {
                 System.out.println("[proxy<-origin] " + exchangeState.id + " end of input");
                 exchangeState.outputEnd = true;

Modified: httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/HttpEntity.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/HttpEntity.java?rev=1769497&r1=1769496&r2=1769497&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/HttpEntity.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/HttpEntity.java Sun Nov 13 14:07:18 2016
@@ -31,6 +31,9 @@ import java.io.Closeable;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.util.List;
+
+import org.apache.hc.core5.http.nio.Supplier;
 
 /**
  * An entity that can be sent or received with an HTTP message.
@@ -124,6 +127,6 @@ public interface HttpEntity extends Enti
      *
      * @since 5.0
      */
-     TrailerSupplier getTrailers();
+    Supplier<List<? extends Header>> getTrailers();
 
 }

Modified: httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/IncomingHttpEntity.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/IncomingHttpEntity.java?rev=1769497&r1=1769496&r2=1769497&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/IncomingHttpEntity.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/IncomingHttpEntity.java Sun Nov 13 14:07:18 2016
@@ -30,12 +30,13 @@ package org.apache.hc.core5.http.impl;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.Collections;
+import java.util.List;
 import java.util.Set;
 
 import org.apache.hc.core5.http.Header;
-import org.apache.hc.core5.http.TrailerSupplier;
 import org.apache.hc.core5.http.impl.io.EmptyInputStream;
 import org.apache.hc.core5.http.io.entity.AbstractImmutableHttpEntity;
+import org.apache.hc.core5.http.nio.Supplier;
 
 /**
  * Represents entity received from an open connection.
@@ -94,7 +95,7 @@ public class IncomingHttpEntity extends
     }
 
     @Override
-    public TrailerSupplier getTrailers() {
+    public Supplier<List<? extends Header>> getTrailers() {
         return null;
     }
 

Modified: httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/BHttpConnectionBase.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/BHttpConnectionBase.java?rev=1769497&r1=1769496&r2=1769497&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/BHttpConnectionBase.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/BHttpConnectionBase.java Sun Nov 13 14:07:18 2016
@@ -36,16 +36,17 @@ import java.net.SocketException;
 import java.net.SocketTimeoutException;
 import java.nio.charset.CharsetDecoder;
 import java.nio.charset.CharsetEncoder;
+import java.util.List;
 import java.util.concurrent.atomic.AtomicReference;
 
 import org.apache.hc.core5.http.ConnectionClosedException;
 import org.apache.hc.core5.http.ContentLengthStrategy;
+import org.apache.hc.core5.http.Header;
 import org.apache.hc.core5.http.HttpConnectionMetrics;
 import org.apache.hc.core5.http.HttpEntity;
 import org.apache.hc.core5.http.HttpHeaders;
 import org.apache.hc.core5.http.HttpMessage;
 import org.apache.hc.core5.http.ProtocolVersion;
-import org.apache.hc.core5.http.TrailerSupplier;
 import org.apache.hc.core5.http.config.H1Config;
 import org.apache.hc.core5.http.impl.BasicHttpConnectionMetrics;
 import org.apache.hc.core5.http.impl.BasicHttpTransportMetrics;
@@ -53,6 +54,7 @@ import org.apache.hc.core5.http.impl.Inc
 import org.apache.hc.core5.http.io.BHttpConnection;
 import org.apache.hc.core5.http.io.SessionInputBuffer;
 import org.apache.hc.core5.http.io.SessionOutputBuffer;
+import org.apache.hc.core5.http.nio.Supplier;
 import org.apache.hc.core5.net.InetAddressUtils;
 import org.apache.hc.core5.util.Args;
 
@@ -134,7 +136,7 @@ class BHttpConnectionBase implements BHt
             final long len,
             final SessionOutputBuffer buffer,
             final OutputStream outputStream,
-            final TrailerSupplier trailers) {
+            final Supplier<List<? extends Header>> trailers) {
         if (len >= 0) {
             return new ContentLengthOutputStream(buffer, outputStream, len);
         } else if (len == ContentLengthStrategy.CHUNKED) {

Modified: httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/ChunkedOutputStream.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/ChunkedOutputStream.java?rev=1769497&r1=1769496&r2=1769497&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/ChunkedOutputStream.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/ChunkedOutputStream.java Sun Nov 13 14:07:18 2016
@@ -29,13 +29,14 @@ package org.apache.hc.core5.http.impl.io
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.List;
 
 import org.apache.hc.core5.http.FormattedHeader;
 import org.apache.hc.core5.http.Header;
 import org.apache.hc.core5.http.StreamClosedException;
-import org.apache.hc.core5.http.TrailerSupplier;
 import org.apache.hc.core5.http.io.SessionOutputBuffer;
 import org.apache.hc.core5.http.message.BasicLineFormatter;
+import org.apache.hc.core5.http.nio.Supplier;
 import org.apache.hc.core5.util.Args;
 import org.apache.hc.core5.util.CharArrayBuffer;
 
@@ -61,7 +62,7 @@ public class ChunkedOutputStream extends
     private boolean wroteLastChunk = false;
     private boolean closed = false;
     private final CharArrayBuffer lineBuffer;
-    private final TrailerSupplier trailers;
+    private final Supplier<List<? extends Header>> trailerSupplier;
 
     /**
      * Default constructor.
@@ -69,17 +70,18 @@ public class ChunkedOutputStream extends
      * @param minChunkSize The minimum chunk size (excluding last chunk)
      * @param buffer Session output buffer
      * @param outputStream Output stream
-     * @param trailers Trailer supplier. May be {@code null}
+     * @param trailerSupplier Trailer supplier. May be {@code null}
      *
      * @since 5.0
      */
-    public ChunkedOutputStream(final int minChunkSize, final SessionOutputBuffer buffer, final OutputStream outputStream, final TrailerSupplier trailers) {
+    public ChunkedOutputStream(final int minChunkSize, final SessionOutputBuffer buffer, final OutputStream outputStream,
+                               final Supplier<List<? extends Header>> trailerSupplier) {
         super();
         this.buffer = Args.notNull(buffer, "Session output buffer");
         this.outputStream = Args.notNull(outputStream, "Output stream");
         this.cache = new byte[minChunkSize];
         this.lineBuffer = new CharArrayBuffer(32);
-        this.trailers = trailers;
+        this.trailerSupplier = trailerSupplier;
     }
 
     /**
@@ -134,9 +136,10 @@ public class ChunkedOutputStream extends
     }
 
     private void writeTrailers() throws IOException {
-        final Header[] headers = this.trailers != null ? this.trailers.get() : null;
-        if (headers != null) {
-            for (final Header header: headers) {
+        final List<? extends Header> trailers = this.trailerSupplier != null ? this.trailerSupplier.get() : null;
+        if (trailers != null) {
+            for (int i = 0; i < trailers.size(); i++) {
+                final Header header = trailers.get(i);
                 if (header instanceof FormattedHeader) {
                     final CharArrayBuffer chbuffer = ((FormattedHeader) header).getBuffer();
                     this.buffer.writeLine(chbuffer, this.outputStream);

Modified: httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/AbstractClassicServerExchangeHandler.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/AbstractClassicServerExchangeHandler.java?rev=1769497&r1=1769496&r2=1769497&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/AbstractClassicServerExchangeHandler.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/AbstractClassicServerExchangeHandler.java Sun Nov 13 14:07:18 2016
@@ -271,7 +271,7 @@ public abstract class AbstractClassicSer
     }
 
     @Override
-    public final void streamEnd(final List<Header> trailers) throws HttpException, IOException {
+    public final void streamEnd(final List<? extends Header> trailers) throws HttpException, IOException {
         Asserts.notNull(inputBuffer, "Input buffer");
         inputBuffer.markEndStream();
     }

Modified: httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/AbstractContentDecoder.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/AbstractContentDecoder.java?rev=1769497&r1=1769496&r2=1769497&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/AbstractContentDecoder.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/AbstractContentDecoder.java Sun Nov 13 14:07:18 2016
@@ -30,7 +30,9 @@ package org.apache.hc.core5.http.impl.ni
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.channels.ReadableByteChannel;
+import java.util.List;
 
+import org.apache.hc.core5.http.Header;
 import org.apache.hc.core5.http.impl.BasicHttpTransportMetrics;
 import org.apache.hc.core5.http.nio.ContentDecoder;
 import org.apache.hc.core5.http.nio.SessionInputBuffer;
@@ -144,4 +146,9 @@ public abstract class AbstractContentDec
         return bytesRead;
     }
 
+    @Override
+    public List<? extends Header> getTrailers() {
+        return null;
+    }
+
 }

Modified: httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/AbstractContentEncoder.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/AbstractContentEncoder.java?rev=1769497&r1=1769496&r2=1769497&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/AbstractContentEncoder.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/AbstractContentEncoder.java Sun Nov 13 14:07:18 2016
@@ -30,7 +30,9 @@ package org.apache.hc.core5.http.impl.ni
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.channels.WritableByteChannel;
+import java.util.List;
 
+import org.apache.hc.core5.http.Header;
 import org.apache.hc.core5.http.impl.BasicHttpTransportMetrics;
 import org.apache.hc.core5.http.nio.ContentEncoder;
 import org.apache.hc.core5.http.nio.SessionOutputBuffer;
@@ -90,10 +92,14 @@ public abstract class AbstractContentEnc
     }
 
     @Override
-    public void complete() throws IOException {
+    public void complete(final List<? extends Header> trailers) throws IOException {
         this.completed = true;
     }
 
+    public final void complete() throws IOException {
+        complete(null);
+    }
+
     protected void assertNotCompleted() {
         Asserts.check(!this.completed, "Encoding process already completed");
     }

Modified: httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/AbstractHttp1StreamDuplexer.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/AbstractHttp1StreamDuplexer.java?rev=1769497&r1=1769496&r2=1769497&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/AbstractHttp1StreamDuplexer.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/AbstractHttp1StreamDuplexer.java Sun Nov 13 14:07:18 2016
@@ -35,6 +35,7 @@ import java.nio.channels.ClosedChannelEx
 import java.nio.channels.ReadableByteChannel;
 import java.nio.channels.SelectionKey;
 import java.nio.channels.WritableByteChannel;
+import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
@@ -42,6 +43,7 @@ import java.util.concurrent.locks.Reentr
 import javax.net.ssl.SSLContext;
 
 import org.apache.hc.core5.http.ConnectionClosedException;
+import org.apache.hc.core5.http.Header;
 import org.apache.hc.core5.http.HttpConnection;
 import org.apache.hc.core5.http.HttpConnectionMetrics;
 import org.apache.hc.core5.http.HttpException;
@@ -411,14 +413,14 @@ abstract class AbstractHttp1StreamDuplex
 
     enum MessageDelineation { NONE, CHUNK_CODED, MESSAGE_HEAD}
 
-    MessageDelineation endOutputStream() throws IOException {
+    MessageDelineation endOutputStream(final List<? extends Header> trailers) throws IOException {
         outputLock.lock();
         try {
             if (outgoingMessage == null) {
                 return MessageDelineation.NONE;
             }
             final ContentEncoder contentEncoder = outgoingMessage.getBody();
-            contentEncoder.complete();
+            contentEncoder.complete(trailers);
             ioSession.setEvent(SelectionKey.OP_WRITE);
             outgoingMessage = null;
             if (contentEncoder instanceof ChunkEncoder) {

Modified: httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ChunkDecoder.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ChunkDecoder.java?rev=1769497&r1=1769496&r2=1769497&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ChunkDecoder.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ChunkDecoder.java Sun Nov 13 14:07:18 2016
@@ -68,8 +68,7 @@ public class ChunkDecoder extends Abstra
 
     private final H1Config constraints;
     private final List<CharArrayBuffer> trailerBufs;
-
-    private Header[] footers;
+    private final List<Header> trailers;
 
     /**
      * @since 4.4
@@ -87,6 +86,7 @@ public class ChunkDecoder extends Abstra
         this.endOfStream = false;
         this.constraints = constraints != null ? constraints : H1Config.DEFAULT;
         this.trailerBufs = new ArrayList<>();
+        this.trailers = new ArrayList<>();
     }
 
     public ChunkDecoder(
@@ -169,10 +169,10 @@ public class ChunkDecoder extends Abstra
     private void processFooters() throws IOException {
         final int count = this.trailerBufs.size();
         if (count > 0) {
-            this.footers = new Header[this.trailerBufs.size()];
+            this.trailers.clear();
             for (int i = 0; i < this.trailerBufs.size(); i++) {
                 try {
-                    this.footers[i] = new BufferedHeader(this.trailerBufs.get(i));
+                    this.trailers.add(new BufferedHeader(this.trailerBufs.get(i)));
                 } catch (final ParseException ex) {
                     throw new IOException(ex.getMessage());
                 }
@@ -269,11 +269,9 @@ public class ChunkDecoder extends Abstra
         return totalRead;
     }
 
-    public Header[] getFooters() {
-        if (this.footers != null) {
-            return this.footers.clone();
-        }
-        return new Header[] {};
+    @Override
+    public List<? extends Header> getTrailers() {
+        return this.trailers.isEmpty() ? null : new ArrayList<>(this.trailers);
     }
 
     @Override

Modified: httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ChunkEncoder.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ChunkEncoder.java?rev=1769497&r1=1769496&r2=1769497&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ChunkEncoder.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ChunkEncoder.java Sun Nov 13 14:07:18 2016
@@ -30,10 +30,10 @@ package org.apache.hc.core5.http.impl.ni
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.channels.WritableByteChannel;
+import java.util.List;
 
 import org.apache.hc.core5.http.FormattedHeader;
 import org.apache.hc.core5.http.Header;
-import org.apache.hc.core5.http.TrailerSupplier;
 import org.apache.hc.core5.http.impl.BasicHttpTransportMetrics;
 import org.apache.hc.core5.http.message.BasicLineFormatter;
 import org.apache.hc.core5.http.nio.SessionOutputBuffer;
@@ -46,9 +46,9 @@ import org.apache.hc.core5.util.CharArra
  * @since 4.0
  */
 public class ChunkEncoder extends AbstractContentEncoder {
+
     private final int fragHint;
     private final CharArrayBuffer lineBuffer;
-    private final TrailerSupplier trailers;
 
     /**
      * @param channel underlying channel.
@@ -64,19 +64,17 @@ public class ChunkEncoder extends Abstra
             final WritableByteChannel channel,
             final SessionOutputBuffer buffer,
             final BasicHttpTransportMetrics metrics,
-            final int fragementSizeHint,
-            final TrailerSupplier trailers) {
+            final int fragementSizeHint) {
         super(channel, buffer, metrics);
         this.fragHint = fragementSizeHint > 0 ? fragementSizeHint : 0;
         this.lineBuffer = new CharArrayBuffer(16);
-        this.trailers = trailers;
     }
 
     public ChunkEncoder(
             final WritableByteChannel channel,
             final SessionOutputBuffer buffer,
             final BasicHttpTransportMetrics metrics) {
-        this(channel, buffer, metrics, 0, null);
+        this(channel, buffer, metrics, 0);
     }
 
     @Override
@@ -129,21 +127,21 @@ public class ChunkEncoder extends Abstra
     }
 
     @Override
-    public void complete() throws IOException {
+    public void complete(final List<? extends Header> trailers) throws IOException {
         assertNotCompleted();
         this.lineBuffer.clear();
         this.lineBuffer.append("0");
         this.buffer.writeLine(this.lineBuffer);
-        writeTrailers();
+        writeTrailers(trailers);
         this.lineBuffer.clear();
         this.buffer.writeLine(this.lineBuffer);
-        super.complete();
+        super.complete(trailers);
     }
 
-    private void writeTrailers() throws IOException {
-        final Header[] headers = this.trailers != null ? this.trailers.get() : null;
-        if (headers != null) {
-            for (final Header header: headers) {
+    private void writeTrailers(final List<? extends Header> trailers) throws IOException {
+        if (trailers != null) {
+            for (int i = 0; i < trailers.size(); i++) {
+                final Header header = trailers.get(i);
                 if (header instanceof FormattedHeader) {
                     final CharArrayBuffer chbuffer = ((FormattedHeader) header).getBuffer();
                     buffer.writeLine(chbuffer);

Modified: httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamDuplexer.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamDuplexer.java?rev=1769497&r1=1769496&r2=1769497&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamDuplexer.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamDuplexer.java Sun Nov 13 14:07:18 2016
@@ -31,12 +31,14 @@ import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.channels.ReadableByteChannel;
 import java.nio.channels.WritableByteChannel;
+import java.util.List;
 import java.util.Queue;
 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.Header;
 import org.apache.hc.core5.http.HttpException;
 import org.apache.hc.core5.http.HttpRequest;
 import org.apache.hc.core5.http.HttpResponse;
@@ -148,8 +150,8 @@ public class ClientHttp1StreamDuplexer e
             }
 
             @Override
-            public void complete() throws IOException {
-                endOutputStream();
+            public void complete(final List<? extends Header> trailers) throws IOException {
+                endOutputStream(trailers);
             }
 
             @Override
@@ -159,7 +161,7 @@ public class ClientHttp1StreamDuplexer e
 
             @Override
             public void abortOutput() throws IOException {
-                final MessageDelineation messageDelineation = endOutputStream();
+                final MessageDelineation messageDelineation = endOutputStream(null);
                 if (messageDelineation == MessageDelineation.MESSAGE_HEAD) {
                     inconsistent = true;
                     requestShutdown(ShutdownType.GRACEFUL);
@@ -266,7 +268,7 @@ public class ClientHttp1StreamDuplexer e
         if (len >= 0) {
             return new LengthDelimitedEncoder(channel, buffer, metrics, len, fragmentSizeHint);
         } else if (len == ContentLengthStrategy.CHUNKED) {
-            return new ChunkEncoder(channel, buffer, metrics, fragmentSizeHint, null);
+            return new ChunkEncoder(channel, buffer, metrics, fragmentSizeHint);
         } else {
             throw new LengthRequiredException("Length required");
         }

Modified: httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamHandler.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamHandler.java?rev=1769497&r1=1769496&r2=1769497&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamHandler.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamHandler.java Sun Nov 13 14:07:18 2016
@@ -93,8 +93,8 @@ class ClientHttp1StreamHandler implement
             }
 
             @Override
-            public void endStream(final List<Header> trailers) throws IOException {
-                outputChannel.complete();
+            public void endStream(final List<? extends Header> trailers) throws IOException {
+                outputChannel.complete(trailers);
                 requestState = MessageState.COMPLETE;
             }
 
@@ -273,7 +273,7 @@ class ClientHttp1StreamHandler implement
         }
         if (contentDecoder.isCompleted()) {
             responseState = MessageState.COMPLETE;
-            exchangeHandler.streamEnd(null);
+            exchangeHandler.streamEnd(contentDecoder.getTrailers());
             return total > 0 ? total : -1;
         } else {
             return total;

Modified: httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/LengthDelimitedEncoder.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/LengthDelimitedEncoder.java?rev=1769497&r1=1769496&r2=1769497&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/LengthDelimitedEncoder.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/LengthDelimitedEncoder.java Sun Nov 13 14:07:18 2016
@@ -136,7 +136,7 @@ public class LengthDelimitedEncoder exte
             }
         }
         if (this.remaining <= 0) {
-            super.complete();
+            super.complete(null);
         }
         return total;
     }
@@ -164,7 +164,7 @@ public class LengthDelimitedEncoder exte
         }
         this.remaining -= bytesWritten;
         if (this.remaining <= 0) {
-            super.complete();
+            super.complete(null);
         }
         return bytesWritten;
     }

Modified: httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamDuplexer.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamDuplexer.java?rev=1769497&r1=1769496&r2=1769497&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamDuplexer.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamDuplexer.java Sun Nov 13 14:07:18 2016
@@ -31,12 +31,14 @@ import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.channels.ReadableByteChannel;
 import java.nio.channels.WritableByteChannel;
+import java.util.List;
 import java.util.Queue;
 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.Header;
 import org.apache.hc.core5.http.HttpException;
 import org.apache.hc.core5.http.HttpRequest;
 import org.apache.hc.core5.http.HttpResponse;
@@ -151,8 +153,8 @@ public class ServerHttp1StreamDuplexer e
             }
 
             @Override
-            public void complete() throws IOException {
-                endOutputStream();
+            public void complete(final List<? extends Header> trailers) throws IOException {
+                endOutputStream(trailers);
             }
 
             @Override
@@ -162,7 +164,7 @@ public class ServerHttp1StreamDuplexer e
 
             @Override
             public void abortOutput() throws IOException {
-                final MessageDelineation messageDelineation = endOutputStream();
+                final MessageDelineation messageDelineation = endOutputStream(null);
                 if (messageDelineation == MessageDelineation.MESSAGE_HEAD) {
                     inconsistent = true;
                 }
@@ -253,7 +255,7 @@ public class ServerHttp1StreamDuplexer e
         if (len >= 0) {
             return new LengthDelimitedEncoder(channel, buffer, metrics, len, fragmentSizeHint);
         } else if (len == ContentLengthStrategy.CHUNKED) {
-            return new ChunkEncoder(channel, buffer, metrics, fragmentSizeHint, null);
+            return new ChunkEncoder(channel, buffer, metrics, fragmentSizeHint);
         } else {
             return new IdentityEncoder(channel, buffer, metrics, fragmentSizeHint);
         }
@@ -427,10 +429,10 @@ public class ServerHttp1StreamDuplexer e
         }
 
         @Override
-        public void complete() throws IOException {
+        public void complete(final List<? extends Header> trailers) throws IOException {
             synchronized (this) {
                 if (direct) {
-                    channel.complete();
+                    channel.complete(trailers);
                 } else {
                     completed = true;
                 }

Modified: httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamHandler.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamHandler.java?rev=1769497&r1=1769496&r2=1769497&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamHandler.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamHandler.java Sun Nov 13 14:07:18 2016
@@ -96,8 +96,8 @@ class ServerHttp1StreamHandler implement
             }
 
             @Override
-            public void endStream(final List<Header> trailers) throws IOException {
-                outputChannel.complete();
+            public void endStream(final List<? extends Header> trailers) throws IOException {
+                outputChannel.complete(trailers);
                 responseState = MessageState.COMPLETE;
             }
 
@@ -320,7 +320,7 @@ class ServerHttp1StreamHandler implement
         }
         if (contentDecoder.isCompleted()) {
             requestState = MessageState.COMPLETE;
-            exchangeHandler.streamEnd(null);
+            exchangeHandler.streamEnd(contentDecoder.getTrailers());
             return total > 0 ? total : -1;
         } else {
             return total;

Modified: httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/entity/AbstractClassicEntityConsumer.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/entity/AbstractClassicEntityConsumer.java?rev=1769497&r1=1769496&r2=1769497&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/entity/AbstractClassicEntityConsumer.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/entity/AbstractClassicEntityConsumer.java Sun Nov 13 14:07:18 2016
@@ -108,7 +108,7 @@ public abstract class AbstractClassicEnt
     }
 
     @Override
-    public final void streamEnd(final List<Header> trailers) throws HttpException, IOException {
+    public final void streamEnd(final List<? extends Header> trailers) throws HttpException, IOException {
         buffer.markEndStream();
     }
 

Modified: httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/AbstractHttpEntity.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/AbstractHttpEntity.java?rev=1769497&r1=1769496&r2=1769497&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/AbstractHttpEntity.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/AbstractHttpEntity.java Sun Nov 13 14:07:18 2016
@@ -28,9 +28,11 @@
 package org.apache.hc.core5.http.io.entity;
 
 import java.util.Collections;
+import java.util.List;
 import java.util.Set;
 
-import org.apache.hc.core5.http.TrailerSupplier;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.nio.Supplier;
 
 /**
  * Abstract base class for mutable entities. Provides the commonly used attributes for streamed and
@@ -79,7 +81,7 @@ public abstract class AbstractHttpEntity
     }
 
     @Override
-    public TrailerSupplier getTrailers() {
+    public Supplier<List<? extends Header>> getTrailers() {
         return null;
     }
 

Modified: httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/HttpEntityWithTrailers.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/HttpEntityWithTrailers.java?rev=1769497&r1=1769496&r2=1769497&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/HttpEntityWithTrailers.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/HttpEntityWithTrailers.java Sun Nov 13 14:07:18 2016
@@ -30,12 +30,14 @@ package org.apache.hc.core5.http.io.enti
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.util.Arrays;
 import java.util.LinkedHashSet;
+import java.util.List;
 import java.util.Set;
 
 import org.apache.hc.core5.http.Header;
 import org.apache.hc.core5.http.HttpEntity;
-import org.apache.hc.core5.http.TrailerSupplier;
+import org.apache.hc.core5.http.nio.Supplier;
 import org.apache.hc.core5.util.Args;
 
 /**
@@ -46,7 +48,7 @@ import org.apache.hc.core5.util.Args;
 public class HttpEntityWithTrailers implements HttpEntity {
 
     private final HttpEntity wrappedEntity;
-    private final Header[] trailers;
+    private final List<Header> trailers;
 
     /**
      * Creates a new entity wrapper.
@@ -54,7 +56,7 @@ public class HttpEntityWithTrailers impl
     public HttpEntityWithTrailers(final HttpEntity wrappedEntity, final Header... trailers) {
         super();
         this.wrappedEntity = Args.notNull(wrappedEntity, "Wrapped entity");
-        this.trailers = trailers;
+        this.trailers = Arrays.asList(trailers);
     }
 
     @Override
@@ -100,10 +102,10 @@ public class HttpEntityWithTrailers impl
     }
 
     @Override
-    public TrailerSupplier getTrailers() {
-        return new TrailerSupplier() {
+    public Supplier<List<? extends Header>> getTrailers() {
+        return new Supplier<List<? extends Header>>() {
             @Override
-            public Header[] get() {
+            public List<? extends Header> get() {
                 return trailers;
             }
         };

Modified: httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/HttpEntityWrapper.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/HttpEntityWrapper.java?rev=1769497&r1=1769496&r2=1769497&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/HttpEntityWrapper.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/HttpEntityWrapper.java Sun Nov 13 14:07:18 2016
@@ -30,10 +30,12 @@ package org.apache.hc.core5.http.io.enti
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.util.List;
 import java.util.Set;
 
+import org.apache.hc.core5.http.Header;
 import org.apache.hc.core5.http.HttpEntity;
-import org.apache.hc.core5.http.TrailerSupplier;
+import org.apache.hc.core5.http.nio.Supplier;
 import org.apache.hc.core5.util.Args;
 
 /**
@@ -101,7 +103,7 @@ public class HttpEntityWrapper implement
     }
 
     @Override
-    public TrailerSupplier getTrailers() {
+    public Supplier<List<? extends Header>> getTrailers() {
         return wrappedEntity.getTrailers();
     }
 

Modified: httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncDataConsumer.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncDataConsumer.java?rev=1769497&r1=1769496&r2=1769497&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncDataConsumer.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncDataConsumer.java Sun Nov 13 14:07:18 2016
@@ -44,6 +44,6 @@ public interface AsyncDataConsumer exten
 
     int consume(ByteBuffer src) throws IOException;
 
-    void streamEnd(List<Header> trailers) throws HttpException, IOException;
+    void streamEnd(List<? extends Header> trailers) throws HttpException, IOException;
 
 }

Modified: httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/BasicRequestConsumer.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/BasicRequestConsumer.java?rev=1769497&r1=1769496&r2=1769497&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/BasicRequestConsumer.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/BasicRequestConsumer.java Sun Nov 13 14:07:18 2016
@@ -91,7 +91,7 @@ public class BasicRequestConsumer<T> imp
     }
 
     @Override
-    public void streamEnd(final List<Header> trailers) throws HttpException, IOException {
+    public void streamEnd(final List<? extends Header> trailers) throws HttpException, IOException {
         dataConsumer.streamEnd(trailers);
     }
 

Modified: httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/BasicResponseConsumer.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/BasicResponseConsumer.java?rev=1769497&r1=1769496&r2=1769497&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/BasicResponseConsumer.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/BasicResponseConsumer.java Sun Nov 13 14:07:18 2016
@@ -97,7 +97,7 @@ public class BasicResponseConsumer<T> im
     }
 
     @Override
-    public void streamEnd(final List<Header> trailers) throws HttpException, IOException {
+    public void streamEnd(final List<? extends Header> trailers) throws HttpException, IOException {
         dataConsumer.streamEnd(trailers);
     }
 

Modified: httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/ContentDecoder.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/ContentDecoder.java?rev=1769497&r1=1769496&r2=1769497&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/ContentDecoder.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/ContentDecoder.java Sun Nov 13 14:07:18 2016
@@ -29,6 +29,9 @@ package org.apache.hc.core5.http.nio;
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
+import java.util.List;
+
+import org.apache.hc.core5.http.Header;
 
 /**
  * Abstract HTTP content decoder. HTTP content decoders can be used
@@ -58,4 +61,13 @@ public interface ContentDecoder {
      */
     boolean isCompleted();
 
+    /**
+     * Returns content trailers if available
+     *
+     * @return list of trailers
+     *
+     * @since 5.0
+     */
+    List<? extends Header> getTrailers();
+
 }

Modified: httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/ContentEncoder.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/ContentEncoder.java?rev=1769497&r1=1769496&r2=1769497&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/ContentEncoder.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/ContentEncoder.java Sun Nov 13 14:07:18 2016
@@ -29,6 +29,9 @@ package org.apache.hc.core5.http.nio;
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
+import java.util.List;
+
+import org.apache.hc.core5.http.Header;
 
 /**
  * Abstract HTTP content encoder. HTTP content encoders can be used
@@ -53,7 +56,7 @@ public interface ContentEncoder {
      *
      * @throws IOException if I/O error occurs while writing content
      */
-    void complete() throws IOException;
+    void complete(List<? extends Header> trailers) throws IOException;
 
     /**
      * Returns {@code true} if the entity has been transferred in its

Modified: httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/DataStreamChannel.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/DataStreamChannel.java?rev=1769497&r1=1769496&r2=1769497&view=diff
==============================================================================
--- httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/DataStreamChannel.java (original)
+++ httpcomponents/httpcore/trunk/httpcore5/src/main/java/org/apache/hc/core5/http/nio/DataStreamChannel.java Sun Nov 13 14:07:18 2016
@@ -47,6 +47,6 @@ public interface DataStreamChannel exten
 
     void requestOutput();
 
-    void endStream(List<Header> trailers) throws IOException;
+    void endStream(List<? extends Header> trailers) throws IOException;
 
 }