Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to put HttpClientHandler and SocketsHttpHandler into HttpClient

HttpClient accepts only one HttpMessageHandler. I have two of them.

HttpClientHandler clientHandler = GetClientHandler();
SocketsHttpHandler socketsHandler = GetSocketsHandler();
HttpClient client = new( /* how to put two handlers here? */ );
like image 618
Max Jacobi Avatar asked Oct 18 '25 15:10

Max Jacobi


2 Answers

At least for .NET 6+, when looking at the source code of HttpClientHandler, one can see that under the covers, this class simply uses a SocketsHttpHandler and maps most properties 1-to-1. So, you can just use a SocketsHttpHandler yourself and set all the same properties. See the source code for reference on any custom logic that the HttpClientHandler may be doing that you may want to consider doing against the SocketsHttpHandler.

Further comments from folks at Microsoft can be seen here, encouraging use of SocketsHttpHandler.

like image 174
Tim Friesen Avatar answered Oct 21 '25 05:10

Tim Friesen


With .NET 8, I'm able to get access to the private Handler property of HttpClientHandler (you can see it in the debugger), which returns SocketsHttpHandler, and set properties on it like ConnectionTimeout, which is not exposed through HttpClientHandler.

Obviously, this code is prone to break due to dependencies on internals which may change over time, but it's easy to reason with and doesn't require reimplementing HttpClientHandler.

public class CustomHttpClientHandler : HttpClientHandler
{
    public TimeSpan ConnectTimeout { get; }

    public CustomHttpClientHandler(TimeSpan connectTimeout)
    {
        ConnectTimeout = connectTimeout;

        if (typeof(HttpClientHandler)
                .GetProperty("Handler", BindingFlags.NonPublic | BindingFlags.Instance)?
                .GetValue(this) is not SocketsHttpHandler socketsHttpHandler)
        {
            throw new InvalidOperationException($"Internals of {nameof(HttpClientHandler)} have changed.");
        }

        socketsHttpHandler.ConnectTimeout = connectTimeout;
    }
}

Usage:

services
    .AddHttpClient()
    .ConfigureHttpClientDefaults(builder =>
    {
        builder.ConfigurePrimaryHttpMessageHandler(
            () => new CustomHttpClientHandler(TimeSpan.FromSeconds(3))
        );
    });

EDIT:

With the comments below, that it is possible to use the SocketsHttpHandler directly as the primary handler, the above can be simplified as:

services
    .AddHttpClient()
    .ConfigureHttpClientDefaults(builder =>
    {
        builder.ConfigurePrimaryHttpMessageHandler(
            () => new SocketsHttpHandler {
                ConnectTimeout = TimeSpan.FromSeconds(3)
            }
        );
    });

This approach is also documented here: https://learn.microsoft.com/en-us/dotnet/core/extensions/httpclient-factory#using-ihttpclientfactory-together-with-socketshttphandler

like image 29
Bart Verkoeijen Avatar answered Oct 21 '25 04:10

Bart Verkoeijen