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 2021/09/04 08:50:38 UTC

[httpcomponents-core] branch HTTPCORE-684 created (now 4bc58d4)

This is an automated email from the ASF dual-hosted git repository.

olegk pushed a change to branch HTTPCORE-684
in repository https://gitbox.apache.org/repos/asf/httpcomponents-core.git.


      at 4bc58d4  HTTPCORE-684: async SSL i/o session fails to propagate end-of-stream events to the protocol handler. This can cause a failure in handling of identity transfer encoded HTTP/1.1 response messages

This branch includes the following new commits:

     new aec1a6c  Bug fix: async HTTP/1.1 server side protocol handler fails to correctly terminate message exchanges with identity transfer encoded responses
     new 4bc58d4  HTTPCORE-684: async SSL i/o session fails to propagate end-of-stream events to the protocol handler. This can cause a failure in handling of identity transfer encoded HTTP/1.1 response messages

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


[httpcomponents-core] 02/02: HTTPCORE-684: async SSL i/o session fails to propagate end-of-stream events to the protocol handler. This can cause a failure in handling of identity transfer encoded HTTP/1.1 response messages

Posted by ol...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

olegk pushed a commit to branch HTTPCORE-684
in repository https://gitbox.apache.org/repos/asf/httpcomponents-core.git

commit 4bc58d4df935d43c5171d4c5174a180f2e78a739
Author: Oleg Kalnichevski <ol...@apache.org>
AuthorDate: Sat Sep 4 10:49:39 2021 +0200

    HTTPCORE-684: async SSL i/o session fails to propagate end-of-stream events to the protocol handler. This can cause a failure in handling of identity transfer encoded HTTP/1.1 response messages
---
 .../src/main/java/org/apache/hc/core5/reactor/ssl/SSLIOSession.java    | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/httpcore5/src/main/java/org/apache/hc/core5/reactor/ssl/SSLIOSession.java b/httpcore5/src/main/java/org/apache/hc/core5/reactor/ssl/SSLIOSession.java
index 45cd4a7..1cd5cc1 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/reactor/ssl/SSLIOSession.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/reactor/ssl/SSLIOSession.java
@@ -569,6 +569,9 @@ public class SSLIOSession implements IOSession {
                 }
             }
         }
