How to make the .net HttpClient use http 2.0?
Asked Answered
F

4

35

I have an asp.net web api hosted on IIS 10 (windows server 2016). When I make a GET request to this from a Microsoft Edge browser, I see that HTTP 2.0 is used in IIS logs

2015-09-20 21:57:59 100.76.48.17 GET /RestController/Native - 443 - 73.181.195.76 HTTP/2.0 Mozilla/5.0+(Windows+NT+10.0;+Win64;+x64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/42.0.2311.135+Safari/537.36+Edge/12.10240 - 200 0 0 7299

However, when a GET request is made through a .net 4.6 client as below,

using (var client = new HttpClient())
{
    client.BaseAddress = new Uri("https://myapp.cloudapp.net/");

    HttpResponseMessage response = await client.GetAsync("RestController/Native");
    if (response.IsSuccessStatusCode)
    {
        await response.Content.CopyToAsync(new MemoryStream(buffer));
    }
}

I see the following HTTP 1.1 log in the server logs

2015-09-20 20:57:41 100.76.48.17 GET /RestController/Native - 443 - 131.107.160.196 HTTP/1.1 - - 200 0 0 707

How can I make the .net client use HTTP/2.0 ?

Facetiae answered 20/9, 2015 at 22:54 Comment(0)
D
7

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).

Dullish answered 20/9, 2015 at 23:11 Comment(3)
The repo you linked to seems to be only for the server components, not the client (there is a test client, which uses TcpClient, but not a real HttpClient)Batfish
@RacilHilan Thank you for answer. "you could implement your own HttpMessageHandler object that implements HTTP/2 " regarding your this comment. could you pl elaborate more?Whittling
@RashminJaviya You can copy the code from their source code using the link in my answer and add it to your project. Remember that the code is from development, so you'll have to do all the testing and possible fixes by yourself. Obviously, that's not a simple task, but it was the only way back when this question was asked. Now, things have changed. See Oliver's answer below if it fits your needs.Dullish
M
46

1.Make sure you are on the latest version of Windows 10.

2.Install WinHttpHandler:

Install-Package System.Net.Http.WinHttpHandler

3.Extend WinHttpHandler to add http2.0 support:

public class Http2CustomHandler : WinHttpHandler
{
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
    {
        request.Version = new Version("2.0");
        return base.SendAsync(request, cancellationToken);
    }
}

4.Pass above handler to the HttpClient constructor

using (var httpClient = new HttpClient(new Http2CustomHandler()))
{
      // your custom code
}
Maccarthy answered 29/3, 2017 at 19:0 Comment(4)
Agree, this is still somehow the only way to get http2.0 support for the HttpClient in the full .net frameworkFlood
Yep this works - thank you. Was scratching my head trying to understand why httpclient on 4.72 framework and win 10 did not work on http2! Seems crazy it is not in the core libraries, and btw it installs a lot of dependencies. Tested from 4.6 up.Kootenay
Non-crossplatformCarbone
That works. But if you need to manage CookieContainer, you also need to set CookieUsePolicy to CookieUsePolicy.UseSpecifiedCookieContainerOshiro
L
8

In addition to WinHttpHandler (as described in Shawinder Sekhon's answer), .NET Core 3.0 includes HTTP/2 support in the default SocketsHttpHandler (#30740). Since HTTP/1.1 is still the default, either the default must be changed by setting HttpClient.DefaultRequestVersion, or Version must be changed on each request. The version can be set when the request message is created:

using (var client = new HttpClient())
{
    client.BaseAddress = new Uri("https://myapp.cloudapp.net/");

    HttpResponseMessage response = await client.SendAsync(
        new HttpRequestMessage(HttpMethod.Get, "RestController/Native")
        {
            Version = HttpVersion.Version20,
        });
    if (response.IsSuccessStatusCode)
    {
        await response.Content.CopyToAsync(new MemoryStream(buffer));
    }
}

Or by using a custom HttpMessageHandler, such as:

public class ForceHttp2Handler : DelegatingHandler
{
    public ForceHttp2Handler(HttpMessageHandler innerHandler)
        : base(innerHandler)
    {
    }

    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        request.Version = HttpVersion.Version20;
        return base.SendAsync(request, cancellationToken);
    }
}

