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?
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.
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.
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:
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.
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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With