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/03 11:36:27 UTC

[httpcomponents-core] branch identity_te_bug_fix created (now a1f5e92)

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

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


      at a1f5e92  Bug fix: async HTTP/1.1 server side protocol handler fails to correctly terminate message exchanges with identity transfer encoded responses

This branch includes the following new commits:

     new a1f5e92  Bug fix: async HTTP/1.1 server side protocol handler fails to correctly terminate message exchanges with identity transfer encoded responses

The 1 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] 01/01: 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 identity_te_bug_fix
in repository https://gitbox.apache.org/repos/asf/httpcomponents-core.git

commit a1f5e920edc450a3e8d8ebd1c5995a746b85e5e1
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