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 2020/11/19 11:30:21 UTC
[httpcomponents-core] 01/02: Optimized request termination by the
non-blocking HTTP/1.1 protocol handler in case of a premature response or
`expect-continue` handshake failure
This is an automated email from the ASF dual-hosted git repository.
olegk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/httpcomponents-core.git
commit 7ad2073d8e4d7bf1982599c103897e200dad0180
Author: Oleg Kalnichevski <ol...@apache.org>
AuthorDate: Wed Nov 18 19:57:01 2020 +0100
Optimized request termination by the non-blocking HTTP/1.1 protocol handler in case of a premature response or `expect-continue` handshake failure
---
.../hc/core5/testing/nio/Http1IntegrationTest.java | 66 ++++++++++++++++++++++
.../http/impl/nio/ClientHttp1StreamDuplexer.java | 6 +-
.../http/impl/nio/ClientHttp1StreamHandler.java | 2 +-
3 files changed, 68 insertions(+), 6 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 67859d6..8c9a20e 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
@@ -859,6 +859,72 @@ public class Http1IntegrationTest extends InternalHttp1ServerTestBase {
}
@Test
+ public void testExpectationFailedCloseConnection() throws Exception {
+ server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+ @Override
+ public AsyncServerExchangeHandler get() {
+ return new MessageExchangeHandler<String>(new StringAsyncEntityConsumer()) {
+
+ @Override
+ protected void handle(
+ final Message<HttpRequest, String> request,
+ final AsyncServerRequestHandler.ResponseTrigger responseTrigger,
+ final HttpContext context) throws IOException, HttpException {
+ responseTrigger.submitResponse(new BasicResponseProducer(HttpStatus.SC_OK, "All is well"), context);
+
+ }
+ };
+ }
+
+ });
+ final InetSocketAddress serverEndpoint = server.start(null, new Decorator<AsyncServerExchangeHandler>() {
+
+ @Override
+ public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler handler) {
+
+ return new BasicAsyncServerExpectationDecorator(handler) {
+
+ @Override
+ protected AsyncResponseProducer verify(final HttpRequest request, final HttpContext context) throws IOException, HttpException {
+ final Header h = request.getFirstHeader("password");
+ if (h != null && "secret".equals(h.getValue())) {
+ return null;
+ } else {
+ final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_UNAUTHORIZED);
+ response.addHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE);
+ return new BasicResponseProducer(response, "You shall not pass");
+ }
+ }
+ };
+
+ }
+ }, Http1Config.DEFAULT);
+
+ client.start();
+ final Future<IOSession> sessionFuture = client.requestSession(
+ new HttpHost("localhost", serverEndpoint.getPort()), TIMEOUT, null);
+ final IOSession ioSession = sessionFuture.get();
+ final ClientSessionEndpoint streamEndpoint = new ClientSessionEndpoint(ioSession);
+
+ final HttpRequest request1 = new BasicHttpRequest(Method.POST, createRequestURI(serverEndpoint, "/echo"));
+ final Future<Message<HttpResponse, String>> future1 = streamEndpoint.execute(
+ new BasicRequestProducer(request1, new MultiBinEntityProducer(
+ new byte[] {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'},
+ 100000,
+ ContentType.TEXT_PLAIN)),
+ new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null);
+ final Message<HttpResponse, String> result1 = future1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
+ Assert.assertNotNull(result1);
+ final HttpResponse response1 = result1.getHead();
+ Assert.assertNotNull(response1);
+ Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, response1.getCode());
+ Assert.assertNotNull("You shall not pass", result1.getBody());
+
+ Assert.assertFalse(streamEndpoint.isOpen());
+ }
+
+ @Test
public void testDelayedExpectationVerification() throws Exception {
server.register("*", new Supplier<AsyncServerExchangeHandler>() {
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamDuplexer.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamDuplexer.java
index ca6c9a2..3cc6758 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamDuplexer.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1StreamDuplexer.java
@@ -186,11 +186,7 @@ public class ClientHttp1StreamDuplexer extends AbstractHttp1StreamDuplexer<HttpR
@Override
public boolean abortGracefully() throws IOException {
final MessageDelineation messageDelineation = endOutputStream(null);
- if (messageDelineation == MessageDelineation.MESSAGE_HEAD) {
- requestShutdown(CloseMode.GRACEFUL);
- return false;
- }
- return true;
+ return messageDelineation != MessageDelineation.MESSAGE_HEAD;
}
@Override
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 e508852..1721b04 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
@@ -234,7 +234,7 @@ class ClientHttp1StreamHandler implements ResourceHolder {
return;
}
if (requestState == MessageState.BODY) {
- if (keepAlive && status >= HttpStatus.SC_CLIENT_ERROR) {
+ if (status >= HttpStatus.SC_CLIENT_ERROR) {
requestState = MessageState.COMPLETE;
if (!outputChannel.abortGracefully()) {
keepAlive = false;