1

I have a piece of code for downloading content from a webpage, as follows:

public class Downloader
{
    private readonly HttpClient _client = new HttpClient();

    public async Task<(HttpStatusCode, string)> GetContent(Uri site)
    {
        HttpStatusCode status = HttpStatusCode.Unused;
        try
        {
            var response = await _client.GetAsync(site);
            status = response.StatusCode;

            var content = await response.Content.ReadAsStringAsync();

            return (status, content);
        }
        catch (Exception e)
        {
            ...
        }
    }
}

Notice that I have one readonly instance of the HttpClient class. I have a single instance of Downloader for my application. Is the initialization of HttpClient shown above, a more efficient way to initialize HttpClient than creating an instance per call to GetContent, as follows:

public class Downloader
{
    public async Task<(HttpStatusCode, string)> GetContent(Uri site)
    {
        HttpStatusCode status = HttpStatusCode.Unused;
        try
        {
            using var client = new HttpClient();
            var response = await client.GetAsync(site);
            status = response.StatusCode;

            var content = await response.Content.ReadAsStringAsync();

            return (status, content);
        }
        catch (Exception e)
        {
            ...
        }
    }
}

Thanks in advance for advice!

Note: I'm not interested in HttpClientFactory for this example, or dependency injection.

Rob
  • 6,819
  • 17
  • 71
  • 131
  • I've had problems before like socket exhaustion - when I say "more efficient" - I'm not necessarily talking about memory footprint but more underlying efficiency. Probably not clear in the above. I know MS built the `HttpClientFactory` to circumvent some of these inefficiencies, I'm just not clear on what they are. – Rob Apr 12 '21 at 19:38
  • 5
    First one is not disposed, so it is probably OK if you have your downloader as singleton. Second one is bad - you will suffer from socket exhaustion. But you can create your own factory and cache HttpClientHandler (it is main reason of socket exhaustion and can be safelly passed into multiplke HttpClient) See more here - https://www.aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/ – eocron Apr 12 '21 at 19:39
  • Perfect - that's the type of detail I'm looking for - socket exhaustion. In that case, approach 1 would be more "efficient". – Rob Apr 12 '21 at 19:42
  • 1
    You can also completely disable SSL certificate checks, saves you up some initial connection time. This will allow man-in-the-middle attack, but it will be sufficient if your code is secured through obscurity. – eocron Apr 12 '21 at 19:45
  • This seems like a really useful and relevant link as well actually: https://stackoverflow.com/questions/18976042/httpclientfactory-create-vs-new-httpclient – Rob Apr 12 '21 at 19:51
  • If you have socket exhaustion, you should study these answers https://stackoverflow.com/questions/15705092/do-httpclient-and-httpclienthandler-have-to-be-disposed-between-requests. What's not clear from your question is the lifetime of the objects involved. How frequently are you creating instances and how long do you expect these instances to live? – Jasen Apr 12 '21 at 20:35
  • 1
    @Jasen, I think OP clearly specified lifetime - singleton. – eocron Apr 12 '21 at 20:42
  • The point is: what kind of application is this? Is it a daily process that fires off one request? Is it a web app that's waiting for download requests? Is it a multi-process app? – Jasen Apr 12 '21 at 20:59
  • 2
    @Jasen, class have self sufficient abstraction and lifetime (per appdomain), there is no need for clarity of dependant classes such as who will use it and how: datacenter, multi-process app, web app, console app, etc. You use net core newtonsoft.json/httpclient/other nugets daily, still it works same everywhere. – eocron Apr 12 '21 at 22:04

1 Answers1

1

A static HttpClient (or an instance field on a singleton, which is essentially the same thing) is the proper way to go for modern versions of .NET Core. The SocketsHttpHandler in .NET Core 2.1 and newer handles the DNS problems inherent in the singleton HttpClient approach.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810