dotnet / dotnet/aspnetcore

Attach HttpRequestMessage to Polly Context in PolicyHttpMessageHandler for access by policy callbacks

Open
#56,943 0 comments 0 reactions 0 assignees View on GitHub
area-networking
Dominant language
C#
Stars
38.4k
Forks
10.9k
Avg merge
2d 6h
Merged PRs (30d)
290

Description

### Is there an existing issue for this?

- [X] I have searched the existing issues

### Is your feature request related to a problem? Please describe the problem.

This is related to https://github.com/App-vNext/Polly/issues/2244

It's a common scenario to get access to the `HttpRequestMessage` object in Polly policy callbacks, most often for logging/monitoring purposes but there are likely other valid scenarios as well.

For stateless policies such as retry, this can be done like the following:

IAsyncPolicy GetPollyPolicy(HttpRequestMessage request)
{
var retryPolicy = Policy
.Handle()
// can access request here via closure
.RetryAsync((result, count) => logger.LogInformation($"Request: {request}"));

return retryPolicy;
}

var handler = new PolicyHttpMessageHandler(GetPollyPolicy) { InnerHandler = new HttpClientHandler() };
var httpClient = new HttpClient(handler);

But this does not work with a stateful policy such as circuit breaker or bulkhead:

IAsyncPolicy GetPollyPolicy(HttpRequestMessage request)
{
var retryPolicy = Policy
.Handle()
// this doesn't work as intended because circuit breaker is stateful so one policy instance should be used across requests
.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30),
(result, time) => logger.LogInformation($"Request: {request}"),
() => {});

return retryPolicy;
}

var handler = new PolicyHttpMessageHandler(GetPollyPolicy) { InnerHandler = new HttpClientHandler() };
var httpClient = new HttpClient(handler);

because by definition, a stateful policy needs to track state across requests so creating a new instance for every request will not work as intended.

For such cases, there's currently no good way to access `HttpRequestMessage` from policy callbacks except to write our own `DelegatingHandler` and flow state manually (i.e. attach `HttpRequestMessage` to `Polly.Context` in the custom `DelegatingHandler`, and then retrieving it from `Polly.Context` in the callbacks). But this seems like a sufficiently routine case that writing a custom handler to flow state should not be necessary.

### Describe the solution you'd like

A possible enhancement is as follows:
1. `PolicyHttpMessageHandler` can attach `HttpRequestMessage` to `Polly.Context`, which can then flow with Polly to the policy callbacks. At the end of the request processing, it can be cleaned up from context to avoid leaking memory or underlying resources (since `HttpRequestMessage` is `IDisposable`).
2. Provide some helper methods to get `HttpRequestMessage` from `Polly.Context`.

### Additional context

_No response_

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.