In this post I take a look at the code in the default implementation of IHttpClientFactory
in ASP.NET Core—DefaultHttpClientFactory
. We’ll see how it ensures that HttpClient
instances created with the factory prevent socket exhaustion, while also ensuring that DNS changes are respected.
This post assumes you already have a general idea of
_IHttpClientFactory_
and what it’s used for, so if it’s new to you, take a look at Steve Gordon’s series on IHttpClientFactory, or see the docs.
The code in this post is based on the HEAD of the master branch of https://github.com/dotnet/runtime at the time of writing, after .NET 5 preview 7. But the code hasn’t changed significantly for 2 years, so I don’t expect it to change much soon!
I’ve taken a couple of liberties with the code by removing null checks, in-lining some trivial methods, and removing some code that’s tangential to this discussion. For the original code, see GitHub.
IHttpClientFactory
allows you to create HttpClient
instances for interacting with HTTP APIs, using best practices to avoid common issues. Before IHttpClientFactory
, it was common to fall into one of two traps when creating HttpClient
instances:
_HttpClient_
instances as required. This can lead to socket exhaustion due to the TIME_WAIT period required after closing a connection._HttpClient_
for the lifetime of the application. This can lead to issues when the DNS record for the HTTP API you’re using changes—the changes will not be respected by the HttpClient
.IHttpClientFactory
was added in .NET Core 2.1, and solves this issue by separating the management of HttpClient
, from the the HttpMessageHandler
chain that is used to send the message. In reality, it is the lifetime of the HttpClientHandler
at the end of the pipeline that is the important thing to manage, as this is the handler that actually makes the connection
In addition to simply managing the handler lifetimes, IHttpClientFactory
also makes it easy to customise the generated HttpClient
and message handler pipeline using an IHttpClientBuilder
. This allows you to “pre-configure” a named of typed HttpClient
that is created using the factory, for example to set the base address or add default headers:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient("github", c =>
{
c.BaseAddress = new Uri("https://api.github.com/");
})
.ConfigureHttpClient(c =>
{
c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
});
}
#asp.net core #httpclient #dependency injection #source code dive