{"id":13,"date":"2024-02-09T06:25:29","date_gmt":"2024-02-09T06:25:29","guid":{"rendered":"https:\/\/denispavlov.net\/Blog\/?p=13"},"modified":"2024-03-20T07:12:08","modified_gmt":"2024-03-20T14:12:08","slug":"production-quality-httpclient","status":"publish","type":"post","link":"https:\/\/denispavlov.net\/Blog\/2024\/02\/09\/production-quality-httpclient\/","title":{"rendered":"Production Quality HttpClient"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">In this post I would like to discuss some of the good and bad ways to use the <code>HttpClient<\/code>. I am going to start from the basics.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">What is the problem with the following code?<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public async Task&lt;string?&gt; GetContentAsync(string url)\n{\n    var client = new HttpClient();\n    var result = await client.GetStringAsync(url);\n    \n    return result;\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Problem is that the <code>HttpClient<\/code> is not being properly disposed, as it implements IDisposable interface. So, let&#8217;s do it.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public async Task&lt;string?&gt; GetContentAsync(string url)\n{\n    var client = new HttpClient();\n    var result = await client.GetStringAsync(url);\n    client.Dispose();\n\n    return result;\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Right? Well, not quite, because if <code>GetStringAsync<\/code> throws an exception, we will not reach the dispose call. Let&#8217;s fix it.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public async Task&lt;string?&gt; GetContentAsync(string url)\n{\n    var client = new HttpClient();\n    string? result;\n    \n    try\n    {\n        result = await client.GetStringAsync(url);\n    }\n    finally\n    {\n        client.Dispose();\n    }\n\n    return result;\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Since finally block will always execute, the client will get disposed. And shorthand code below actually achieves the same thing.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public async Task&lt;string?&gt; GetContentAsync(string url)\n{\n    using var client = new HttpClient();\n    var result = await client.GetStringAsync(url);\n\n    return result;\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The using statement will take care of calling dispose when client is no longer in scope. <strong>So, are we good?<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This is really beginning of what I wanted to discuss. The last code block is probably fine for Computer Science 101. However, in a production application where this method might be called thousands of times we will eventually get a nasty <code>System.Net.Sockets.SocketException<\/code>. What&#8217;s worse is that it will probably pass all unit tests and QA process successfully.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The problem is that when <code>HttpClient<\/code> is disposed, the underlying sockets are not immediately released, leading to a situation where you may be creating <code>HttpClients<\/code> faster than releasing all the resources, hence the exception. First thought might be to use a Singleton pattern on the <code>HttpClient<\/code> so it is only initialized once and used throughout the application. However, there is an issue with that as well because your <code>HttpClient<\/code> will not respond to DNS changes unless constructed correctly. So the recommended solution is dependency injection of <code>HttpClientFactory<\/code>. Microsoft has good documentation on different ways of using the factory. My favorite pattern is the <a data-type=\"link\" data-id=\"https:\/\/learn.microsoft.com\/en-us\/aspnet\/core\/fundamentals\/http-requests?view=aspnetcore-8.0#typed-clients\" href=\"https:\/\/learn.microsoft.com\/en-us\/aspnet\/core\/fundamentals\/http-requests?view=aspnetcore-8.0#typed-clients\">Typed client<\/a>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public class HttpService\n{\n    private readonly HttpClient _client;\n\n    public HttpService(HttpClient client)\n    {\n        _client = client;\n    }\n\n    public async Task&lt;string?&gt; GetContentAsync(string url)\n        =&gt; await _client.GetStringAsync(url);\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">And register the service as follows.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>builder.Services.AddHttpClient&lt;HttpService&gt;();<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">We can also utilize <code>Microsoft.Extensions.Http.Polly<\/code> to add some resiliency and exponential backoff on retries (as well as configure things like global message handlers, request headers and base URL on the client).<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>var httpRetryPolicy = HttpPolicyExtensions\n    .HandleTransientHttpError()\n    .WaitAndRetryAsync(3, retryAttempt\n        =&gt; TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));\n\nbuilder.Services.AddHttpClient&lt;HttpService&gt;()\n    .AddPolicyHandler(httpRetryPolicy);<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Voila! The above solution will avoid socket exhaustion and respond to DNS changes!<\/strong><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"wp-block-paragraph\">However, sometimes the only option is to build an HTTP service without any dependencies that will be used as a Singleton. I&#8217;ve had this happen to me when I was asked to write a class library that integrates with a third party API, which would be consumed by legacy .NET Framework application written in VB.NET. Luckily, there is an alternative solution to achieve the same <code>HttpClient<\/code> stability as above without using dependency injection and <code>HttpClientFactory<\/code>.<\/p>\n\n\n\n<script src=\"https:\/\/gist.github.com\/MechStar\/fbeba07819944407c47ea201438945fa.js\"><\/script>\n\n\n\n<p class=\"wp-block-paragraph\">NOTE: The class is <code>sealed<\/code> because we did not implement <a href=\"https:\/\/learn.microsoft.com\/en-us\/dotnet\/standard\/garbage-collection\/implementing-dispose#implement-the-dispose-pattern\">Dispose pattern<\/a> to allow for safe inheritance.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The above alternative will also avoid socket exhaustion and respond to DNS changes, when used as a long-lived Singleton!<\/strong><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity is-style-dots\"\/>\n\n\n","protected":false},"excerpt":{"rendered":"<p>In this post I would like to discuss some of the good and bad ways to use the HttpClient. I am going to start from the basics. What is the problem with the following code? Problem is that the HttpClient is not being properly disposed, as it implements IDisposable interface. So, let&#8217;s do it. Right? [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"footnotes":""},"categories":[1],"tags":[6,7,3,4,8,5],"class_list":["post-13","post","type-post","status-publish","format-standard","hentry","category-net","tag-dependency-injection","tag-exponential-backoff","tag-httpclient","tag-httpclientfactory","tag-idisposable","tag-polly"],"_links":{"self":[{"href":"https:\/\/denispavlov.net\/Blog\/wp-json\/wp\/v2\/posts\/13","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/denispavlov.net\/Blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/denispavlov.net\/Blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/denispavlov.net\/Blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/denispavlov.net\/Blog\/wp-json\/wp\/v2\/comments?post=13"}],"version-history":[{"count":39,"href":"https:\/\/denispavlov.net\/Blog\/wp-json\/wp\/v2\/posts\/13\/revisions"}],"predecessor-version":[{"id":160,"href":"https:\/\/denispavlov.net\/Blog\/wp-json\/wp\/v2\/posts\/13\/revisions\/160"}],"wp:attachment":[{"href":"https:\/\/denispavlov.net\/Blog\/wp-json\/wp\/v2\/media?parent=13"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/denispavlov.net\/Blog\/wp-json\/wp\/v2\/categories?post=13"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/denispavlov.net\/Blog\/wp-json\/wp\/v2\/tags?post=13"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}