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

[API Proposal]: Allow retrieval of HttpRequestMessage from Polly.Context #4957

Closed
martintmk opened this issue Feb 20, 2024 · 10 comments · Fixed by #5460
Closed

[API Proposal]: Allow retrieval of HttpRequestMessage from Polly.Context #4957

martintmk opened this issue Feb 20, 2024 · 10 comments · Fixed by #5460
Assignees
Labels
api-approved API was approved in API review, it can be implemented api-suggestion Early API idea and discussion, it is NOT ready for implementation area-resilience

Comments

@martintmk
Copy link
Contributor

Background and motivation

Clients using new resilience APIs based on top of Polly v8 might want to access HttpRequestMessage associated with the attempt being executed. This would allow the following scenarios:

Metering Enrichment

Client can decide to add additional metrics extracted from HttpRequestMessage using metering enrichment.

Custom Routing

For hedging or retries, the client might decide to change the URL for secondary attempts. This would also enable dynamic URL resolution.

Access to HttpRequestMessage in callbacks

For logging/other purposes the client uses callbacks and can be interested in the current request message.

API Proposal

namespace Polly;

public static class HttpResilienceContextExtensions
{
    public static HttpRequestMessage? GetRequestMessage(this ResilienceContext context);
    public static void SetRequestMessage(this ResilienceContext context, HttpRequestMessage? requestMessage);
}

API Usage

services
    .AddHttpClient("my-client", client => client.BaseAddress = new Uri("https://primary-endpoint"))
    .AddStandardHedgingHandler()
    .Configure(options =>
    {
        options.Hedging.OnHedging = args =>
        {
            // Retrieve the request message
            HttpRequestMessage request = args.ActionContext.GetRequestMessage();

            UriBuilder uriBuilder = new(request.RequestUri);
            uriBuilder.Host = "secondary-endpoint";

            // Override the request URI
            request.RequestUri = uriBuilder.Uri;

            return default;
        };
    });

The example above demonstrates how the new API can be used to change the URL of outgoing request. Similar approach can be used for retries or telemetry enrichment.

Alternative Designs

The above example can be rewritten as:

ResiliencePropertyKey<HttpRequestMessage> requestMessageKey = new("Resilience.Http.RequestMessage");

services
    .AddHttpClient("my-client", client => client.BaseAddress = new Uri("https://primary-endpoint"))
    .AddStandardHedgingHandler()
    .Configure(options =>
    {
        options.Hedging.OnHedging = args =>
        {
            // Retrieve the request message
            HttpRequestMessage? request = args.ActionContext.Properties.GetValue(requestMessageKey, null!);

            UriBuilder uriBuilder = new(request.RequestUri);
            uriBuilder.Host = "secondary-endpoint";

            // Override the request URI
            request.RequestUri = uriBuilder.Uri;

            return default;
        };
    });

However, this relies on some internal naming:
https://github.com/dotnet/extensions/blob/a8e17517bd5150cc0f992b66aa6591c7c6fbdfc4/src/Libraries/Microsoft.Extensions.Http.Resilience/Internal/ResilienceKeys.cs#L13C92-L13C122

It would be better to have built-in support for retrieving HttpRequestMessage from ResilienceContext as I suspect this will be a common scenario.

Risks

Caller retrieving and modifying HttpRequestMessage associated with the current attempt in a non-compatible way or doing some changes that will corrupt the request.

@martintmk martintmk added untriaged api-suggestion Early API idea and discussion, it is NOT ready for implementation labels Feb 20, 2024
@joperezr joperezr removed the untriaged label Mar 4, 2024
@joperezr
Copy link
Member

joperezr commented Mar 4, 2024

Given you are the main expert on this, are you expecting any feedback from anyone else @martintmk or is this ready for review?

@martintmk
Copy link
Contributor Author

Given you are the main expert on this, are you expecting any feedback from anyone else @martintmk or is this ready for review?

I believe this is ready for review.

@davemyers-dev
Copy link

@martintmk I'd love to see this built in! We're trying to rollout new v8 policy guidance & extensions for our enterprise and using information from the request message in our Retry logging is a common use case.

@iliar-turdushev
Copy link
Contributor

