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:39 UTC
[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
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