Skip to content

HttpClient does not retry when HTTP_1_1_REQUIRED returned in GOAWAY #133317

Description

@drmathias

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions