Skip to content
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

Build HTTP request in declarative client Publisher #10626

Merged
merged 8 commits into from
Mar 21, 2024

Conversation

jeremyg484
Copy link
Contributor

@jeremyg484 jeremyg484 commented Mar 19, 2024

HttpClientIntroductionAdvice is refactored to ensure that binding to a new HTTP request will happen within the reactive context when using a Publisher return type in a declarative HTTP client.

This fixes the issue of the same MutableHttpRequest instance being re-used on retry attempts when the client is annotated with @Retryable, which can lead to an incorrect state when used in conjunction with client filters that modify the request, for example by adding header fields.

This makes the reactive behavior consistent with the blocking behavior.

Tests are enhanced to check that the expected number of new request instances are constructed and bound when using @Retryable with the declarative HTTP client.

Background:

The prior inconsistency in behavior was found while searching for the root of a Micronaut Security issue where it was shown that the Authorization header was being added multiple times when using @Retryable in conjunction with the declarative http client and the ClientCredentialsHttpClientFilter:
micronaut-projects/micronaut-security#1619

The additional header values only get sent when returning a reactive type from the declarative client method. This was traced to the difference in the way DefaultRetryInterceptor implements retry for blocking methods vs reactive methods. In the blocking case, the retryable method gets re-invoked, whereas in the reactive case the returned Publisher is simply re-subscribed. This results in any state that is captured from outside of the Publisher lambda(s) being re-used in subsequent retries.

HttpClientIntroductionAdvice was building and binding the HTTP request prior to constructing the Publisher to be returned in the reactive return type code path, resulting in the same MutableHttpRequest instance from the first request being used on all subsequent retry attempts.

Note For Reviewers
In order to detangle the request construction and binding to make it able to be done within a Publisher, I broke the prior intercept method of HttpClientIntroductionAdvice down into a series of smaller methods. This is making the diff on that class a bit of a mess.

HttpClientIntroductionAdvice is refactored to ensure that binding to a
new HTTP request will happen within the reactive context when using a
Publisher return type in a declarative HTTP client.

This fixes the issue of the same MutableHttpRequest instance being
re-used on retry attempts when the client is annotated with Retryable,
which can lead to an incorrect state when used in conjunction with
client filters that modify the request, for example by adding header
fields.

This makes the reactive behavior consistent with the blocking
behavior.

Tests are enhanced to check that the expected number of new
request instances are constructed and bound when using Retryable with
the declarative HTTP client.
@jeremyg484 jeremyg484 self-assigned this Mar 19, 2024
Co-authored-by: Tim Yates <tim.yates@gmail.com>
Co-authored-by: Tim Yates <tim.yates@gmail.com>
Copy link

sonarcloud bot commented Mar 20, 2024

@sdelamo sdelamo added the type: bug Something isn't working label Mar 21, 2024
@sdelamo sdelamo merged commit 988c602 into 4.4.x Mar 21, 2024
17 checks passed
@sdelamo sdelamo deleted the declarative-http-client-retryable-publisher branch March 21, 2024 08:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug Something isn't working
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

None yet

4 participants