Skip to content

Commit a633e88

Browse files
authored
HTTP/3: Test abort at end of request (#43183)
1 parent 4db440d commit a633e88

File tree

2 files changed

+68
-0
lines changed

2 files changed

+68
-0
lines changed

src/Servers/Kestrel/test/Interop.FunctionalTests/Http3/Http3RequestTests.cs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,58 @@ public async Task POST_ServerAbort_ClientReceivesAbort(HttpProtocols protocol)
537537
}
538538
}
539539

540+
[ConditionalFact]
541+
[MsQuicSupported]
542+
public async Task POST_ServerAbortAfterWrite_ClientReceivesAbort()
543+
{
544+
// Arrange
545+
var builder = CreateHostBuilder(async context =>
546+
{
547+
Logger.LogInformation("Server writing content.");
548+
await context.Response.Body.WriteAsync(new byte[16]);
549+
550+
// Note that there is a race here on what is sent before the abort is processed.
551+
// Abort may happen before or after response headers have been sent.
552+
Logger.LogInformation("Server aborting.");
553+
context.Abort();
554+
}, protocol: HttpProtocols.Http3);
555+
556+
using (var host = builder.Build())
557+
using (var client = HttpHelpers.CreateClient())
558+
{
559+
await host.StartAsync().DefaultTimeout();
560+
561+
for (var i = 0; i < 100; i++)
562+
{
563+
Logger.LogInformation($"Client sending request {i}");
564+
565+
var request = new HttpRequestMessage(HttpMethod.Get, $"https://127.0.0.1:{host.GetPort()}/");
566+
request.Version = GetProtocol(HttpProtocols.Http3);
567+
request.VersionPolicy = HttpVersionPolicy.RequestVersionExact;
568+
569+
// Act
570+
var sendTask = client.SendAsync(request, CancellationToken.None);
571+
572+
// Assert
573+
var ex = await Assert.ThrowsAsync<HttpRequestException>(async () =>
574+
{
575+
// Note that there is a race here on what is sent before the abort is processed.
576+
// Abort may happen before or after response headers have been sent.
577+
Logger.LogInformation($"Client awaiting response {i}");
578+
var response = await sendTask;
579+
580+
Logger.LogInformation($"Client awaiting content {i}");
581+
await response.Content.ReadAsByteArrayAsync();
582+
}).DefaultTimeout();
583+
584+
var protocolException = ex.GetProtocolException();
585+
Assert.Equal((long)Http3ErrorCode.InternalError, protocolException.ErrorCode);
586+
}
587+
588+
await host.StopAsync().DefaultTimeout();
589+
}
590+
}
591+
540592
// Verify HTTP/2 and HTTP/3 match behavior
541593
[ConditionalTheory]
542594
[MsQuicSupported]

src/Servers/Kestrel/test/Interop.FunctionalTests/HttpHelpers.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,22 @@ namespace Interop.FunctionalTests;
1919

2020
internal static class HttpHelpers
2121
{
22+
public static HttpProtocolException GetProtocolException(this Exception ex)
23+
{
24+
var current = ex;
25+
while (current != null)
26+
{
27+
if (current is HttpProtocolException httpProtocolException)
28+
{
29+
return httpProtocolException;
30+
}
31+
32+
current = current.InnerException;
33+
}
34+
35+
throw new Exception($"Couldn't find {nameof(HttpProtocolException)}. Original error: {ex}");
36+
}
37+
2238
public static HttpMessageInvoker CreateClient(TimeSpan? idleTimeout = null, TimeSpan? expect100ContinueTimeout = null, bool includeClientCert = false)
2339
{
2440
var handler = new SocketsHttpHandler();

0 commit comments

Comments
 (0)