which can delegate to SocketsHttpHandler, WinHttpHandler, or any other HttpMessageHandler which supports HTTP/2:

using (var client = new HttpClient(new ForceHttp2Handler(new SocketsHttpHandler())))
{
    client.BaseAddress = new Uri("https://myapp.cloudapp.net/");

    HttpResponseMessage response = await client.GetAsync("RestController/Native");
    if (response.IsSuccessStatusCode)
    {
        await response.Content.CopyToAsync(new MemoryStream(buffer));
    }
}
Laurettalaurette answered 27/11, 2019 at 23:30 Comment(2)
Why not change HttpClient.DefaultRequestVersion to HttpVersion.Version20?Strawberry
@JoeHuang Sure, if you would like to use HTTP/2 by default for all HttpClients, setting HttpClient.DefaultRequestVersion is a great idea. I'll add it to the answer.Laurettalaurette
D
7

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).

Dullish answered 20/9, 2015 at 23:11 Comment(3)
The repo you linked to seems to be only for the server components, not the client (there is a test client, which uses TcpClient, but not a real HttpClient)Batfish
@RacilHilan Thank you for answer. "you could implement your own HttpMessageHandler object that implements HTTP/2 " regarding your this comment. could you pl elaborate more?Whittling
@RashminJaviya You can copy the code from their source code using the link in my answer and add it to your project. Remember that the code is from development, so you'll have to do all the testing and possible fixes by yourself. Obviously, that's not a simple task, but it was the only way back when this question was asked. Now, things have changed. See Oliver's answer below if it fits your needs.Dullish
A
5

HTTP/2 looks like it will be supported in C# client calls with .NET 4.6.2

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

HTTP/2 Support (Windows 10)

HTTP/2 is a new version of the HTTP protocol that provides much better connection utilization (fewer round-trips between client and server), resulting in lower latency web page loading for users. Web pages (as opposed to services) benefit the most from HTTP/2, since the protocol optimizes for multiple artifacts being requested as part of a single experience. HTTP/2 support has been added to ASP.NET in the .NET Framework 4.6. Because networking functionality exists at multiple layers, new features were required in Windows, in IIS, and in ASP.NET to enable HTTP/2. You must be running on Windows 10 to use HTTP/2 with ASP.NET.

HTTP/2 is also supported and on by default for Windows 10 Universal Windows Platform (UWP) apps that use the System.Net.Http.HttpClient API.

Algebraist answered 19/7, 2016 at 14:56 Comment(4)
This is only the server-side, not the client.Werewolf
Yeah, I am running VS2017 with .net 4.7 installed and I only see HTTP.version 1.0 and 1.1.Bolero
Saw this comment: To use HTTP/2 protocol support, you must use .NET Core. You must also use a supported version of Windows 10. And you need to manually specify HttpRequestMessage.Version = new Version(2,0); You can also use the separate System.Net.Http.WinHttpHandler package which provides an alternate handler for HttpClient. You need to create that WinHttpHandler and pass it into the constructor of HttpClient. This WinHttpHandler package is supported on both .NET Core .NET Framework over here: github.com/dotnet/corefx/issues/4870Zamindar
Also this at same page: you can use WinHttpHandler on .NET Framework. You must only use the SendAsync() methods from HttpClient. Those are the only ones that allow you to pass in an HttpRequestMessage. Other methods, use a default HttpRequestMessage that uses Version(1,1) only. You have to set the .Version field in HttpRequestMessage to 2.0 as indicated. You have to use a current version of Windows 10.Zamindar

© 2022 - 2024 — McMap. All rights reserved.