diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs index d7d7a88970375..0300d3c88a6b6 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs @@ -1275,6 +1275,23 @@ private async ValueTask SendDataAsync(ReadOnlyMemory buffer, CancellationT await _connection.SendStreamDataAsync(StreamId, current, flush, _requestBodyCancellationSource.Token).ConfigureAwait(false); } } + catch (OperationCanceledException e) when (e.CancellationToken == _requestBodyCancellationSource.Token) + { + lock (SyncObject) + { + if (_resetException is Exception resetException) + { + if (_canRetry) + { + ThrowRetry(SR.net_http_request_aborted, resetException); + } + + ThrowRequestAborted(resetException); + } + } + + throw; + } finally { linkedRegistration.Dispose(); diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http2.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http2.cs index 668d194ebef1c..e61e728ede997 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http2.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http2.cs @@ -903,6 +903,39 @@ await Assert.ThrowsAnyAsync(() => } } + [ConditionalFact(nameof(SupportsAlpn))] + public async Task GoAwayFrame_RequestWithBody_ServerDisconnect_AbortStreamsAndThrowIOException() + { + using (Http2LoopbackServer server = Http2LoopbackServer.CreateServer()) + using (HttpClient client = CreateHttpClient()) + { + var request = new HttpRequestMessage(HttpMethod.Post, server.Address); + request.Version = new Version(2, 0); + var content = new string('*', 300); + var stream = new CustomContent.SlowTestStream(Encoding.UTF8.GetBytes(content), null, count: 60); + request.Content = new CustomContent(stream); + + Task sendTask = client.SendAsync(request); + + Http2LoopbackConnection connection = await server.EstablishConnectionAsync(); + (int streamId, _) = await connection.ReadAndParseRequestHeaderAsync(readBody: false); + await connection.SendDefaultResponseHeadersAsync(streamId); + + await connection.SendGoAway(0, errorCode: ProtocolErrors.PROTOCOL_ERROR); + + // Expect client to detect that server has disconnected and throw an exception + var exception = await Assert.ThrowsAnyAsync(() => + new Task[] + { + sendTask + }.WhenAllOrAnyFailed(TestHelper.PassingTestTimeoutMilliseconds)); + + Assert.IsType(exception.InnerException); + Assert.NotNull(exception.InnerException.InnerException); + Assert.Contains("PROTOCOL_ERROR", exception.InnerException.InnerException.Message); + } + } + [ConditionalFact(nameof(SupportsAlpn))] public async Task GoAwayFrame_UnprocessedStreamFirstRequestFinishedFirst_RequestRestarted() { @@ -2790,8 +2823,8 @@ public async Task PostAsyncDuplex_ServerResetsStream_Throws() // Trying to read on the response stream should fail now, and client should ignore any data received await AssertProtocolErrorForIOExceptionAsync(SendAndReceiveResponseDataAsync(contentBytes, responseStream, connection, streamId), ProtocolErrors.ENHANCE_YOUR_CALM); - // Attempting to write on the request body should now fail with OperationCanceledException. - Exception e = await Assert.ThrowsAnyAsync(async () => { await SendAndReceiveRequestDataAsync(contentBytes, requestStream, connection, streamId); }); + // Attempting to write on the request body should now fail with IOException. + Exception e = await Assert.ThrowsAnyAsync(async () => { await SendAndReceiveRequestDataAsync(contentBytes, requestStream, connection, streamId); }); // Propagate the exception to the request stream serialization task. // This allows the request processing to complete.