Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HttpClient in using statement causes Task cancelled

I created a FileResult : IHttpActionResult webapi return type for my api calls. The FileResult downloads a file from another url and then returns the stream to the client.

Initially my code had a using statement like below:

public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
    try
    {
        HttpResponseMessage response;
        using (var httpClient = new HttpClient())
        {

            response = new HttpResponseMessage(HttpStatusCode.OK)
            {
                Content = new System.Net.Http.StreamContent(
                                    await httpClient.GetStreamAsync(this.filePath))
            };
        }
        return response;
    }
    catch (WebException exception)
    {...}
}

However this would intermittently cause a TaskCanceledException. I know that if the HttpClient is disposed before the asychronous call is finished the Task's state will change to canceled. However since I use an await in: Content = new System.Net.Http.StreamContent(await httpClient.GetStreamAsync(this.filePath)) that should prevent the HttpClient from being disposed off in the middle of the task completion.

Why does that task get canceled? It is not because of a timeout since this has happened on the smallest requests and doesn't always occur on large requests.

When I removed the using statement the code worked properly:

public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
    try
    {
        HttpResponseMessage response;
        var httpClient = new HttpClient();

        response = new HttpResponseMessage(HttpStatusCode.OK)
        {
            Content = new System.Net.Http.StreamContent(
                                await httpClient.GetStreamAsync(this.filePath))
        };
        return response;
    }
    catch (WebException exception)
    {...}
}

Any idea why the using caused the issue?

like image 405
Rafi Avatar asked Oct 13 '15 06:10

Rafi


People also ask

Why am I getting a taskcanceledexception in httpclient?

There's 2 likely reasons that a TaskCanceledException would be thrown: Something called Cancel () on the CancellationTokenSource associated with the cancellation token before the task completed. The request timed out, i.e. didn't complete within the timespan you specified on HttpClient.Timeout.

How to tell if a httpclient request was canceled or not?

So, there’s no way to tell from the exception if the request was actually canceled, or if a timeout occurred. Fortunately, thanks to HttpClient ’s flexibility, it’s quite easy to make up for this design flaw.

What does it mean when httpclient timeout a request?

The request timed out, i.e. didn't complete within the timespan you specified on HttpClient.Timeout. My guess is it was a timeout. (If it was an explicit cancellation, you probably would have figured that out.) You can be more certain by inspecting the exception:

How many httpclient requests can I reuse?

You should try to reuse a single HttpClient as much as possible for multiple requests. Without seeing the rest of your code, my guess is that your tasks are being canceled because they are timing out.


1 Answers

I know that if the HttpClient is disposed before the asychronous call is finished the Task's state will change to canceled. However since I use an await in: Content = new System.Net.Http.StreamContent(await httpClient.GetStreamAsync(this.filePath)) that should prevent the HttpClient from being disposed off in the middle of the task completion.

But what does that task do? It gets the stream. So, your code ends up with a Stream that may or may not be completely read when it closes the HttpClient.

HttpClient is specifically designed for reuse (and simultaneous use), so I recommend removing the using completely and moving the HttpClient declaration to a static class member. But if you want to close and reopen the clients, you should be able to get it working by reading the stream entirely into memory before closing the HttpClient.

like image 139
Stephen Cleary Avatar answered Sep 24 '22 13:09

Stephen Cleary