-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Bound an exchange by one request timeout #2314
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -109,6 +109,10 @@ public interface AsyncHttpClientConfig { | |
|
|
||
| /** | ||
| * Return the maximum time an {@link AsyncHttpClient} waits until the response is completed. | ||
| * <p> | ||
| * By default this bounds each attempt within an exchange rather than the exchange as a whole: a redirect, a | ||
| * retry and an auth replay each start it again, so a chain of n hops may run for n times this value. Set | ||
| * {@link #isUseAbsoluteRequestDeadline()} to bound the exchange instead. | ||
| * | ||
| * @return the maximum time an {@link AsyncHttpClient} waits until the response is completed. | ||
| */ | ||
|
|
@@ -142,6 +146,30 @@ default boolean isUseEventLoopTimeouts() { | |
| return false; | ||
| } | ||
|
|
||
| /** | ||
| * Whether {@link #getRequestTimeout()} is a deadline for the whole exchange rather than for each attempt | ||
| * within it. | ||
| * <p> | ||
| * A redirect, a retry and an auth replay all continue the same exchange on the same response future, but | ||
| * each builds its own timeout state. Anchoring the deadline on that state gives every hop a fresh budget, | ||
| * which is why a five-redirect chain can legitimately take six times the configured timeout today. Enabling | ||
| * this anchors it on when the exchange was submitted instead, so a later hop gets whatever is left and the | ||
| * caller's total wait is bounded by the one value. | ||
| * <p> | ||
| * Off by default because turning it on shortens exchanges that rely on the per-attempt behaviour. A caller | ||
| * working to an end-to-end budget wants it on; {@link Request#getUseAbsoluteRequestDeadline()} sets it for a | ||
| * single request. | ||
| * <p> | ||
| * As with every option on this interface, the {@code org.asynchttpclient.useAbsoluteRequestDeadline} | ||
| * property is read by {@link DefaultAsyncHttpClientConfig.Builder}, not here: an implementation of this | ||
| * interface that does not override this method gets {@code false} whatever the property says. | ||
| * | ||
| * @return {@code true} to treat the request timeout as a deadline for the whole exchange | ||
| */ | ||
| default boolean isUseAbsoluteRequestDeadline() { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ignore my earlier comment on this one, you were right. Every default on this interface hardcodes its value, so delegating here would have made this the odd one out. No need to change the others either, leave it as it is. |
||
| return false; | ||
| } | ||
|
|
||
| /** | ||
| * Is HTTP redirect enabled | ||
| * | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -139,7 +139,19 @@ public boolean exitAfterHandlingRedirect(Channel channel, NettyResponseFuture<?> | |
| .setNameResolver(request.getNameResolver()) | ||
| .setProxyServer(request.getProxyServer()) | ||
| .setRealm(stripAuth ? null : request.getRealm()) | ||
| .setRequestTimeout(request.getRequestTimeout()); | ||
| .setRequestTimeout(request.getRequestTimeout()) | ||
| // Dropped here until now, so a per-request read timeout reverted to the config default | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These read as a description of the change rather than of the code. AGENTS.md asks us not to, and "until now" will not mean much to whoever reads this next year. The revapi one in DefaultRequest is the same. |
||
| // on every hop after the first. | ||
| .setReadTimeout(request.getReadTimeout()); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Worth noting this one changes behaviour outside the flag too. Anyone with a short per request read timeout and followRedirect was getting the config default on the later hops and now gets their own value. Correct, but it should probably get a line in the release notes. |
||
|
|
||
| // Also dropped, which left the request disagreeing with the deadline the exchange was being | ||
| // held to: the future carries the flag, so a filter or a signature calculator reading the | ||
| // request saw per-attempt timeouts while the exchange was bounded as a whole. Only when it was | ||
| // set, since the setter takes a primitive and null means defer to the client config. | ||
| Boolean useAbsoluteRequestDeadline = request.getUseAbsoluteRequestDeadline(); | ||
| if (useAbsoluteRequestDeadline != null) { | ||
| requestBuilder.setUseAbsoluteRequestDeadline(useAbsoluteRequestDeadline); | ||
| } | ||
|
|
||
| if (stripAuth) { | ||
| future.setRealm(null); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.