diff --git a/src/ReverseProxy/Forwarder/HttpForwarder.cs b/src/ReverseProxy/Forwarder/HttpForwarder.cs index 66880c895..ef3f03587 100644 --- a/src/ReverseProxy/Forwarder/HttpForwarder.cs +++ b/src/ReverseProxy/Forwarder/HttpForwarder.cs @@ -456,13 +456,18 @@ private async ValueTask HandleRequestFailureAsync(HttpContext co { if (requestException is OperationCanceledException) { - if (!context.RequestAborted.IsCancellationRequested && requestCancellationSource.IsCancellationRequested) + if (context.RequestAborted.IsCancellationRequested) { - return await ReportErrorAsync(ForwarderError.RequestTimedOut, StatusCodes.Status504GatewayTimeout); + return await ReportErrorAsync(ForwarderError.RequestCanceled, StatusCodes.Status502BadGateway); } else { - return await ReportErrorAsync(ForwarderError.RequestCanceled, StatusCodes.Status502BadGateway); +#if NET6_0_OR_GREATER + Debug.Assert(requestCancellationSource.IsCancellationRequested || requestException.ToString().Contains("ConnectTimeout"), requestException.ToString()); +#else + Debug.Assert(requestCancellationSource.IsCancellationRequested || requestException.ToString().Contains("ConnectHelper"), requestException.ToString()); +#endif + return await ReportErrorAsync(ForwarderError.RequestTimedOut, StatusCodes.Status504GatewayTimeout); } } diff --git a/test/ReverseProxy.Tests/Forwarder/HttpForwarderTests.cs b/test/ReverseProxy.Tests/Forwarder/HttpForwarderTests.cs index c1f9fd8d9..e1fa6ce89 100644 --- a/test/ReverseProxy.Tests/Forwarder/HttpForwarderTests.cs +++ b/test/ReverseProxy.Tests/Forwarder/HttpForwarderTests.cs @@ -1360,6 +1360,40 @@ public async Task RequestTimedOut_Returns504() events.AssertContainProxyStages(new[] { ForwarderStage.SendAsyncStart }); } + [Fact] + public async Task RequestConnectTimedOut_Returns504() + { + var events = TestEventListener.Collect(); + + var httpContext = new DefaultHttpContext(); + httpContext.Request.Method = "GET"; + httpContext.Request.Host = new HostString("example.com:3456"); + + var proxyResponseStream = new MemoryStream(); + httpContext.Response.Body = proxyResponseStream; + + var destinationPrefix = "https://microsoft.com:123/"; // Port that doesn't accept connections + var sut = CreateProxy(); + + using var client = new HttpMessageInvoker(new SocketsHttpHandler + { + // Time out immediately + ConnectTimeout = TimeSpan.FromTicks(1) + }); + + var proxyError = await sut.SendAsync(httpContext, destinationPrefix, client); + + Assert.Equal(ForwarderError.RequestTimedOut, proxyError); + Assert.Equal(StatusCodes.Status504GatewayTimeout, httpContext.Response.StatusCode); + Assert.Equal(0, proxyResponseStream.Length); + var errorFeature = httpContext.Features.Get(); + Assert.Equal(ForwarderError.RequestTimedOut, errorFeature.Error); + Assert.IsAssignableFrom(errorFeature.Exception); + + AssertProxyStartFailedStop(events, destinationPrefix, httpContext.Response.StatusCode, errorFeature.Error); + events.AssertContainProxyStages(new[] { ForwarderStage.SendAsyncStart }); + } + [Fact] public async Task RequestCanceled_Returns502() {