Description
LoggingHttpMessageHandler changes the observable behavior of HttpRequestMessage when a known header is added using HttpHeaders.TryAddWithoutValidation().
When LogLevel.Trace is enabled for Microsoft.Extensions.Http, the logging pipeline enumerates HttpHeaders, which causes lazily stored raw header values to become materialized as typed headers.
As a result, the request sent over the wire differs from the original request.
This does not happen when:
- using new HttpClient();
- or calling RemoveAllLoggers() on IHttpClientBuilder;
- or disabling Trace logging.
Reproduction Steps
Mimimal reproduction:
var services = new ServiceCollection();
services.AddLogging(builder =>
{
builder.SetMinimumLevel(LogLevel.Trace);
});
services.AddHttpClient("test");
var provider = services.BuildServiceProvider();
var factory = provider.GetRequiredService<IHttpClientFactory>();
var client = factory.CreateClient("test");
var request = new HttpRequestMessage(HttpMethod.Get, "https://example.com");
request.Headers.TryAddWithoutValidation(
"Accept",
"application/vnd.example+json;version=1");
await client.SendAsync(request);
If logging is removed:
services.AddHttpClient("test")
.RemoveAllLoggers();
the original raw header value is preserved.
Expected behavior
Logging should be non-invasive.
Enumerating headers for logging purposes should not modify the behavior or serialization of the outgoing request.
Actual behavior
The request is modified before being sent.
For example we added the following header into httpRequest:
Accept: application/vnd.mycompany.json;version=2.1.4
is sent as:
Accept: application/vnd.mycompany.json; version=2.1.4
after Trace logging enumerates the headers.
Regression?
No response
Known Workarounds
No response
Configuration
- .NET 10
- Microsoft.Extensions.Http
Other information
I did little investigation.
The issue appears to originate from
Microsoft.Extensions.Http.Logging.HttpHeadersLogValue
specifically:
foreach (KeyValuePair<string, IEnumerable<string>> kvp in Headers)
{
values.Add(new KeyValuePair<string, object>(kvp.Key, kvp.Value));
}
Enumerating Headers forces lazy parsing/materialization of known headers.
After this happens, the request is serialized differently.
Description
LoggingHttpMessageHandlerchanges the observable behavior ofHttpRequestMessagewhen a known header is added usingHttpHeaders.TryAddWithoutValidation().When
LogLevel.Traceis enabled forMicrosoft.Extensions.Http, the logging pipeline enumerates HttpHeaders, which causes lazily stored raw header values to become materialized as typed headers.As a result, the request sent over the wire differs from the original request.
This does not happen when:
Reproduction Steps
Mimimal reproduction:
If logging is removed:
the original raw header value is preserved.
Expected behavior
Logging should be non-invasive.
Enumerating headers for logging purposes should not modify the behavior or serialization of the outgoing request.
Actual behavior
The request is modified before being sent.
For example we added the following header into httpRequest:
Accept: application/vnd.mycompany.json;version=2.1.4is sent as:
Accept: application/vnd.mycompany.json; version=2.1.4after Trace logging enumerates the headers.
Regression?
No response
Known Workarounds
No response
Configuration
Other information
I did little investigation.
The issue appears to originate from
Microsoft.Extensions.Http.Logging.HttpHeadersLogValuespecifically:
Enumerating Headers forces lazy parsing/materialization of known headers.
After this happens, the request is serialized differently.