73

In order to reuse open TCP connections with HttpClient you have to share a single instance for all requests.

This means that we cannot simply instantiate HttpClient with different settings (e.g. timeout or headers).

How can we share the connections and use different settings at the same time? This was very easy, in fact the default, with the older HttpWebRequest and WebClient infrastructure.

Note, that simply setting HttpClient.Timeout before making a request is not thread safe and would not work in a concurrent application (e.g. an ASP.NET web site).

4
  • 1
    Which settings are you looking to vary on the same instance other than Timeout? Things like headers are settable per request, and varying BaseAddress doesn't make much sense because each different one will require a new connection anyway. Practically speaking, Timeout might be the only property you'd want to vary on the same instance. I can show you how to use a different timeout per request if that answers the question. Commented Oct 22, 2017 at 14:08
  • @ToddMenier that's a good point. That would answer the question, yes! Commented Oct 22, 2017 at 14:58
  • It should be clarified that reusing HttpClient means you are reusing a single TCP connection. There is no "pooling" involved here. Commented Oct 24, 2017 at 17:37
  • @ToddMenier I'm pretty sure that's not true. This also would imply that parallel requests on the same HttpClient object are not executed in parallel which I am quite sure as well is not the case. Commented Nov 14, 2017 at 18:25

3 Answers 3

119

Under the hood, HttpClient just uses a cancellation token to implement the timeout behavior. You can do the same directly if you want to vary it per request:

using var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromSeconds(30));
await httpClient.GetAsync("http://www.google.com", cts.Token);

Note that the default timeout for HttpClient is 100 seconds, and the request will still be canceled at that point even if you've set a higher value at the request level. To fix this, set a "max" timeout on the HttpClient, which can be infinite:

httpClient.Timeout = System.Threading.Timeout.InfiniteTimeSpan;
Sign up to request clarification or add additional context in comments.

2 Comments

As was mentioned in another answer, this can only reduce the time of cancellation. Therefore, if you plan on using this method, it might be best to set the default timeout to infinite (or some other amount you can be happy with), and plan on providing your own cancellation token to each and every request.
@Todd Menier: I follow your answer for a similar problem but I cannot solve it, maybe you can help me: stackoverflow.com/questions/54326681/…
15

The accepted answer is great, but I wanted to give another scenario for anyone looking for this in the future. In my case, I was already using a CancellationTokenSource that would cancel its token when the user chose to cancel. So in that case you can still use this technique by using the CreateLinkedTokenSource method of CancellationTokenSource. So in my scenario the http operation will cancel either by the timeout or the user's intervention. Here's a sample:

public async static Task<HttpResponseMessage> SendRequest(CancellationToken cancellationToken)
{
    var ctsForTimeout = new CancellationTokenSource();
    ctsForTimeout.CancelAfter(TimeSpan.FromSeconds(5));
    var cancellationTokenForTimeout = ctsForTimeout.Token;

    using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, cancellationTokenForTimeout))
    {
        try
        {
            return await httpClient.GetAsync("http://asdfadsf", linkedCts.Token);
        }
        catch
        {
            //just for illustration purposes
            if (cancellationTokenForTimeout.IsCancellationRequested)
            {
                Console.WriteLine("timeout");
            }
            else if (cancellationToken.IsCancellationRequested)
            {
                Console.WriteLine("other cancellation token cancelled");
            }
            throw;
        }
    }
}

Comments

-1

If you're already using a CancellationToken perhaps from an async API endpoint, you can combine it with another CancellationToken using CancellationTokenSource.CreateLinkedTokenSource.

Here's an example:

public async Task<HttpResponseMessage> SendRequestWithTimeout(HttpRequestMessage request, Timespan timeout, CancellationToken ct){
    var timeoutCt = new CancellationTokenSource(timeout).Token;
    var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(ct, timeoutCt).Token;
    return await httpClient.SendAsync(request, linkedCts);
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.