diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/config/RequestConfig.java b/httpclient5/src/main/java/org/apache/hc/client5/http/config/RequestConfig.java index f98e3c315..e3cf0efab 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/config/RequestConfig.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/config/RequestConfig.java @@ -57,6 +57,7 @@ public class RequestConfig implements Cloneable { private final Collection proxyPreferredAuthSchemes; private final Timeout connectionRequestTimeout; private final Timeout connectTimeout; + private final Timeout responseTimeout; private final boolean contentCompressionEnabled; private final boolean hardCancellationEnabled; @@ -65,7 +66,7 @@ public class RequestConfig implements Cloneable { */ protected RequestConfig() { this(false, null, null, false, false, 0, false, null, null, - DEFAULT_CONNECTION_REQUEST_TIMEOUT, DEFAULT_CONNECT_TIMEOUT, false, false); + DEFAULT_CONNECTION_REQUEST_TIMEOUT, DEFAULT_CONNECT_TIMEOUT, null, false, false); } RequestConfig( @@ -80,6 +81,7 @@ public class RequestConfig implements Cloneable { final Collection proxyPreferredAuthSchemes, final Timeout connectionRequestTimeout, final Timeout connectTimeout, + final Timeout responseTimeout, final boolean contentCompressionEnabled, final boolean hardCancellationEnabled) { super(); @@ -94,6 +96,7 @@ public class RequestConfig implements Cloneable { this.proxyPreferredAuthSchemes = proxyPreferredAuthSchemes; this.connectionRequestTimeout = connectionRequestTimeout; this.connectTimeout = connectTimeout; + this.responseTimeout = responseTimeout; this.contentCompressionEnabled = contentCompressionEnabled; this.hardCancellationEnabled = hardCancellationEnabled; } @@ -230,7 +233,6 @@ public class RequestConfig implements Cloneable { * such as {@code SSL} or {@code TLS} protocol negotiation). *

* A timeout value of zero is interpreted as an infinite timeout. - * A negative value is interpreted as undefined (system default). *

*

* Default: 3 minutes @@ -240,6 +242,26 @@ public class RequestConfig implements Cloneable { return connectTimeout; } + /** + * Determines the timeout until arrival of a response from the opposite + * endpoint. + *

+ * A timeout value of zero is interpreted as an infinite timeout. + *

+ *

+ * Please note that response timeout may be unsupported by + * HTTP transports with message multiplexing. + *

+ *

+ * Default: {@code null} + *

+ * + * @since 5.0 + */ + public Timeout getResponseTimeout() { + return responseTimeout; + } + /** * Determines whether the target server is requested to compress content. *

@@ -343,6 +365,7 @@ public class RequestConfig implements Cloneable { private Collection proxyPreferredAuthSchemes; private Timeout connectionRequestTimeout; private Timeout connectTimeout; + private Timeout responseTimeout; private boolean contentCompressionEnabled; private boolean hardCancellationEnabled; @@ -422,6 +445,16 @@ public class RequestConfig implements Cloneable { return this; } + public Builder setResponseTimeout(final Timeout responseTimeout) { + this.responseTimeout = responseTimeout; + return this; + } + + public Builder setResponseTimeout(final long responseTimeout, final TimeUnit timeUnit) { + this.responseTimeout = Timeout.of(responseTimeout, timeUnit); + return this; + } + public Builder setContentCompressionEnabled(final boolean contentCompressionEnabled) { this.contentCompressionEnabled = contentCompressionEnabled; return this; @@ -445,6 +478,7 @@ public class RequestConfig implements Cloneable { proxyPreferredAuthSchemes, connectionRequestTimeout != null ? connectionRequestTimeout : DEFAULT_CONNECTION_REQUEST_TIMEOUT, connectTimeout != null ? connectTimeout : DEFAULT_CONNECT_TIMEOUT, + responseTimeout, contentCompressionEnabled, hardCancellationEnabled); } diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/InternalHttpAsyncExecRuntime.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/InternalHttpAsyncExecRuntime.java index f0d0399d7..631db1ce8 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/InternalHttpAsyncExecRuntime.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/InternalHttpAsyncExecRuntime.java @@ -238,6 +238,11 @@ class InternalHttpAsyncExecRuntime implements AsyncExecRuntime { if (log.isDebugEnabled()) { log.debug(ConnPoolSupport.getId(endpoint) + ": executing " + ConnPoolSupport.getId(exchangeHandler)); } + final RequestConfig requestConfig = context.getRequestConfig(); + final Timeout responseTimeout = requestConfig.getResponseTimeout(); + if (responseTimeout != null) { + endpoint.setSocketTimeout(responseTimeout); + } endpoint.execute(exchangeHandler, context); if (context.getRequestConfig().isHardCancellationEnabled()) { return new Cancellable() { diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/MinimalHttpAsyncClient.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/MinimalHttpAsyncClient.java index d0f5e0057..a8ef3dc1e 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/MinimalHttpAsyncClient.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/MinimalHttpAsyncClient.java @@ -265,6 +265,7 @@ public final class MinimalHttpAsyncClient extends AbstractMinimalHttpAsyncClient } final Timeout connectionRequestTimeout = requestConfig.getConnectionRequestTimeout(); final Timeout connectTimeout = requestConfig.getConnectTimeout(); + final Timeout responseTimeout = requestConfig.getResponseTimeout(); final HttpHost target = new HttpHost(request.getAuthority(), request.getScheme()); final Future leaseFuture = leaseEndpoint( @@ -393,6 +394,9 @@ public final class MinimalHttpAsyncClient extends AbstractMinimalHttpAsyncClient } }; + if (responseTimeout != null) { + endpoint.setSocketTimeout(responseTimeout); + } endpoint.execute(internalExchangeHandler, pushHandlerFactory, clientContext); } @@ -463,6 +467,10 @@ public final class MinimalHttpAsyncClient extends AbstractMinimalHttpAsyncClient } } + public void setSocketTimeout(final Timeout timeout) { + connectionEndpoint.setSocketTimeout(timeout); + } + @Override public void releaseAndReuse() { if (released.compareAndSet(false, true)) { 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 596b464ba..6338e62d3 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 @@ -187,6 +187,11 @@ class InternalExecRuntime implements ExecRuntime, Cancellable { if (!endpoint.isConnected()) { connectEndpoint(endpoint, context); } + final RequestConfig requestConfig = context.getRequestConfig(); + final Timeout responseTimeout = requestConfig.getResponseTimeout(); + if (responseTimeout != null) { + endpoint.setSocketTimeout(responseTimeout); + } return endpoint.execute(request, requestExecutor, context); }