How to Make the .Net Httpclient Use Http 2.0

How to make the .net HttpClient use http 2.0?

HttpClient does not support HTTP/2 yet. It will be available in the next release (code name KATANA). Here is the link to their source code for the next release.

Till then, you could implement your own HttpMessageHandler object that implements HTTP/2 and pass it to the HttpClient's constructor (you probably can use their source code from KATANA).

HttpRequest Http 2.0

You're not using .NET-Core.

The version you're using - 4.0.30319.42000 - corresponds to .NET Framework 4.6+, not .NET core.

From How to: Determine which .NET Framework versions are installed:

The common language runtime (CLR), which manages and executes your
app's code. A single CLR version typically supports multiple .NET
Framework versions. For example, CLR version 4.0.30319.xxxxx where
xxxxx is less than 42000, supports .NET Framework versions 4 through
4.5.2. CLR version greater than or equal to 4.0.30319.42000 supports .NET Framework versions starting with .NET Framework 4.6.

Only Http versions 1/1.1 are supported in .NET Framework, however this question How to make the .net HttpClient use http 2.0? has some workarounds.

I've tested your code, which works for me on .NET core 3.1 and .NET 6. Demo in .NET 6

Use HTTP 2 with HttpClient in .Net

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.

How to specify HTTP/2 prior knowledge mode for HttpClient in dotnet core?

My question is about how to actually do what curl is doing ? But in C#.

Please try to set the System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport switch to true, like below.

AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);

SocketsHttpHandler handler = new SocketsHttpHandler();
using (var client = new HttpClient(handler))
{
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost:8080");
request.Version = new Version(2, 0);

var response = client.SendAsync(request).Result;

if (response.IsSuccessStatusCode)
{
var s = response.Content.ReadAsStringAsync().Result;
Console.WriteLine(s);
}
}

Test Result

Sample Image

How to send HTTP 2.0 request in Visual C#

HTTP 2.0 support in Framework 4.6.2 version only.

https://msdn.microsoft.com/en-us/library/ms171868(v=vs.110).aspx

How do I enable http2 in C# Kestrel web server over plain http?

Unencrypted http2 can be necessary for load balancers, proxies, etc.

You must do three things to use http2 over unencrypted channel.

Setup Kestrel to use http2 on your server:

builder.ConfigureWebHostDefaults((webBuilder) =>
{
// this will keep your other end points settings such as --urls parameter
webBuilder.ConfigureKestrel((options) =>
{
// trying to use Http1AndHttp2 causes http2 connections to fail with invalid protocol error
// according to Microsoft dual http version mode not supported in unencrypted scenario: https://learn.microsoft.com/en-us/aspnet/core/grpc/troubleshoot?view=aspnetcore-3.0
options.ConfigureEndpointDefaults(lo => lo.Protocols = HttpProtocols.Http2);
});
});

For .net 5+, create your HttpClient instance, then create a message and specify the version:

var request = new HttpRequestMessage(HttpMethod.Get, uri)
{
Version = HttpVersion.Version20,
VersionPolicy = HttpVersionPolicy.RequestVersionOrHigher
};

For .net core 3.1 and older, set a flag to enable http2 unencrypted. Then, when you create an HttpClient, specify the version:

AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
var client = new HttpClient { BaseAddress = new Uri(baseUrl), DefaultRequestVersion = new Version(2, 0) };

If you need to support both http1 and http2 on a completely unencrypted host, then you will need to listen on two ports, one for each http version. Then your load balancer or proxy would need to handle the http version and direct to the appropriate port.

You won't see http2 on your browser and will likely get a protocol error, so in those cases you can use an http1 protocol directive just for development environment. Not ideal, but it at least lets you test locally.



Related Topics



Leave a reply



Submit