Open
Description
Describe the bug
The MessageHandler relies on the Middleware to initialize HeaderPropagationValues.Headers
to detect misconfiguration. However this means the Messagehandler can not be used outside of an http request and currently throws:
System.InvalidOperationException: The HeaderPropagationValues.Headers property has not been initialized. If using this HttpClient as part of an http request, register the header propagation middleware by adding 'app.UseHeaderPropagation() in the 'Configure(...)' method. Otherwise, use HeaderPropagationProcessor.ProcessRequest() before using the HttpClient.
To Reproduce
Steps to reproduce the behavior:
Code example
public void ConfigureServices(IServiceCollection services)
{
services
.AddHttpClient("test")
.AddHeaderPropagation();
services.AddHostedService<SampleHostedService>();
}
public class SampleHostedService : IHostedService
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly HeaderPropagationProcessor _headerPropagationProcessor;
private readonly ILogger _logger;
public SampleHostedService(IHttpClientFactory httpClientFactory, HeaderPropagationProcessor headerPropagationProcessor, ILogger<SampleHostedService> logger)
{
_httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory));
_headerPropagationProcessor = headerPropagationProcessor ?? throw new ArgumentNullException(nameof(headerPropagationProcessor));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public Task StartAsync(CancellationToken cancellationToken)
{
return DoWorkAsync();
}
private async Task DoWorkAsync()
{
_logger.LogInformation("Background Service is working.");
var client = _httpClientFactory.CreateClient("test");
var result = await client.GetAsync("http://localhost:62013/forwarded");
_logger.LogInformation("Background Service:\n{result}", result);
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}
Expected behavior
The HttpClient should work if used outside of a request or, if we don't wont to support this use case, the exception should be updated to explain that such use case is not supported..
Possible solutions
I had investigated possible solutions. Not sure what is your preferred options and if we are still in time to do breaking changes on this middleware.
Here are some options:
- Remove the check that throws the exception which means we would not be able anymore to detect misconfiguration. Can this be replaced with an analyzer?
On top of the risk that the feature might not work due to misconfiguration, there is also the risk that having the valuefilter not execute (e.g. to set some headers with default values) when not called from outside a request can be unexpected. - Update the exception message to also explain that a
HttpClient
with theHeaderPropagationMessageHandler
can not be used outside of a request. This is not ideal as it means you end up duplicating yourHttpClient
s configuration. - Extract the functionality from the Middleware to a generic Processor that can be called outside a http request. This will not change the middleware behaviour, but will allow to explicitly call the Processor when consuming an HttpClient outside of a http request. Ideally this will also include changing the valueFactory to receive a headers collection instead of the HttpContext, so that headers can be passed in different scenarios, for example when consuming messages from a queue, however I guess this scenario is out of scope for a middleware. See draft PR HeaderPropagation: add support for hosted services #12170 . The main issue I see with this approach is that when the processor is used directly you must take extra care as you need a different async context per request.
Looking forward for your feedback.
Thank you,
Alessio
/cc @rynowak