aspnetcore: HttpSys request header dictionary does not remove headers when Remove() is called
Is there an existing issue for this?
- I have searched the existing issues
Describe the bug
When using the HttpSys server, the request header dictionary (HttpRequest.Headers) behaves unexpectedly when trying to remove a header using IHeaderDictionary.Remove(). The Remove() method fails to remove some headers, even when those headers are present in the request - when enumerating the collection after the call to Remove(), the original header is still returned.
Expected Behavior
If a header is present in the header dictionary, it should be removed from the collection when Remove() is called. Subsequent operations on the collection (enumeration, indexer, ContainsKey, etc.) should not return the removed header.
Steps To Reproduce
Create an ASP.NET Core app, using HttpSys as the server. The code below reproduces the issue.
in Startup.cs:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.Run(httpContext =>
{
var headers = httpContext.Request.Headers;
bool removed = headers.Remove("Connection");
Debug.WriteLine(removed); // prints 'false'
string connection = headers["Connection"];
Debug.WriteLine(connection); // prints 'Close'
return Task.CompletedTask;
});
}
in Program.cs:
var builder = Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseHttpSys(options =>
{
options.UrlPrefixes.Add("http://+:5000/");
});
webBuilder.UseStartup<Startup>();
});
var app = builder.Build();
app.Run();
This curl command was used: curl --verbose --header Connection:Close http://localhost:5000/
Note that a number of other headers such as ‘Accept-Encoding’ also reproduce the issue.
Exceptions (if any)
No response
.NET Version
net6.0
Anything else?
Relevant line of code in RequestHeaders implementation: https://github.com/dotnet/aspnetcore/blob/main/src/Shared/HttpSys/RequestProcessing/RequestHeaders.Generated.cs#L1860
If the _Connection field hasn’t been populated at this point in the code, PropertiesTryRemove() returns false and the header isn’t removed. Later on, if the same header is retrieved using PropertiesTryGetValue(), the actual header value is retrieved by calling NativeRequestContext.GetKnownHeader().
Other details:
- This issue appears to affect all the ‘known’ headers listed in the RequestHeaders.Generated.cs file.
- The issue does not repro if the header was previously loaded, due to enumerating the header dictionary or retrieving the header using
request.Headers["Connection"]. - The issue does not repro on Kestrel.
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Reactions: 1
- Comments: 19 (19 by maintainers)
Commits related to this issue
- Fix header issue #43230 — committed to singh733/aspnetcore by singh733 2 years ago
- Ensure HttpSys request header dictionary removes known headers when Remove is called before Get #43230 (#44860) — committed to dotnet/aspnetcore by singh733 2 years ago
Try it and write the tests. The tests will be the easiest way to verify the behavior is as expected.
Kestrel eagerly loads all headers from the request. Kestrel parses the HTTP request directly into the header collection. HttpSys is different because there is a windows kernel component that does the actual parsing. That surfaces the headers to HttpSys via a pre-parsed memory structure. HttpSys lazily reads headers from the structure when the caller asks for them. See https://github.com/dotnet/aspnetcore/blob/3ea008c80d5cc63de7f90ddfd6823b7b006251ff/src/Shared/HttpSys/RequestProcessing/RequestHeaders.cs#L60-L63
The .tt file in Http.Sys is part of a much older code generation tool that isn’t set up correctly to work right now. We’ll have to fix or replace it. See https://github.com/dotnet/aspnetcore/issues/5869.
Kestrel uses a different tool that we could adapt for use in Http.Sys: https://github.com/dotnet/aspnetcore/tree/main/src/Servers/Kestrel/tools/CodeGenerator