-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Add CancellationToken support for LoadIntoBufferAsync #103991
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
cd3afd3
09ea276
70ec5c1
f08c166
9d36763
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -509,6 +509,93 @@ public async Task LoadIntoBufferAsync_ThrowIOExceptionInOverriddenAsyncMethod_Th | |
| Assert.IsType<IOException>(ex.InnerException); | ||
| } | ||
|
|
||
| [Fact] | ||
| public async Task LoadIntoBufferAsync_Buffered_IgnoresCancellationToken() | ||
| { | ||
| string content = Guid.NewGuid().ToString(); | ||
|
|
||
| await LoopbackServer.CreateClientAndServerAsync( | ||
| async uri => | ||
| { | ||
| using HttpClient httpClient = CreateHttpClient(); | ||
|
|
||
| HttpResponseMessage response = await httpClient.GetAsync( | ||
| uri, | ||
| HttpCompletionOption.ResponseContentRead); | ||
|
|
||
| CancellationToken cancellationToken = new CancellationToken(canceled: true); | ||
|
|
||
| await response.Content.LoadIntoBufferAsync(cancellationToken); | ||
| }, | ||
| async server => | ||
| { | ||
| await server.AcceptConnectionSendResponseAndCloseAsync(content: content); | ||
| }); | ||
| } | ||
|
|
||
| [Fact] | ||
| public async Task LoadIntoBufferAsync_Unbuffered_CanBeCanceled_AlreadyCanceledCts() | ||
| { | ||
| await LoopbackServer.CreateClientAndServerAsync( | ||
| async uri => | ||
| { | ||
| using HttpClient httpClient = CreateHttpClient(); | ||
|
|
||
| HttpResponseMessage response = await httpClient.GetAsync( | ||
| uri, | ||
| HttpCompletionOption.ResponseHeadersRead); | ||
|
|
||
| CancellationToken cancellationToken = new CancellationToken(canceled: true); | ||
|
|
||
| Task task = response.Content.LoadIntoBufferAsync(cancellationToken); | ||
|
|
||
| var exception = await Assert.ThrowsAsync<TaskCanceledException>(() => task); | ||
|
|
||
| Assert.Equal(cancellationToken, exception.CancellationToken); | ||
| }, | ||
| async server => | ||
| { | ||
| await IgnoreExceptions(server.AcceptConnectionSendResponseAndCloseAsync()); | ||
| }); | ||
| } | ||
|
|
||
| [OuterLoop("Uses Task.Delay")] | ||
| [Fact] | ||
| public async Task LoadIntoBufferAsync_Unbuffered_CanBeCanceled() | ||
| { | ||
| var cts = new CancellationTokenSource(); | ||
|
|
||
| await LoopbackServer.CreateClientAndServerAsync( | ||
| async uri => | ||
| { | ||
| using HttpClient httpClient = base.CreateHttpClient(); | ||
|
|
||
| HttpResponseMessage response = await httpClient.GetAsync( | ||
| uri, | ||
| HttpCompletionOption.ResponseHeadersRead); | ||
|
|
||
| CancellationToken cancellationToken = cts.Token; | ||
|
|
||
| Task task = response.Content.LoadIntoBufferAsync(cancellationToken); | ||
|
|
||
| var exception = await Assert.ThrowsAsync<TaskCanceledException>(() => task); | ||
|
|
||
| Assert.Equal(cancellationToken, exception.CancellationToken); | ||
| }, | ||
| async server => | ||
| { | ||
| await server.AcceptConnectionAsync(async connection => | ||
| { | ||
| await connection.ReadRequestHeaderAsync(); | ||
| await connection.SendResponseAsync(LoopbackServer.GetHttpResponseHeaders(contentLength: 100)); | ||
| await Task.Delay(250); | ||
| cts.Cancel(); | ||
| await Task.Delay(500); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do the delays need to be this long? Might want to mark the test as outerloop if there's no way to shrink them. Even better would be to find a way to make it more deterministic without timeouts / delays.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not sure but I assume that these delays try to enforce the LoadIntoBuffer task to actively wait on the cancellation token.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A deterministic version would look something like a revert of ef6a5a2. var observedCancellation = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);var exception = await Assert.ThrowsAsync<TaskCanceledException>(() => task);
observedCancellation.SetResult();await connection.SendResponseAsync(LoopbackServer.GetHttpResponseHeaders(contentLength: 100));
await Task.Yield();
cts.Cancel();
await observedCancellation.Task.WaitAsync(TestHelper.PassingTestTimeout);But as written, this test isn't enforcing that the client actually honors the cancellation token before receiving the whole response. Given that this test is a copy-paste of existing test logic we have in this file, I'd keep it as-is in this PR.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the browser the server side is played by xharness and driven via WebSocket, that makes it lot slower to react to anything. It could be probably rewritten in deterministic way, but it could be separate PR if desired. |
||
| await IgnoreExceptions(connection.SendResponseAsync(new string('a', 100))); | ||
| }); | ||
| }); | ||
| } | ||
|
|
||
| [Theory] | ||
| [InlineData(true)] | ||
| [InlineData(false)] | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.