Skip to content

Commit e90a1e7

Browse files
authored
Cancel the token when it's created after an Abort/Disconnect #17278 (#35418)
1 parent d05a904 commit e90a1e7

File tree

2 files changed

+99
-0
lines changed

2 files changed

+99
-0
lines changed

src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,10 @@ public void Abort()
181181
}
182182
_requestAbortSource.Dispose();
183183
}
184+
else
185+
{
186+
_disconnectToken = new CancellationToken(canceled: true);
187+
}
184188
ForceCancelRequest();
185189
Request.Dispose();
186190
// Only Abort, Response.Dispose() tries a graceful flush

src/Servers/HttpSys/test/FunctionalTests/RequestTests.cs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,101 @@ public async Task Request_EscapedControlCharacters_400()
468468
}
469469
}
470470

471+
[ConditionalFact]
472+
public async Task RequestAborted_AfterAccessingProperty_Notified()
473+
{
474+
var registered = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
475+
var result = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
476+
using var server = Utilities.CreateHttpServerReturnRoot("/", out var address, async httpContext =>
477+
{
478+
var ct = httpContext.RequestAborted;
479+
480+
if (!ct.CanBeCanceled || ct.IsCancellationRequested)
481+
{
482+
result.SetException(new Exception("The CT isn't valid."));
483+
return;
484+
}
485+
486+
ct.Register(() => result.SetResult());
487+
488+
registered.SetResult();
489+
490+
// Don't exit until it fires or else it could be disposed.
491+
await result.Task.DefaultTimeout();
492+
});
493+
494+
// Send a request and then abort.
495+
496+
var uri = new Uri(address);
497+
StringBuilder builder = new StringBuilder();
498+
builder.AppendLine("POST / HTTP/1.1");
499+
builder.AppendLine("Connection: close");
500+
builder.AppendLine("Content-Length: 10");
501+
builder.Append("HOST: ");
502+
builder.AppendLine(uri.Authority);
503+
builder.AppendLine();
504+
505+
byte[] request = Encoding.ASCII.GetBytes(builder.ToString());
506+
507+
using var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
508+
509+
await socket.ConnectAsync(uri.Host, uri.Port);
510+
socket.Send(request);
511+
512+
// Wait for the token to be setup before aborting.
513+
await registered.Task.DefaultTimeout();
514+
515+
socket.Close();
516+
517+
await result.Task.DefaultTimeout();
518+
}
519+
520+
[ConditionalFact]
521+
public async Task RequestAbortedDurringRead_BeforeAccessingProperty_TokenAlreadyCanceled()
522+
{
523+
var requestAborted = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
524+
var result = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
525+
using var server = Utilities.CreateHttpServerReturnRoot("/", out var address, async httpContext =>
526+
{
527+
await requestAborted.Task.DefaultTimeout();
528+
try
529+
{
530+
await httpContext.Request.Body.ReadAsync(new byte[10]).DefaultTimeout();
531+
result.SetException(new Exception("This should have aborted"));
532+
return;
533+
}
534+
catch (IOException)
535+
{
536+
}
537+
538+
result.SetResult(httpContext.RequestAborted.IsCancellationRequested);
539+
});
540+
541+
// Send a request and then abort.
542+
543+
var uri = new Uri(address);
544+
StringBuilder builder = new StringBuilder();
545+
builder.AppendLine("POST / HTTP/1.1");
546+
builder.AppendLine("Connection: close");
547+
builder.AppendLine("Content-Length: 10");
548+
builder.Append("HOST: ");
549+
builder.AppendLine(uri.Authority);
550+
builder.AppendLine();
551+
552+
byte[] request = Encoding.ASCII.GetBytes(builder.ToString());
553+
554+
using var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
555+
556+
await socket.ConnectAsync(uri.Host, uri.Port);
557+
socket.Send(request);
558+
socket.Close();
559+
560+
requestAborted.SetResult();
561+
562+
var wasCancelled = await result.Task;
563+
Assert.True(wasCancelled);
564+
}
565+
471566
private IServer CreateServer(out string root, RequestDelegate app)
472567
{
473568
// TODO: We're just doing this to get a dynamic port. This can be removed later when we add support for hot-adding prefixes.

0 commit comments

Comments
 (0)