Skip to content

Commit 6350af9

Browse files
authored
HTTP/3: Add tests for issue and unskip fixed tests (#35442)
1 parent 6475919 commit 6350af9

File tree

1 file changed

+160
-2
lines changed

1 file changed

+160
-2
lines changed

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

Lines changed: 160 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Except
174174
[ConditionalTheory]
175175
[MsQuicSupported]
176176
[InlineData(HttpProtocols.Http3, 11)]
177-
[InlineData(HttpProtocols.Http3, 1024, Skip = "HttpClient issue https://github.com/dotnet/runtime/issues/56115")]
177+
[InlineData(HttpProtocols.Http3, 1024)]
178178
[InlineData(HttpProtocols.Http2, 11)]
179179
[InlineData(HttpProtocols.Http2, 1024)]
180180
public async Task GET_ServerStreaming_ClientReadsPartialResponse(HttpProtocols protocol, int clientBufferSize)
@@ -219,7 +219,7 @@ public async Task GET_ServerStreaming_ClientReadsPartialResponse(HttpProtocols p
219219

220220
[ConditionalTheory]
221221
[MsQuicSupported]
222-
[InlineData(HttpProtocols.Http3, Skip = "https://github.com/dotnet/runtime/issues/56969")]
222+
[InlineData(HttpProtocols.Http3)]
223223
[InlineData(HttpProtocols.Http2)]
224224
public async Task POST_ClientSendsOnlyHeaders_RequestReceivedOnServer(HttpProtocols protocol)
225225
{
@@ -514,6 +514,164 @@ public async Task GET_MultipleRequestsInSequence_ReusedState()
514514
}
515515
}
516516

517+
// Verify HTTP/2 and HTTP/3 match behavior
518+
[ConditionalTheory]
519+
[MsQuicSupported]
520+
[InlineData(HttpProtocols.Http3, Skip = "https://github.com/dotnet/runtime/issues/56129")]
521+
[InlineData(HttpProtocols.Http2)]
522+
public async Task POST_ClientCancellationBidirectional_RequestAbortRaised(HttpProtocols protocol)
523+
{
524+
// Arrange
525+
var cancelledTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
526+
var readAsyncTask = new TaskCompletionSource<Task>(TaskCreationOptions.RunContinuationsAsynchronously);
527+
var clientHasCancelledSyncPoint = new SyncPoint();
528+
529+
var builder = CreateHostBuilder(async context =>
530+
{
531+
context.RequestAborted.Register(() =>
532+
{
533+
Logger.LogInformation("Server received request aborted.");
534+
cancelledTcs.SetResult();
535+
});
536+
537+
var requestBody = context.Request.Body;
538+
var responseBody = context.Response.Body;
539+
540+
// Read content
541+
var data = await requestBody.ReadAtLeastLengthAsync(TestData.Length);
542+
543+
await responseBody.WriteAsync(data);
544+
await responseBody.FlushAsync();
545+
546+
await clientHasCancelledSyncPoint.WaitForSyncPoint().DefaultTimeout();
547+
clientHasCancelledSyncPoint.Continue();
548+
549+
for (var i = 0; i < 5; i++)
550+
{
551+
await Task.Delay(100);
552+
553+
Logger.LogInformation($"Server writing to response after cancellation {i}.");
554+
await responseBody.WriteAsync(data);
555+
await responseBody.FlushAsync();
556+
}
557+
558+
// Wait for task cancellation
559+
await cancelledTcs.Task;
560+
561+
readAsyncTask.SetResult(requestBody.ReadAsync(data).AsTask());
562+
}, protocol: protocol);
563+
564+
var httpClientHandler = new HttpClientHandler();
565+
httpClientHandler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
566+
567+
using (var host = builder.Build())
568+
using (var client = new HttpClient(httpClientHandler))
569+
{
570+
await host.StartAsync().DefaultTimeout();
571+
572+
var cts = new CancellationTokenSource();
573+
var requestContent = new StreamingHttpContext();
574+
575+
var request = new HttpRequestMessage(HttpMethod.Post, $"https://127.0.0.1:{host.GetPort()}/");
576+
request.Content = requestContent;
577+
request.Version = GetProtocol(protocol);
578+
request.VersionPolicy = HttpVersionPolicy.RequestVersionExact;
579+
580+
// Act
581+
var responseTask = client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
582+
583+
var requestStream = await requestContent.GetStreamAsync().DefaultTimeout();
584+
585+
// Send headers
586+
await requestStream.FlushAsync().DefaultTimeout();
587+
// Write content
588+
await requestStream.WriteAsync(TestData).DefaultTimeout();
589+
590+
var response = await responseTask.DefaultTimeout();
591+
592+
var responseStream = await response.Content.ReadAsStreamAsync().DefaultTimeout();
593+
594+
var data = await responseStream.ReadAtLeastLengthAsync(TestData.Length).DefaultTimeout();
595+
596+
Logger.LogInformation("Client canceled request.");
597+
response.Dispose();
598+
599+
await clientHasCancelledSyncPoint.WaitToContinue().DefaultTimeout();
600+
601+
// Assert
602+
await cancelledTcs.Task.DefaultTimeout();
603+
604+
var serverWriteTask = await readAsyncTask.Task.DefaultTimeout();
605+
606+
await Assert.ThrowsAnyAsync<Exception>(() => serverWriteTask).DefaultTimeout();
607+
608+
await host.StopAsync().DefaultTimeout();
609+
}
610+
611+
// Ensure this log wasn't written:
612+
// Critical: Http3OutputProducer.ProcessDataWrites observed an unexpected exception.
613+
var badLogWrite = TestSink.Writes.FirstOrDefault(w => w.LogLevel == LogLevel.Critical);
614+
if (badLogWrite != null)
615+
{
616+
Assert.True(false, "Bad log write: " + badLogWrite + Environment.NewLine + badLogWrite.Exception);
617+
}
618+
}
619+
620+
// Verify HTTP/2 and HTTP/3 match behavior
621+
[ConditionalTheory]
622+
[MsQuicSupported]
623+
[InlineData(HttpProtocols.Http3, Skip = "https://github.com/dotnet/runtime/issues/56129")]
624+
[InlineData(HttpProtocols.Http2)]
625+
public async Task GET_ClientCancellationAfterResponseHeaders_RequestAbortRaised(HttpProtocols protocol)
626+
{
627+
// Arrange
628+
var cancelledTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
629+
630+
var builder = CreateHostBuilder(async context =>
631+
{
632+
context.RequestAborted.Register(() =>
633+
{
634+
Logger.LogInformation("Server received request aborted.");
635+
cancelledTcs.SetResult();
636+
});
637+
638+
var responseBody = context.Response.Body;
639+
await responseBody.WriteAsync(TestData);
640+
await responseBody.FlushAsync();
641+
642+
// Wait for task cancellation
643+
await cancelledTcs.Task;
644+
}, protocol: protocol);
645+
646+
var httpClientHandler = new HttpClientHandler();
647+
httpClientHandler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
648+
649+
using (var host = builder.Build())
650+
using (var client = new HttpClient(httpClientHandler))
651+
{
652+
await host.StartAsync().DefaultTimeout();
653+
654+
var request = new HttpRequestMessage(HttpMethod.Get, $"https://127.0.0.1:{host.GetPort()}/");
655+
request.Version = GetProtocol(protocol);
656+
request.VersionPolicy = HttpVersionPolicy.RequestVersionExact;
657+
658+
// Act
659+
var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
660+
661+
var responseStream = await response.Content.ReadAsStreamAsync().DefaultTimeout();
662+
663+
var data = await responseStream.ReadAtLeastLengthAsync(TestData.Length).DefaultTimeout();
664+
665+
Logger.LogInformation("Client canceled request.");
666+
response.Dispose();
667+
668+
// Assert
669+
await cancelledTcs.Task.DefaultTimeout();
670+
671+
await host.StopAsync().DefaultTimeout();
672+
}
673+
}
674+
517675
[ConditionalFact]
518676
[MsQuicSupported]
519677
public async Task StreamResponseContent_DelayAndTrailers_ClientSuccess()

0 commit comments

Comments
 (0)