Httpclient - a Task Was Cancelled

HttpClient - A task was cancelled?

There's 2 likely reasons that a TaskCanceledException would be thrown:

  1. Something called Cancel() on the CancellationTokenSource associated with the cancellation token before the task completed.
  2. 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:

try
{
var response = task.Result;
}
catch (TaskCanceledException ex)
{
// Check ex.CancellationToken.IsCancellationRequested here.
// If false, it's pretty safe to assume it was a timeout.
}

HttpClient - task was cancelled - How to get the exact error message?

The default HttpClient.Timeout value is 100 seconds (00:01:40). If you do a timestamp in your catch block you will notice that tasks begin to get canceled at exactly that time. Apparently there is a limited number of HTTP requests you can do per second, others get queued. Queued requests get canceled on timeout. Out of all 600k of tasks I personally got only 2500 successful, others got canceled.

I also find it unlikely, that you will be able to run the whole 600000 of tasks. Many network drivers let through high number of requests only for a small time, and reduce that number to a very low value after some time. My network card allowed me to send only 921 requests within 36 seconds and dropped that speed to only one request per second. At that speed it will take a week to complete all the tasks.

If you are able to bypass that limitation, make sure you build the code for 64-bit platform as the app is very hungry for memory.

HttpClient in using statement causes Task cancelled

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.

Handle A task was canceled exception

I'm 99% sure this error is due to a timeout, or the fact you don't actually await your Start method in MainAsync

I've addressed the timeout issue in the follow code along with some other small changes, which don't necessarily answer your question but hopefully help you nevertheless

class Program
{
private static HttpClient httpClient;

static void Main(string[] args)
{
httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("your base url");
// add any default headers here also
httpClient.Timeout = new TimeSpan(0, 2, 0); // 2 minute timeout

MainAsync().Wait();
}

static async Task MainAsync()
{
await new MyClass(httpClient).StartAsync();
}
}

What I've done here is move the HttpClient out from your Upload() method because this class is designed to be reused many times. I've passed the httpClient object to MyClass's constructor which you'll see in the next code snippet.

I've also changed MainAsync() to await the StartAsync (renamed from Start to StartAsync because it's convention to suffix async methods) because in your original code MainAsync() wasn't actually awaiting anything

As I mentioned in the comments, you can change MainAsync().Wait() to await MainAsync() if you change Main to be static async Task Main, which will require you to change your build language to C# 7.1 or above

public class MyClass
{
private Dictionary<string, string> myDictionary;
private readonly HttpClient httpClient;

public MyClass(HttpClient httpClient)
{
this.httpClient = httpClient;
}

public async Task StartAsync()
{
myDictionary = await UploadAsync("some file path");
}

public async Task<Dictionary<string, string>> UploadAsync(string filePath)
{
byte[] fileContents;
using (FileStream stream = File.Open(filePath, FileMode.Open))
{
fileContents = new byte[stream.Length];
await stream.ReadAsync(fileContents, 0, (int)stream.Length);
}

HttpRequestMessage requestMessage = new HttpRequestMessage();
// your request stuff here

HttpResponseMessage httpResponse = await httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseContentRead, CancellationToken.None);

// parse response and return the dictionary
}
}

In MyClass I've made the following changes

Added a HttpClient parameter to the class' constructor, so we can pass our global HttpClient object to this class for it to reuse (which is what we do in MainAsync())

As mentioned before, I've renamed Start and Upload to StartAsync and UploadAsync because it's good practice to suffix async methods with Async

Start was changed from void to a Task because you should only use async void for event handlers

I changed the way your file is read to also be async, because it seems wasteful to have an async method that then blocks the CPU waiting for File.ReadAllBytes to finish. Wherever possible you should use async/await for I/O.

HttpClient c# - A task was canceled at SendASync

You should await both the makeNetworkCallCheckResponseStatusAndExecuteCorrospondingAction method and the getResponseFromUrlAsync method. This means that you need to change the return type from void to Task:

public static async Task getResponseFromUrlAsync<T>(T payload, string url, Action<string> onSuccess, Action<string> onFailure)
{
string contentType = "application/json";
httpClient = new HttpClient();
httpClient.Timeout = TimeSpan.FromMinutes(30);
HttpRequestMessage requestMsg = new HttpRequestMessage();
requestMsg.RequestUri = new Uri(NetworkCallUrls.baseUri + url);
Utils.debugLog("Url", NetworkCallUrls.baseUri + url);

string auth = "Bearer " + Objects.GlobalVars.GetValue<string>("access_token"); // //"x1VwaR1otS66ZCTlgtv3X9aaSNpDOn"; //
httpClient.DefaultRequestHeaders.Add("Authorization", auth);
requestMsg.Method = HttpMethod.Post;

requestMsg.Content = new StringContent(
Utils.stringifyData(payload),
Encoding.UTF8,
contentType);

httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));//ACCEPT header

await makeNetworkCallCheckResponseStatusAndExecuteCorrospondingAction(requestMsg, onSuccess, onFailure, progressBarStatus)
.ConfigureAwait(false);
}

internal static void disposeConnection(HttpClient httpClient)
{
httpClient.Dispose();
httpClient = null;
}

private static async Task makeNetworkCallCheckResponseStatusAndExecuteCorrospondingAction(
HttpRequestMessage requestMsg, Action<string> onSuccess,
Action<string> onFailure, Action<bool> progressBarStatus)
{
Utils.debugLog("IN MAKE NETWORK CALL 1");
HttpResponseMessage response = await httpClient.SendAsync(requestMsg).ConfigureAwait(false);
Utils.debugLog("IN MAKE NETWORK CALL 2");
ResponseStatus responseStatus = checkResponseStatusAndExecuteActionAccordinglyAsync(response);
Utils.debugLog("IN MAKE NETWORK CALL 3");
if (responseStatus.isSuccess)
{
Utils.debugLog("IN MAKE NETWORK CALL 4");
string responseString = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
Utils.debugLog("IN MAKE NETWORK CALL 5");
onSuccess(responseString);
Utils.debugLog("IN MAKE NETWORK CALL 6");
}
else
{
Utils.debugLog("IN MAKE NETWORK CALL 7");
onFailure(responseStatus.failureResponse);
Utils.debugLog("IN MAKE NETWORK CALL 8");
}
Utils.debugLog("IN MAKE NETWORK CALL 9");
disposeConnection(httpClient);
Utils.debugLog("IN MAKE NETWORK CALL 10");
}

...and await the method when you call it:

await getResponseFromUrlAsync<..>(...);


Related Topics



Leave a reply



Submit