HttpClient in using statement causes canceled task

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

My code initially had a using statement, as shown 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 periodically result in a TaskCanceledException . I know that if the HttpClient is deleted before the asynchronous call is completed, the status of the Task will be canceled. However, since I use the wait in: Content = new System.Net.Http.StreamContent(await httpClient.GetStreamAsync(this.filePath)) , which should prevent HttpClient from being deleted in the middle of the task completion.

Why is this task canceled? This is not due to a timeout, as this happened on the smallest requests and does not always happen on large requests.

When I removed the using statement, the code worked correctly:

 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 use caused the problem?

+7
c # asp.net-web-api using-statement task-parallel-library
source share
2 answers

I know that if the HttpClient is deleted before the asynchronous call completes, the Task state will be canceled. However, since I use the wait in: Content = new System.Net.Http.StreamContent (waiting for httpClient.GetStreamAsync (this.filePath)), which should prevent HttpClient from being deleted in the middle of the task completion.

But what does this task do? He receives a stream. Thus, your code ends with a Stream sign, which may or may not be fully read when it closes the HttpClient .

HttpClient specifically designed for reuse (and concurrent use), so I recommend completely removing using and moving the HttpClient to a member of the static class. But if you want to close and reopen clients, you can make it work by fully reading the stream in memory before closing HttpClient .

+9
source

I had a similar problem with exceptions thrown by Task. If you try to catch an AggregateException or you have the entire Exception block under your WebException , it may seem like you will catch it, with one exception to the entry that says: "The task was canceled"

I did some investigation and found that the AggregateException quite misleading, as described in various threads;

Setting HttpClient to too short a process failure timeout

How to find out when the HttpClient timeout?

Error in httpclientgetasync should throw webexception not taskcanceledexception

I ended up changing my code to set an explicit timeout (where asyncTimeoutInMins read from the app.config file);

  string jsonResponse = string.Empty; try { using (HttpClient httpClient = new HttpClient()) { httpClient.BaseAddress = new Uri(Properties.Settings.Default.MyWebService); httpClient.DefaultRequestHeaders.Accept.Clear(); httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); httpClient.Timeout = new TimeSpan(0, asyncTimeoutInMins, 0); HttpResponseMessage response; response = await httpClient.GetAsync("/myservice/resource"); // Check the response StatusCode if (response.IsSuccessStatusCode) { // Read the content of the response into a string jsonResponse = await response.Content.ReadAsStringAsync(); } else if (response.StatusCode == HttpStatusCode.Forbidden) { jsonResponse = await response.Content.ReadAsStringAsync(); Logger.Instance.Warning(new HttpRequestException(string.Format("The response StatusCode was {0} - {1}", response.StatusCode.ToString(), jsonResponse))); Environment.Exit((int)ExitCodes.Unauthorised); } else { jsonResponse = await response.Content.ReadAsStringAsync(); Logger.Instance.Warning(new HttpRequestException(string.Format("The response StatusCode was {0} - {1}", response.StatusCode.ToString(), jsonResponse))); Environment.Exit((int)ExitCodes.ApplicationError); } } } catch (HttpRequestException reqEx) { Logger.Instance.Error(reqEx); Console.WriteLine("HttpRequestException : {0}", reqEx.InnerException.Message); Environment.Exit((int)ExitCodes.ApplicationError); } catch (Exception ex) { Logger.Instance.Error(ex); throw; } return jsonResponse; 
+4
source

All Articles