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 2019/09/30 13:09:11 UTC

[httpcomponents-core] 02/02: HTTPCORE-604: async HTTP/1.1 protocol handler must close the i/o session if it cannot be kept alive before passing control to the message exchange hanlder

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

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

commit e326a64971573cd9a42dc77d65174a906bad85c0
Author: Oleg Kalnichevski <ol...@apache.org>
AuthorDate: Mon Sep 30 14:48:52 2019 +0200

    HTTPCORE-604: async HTTP/1.1 protocol handler must close the i/o session if it cannot be kept alive before passing control to the message exchange hanlder
---
 .../hc/core5/testing/nio/Http1IntegrationTest.java | 68 ++++++++++++++++++++++
 .../testing/nio/Http1ServerAndRequesterTest.java   | 29 +++++++++
 .../http/impl/nio/ClientHttp1StreamHandler.java    |  7 ++-
 3 files changed, 101 insertions(+), 3 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 e167c38..e82b566 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
@@ -108,6 +108,7 @@ 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.AsyncRequestBuilder;
 import org.apache.hc.core5.http.nio.support.BasicAsyncServerExpectationDecorator;
 import org.apache.hc.core5.http.nio.support.BasicRequestConsumer;
 import org.apache.hc.core5.http.nio.support.BasicRequestProducer;
@@ -221,6 +222,40 @@ public class Http1IntegrationTest extends InternalHttp1ServerTestBase {
     }
 
     @Test
+    public void testSimpleGetConnectionClose() throws Exception {
+        server.register("/hello", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new SingleLineResponseHandler("Hi there");
+            }
+
+        });
+        final InetSocketAddress serverEndpoint = server.start();
+
+        client.start();
+        final URI requestURI = createRequestURI(serverEndpoint, "/hello");
+        for (int i = 0; i < 5; i++) {
+            final Future<ClientSessionEndpoint> connectFuture = client.connect(
+                    "localhost", serverEndpoint.getPort(), TIMEOUT);
+            try (final ClientSessionEndpoint streamEndpoint = connectFuture.get()) {
+                final Future<Message<HttpResponse, String>> future = streamEndpoint.execute(
+                        AsyncRequestBuilder.get(requestURI)
+                                .addHeader(HttpHeaders.CONNECTION, "close")
+                                .build(),
+                        new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null);
+                final Message<HttpResponse, String> result = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
+                Assert.assertNotNull(result);
+                final HttpResponse response1 = result.getHead();
+                final String entity1 = result.getBody();
+                Assert.assertNotNull(response1);
+                Assert.assertEquals(200, response1.getCode());
+                Assert.assertEquals("Hi there", entity1);
+            }
+        }
+    }
+
+    @Test
     public void testSimpleGetIdentityTransfer() throws Exception {
         server.register("/hello", new Supplier<AsyncServerExchangeHandler>() {
 
@@ -654,6 +689,39 @@ public class Http1IntegrationTest extends InternalHttp1ServerTestBase {
     }
 
     @Test
+    public void testSimpleHeadConnectionClose() throws Exception {
+        server.register("/hello", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new SingleLineResponseHandler("Hi there");
+            }
+
+        });
+        final InetSocketAddress serverEndpoint = server.start();
+
+        client.start();
+        final URI requestURI = createRequestURI(serverEndpoint, "/hello");
+        for (int i = 0; i < 5; i++) {
+            final Future<ClientSessionEndpoint> connectFuture = client.connect(
+                    "localhost", serverEndpoint.getPort(), TIMEOUT);
+            try (final ClientSessionEndpoint streamEndpoint = connectFuture.get()) {
+                final Future<Message<HttpResponse, String>> future = streamEndpoint.execute(
+                        AsyncRequestBuilder.head(requestURI)
+                                .addHeader(HttpHeaders.CONNECTION, "close")
+                                .build(),
+                        new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null);
+                final Message<HttpResponse, String> result = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
+                Assert.assertNotNull(result);
+                final HttpResponse response1 = result.getHead();
+                Assert.assertNotNull(response1);
+                Assert.assertEquals(200, response1.getCode());
+                Assert.assertNull(result.getBody());
+            }
+        }
+    }
+
+    @Test
     public void testHeadPipelined() throws Exception {
         server.register("/hello", new Supplier<AsyncServerExchangeHandler>() {
 
diff --git a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http1ServerAndRequesterTest.java b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http1ServerAndRequesterTest.java
index 04d8f5e..ee2679d 100644
--- a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http1ServerAndRequesterTest.java
+++ b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http1ServerAndRequesterTest.java
@@ -53,6 +53,7 @@ import org.apache.hc.core5.http.impl.bootstrap.AsyncServerBootstrap;
 import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester;
 import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer;
 import org.apache.hc.core5.http.impl.bootstrap.StandardFilters;
+import org.apache.hc.core5.http.message.BasicHttpRequest;
 import org.apache.hc.core5.http.nio.AsyncClientEndpoint;
 import org.apache.hc.core5.http.nio.AsyncDataConsumer;
 import org.apache.hc.core5.http.nio.AsyncEntityProducer;
@@ -394,4 +395,32 @@ public class Http1ServerAndRequesterTest {
         }
     }
 
+    @Test
+    public void testNonPersistentHeads() throws Exception {
+        server.start();
+        final Future<ListenerEndpoint> future = server.listen(new InetSocketAddress(0));
+        final ListenerEndpoint listener = future.get();
+        final InetSocketAddress address = (InetSocketAddress) listener.getAddress();
+        requester.start();
+
+        final HttpHost target = new HttpHost(scheme.id, "localhost", address.getPort());
+        final Queue<Future<Message<HttpResponse, String>>> queue = new LinkedList<>();
+
+        for (int i = 0; i < 20; i++) {
+            final HttpRequest head = new BasicHttpRequest(Methods.HEAD, target, "/no-keep-alive/stuff?p=" + i);
+            queue.add(requester.execute(
+                    new BasicRequestProducer(head, null),
+                    new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null));
+        }
+
+        while (!queue.isEmpty()) {
+            final Future<Message<HttpResponse, String>> resultFuture = queue.remove();
+            final Message<HttpResponse, String> message = resultFuture.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
+            Assert.assertThat(message, CoreMatchers.notNullValue());
+            final HttpResponse response = message.getHead();
+            Assert.assertThat(response.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK));
+            Assert.assertThat(message.getBody(), CoreMatchers.nullValue());
+        }
+    }
+
 }
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamHandler.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamHandler.java
index 2edb807..aebd5aa 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamHandler.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamHandler.java
@@ -247,11 +247,12 @@ class ClientHttp1StreamHandler implements ResourceHolder {
         context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response);
         httpProcessor.process(response, entityDetails, context);
 
+        if (entityDetails == null && !keepAlive) {
+            outputChannel.close();
+        }
+
         exchangeHandler.consumeResponse(response, entityDetails, context);
         if (entityDetails == null) {
-            if (!keepAlive) {
-                outputChannel.close();
-            }
             responseState = MessageState.COMPLETE;
         } else {
             responseState = MessageState.BODY;