HttpResponse.OnCompleted callback not invoked on exception when using TestServer
- Dominant language
- C#
- Stars
- 38.4k
- Forks
- 10.9k
- Avg merge
- 2d 5h
- Merged PRs (30d)
- 276
Description
### Is there an existing issue for this?
- [X] I have searched the existing issues
### Describe the bug
We have tests for custom middleware using TestServer as per [Test ASP.NET Core middleware](https://learn.microsoft.com/en-us/aspnet/core/test/middleware?view=aspnetcore-8.0) documentation article. This middleware does some I/O bound work in a parallel thread and uses OnCompleted callback to synchronize and clean up with the request completion.
The custom middleware needs to execute properly in face of exceptions so that's what we test as well. However, due to some test flakiness, we have discovered that the OnCompleted is not invoked when an Exception propagates when using TestServer. In constrast, all of kestrel, IIS and HTTP.sys uniformly call OnCompleted even in face of exception which is seems intuitive.
For now, we have added a middleware that swallows every exception so we're fine.
### Expected Behavior
- The discrepancy between TestServer and actual servers should be documented
- If feasible, TestServer should be adjusted to call OnCompleted even when an Exception propagates to align with the behavior of actual web servers
### Steps To Reproduce
The following ASP.NET Core app will log "OnCompleted called" after logging the exception when `GET /throw` request is sent
```csharp
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/throw", context =>
{
context.Response.OnCompleted(() =>
{
var logger = context.RequestServices.GetRequiredService>();
logger.LogInformation("OnCompleted called");
return Task.CompletedTask;
});
throw new Exception();
});
app.Run();
```
The following xunit scenario will fail
```csharp
[Fact]
public async Task OnCompletedIsNotCalledOnExceptionWhenUsingTestHost()
{
var onCompletedWasCalled = false;
using var host = await new HostBuilder()
.ConfigureWebHost(webBuilder => webBuilder
.UseTestServer()
.Configure(app =>
{
app.MapWhen(
_ => true,
e => e.Run(context =>
{
context.Response.OnCompleted(() =>
{
onCompletedWasCalled = true;
return Task.CompletedTask;
});
throw new Exception();
}));
}))
.StartAsync();
// GetAsync propagates the exception
var exception = await Record.ExceptionAsync(async () => await host.GetTestClient().GetAsync("/"));
Assert.NotNull(exception);
await host.StopAsync();
Assert.True(onCompletedWasCalled); // Expected: True, Actual: False
}
```
### Exceptions (if any)
_No response_
### .NET Version
8.0.300-preview.24203.14
### Anything else?
Happens with .NET 6, 7, and 8 alongside corresponding Microsoft.AspNetCore.TestHost package versions (e.g. 6.0.29, 7.0.18, 8.0.4).
Contributor guide
Assessment
This issue has not been assessed yet.