c# - HttpClient with HttpClientHandler cancellation not working

Viewed 930

I am facing an issue with HttpClient (.NETStandard v2.1, System.Net.Http, targeting mono runtime). I want to set timeout for HttpClient per request by passing a CancellationToken in SendAsync.

It is working properly when using the parameterless constructor of HttpClient, but it is ignored when passing to the HttpClient constructor an instance of HttpClientHandler. The operation is cancelled after 75 secs.

To illustrate:

public static async Task<HttpResponseMessage> Send()
{
    var req = new HttpRequestMessage(HttpMethod.Get, new Uri("someURL"));
    var handler = new HttpClientHandler{ CookieContainer = new CookieContainer() };  
    /* var client = new HttpClient(); <--- This is working */          
    var client = new HttpClient(handler);
    var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
    cts.Token.Register(() => Debug.WriteLine("TASK CANCELLED"));
    return await client.SendAsync(req, cts.Token);
}

After 5 secs the debug output writes "TASK CANCELLED" but the SendAsync continues for a total of 75 secs. If i use the parameterless constructor the SendAsync cancels after 5 secs.

I need the HttpClientHandler to use the CookieContainer property. What am i missing here?


EDIT

After further investigation and the hints of @Lasse Vågsæther Karlsen i figured out the following:

  1. .NET Core 3 runtime

    • If the url is not reachable or internet connection is down the HttpClient throws HttpRequestException after 3 secs
    • Otherwise it cancels as expected after 5 secs
  2. Mono runtime (Xamarin.Forms project)

    • If the url is not reachable or internet connection is down the HttpClient throws OperationCanceledException after 75 secs
    • Otherwise it cancels as expected after 5 secs

Maybe it is related to this.

Still an open issue though...

1 Answers

The simplest method I found was using the Polly library and wrapping the methods that would not honor the CancellationToken. You don't even need a CancellationToken. Example:

var policy = Policy.TimeoutAsync(TimeSpan.FromMilliseconds(3000),
    TimeoutStrategy.Pessimistic,
    (context, timespan, task) => throw new Exception("Cannot connect to server."));

await policy.ExecuteAsync(async () =>
{
    var httpClient = new HttpClient();
    var response = await httpClient.PostAsync(...);
    response.EnsureSuccessStatusCode();
    ...
});
Related