+        if (endOfStream) {
+            ensureHandler().inputReady(protocolSession, null);
+        }
     }
 
     private void encryptData(final IOSession protocolSession) throws IOException {

[httpcomponents-core] 01/02: Bug fix: async HTTP/1.1 server side protocol handler fails to correctly terminate message exchanges with identity transfer encoded responses

Posted by ol...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

olegk pushed a commit to branch HTTPCORE-684
in repository https://gitbox.apache.org/repos/asf/httpcomponents-core.git

commit aec1a6c00512cc061a5fcad8db0c657d92787e0a
Author: Oleg Kalnichevski <ol...@apache.org>
AuthorDate: Fri Sep 3 13:35:57 2021 +0200

    Bug fix: async HTTP/1.1 server side protocol handler fails to correctly terminate message exchanges with identity transfer encoded responses
---
 .../hc/core5/testing/nio/Http1IntegrationTest.java | 112 ++++++++++++++++++---
 .../http/impl/nio/AbstractHttp1StreamDuplexer.java |   8 ++
 .../http/impl/nio/ServerHttp1StreamDuplexer.java   |   8 +-
 3 files changed, 115 insertions(+), 13 deletions(-)

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 8c9a20e..ccda705 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
@@ -114,6 +114,7 @@ import org.apache.hc.core5.http.nio.support.BasicRequestConsumer;
 import org.apache.hc.core5.http.nio.support.BasicRequestProducer;
 import org.apache.hc.core5.http.nio.support.BasicResponseConsumer;
 import org.apache.hc.core5.http.nio.support.BasicResponseProducer;
+import org.apache.hc.core5.http.nio.support.ImmediateResponseExchangeHandler;
 import org.apache.hc.core5.http.nio.support.classic.AbstractClassicEntityConsumer;
 import org.apache.hc.core5.http.nio.support.classic.AbstractClassicEntityProducer;
 import org.apache.hc.core5.http.nio.support.classic.AbstractClassicServerExchangeHandler;
@@ -270,19 +271,106 @@ public class Http1IntegrationTest extends InternalHttp1ServerTestBase {
         final InetSocketAddress serverEndpoint = server.start(httpProcessor, Http1Config.DEFAULT);
 
         client.start();
-        final Future<ClientSessionEndpoint> connectFuture = client.connect("localhost", serverEndpoint.getPort(), TIMEOUT);
-        final ClientSessionEndpoint streamEndpoint = connectFuture.get();
 
-        final Future<Message<HttpResponse, String>> future = streamEndpoint.execute(
-                new BasicRequestProducer(Method.GET, createRequestURI(serverEndpoint, "/hello")),
-                new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null);
-        final Message<HttpResponse, String> result = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
-        Assert.assertNotNull(result);
-        final HttpResponse response = result.getHead();
-        final String entity = result.getBody();
-        Assert.assertNotNull(response);
-        Assert.assertEquals(200, response.getCode());
-        Assert.assertEquals("Hi there", entity);
+        final int reqNo = 5;
+
+        for (int i = 0; i < reqNo; i++) {
+            final Future<ClientSessionEndpoint> connectFuture = client.connect("localhost", serverEndpoint.getPort(), TIMEOUT);
+            final ClientSessionEndpoint streamEndpoint = connectFuture.get();
+
+            final Future<Message<HttpResponse, String>> future = streamEndpoint.execute(
+                    new BasicRequestProducer(Method.GET, createRequestURI(serverEndpoint, "/hello")),
+                    new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null);
+            final Message<HttpResponse, String> result = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
+
+            streamEndpoint.close();
+
+            Assert.assertNotNull(result);
+            final HttpResponse response = result.getHead();
+            final String entity = result.getBody();
+            Assert.assertNotNull(response);
+            Assert.assertEquals(200, response.getCode());
+            Assert.assertEquals("Hi there", entity);
+        }
+
+    }
+
+    @Test
+    public void testPostIdentityTransfer() throws Exception {
+        server.register("/hello", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new SingleLineResponseHandler("Hi there");
+            }
+
+        });
+        final HttpProcessor httpProcessor = new DefaultHttpProcessor(new RequestValidateHost());
+        final InetSocketAddress serverEndpoint = server.start(httpProcessor, Http1Config.DEFAULT);
+
+        client.start();
+
+        final int reqNo = 5;
+
+        for (int i = 0; i < reqNo; i++) {
+            final Future<ClientSessionEndpoint> connectFuture = client.connect("localhost", serverEndpoint.getPort(), TIMEOUT);
+            final ClientSessionEndpoint streamEndpoint = connectFuture.get();
+
+            final Future<Message<HttpResponse, String>> future = streamEndpoint.execute(
+                    new BasicRequestProducer(Method.POST,
+                            createRequestURI(serverEndpoint, "/hello"),
+                            new MultiLineEntityProducer("Hello", 16 * i)),
+                    new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null);
+            final Message<HttpResponse, String> result = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
+
+            streamEndpoint.close();
+
+            Assert.assertNotNull(result);
+            final HttpResponse response = result.getHead();
+            final String entity = result.getBody();
+            Assert.assertNotNull(response);
+            Assert.assertEquals(200, response.getCode());
+            Assert.assertEquals("Hi there", entity);
+        }
+    }
+
+    @Test
+    public void testPostIdentityTransferOutOfSequenceResponse() throws Exception {
+        server.register("/hello", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new ImmediateResponseExchangeHandler(500, "Go away");
+            }
+
+        });
+        final HttpProcessor httpProcessor = new DefaultHttpProcessor(new RequestValidateHost());
+        final InetSocketAddress serverEndpoint = server.start(httpProcessor, Http1Config.DEFAULT);
+
+        client.start();
+
+        final int reqNo = 5;
+
+        for (int i = 0; i < reqNo; i++) {
+            final Future<ClientSessionEndpoint> connectFuture = client.connect("localhost", serverEndpoint.getPort(), TIMEOUT);
+            final ClientSessionEndpoint streamEndpoint = connectFuture.get();
+
+            final Future<Message<HttpResponse, String>> future = streamEndpoint.execute(
+                    new BasicRequestProducer(Method.POST,
+                            createRequestURI(serverEndpoint, "/hello"),
+                            new MultiLineEntityProducer("Hello", 16 * i)),
+                    new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null);
+            final Message<HttpResponse, String> result = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
+
+            streamEndpoint.close();
+
+            Assert.assertNotNull(result);
+            final HttpResponse response = result.getHead();
+            final String entity = result.getBody();
+            Assert.assertNotNull(response);
+            Assert.assertEquals(500, response.getCode());
+            Assert.assertEquals("Go away", entity);
+        }
     }
 
     @Test
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 35cf169..0a24189 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
@@ -140,6 +140,14 @@ abstract class AbstractHttp1StreamDuplexer<IncomingMessage extends HttpMessage,
         return ioSession.getId();
     }
 
+    boolean isActive() {
+        return connState == ConnectionState.ACTIVE;
+    }
+
+    boolean isShuttingDown() {
+        return connState.compareTo(ConnectionState.GRACEFUL_SHUTDOWN) >= 0;
+    }
+
     void shutdownSession(final CloseMode closeMode) {
         if (closeMode == CloseMode.GRACEFUL) {
             connState = ConnectionState.GRACEFUL_SHUTDOWN;
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 2b6481b..d256fef 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
@@ -393,6 +393,9 @@ public class ServerHttp1StreamDuplexer extends AbstractHttp1StreamDuplexer<HttpR
             }
             incoming = null;
         }
+        if (isShuttingDown() && outputIdle() && inputIdle()) {
+            shutdownSession(CloseMode.IMMEDIATE);
+        }
     }
 
     @Override
@@ -423,7 +426,7 @@ public class ServerHttp1StreamDuplexer extends AbstractHttp1StreamDuplexer<HttpR
             }
             outgoing = null;
         }
-        if (outgoing == null && isOpen()) {
+        if (outgoing == null && isActive()) {
             final ServerHttp1StreamHandler handler = pipeline.poll();
             if (handler != null) {
                 outgoing = handler;
@@ -433,6 +436,9 @@ public class ServerHttp1StreamDuplexer extends AbstractHttp1StreamDuplexer<HttpR
                 }
             }
         }
+        if (isShuttingDown() && outputIdle() && inputIdle()) {
+            shutdownSession(CloseMode.IMMEDIATE);
+        }
     }
 
     @Override