From 1111c62dd3bb4b15c7804a6a3fd4386bf0b199c3 Mon Sep 17 00:00:00 2001 From: Oleg Kalnichevski Date: Wed, 17 Jan 2024 16:58:46 +0100 Subject: [PATCH] Classic ExecRuntime to support information responses (1xx) --- .../sync/TestClientRequestExecution.java | 12 +++++++++- .../hc/client5/http/classic/ExecRuntime.java | 19 ++++++++++++++++ .../impl/classic/InternalExecRuntime.java | 12 +++++++++- .../http/impl/classic/MainClientExec.java | 2 +- .../http/impl/classic/TestMainClientExec.java | 22 ++++++++++++++----- 5 files changed, 58 insertions(+), 9 deletions(-) diff --git a/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/TestClientRequestExecution.java b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/TestClientRequestExecution.java index dce158a99..537fd9c54 100644 --- a/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/TestClientRequestExecution.java +++ b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/TestClientRequestExecution.java @@ -57,6 +57,7 @@ import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.impl.io.HttpRequestExecutor; import org.apache.hc.core5.http.io.HttpClientConnection; import org.apache.hc.core5.http.io.HttpRequestHandler; +import org.apache.hc.core5.http.io.HttpResponseInformationCallback; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.entity.InputStreamEntity; import org.apache.hc.core5.http.io.entity.StringEntity; @@ -137,8 +138,17 @@ public abstract class TestClientRequestExecution { final ClassicHttpRequest request, final HttpClientConnection conn, final HttpContext context) throws IOException, HttpException { + return execute(request, conn, null, context); + } - final ClassicHttpResponse response = super.execute(request, conn, context); + @Override + public ClassicHttpResponse execute( + final ClassicHttpRequest request, + final HttpClientConnection conn, + final HttpResponseInformationCallback informationCallback, + final HttpContext context) throws IOException, HttpException { + + final ClassicHttpResponse response = super.execute(request, conn, informationCallback, context); final Object marker = context.getAttribute(MARKER); if (marker == null) { context.setAttribute(MARKER, Boolean.TRUE); diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/classic/ExecRuntime.java b/httpclient5/src/main/java/org/apache/hc/client5/http/classic/ExecRuntime.java index 53604ddb1..3bb334dd0 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/classic/ExecRuntime.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/classic/ExecRuntime.java @@ -37,6 +37,7 @@ import org.apache.hc.core5.concurrent.CancellableDependency; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpException; +import org.apache.hc.core5.http.io.HttpResponseInformationCallback; import org.apache.hc.core5.util.TimeValue; /** @@ -141,6 +142,24 @@ public interface ExecRuntime { ClassicHttpRequest request, HttpClientContext context) throws IOException, HttpException; + /** + * Executes HTTP request using the given context. + * + * @param id unique operation ID or {@code null}. + * @param request the request message. + * @param informationCallback information (1xx) response handler + * @param context the execution context. + * + * @since 5.4 + */ + default ClassicHttpResponse execute( + String id, + ClassicHttpRequest request, + HttpResponseInformationCallback informationCallback, + HttpClientContext context) throws IOException, HttpException { + return execute(id, request, context); + } + /** * Determines of the connection is considered re-usable. * diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/InternalExecRuntime.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/InternalExecRuntime.java index 7f5c47a58..9206b9432 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/InternalExecRuntime.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/InternalExecRuntime.java @@ -48,6 +48,7 @@ import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ConnectionRequestTimeoutException; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.impl.io.HttpRequestExecutor; +import org.apache.hc.core5.http.io.HttpResponseInformationCallback; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.TimeValue; @@ -205,6 +206,15 @@ class InternalExecRuntime implements ExecRuntime, Cancellable { final String id, final ClassicHttpRequest request, final HttpClientContext context) throws IOException, HttpException { + return execute(id, request, null, context); + } + + @Override + public ClassicHttpResponse execute( + final String id, + final ClassicHttpRequest request, + final HttpResponseInformationCallback informationCallback, + final HttpClientContext context) throws IOException, HttpException { final ConnectionEndpoint endpoint = ensureValid(); if (!endpoint.isConnected()) { connectEndpoint(endpoint, context); @@ -223,7 +233,7 @@ class InternalExecRuntime implements ExecRuntime, Cancellable { return endpoint.execute( id, request, - requestExecutor::execute, + (r, conn, c) -> requestExecutor.execute(r, conn, informationCallback, c), context); } diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/MainClientExec.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/MainClientExec.java index 4ff703853..5e8d9f1ee 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/MainClientExec.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/MainClientExec.java @@ -113,7 +113,7 @@ public final class MainClientExec implements ExecChainHandler { httpProcessor.process(request, request.getEntity(), context); - final ClassicHttpResponse response = execRuntime.execute(exchangeId, request, context); + final ClassicHttpResponse response = execRuntime.execute(exchangeId, request, null, context); context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response); httpProcessor.process(response, response.getEntity(), context); diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestMainClientExec.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestMainClientExec.java index ef374748b..b90104132 100644 --- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestMainClientExec.java +++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestMainClientExec.java @@ -97,13 +97,14 @@ public class TestMainClientExec { Mockito.when(execRuntime.execute( Mockito.anyString(), Mockito.same(request), + Mockito.any(), Mockito.any())).thenReturn(response); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context); final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null); Mockito.verify(httpProcessor).process(request, null, context); - Mockito.verify(execRuntime).execute("test", request, context); + Mockito.verify(execRuntime).execute("test", request, null, context); Mockito.verify(httpProcessor).process(response, responseEntity, context); Assertions.assertEquals(route, context.getHttpRoute()); @@ -124,6 +125,7 @@ public class TestMainClientExec { Mockito.when(execRuntime.execute( Mockito.anyString(), Mockito.same(request), + Mockito.any(), Mockito.any())).thenReturn(response); Mockito.when(reuseStrategy.keepAlive( Mockito.same(request), @@ -132,7 +134,7 @@ public class TestMainClientExec { final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context); final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null); - Mockito.verify(execRuntime).execute("test", request, context); + Mockito.verify(execRuntime).execute("test", request, null, context); Mockito.verify(execRuntime, Mockito.times(1)).markConnectionNonReusable(); Mockito.verify(execRuntime, Mockito.never()).releaseEndpoint(); @@ -152,6 +154,7 @@ public class TestMainClientExec { Mockito.when(execRuntime.execute( Mockito.anyString(), Mockito.same(request), + Mockito.any(), Mockito.any())).thenReturn(response); Mockito.when(reuseStrategy.keepAlive( Mockito.same(request), @@ -161,7 +164,7 @@ public class TestMainClientExec { final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context); final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null); - Mockito.verify(execRuntime).execute("test", request, context); + Mockito.verify(execRuntime).execute("test", request, null, context); Mockito.verify(execRuntime).markConnectionNonReusable(); Mockito.verify(execRuntime).releaseEndpoint(); @@ -184,6 +187,7 @@ public class TestMainClientExec { Mockito.when(execRuntime.execute( Mockito.anyString(), Mockito.same(request), + Mockito.any(), Mockito.any())).thenReturn(response); Mockito.when(reuseStrategy.keepAlive( Mockito.same(request), @@ -196,7 +200,7 @@ public class TestMainClientExec { final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context); final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null); - Mockito.verify(execRuntime).execute("test", request, context); + Mockito.verify(execRuntime).execute("test", request, null, context); Mockito.verify(execRuntime).markConnectionReusable(null, TimeValue.ofMilliseconds(678L)); Mockito.verify(execRuntime, Mockito.never()).releaseEndpoint(); @@ -214,6 +218,7 @@ public class TestMainClientExec { Mockito.when(execRuntime.execute( Mockito.anyString(), Mockito.same(request), + Mockito.any(), Mockito.any())).thenReturn(response); Mockito.when(reuseStrategy.keepAlive( Mockito.same(request), @@ -226,7 +231,7 @@ public class TestMainClientExec { final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context); final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null); - Mockito.verify(execRuntime).execute("test", request, context); + Mockito.verify(execRuntime).execute("test", request, null, context); Mockito.verify(execRuntime).releaseEndpoint(); Assertions.assertNotNull(finalResponse); @@ -247,6 +252,7 @@ public class TestMainClientExec { Mockito.when(execRuntime.execute( Mockito.anyString(), Mockito.same(request), + Mockito.any(), Mockito.any())).thenReturn(response); Mockito.when(reuseStrategy.keepAlive( Mockito.same(request), @@ -255,7 +261,7 @@ public class TestMainClientExec { final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context); final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null); - Mockito.verify(execRuntime, Mockito.times(1)).execute("test", request, context); + Mockito.verify(execRuntime, Mockito.times(1)).execute("test", request, null, context); Mockito.verify(execRuntime, Mockito.never()).disconnectEndpoint(); Mockito.verify(execRuntime, Mockito.never()).releaseEndpoint(); @@ -276,6 +282,7 @@ public class TestMainClientExec { Mockito.when(execRuntime.execute( Mockito.anyString(), Mockito.same(request), + Mockito.any(), Mockito.any())).thenThrow(new ConnectionShutdownException()); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context); @@ -293,6 +300,7 @@ public class TestMainClientExec { Mockito.when(execRuntime.execute( Mockito.anyString(), Mockito.same(request), + Mockito.any(), Mockito.any())).thenThrow(new RuntimeException("Ka-boom")); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context); @@ -310,6 +318,7 @@ public class TestMainClientExec { Mockito.when(execRuntime.execute( Mockito.anyString(), Mockito.same(request), + Mockito.any(), Mockito.any())).thenThrow(new HttpException("Ka-boom")); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context); @@ -327,6 +336,7 @@ public class TestMainClientExec { Mockito.when(execRuntime.execute( Mockito.anyString(), Mockito.same(request), + Mockito.any(), Mockito.any())).thenThrow(new IOException("Ka-boom")); final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);