@martintmk I have 2 questions:

  • As I understand, SetRequestMessage will be useful for the Hedging handler when a user wants to replace the request object with a custom one, right?
  • Do we want to make the second argument of the SetRequestMessage method non-nullable, i.e. SetRequestMessage(this ResilienceContext context, HttpRequestMessage requestMessage)?

Apart from that, the proposal looks good to me.

@RussKie RussKie added waiting-author-feedback 📭 The author of this issue needs to respond in order for us to continue investigating this issue. and removed waiting-on-team 👋 labels Aug 29, 2024
@martintmk
Copy link
Contributor Author

Hey @iliar-turdushev

As I understand, SetRequestMessage will be useful for the Hedging handler when a user wants to replace the request object with a custom one, right?

Yup, they can also access the request message, extract some information and use it for telemetry. The latter was my primary motivation for these APIs.

Do we want to make the second argument of the SetRequestMessage method non-nullable, i.e. SetRequestMessage(this ResilienceContext context, HttpRequestMessage requestMessage)?

I think we shall still keep the option to "reset" the request message. So my vote would be to keep the nullable option.

@dotnet-policy-service dotnet-policy-service bot removed the waiting-author-feedback 📭 The author of this issue needs to respond in order for us to continue investigating this issue. label Aug 29, 2024
@RussKie
Copy link
Member

RussKie commented Aug 29, 2024

@joperezr @geeknoid any thoughts or objections?

@geeknoid
Copy link
Member

LGTM.

@RussKie
Copy link
Member

RussKie commented Aug 29, 2024

Approved.

@martintmk feel free to send a pull-request with the necessary changes, if you fancy it.

@RussKie RussKie added the api-approved API was approved in API review, it can be implemented label Aug 29, 2024
@iliar-turdushev
Copy link
Contributor

iliar-turdushev commented Sep 21, 2024

The implementation is blocked because of the following issue in Polly App-vNext/Polly#2299. The issue doesn't allow us to make requestMessage argument of SetRequestMessage nullable.

public static void SetRequestMessage(this ResilienceContext context, HttpRequestMessage? requestMessage);

@martintmk Do we really want to allow requestMessage be nullable? In our implementation if ResilienceContext doesn't include requestMessage or if its values is null then we throw an exception. Do we want to allow our customers to create a situation that will lead to an exception?

@martintmk
Copy link
Contributor Author

The implementation is blocked because of the following issue in Polly App-vNext/Polly#2299. The issue doesn't allow us to make requestMessage argument of SetRequestMessage nullable.

public static void SetRequestMessage(this ResilienceContext context, HttpRequestMessage? requestMessage);

@martintmk Do we really want to allow requestMessage be nullable? In our implementation if ResilienceContext doesn't include requestMessage or if its values is null then we throw an exception. Do we want to allow our customers to create a situation that will lead to an exception?

Here, I wanted to mimic the behavior of SetPolicyExecutionContext:

https://github.com/dotnet/aspnetcore/blob/d691d6a72d3e82114987fe637643fcc209c5d245/src/HttpClientFactory/Polly/src/HttpRequestMessageExtensions.cs#L50

Another scenario where setting the null can be valuable is the reset semantics. Caller can reset the message and the hedging stack can re-initialize it.

iliar-turdushev added a commit to iliar-turdushev/dotnet-extensions that referenced this issue Sep 27, 2024
Updates Polly libraries to version 8.4.2
RussKie pushed a commit that referenced this issue Sep 30, 2024
Updates Polly libraries to version 8.4.2
iliar-turdushev added a commit to iliar-turdushev/dotnet-extensions that referenced this issue Sep 30, 2024
Adds extension methods allowing retrieval of the request message from resilience context
iliar-turdushev added a commit to iliar-turdushev/dotnet-extensions that referenced this issue Sep 30, 2024
Replaces usage of ResilienceKeys.RequestMessage variable with corresponding Get/Set methods
iliar-turdushev added a commit to iliar-turdushev/dotnet-extensions that referenced this issue Sep 30, 2024
Marks new API as Experimental
@RussKie RussKie closed this as completed in 18002be Oct 1, 2024
@github-actions github-actions bot locked and limited conversation to collaborators Nov 1, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
api-approved API was approved in API review, it can be implemented api-suggestion Early API idea and discussion, it is NOT ready for implementation area-resilience
Projects
None yet
7 participants