C#: HttpClient should NOT be disposed

Some days ago, I got on my news feed a blog post about how HttpClient on C# should not be disposed.

Although the post was from 2016, it just come into my radar now. And it got me curious…
Curious to a point where I ended up doing some investigation on the behavior of HttpClient.

TL;DR

The conclusion I got to is that long-lived application (such as Web Applications) should have a single instance of HttpClient for each HTTP endpoint it consumes.

For instance: if the application calls two REST services say ServiceA and ServiceB, it should have two classes ServiceAClient and ServiceBClient (or ServiceAHttpClient if you prefer) and each class should have a single instance of HttpClient for each endpoint of that service.

This will allow you to set default values and Headers for each Service while allowing the HttpClient class to optimize the HTTP Connections to the service by using connection pooling to ensure maximum performance and stability.

There can be two main approaches for this:

  1. Either the class declares a static HttpClient variable
class ServiceAClient
{
private static readonly HttpClient httpClientEndpoint1;
static ServiceAClient()
{
httpClientEndpoint1= new HttpClient();
httpClientEndpoint1.BaseAddress = "http://.../";
// You may initialize the default headers here
}

// Declare the service operations here as public, async
// Keep the methods stateless
}

or

2. Declare a non-static instance of HttpClient variable and make the ServiceAClient class a singleton (either by implementing the singleton pattern or configure it as a singleton on the DI engine)

It’s very important that the business methods of this class do NOT store any internal state to allow it to be thread safe.

Now, the juicy part

The blog post from Simon Timms provide a very good explanation on the potential exhaustion of available TCP ports due to connections on TIME_WAIT state.

I wrote a console application to test several scenarios and I was able to watch the connection pooling in action.

The following code makes use of connection pooling

class Program
{
private readonly static HttpClient httpClient = new HttpClient();
static void Main(string[] args)
{
for (int i = 0; i < 10; i++)
{
var url = i % 2 == 0 ? url1 : url2;
var result = httpClient.GetAsync(url);
Console.WriteLine(result.Result.StatusCode);
}
}
}
screenshot taken after the execution of the loop: 3 HTTP connections for all the iterations

As opposed to declaring a HttpClient on each iteration:

class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 10; i++)
{
using (var httpClient = new HttpClient())
{
var url = i % 2 == 0 ? url1 : url2;
var result = httpClient.GetAsync(url);
Console.WriteLine(result.Result.StatusCode);
}
}
}
}

will close the HTTP connection to the server immediately, leaving the port in TIME_WAIT state.

c:\> netstat -n
(...)
TCP 192.168.1.220:16523 216.58.210.132:443 TIME_WAIT
TCP 192.168.1.220:16524 216.58.210.163:443 TIME_WAIT
TCP 192.168.1.220:16526 216.58.210.132:443 TIME_WAIT
TCP 192.168.1.220:16527 216.58.210.163:443 TIME_WAIT
TCP 192.168.1.220:16529 216.58.210.132:443 TIME_WAIT
TCP 192.168.1.220:16530 216.58.210.163:443 TIME_WAIT
TCP 192.168.1.220:16534 216.58.210.132:443 TIME_WAIT
TCP 192.168.1.220:16535 216.58.210.163:443 TIME_WAIT
TCP 192.168.1.220:16537 216.58.210.132:443 TIME_WAIT
TCP 192.168.1.220:16538 216.58.210.163:443 TIME_WAIT

By keeping the HTTP Connections opened in the first scenario, the program will gain huge benefits as it’s avoiding the TCP slow start on every new connection while also prevent the available ports exhaustion.
Also, if it is an HTTPS connection, it will also avoid the TLS handshake at the beginning of each connection

What about documentation?

This is where things get messy and contradictory…

The MSDN page for HttpClient doesn’t provide any useful information about life cycle management of the class. Yet, it mentions:

Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.
—- MSDN page for HttpClient

This means that the methods that make the HTTP calls are not thread safe, hence the class should be transient.

Still, the same page also redirects to the “Call a Web API from C#” tutorial which explains the use of HttpClient in detail.
And in this page, there is a section called “Create and Initialize HttpClient” that provides the information we are looking for.

HttpClient is intended to be instantiated once and reused throughout the life of an application. The following conditions can result in SocketException errors:
* Creating a new HttpClient instance per request.
* Server under heavy load.
Creating a new HttpClient instance per request can exhaust the available sockets.
-- Call a Web API from C# Tutorial

So, this means that the class should not be transient but it still does not guarantee that the instance members are thread safe.

Thread safety

Michael Taylor did some investigation and concluded that the HttpClient is NOT thread safe. Essentially because of the BaseAddress and DefaultRequestHeaders properties.

His recommendations are:

1. Ensure the creation and initialization of the client is done on a single thread, preferably through a factory.
2. Ensure that no code outside the initialization tries to modify any of the properties.
3. Create a new client for each unique base address and request header combinations.
Michael Taylog blog post on HttpClient

So, you should be really careful about how HttpClient is instantiated and kept on the life cycle of your application.

What now?

Based on the everything above, I would recommend to:

  • If all the operations for a service share the same set of default headers, then have an instance of HttpClient for each endpoint that your application is communicating to .
  • If an endpoint requires different header for each HTTP method, then you’ll need to create an instance of HttpClient for each combination of verb+endpoint.

These client classes should be made Singleton across the application or the HttpClient variable should be declared private static readonly within the Client class.

This will allow you to:

  • Benefit from the performance optimizations provided by connection pooling;
  • Avoid running out of available ports due to connections in TIME_WAIT state if the server gets heavy load;
  • Use default BaseAddress and HTTP Headers for each service your application integrates to

A final note about Disposable

.NET Framework provides the IDisposable interface that classes that hold resources that should be released must implement to ensure proper release of those resources (e.g. file handlers, TCP connections, …)
And because HttpClient actually opens TCP connections, it makes sense to make it IDisposable.

The issue is that many C# programmers are used to wrap IDisposable objects within using blocks to ensure that the resources are properly released within the life cycle of the application.

In the case long-living applications (like a Web Application) it doesn’t make sense to dispose instances of HttpClient because it will cause the connections to be closed, hence occurring in all the penalties and risks identified above.

So, beware about this issue and really think about the life cycle of the objects on your application.