Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use HTTP 2 with HttpClient in .Net

I'm trying to request data over HTTP 2.0. I'm using the HttpClient from .Net Core 2.2. I'm on Windows 10 but will run on Linux in production. The problem is that the version on the response seems to always be "1.1". What I am doing wrong?

using (var client = new HttpClient())
{
    using (var request = new HttpRequestMessage(new HttpMethod("GET"),
    "https://duckduckgo.com/"
    ))
    {
        request.Version = new Version(2, 0);
        var response = await client.SendAsync(request);

        Console.WriteLine(response.Version);
    }
}
like image 821
Robert M Avatar asked Dec 13 '18 14:12

Robert M


People also ask

How do I enable HTTP2 in IIS?

Launch your browser from your Windows 10 or Windows Server 2016 machine and hit F12, (or go to Settings and enable F12 Developer Tools), and then switch to the Network tab. Browse to https://localhost and voila, you are on HTTP/2!

What is the difference between HTTP1 and HTTP2?

HTTP2 is much faster and more reliable than HTTP1. HTTP1 loads a single request for every TCP connection, while HTTP2 avoids network delay by using multiplexing. HTTP is a network delay sensitive protocol in the sense that if there is less network delay, then the page loads faster.

Is http 2 backwards compatible?

Browser Compatibility: HTTP/2 is compatible with almost all browsers and is backward compatible with previous protocol versions like HTTP/1.1. The standardization effort was supported by most client browsers including Chrome and Firefox with the condition that it should be used only over TLS.


2 Answers

Update - .NET Core 3.0

.NET Core 3.0 now supports HTTP/2. The following code will print 2.0 :

var client = new HttpClient();

var req = new HttpRequestMessage(HttpMethod.Get, "https://http2.akamai.com/demo"){
             Version = new Version(2, 0) 
             };

var x = await client.SendAsync(req);
var version = x.Version;

Console.WriteLine(version);

Original Answer

You can't use HTTP/2 with HttpClient in .NET Core 2.1 or 2.2, even if you explicitly set the version in the request. You'll have to explicitly configure .NET Core to use the old HttpClientHandler instance that came with .NET Core 2.0, either by setting an App Context switch with :

AppContext.SetSwitch("System.Net.Http.UseSocketsHttpHandler", false);

Or by setting the DOTNET_SYSTEM_NET_HTTP_USESOCKETSHTTPHANDLER environment variable to 0 or false.

The discussion in this Github issue shows that HTTP/2 support is planned for .NET Core 3.0. The 3.0 Preview 1 released at Microsoft Connect 2018 doesn't support HTTP/2 yet.

The HttpClientHandler used up to .NET Core 2.0 supported HTTP/2. The following code will return 2.0 in a project that targets Core 2.0 :

var client = new HttpClient();

var req = new HttpRequestMessage(HttpMethod.Get, "https://http2.akamai.com/demo")
{
    Version = new Version(2, 0)
};

var x = await client.SendAsync(req);
var version = x.Version;

Console.WriteLine(version);

Just make sure you thoroughly clean your project if you change the target runtime - delete bin, obj and all target files, or you may end up running with the wrong runtime as I did.

In 2.1 a new, far faster SocketsHttpClientHandler was added as a default. The new handler doesn't support HTTP/2 yet. The same code will return 1.1 as the protocol version.

If the app context switch is set before creating the HttpClient though, HTTP/2 is used. This code will return 2.0. Interestingly, there's no need to specify the HTTP version. When HTTP/2 is available, the actual protocol version is negotiated. Both the Akamai URL and https://www.google.com will use HTTP/2 even though the version wasn't specified:

AppContext.SetSwitch("System.Net.Http.UseSocketsHttpHandler", false);
var client = new HttpClient();

var req = new HttpRequestMessage(HttpMethod.Get, "https://http2.akamai.com/demo");
var x = await client.SendAsync(req);

var version = x.Version;

The switch and environment variable are explained in the official announcement for .NET Core 2.1 SDK Preview 2:

Sockets Performance and SocketsHttpHandler

We made major improvements to sockets in .NET Core 2.1. Sockets are the basis of both outgoing and incoming networking communication. The higher-level networking APIs in .NET Core 2.1, including HttpClient and Kestrel, are now based on .NET sockets. In earlier versions, these higher-level APIs were based on native networking implementations.

...

You can use one of the following mechanisms to configure a process to use the older HttpClientHandler:

From code, use the AppContext class:

AppContext.SetSwitch("System.Net.Http.UseSocketsHttpHandler", false);

The AppContext switch can also be set by config file.

The same can be achieved via the environment variable DOTNET_SYSTEM_NET_HTTP_USESOCKETSHTTPHANDLER. To opt out, set the value to either false or 0.

like image 187
Panagiotis Kanavos Avatar answered Sep 27 '22 21:09

Panagiotis Kanavos


The current suggested pattern is to register HttpClient dependencies in the ConfigureServices method: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-requests.

Here is an example of registering a default HttpClient using a typed client and configuring it to use HTTP/2:

public void ConfigureServices(IServiceCollection services)
{
...
    services.AddHttpClient<ExampleService>()
        .ConfigureHttpClient((client) =>
        {
            client.DefaultRequestVersion = new Version(2, 0);
        });
...
}
like image 45
undrivendev Avatar answered Sep 27 '22 22:09

undrivendev