Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/Hosting/TestHost/src/HttpContextBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,14 @@ internal void RegisterResponseReadCompleteCallback(Action<HttpContext> responseR
/// <returns></returns>
internal Task<HttpContext> SendAsync(CancellationToken cancellationToken)
{
var registration = cancellationToken.Register(ClientInitiatedAbort);
var registration = cancellationToken.Register(() =>
{
ClientInitiatedAbort();

// The client gave up on this request. Fail its task now, the way a real server would, instead of
// waiting for an application that may never check RequestAborted.
_responseTcs.TrySetException(new OperationCanceledException("The request was canceled.", cancellationToken));
});

// Everything inside this function happens in the SERVER's execution context (unless PreserveExecutionContext is true)
async Task RunRequestAsync()
Expand Down
34 changes: 34 additions & 0 deletions src/Hosting/TestHost/test/TestClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1081,6 +1081,40 @@ public async Task ClientCancellationAbortsRequest()
var exception = await Assert.ThrowsAnyAsync<OperationCanceledException>(async () => await tcs.Task);
}

[Fact]
public async Task ClientCancellationThrowsWithoutWaitingForApplication()
{
var applicationStarted = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
var applicationCompleted = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);

var builder = new HostBuilder()
.ConfigureWebHost(webHostBuilder =>
{
webHostBuilder
.UseTestServer()
.Configure(app => app.Run(async ctx =>
{
// The application never observes RequestAborted and keeps running after the client gives up.
applicationStarted.SetResult();
await applicationCompleted.Task;
}));
});
using var host = builder.Build();
await host.StartAsync();

using var server = host.GetTestServer();
using var client = server.CreateClient();
using var cts = new CancellationTokenSource();

var requestTask = client.GetAsync("http://localhost:12345", cts.Token);
await applicationStarted.Task.DefaultTimeout();
cts.Cancel();

await Assert.ThrowsAnyAsync<OperationCanceledException>(() => requestTask.DefaultTimeout());

applicationCompleted.SetResult();
}

[Fact]
public async Task AsyncLocalValueOnClientIsNotPreserved()
{
Expand Down
Loading