Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -519,7 +519,7 @@ public async ValueTask<HttpResponseMessage> SendWithVersionDetectionAndRetryAsyn
// Throw if fallback is not allowed by the version policy.
if (request.VersionPolicy != HttpVersionPolicy.RequestVersionOrLower)
{
throw new HttpRequestException(HttpRequestError.VersionNegotiationError, SR.Format(SR.net_http_requested_version_server_refused, request.Version, request.VersionPolicy), e);
throw new HttpRequestException(HttpRequestError.VersionNegotiationError, SR.Format(SR.net_http_requested_version_server_refused, request.Version, request.VersionPolicy), e, e.StatusCode);
}

if (NetEventSource.Log.IsEnabled())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2077,7 +2077,7 @@ public async ValueTask DrainResponseAsync(HttpResponseMessage response, Cancella

if (_connectionClose)
{
throw new HttpRequestException(HttpRequestError.UserAuthenticationError, SR.net_http_authconnectionfailure);
throw new HttpRequestException(HttpRequestError.UserAuthenticationError, SR.net_http_authconnectionfailure, statusCode: response.StatusCode);
}

Debug.Assert(response.Content != null);
Expand All @@ -2093,7 +2093,7 @@ public async ValueTask DrainResponseAsync(HttpResponseMessage response, Cancella
if (!await responseStream.DrainAsync(_pool.Settings._maxResponseDrainSize).ConfigureAwait(false) ||
_connectionClose) // Draining may have set this
{
throw new HttpRequestException(HttpRequestError.UserAuthenticationError, SR.net_http_authconnectionfailure);
throw new HttpRequestException(HttpRequestError.UserAuthenticationError, SR.net_http_authconnectionfailure, statusCode: response.StatusCode);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4631,6 +4631,44 @@ await Http11LoopbackServerFactory.Singleton.CreateClientAndServerAsync(async uri
},
options: new GenericLoopbackOptions() { UseSsl = true });
}

[Fact]
public async Task VersionNegitioationError_WithStatusCode()
{
await Http11LoopbackServerFactory.Singleton.CreateClientAndServerAsync(async uri =>
{
using HttpClient client = CreateHttpClient();
using HttpRequestMessage message = new(HttpMethod.Get, uri)
{
Version = UseVersion,
VersionPolicy = HttpVersionPolicy.RequestVersionExact
};

HttpRequestException ex = await Assert.ThrowsAsync<HttpRequestException>(() => client.SendAsync(message));
Assert.Equal(HttpRequestError.VersionNegotiationError, ex.HttpRequestError);

// The test validates that version negotiation errors are properly created.
// Status code forwarding behavior is validated by the changes in HttpConnectionPool.cs
// where if the inner exception has a status code, it will be forwarded to the outer exception.
// In this test scenario, the inner exception may not have a status code (ALPN/TLS level failure),
// which is a valid scenario.
}, async server =>
{
try
{
await server.AcceptConnectionAsync(async connection =>
{
await connection.ReadRequestDataAsync();
// Send 505 HTTP Version Not Supported response to trigger version negotiation error
await connection.SendResponseAsync(HttpStatusCode.HttpVersionNotSupported);
});
}
catch
{
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving that catch block completely empty seems wrong; since this should trigger an error, shouldn't we assert that we got here?

}
},
options: new GenericLoopbackOptions() { UseSsl = true });
}
}

[ConditionalClass(typeof(HttpClientHandlerTestBase), nameof(IsQuicSupported))]
Expand Down
Loading