Overview
While investigating #37423, it became apparent that our HTTP client support is inconsistent in how it handles a timeout of 0. This issue supersedes that issue, and instead of rejecting non-positive values in JettyClientHttpRequestFactory, we will consistently treat 0 as "no timeout" for all timeouts in our HTTP client support, which is the norm for socket timeout settings.
Rationale: 0 has no other sensible meaning for a timeout, and for setters that reject negative values, there is otherwise no way to configure "no timeout".
Connect Timeouts
The current state for connect timeouts (and the connection request timeout for Apache HttpComponents) is as follows.
SimpleClientHttpRequestFactory: 0 is infinite, but negative int values are silently ignored (falling back to the system default), and sub-millisecond Duration values are truncated to 0 and therefore treated as infinite.
HttpComponentsClientHttpRequestFactory: 0 is infinite, but sub-millisecond Duration values are truncated to 0 and therefore treated as infinite.
ReactorClientHttpRequestFactory: 0 is infinite, but sub-millisecond Duration values are truncated to 0 and therefore treated as infinite.
JettyClientHttpRequestFactory: 0 is infinite, but setConnectTimeout(Duration) accepts negative values, and sub-millisecond Duration values are truncated to 0 and therefore treated as infinite.
Note that JdkClientHttpRequestFactory does not support configuring a connect timeout, which is configured on the java.net.http.HttpClient instead.
Read Timeouts
The current state for read timeouts is as follows.
SimpleClientHttpRequestFactory: 0 is infinite (delegates to URLConnection), but negative values are silently ignored (falling back to the system default), and sub-millisecond Duration values are truncated to 0 and therefore treated as infinite.
HttpComponentsClientHttpRequestFactory: 0 is infinite, but sub-millisecond Duration values are truncated to 0 and therefore treated as infinite.
JdkClientHttpRequestFactory: 0 is infinite since #37234, but this is only documented for setReadTimeout(int).
JdkClientHttpConnector: 0 is infinite since #37412, but negative values are only rejected by the JDK when a request is executed.
ReactorClientHttpRequestFactory: 0 is rejected.
JettyClientHttpRequestFactory: setReadTimeout(long) rejects 0, whereas setReadTimeout(Duration) accepts Duration.ZERO, which then results in every request failing with IOException: Request timed out.
HTTP Service Clients
AbstractReactorHttpExchangeAdapter#setBlockTimeout(Duration) (used by HTTP Service clients) currently only supports null as "no timeout". A block timeout of Duration.ZERO results in every blocking request failing immediately with IllegalStateException: Timeout on blocking read.
Proposal
ReactorClientHttpRequestFactory: accept 0 for the read timeout and map it to HttpClient.responseTimeout(null). Note that Reactor Netty raises a Duration below 1ms to 1ms, so Duration.ZERO cannot simply be passed through. Since the default client is configured with a 10 second response timeout, 0 must explicitly reset it.
JettyClientHttpRequestFactory: accept 0 for both read timeout variants, and avoid the immediate timeout in JettyClientHttpRequest when waiting for the response.
AbstractReactorHttpExchangeAdapter: treat a block timeout of Duration.ZERO as "no timeout".
- Only treat an actual zero value as "no timeout". Negative values are rejected for all timeouts, and sub-millisecond
Duration values are rejected wherever they would otherwise be truncated to 0. The JDK-based implementations pass the Duration through as is, so they continue to accept sub-millisecond values.
- Consistently document "A timeout value of 0 specifies an infinite timeout." for all timeout setters.
Note that this is a change in behavior for the following.
SimpleClientHttpRequestFactory: currently silently ignores negative values (falling back to the system default) and treats sub-millisecond Duration values as infinite.
HttpComponentsClientHttpRequestFactory: currently treats sub-millisecond Duration values as infinite for the read timeout and the connection request timeout.
ReactorClientHttpRequestFactory: currently treats sub-millisecond Duration values as infinite for the connect timeout.
JettyClientHttpRequestFactory: currently accepts negative Duration values and treats sub-millisecond Duration values as infinite for the connect timeout.
AbstractReactorHttpExchangeAdapter: currently accepts negative Duration values for the block timeout.
Related Issues
Overview
While investigating #37423, it became apparent that our HTTP client support is inconsistent in how it handles a timeout of
0. This issue supersedes that issue, and instead of rejecting non-positive values inJettyClientHttpRequestFactory, we will consistently treat0as "no timeout" for all timeouts in our HTTP client support, which is the norm for socket timeout settings.Rationale:
0has no other sensible meaning for a timeout, and for setters that reject negative values, there is otherwise no way to configure "no timeout".Connect Timeouts
The current state for connect timeouts (and the connection request timeout for Apache HttpComponents) is as follows.
SimpleClientHttpRequestFactory:0is infinite, but negativeintvalues are silently ignored (falling back to the system default), and sub-millisecondDurationvalues are truncated to0and therefore treated as infinite.HttpComponentsClientHttpRequestFactory:0is infinite, but sub-millisecondDurationvalues are truncated to0and therefore treated as infinite.ReactorClientHttpRequestFactory:0is infinite, but sub-millisecondDurationvalues are truncated to0and therefore treated as infinite.JettyClientHttpRequestFactory:0is infinite, butsetConnectTimeout(Duration)accepts negative values, and sub-millisecondDurationvalues are truncated to0and therefore treated as infinite.Note that
JdkClientHttpRequestFactorydoes not support configuring a connect timeout, which is configured on thejava.net.http.HttpClientinstead.Read Timeouts
The current state for read timeouts is as follows.
SimpleClientHttpRequestFactory:0is infinite (delegates toURLConnection), but negative values are silently ignored (falling back to the system default), and sub-millisecondDurationvalues are truncated to0and therefore treated as infinite.HttpComponentsClientHttpRequestFactory:0is infinite, but sub-millisecondDurationvalues are truncated to0and therefore treated as infinite.JdkClientHttpRequestFactory:0is infinite since #37234, but this is only documented forsetReadTimeout(int).JdkClientHttpConnector:0is infinite since #37412, but negative values are only rejected by the JDK when a request is executed.ReactorClientHttpRequestFactory:0is rejected.JettyClientHttpRequestFactory:setReadTimeout(long)rejects0, whereassetReadTimeout(Duration)acceptsDuration.ZERO, which then results in every request failing withIOException: Request timed out.HTTP Service Clients
AbstractReactorHttpExchangeAdapter#setBlockTimeout(Duration)(used by HTTP Service clients) currently only supportsnullas "no timeout". A block timeout ofDuration.ZEROresults in every blocking request failing immediately withIllegalStateException: Timeout on blocking read.Proposal
ReactorClientHttpRequestFactory: accept0for the read timeout and map it toHttpClient.responseTimeout(null). Note that Reactor Netty raises aDurationbelow 1ms to 1ms, soDuration.ZEROcannot simply be passed through. Since the default client is configured with a 10 second response timeout,0must explicitly reset it.JettyClientHttpRequestFactory: accept0for both read timeout variants, and avoid the immediate timeout inJettyClientHttpRequestwhen waiting for the response.AbstractReactorHttpExchangeAdapter: treat a block timeout ofDuration.ZEROas "no timeout".Durationvalues are rejected wherever they would otherwise be truncated to0. The JDK-based implementations pass theDurationthrough as is, so they continue to accept sub-millisecond values.Note that this is a change in behavior for the following.
SimpleClientHttpRequestFactory: currently silently ignores negative values (falling back to the system default) and treats sub-millisecondDurationvalues as infinite.HttpComponentsClientHttpRequestFactory: currently treats sub-millisecondDurationvalues as infinite for the read timeout and the connection request timeout.ReactorClientHttpRequestFactory: currently treats sub-millisecondDurationvalues as infinite for the connect timeout.JettyClientHttpRequestFactory: currently accepts negativeDurationvalues and treats sub-millisecondDurationvalues as infinite for the connect timeout.AbstractReactorHttpExchangeAdapter: currently accepts negativeDurationvalues for the block timeout.Related Issues
JettyClientHttpRequestFactory#37423JdkClientHttpRequestmay block indefinitely #31911