Description
Given a request is sent using HTTP/2 and a server responds with GOAWAY error code HTTP_1_1_REQUIRED, the HttpClient appears to retry using HTTP/2 rather than attempting to downgrade to HTTP/1.1
RFC9113 indicates that HTTP_1_1_REQUIRED can be returned per endpoint.
The endpoint requires that HTTP/1.1 be used instead of HTTP/2.
An example is a reverse proxy that accepts HTTP/2 connections and is configured to forward requests to multiple servers. One server only supports HTTP/1.1, so a policy is configured on the proxy to return HTTP_1_1_REQUIRED back to the client for HTTP/2 requests sent to those specific endpoints.
Reproduction Steps
var handler = new SocketsHttpHandler
{
ConnectCallback = async (context, cancellationToken) =>
{
var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
await socket.ConnectAsync(IPAddress.Loopback, port, cancellationToken);
return new NetworkStream(socket, ownsSocket: true);
},
SslOptions = new SslClientAuthenticationOptions
{
RemoteCertificateValidationCallback = (_, _, _, _) => true
}
};
using var httpClient = new HttpClient(handler)
{
BaseAddress = new Uri("https://example.com"),
DefaultRequestVersion = HttpVersion.Version20,
DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrLower
};
await httpClient.GetAsync("/");
Expected behavior
HTTPClient retries the request using HTTP/1.1 and caches the HTTP version.
Actual behavior
Retries using HTTP/2 observed when debugging until the following exception gets thrown.
Exception thrown: 'System.Net.Http.HttpRequestException' in System.Private.CoreLib.dll: 'An error occurred while sending the request.'
Inner exceptions found, see $exception in variables window for more details.
Innermost exception System.ObjectDisposedException : Cannot access a disposed object.
Object name: 'System.Net.Security.SslStream'.
at System.ThrowHelper.ThrowObjectDisposedException(Object instance)
at System.Net.Security.SslStream.g__ThrowExceptional|131_0(ExceptionDispatchInfo e)
at System.Net.Security.SslStream.WriteAsync(ReadOnlyMemory`1 buffer, CancellationToken cancellationToken)
at System.Net.Http.Http2Connection.d__54.MoveNext()
Regression?
No
Known Workarounds
No response
Configuration
.NET 10
Other information
This was discussed on this issue.
#63715
However I think the conclusion not to retry with HTTP/1.1 should be revisited. It should not be assumed that the server is misconfigured if it is returning a specific error code.
Description
Given a request is sent using HTTP/2 and a server responds with GOAWAY error code HTTP_1_1_REQUIRED, the
HttpClientappears to retry using HTTP/2 rather than attempting to downgrade to HTTP/1.1RFC9113 indicates that HTTP_1_1_REQUIRED can be returned per endpoint.
An example is a reverse proxy that accepts HTTP/2 connections and is configured to forward requests to multiple servers. One server only supports HTTP/1.1, so a policy is configured on the proxy to return HTTP_1_1_REQUIRED back to the client for HTTP/2 requests sent to those specific endpoints.
Reproduction Steps
Expected behavior
HTTPClientretries the request using HTTP/1.1 and caches the HTTP version.Actual behavior
Retries using HTTP/2 observed when debugging until the following exception gets thrown.
Regression?
No
Known Workarounds
No response
Configuration
.NET 10
Other information
This was discussed on this issue.
#63715
However I think the conclusion not to retry with HTTP/1.1 should be revisited. It should not be assumed that the server is misconfigured if it is returning a specific error code.