Setting an invalid Transfer-Encoding response header sometimes kills the connection instead of responding with a 500 status
- Dominant language
- C#
- Stars
- 38.4k
- Forks
- 10.9k
- Avg merge
- 2d 10h
- Merged PRs (30d)
- 281
Description
### Describe the bug
Setting a Transfer-Encoding response header which is invalid for a given request or response (e.g. 204/205/304 responses and HEAD requests) without manually writing to or flushing the response body kills the connection instead of responding with a 500 status code.
### To Reproduce
Steps to reproduce the behavior:
1. Targeting ASP.NET Core 2.2 (though this bug likely existed earlier), add the following middleware:
```C#
app.Run(context =>
{
context.Response.StatusCode = 204;
context.Response.Headers["Transfer-Encoding"] = "chunked";
return Task.CompletedTask;
});
```
2. Run the app and test the endpoint by making a GET request. If you have a project reference to Kestrel.Core, make sure you run the app in release mode to avoid a Debug.Assert.
4. See a connection reset error.
### Expected behavior
A 500 response. This can be observed if you make a slight change to the above middleware to manually flush the response body:
```C#
app.Run(context =>
{
context.Response.StatusCode = 204;
context.Response.Headers["Transfer-Encoding"] = "chunked";
return context.Response.Body.FlushAsync();
});
```
### Additional context
Here are what the server logs look like when this bug is hit:
```
dbug: Microsoft.AspNetCore.Server.Kestrel[1]
Connection id "0HLIPIMQJ59NK" started.
dbug: Microsoft.AspNetCore.Server.Kestrel[1]
Connection id "0HLIPIMQJ59NL" started.
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
Request starting HTTP/1.1 GET http://localhost:5000/
fail: Microsoft.AspNetCore.Server.Kestrel[13]
Connection id "0HLIPIMQJ59NK", Request id "0HLIPIMQJ59NK:00000001": An unhandled exception was thrown by the application.
System.InvalidOperationException: Setting the header Transfer-Encoding is not allowed on responses with status code 204.
warn: Microsoft.AspNetCore.Server.Kestrel[0]
Connection processing ended abnormally.
System.InvalidOperationException: StatusCode cannot be set because the response has already started.
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ThrowResponseAlreadyStartedException(String value) in C:\dev\aspnet\AspNetCore\src\Servers\Kestrel\Core\src\Internal\Http\HttpProtocol.cs:line 1209
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.set_StatusCode(Int32 value) in C:\dev\aspnet\AspNetCore\src\Servers\Kestrel\Core\src\Internal\Http\HttpProtocol.cs:line 207
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.SetErrorResponseHeaders(Int32 statusCode) in C:\dev\aspnet\AspNetCore\src\Servers\Kestrel\Core\src\Internal\Http\HttpProtocol.cs:line 1245
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.CreateResponseHeader(Boolean appCompleted) in C:\dev\aspnet\AspNetCore\src\Servers\Kestrel\Core\src\Internal\Http\HttpProtocol.cs:line 1171
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProduceEndAwaited()
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application) in C:\dev\aspnet\AspNetCore\src\Servers\Kestrel\Core\src\Internal\Http\HttpProtocol.cs:line 597
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequestsAsync[TContext](IHttpApplication`1 application) in C:\dev\aspnet\AspNetCore\src\Servers\Kestrel\Core\src\Internal\Http\HttpProtocol.cs:line 465
dbug: Microsoft.AspNetCore.Server.Kestrel[10]
Connection id "0HLIPIMQJ59NK" disconnecting.
dbug: Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets[7]
Connection id "0HLIPIMQJ59NK" sending FIN because: "The Socket transport's send loop completed gracefully."
dbug: Microsoft.AspNetCore.Server.Kestrel[2]
```
Contributor guide
Research direction
Reproduce the middleware example against Kestrel and start by reading HttpProtocol.CreateResponseHeader and ProduceEndAwaited in Kestrel.Core, which appear in the reported stack trace. Trace how the invalid Transfer-Encoding header is handled when the application completes without flushing. Done means the invalid response produces HTTP 500 rather than resetting the connection.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 64/100