HttpClient has a built-in timeout function (although it is asynchronous, that is, timeouts can be considered orthogonal for the HTTP request function and, thus, handled by universal asynchronous utilities, but this is aloof), and when the timeout is triggered , ll throw a TaskCanceledException (wrapped in an AggregateException ).
TCE contains a CancellationToken , which is equal to CancellationToken.None .
Now, if I provide an HttpClient my CancellationToken and use it to cancel the operation before its end (or timeout), I get the same TaskCanceledException , again with CancellationToken.None .
Is there another way, by looking only at the exception thrown to find out if the timeout cancels the request without making my CancellationToken available to the code that checks for the exception?
PS Could this be a mistake, and the CancellationToken somehow incorrectly fixed to the CancellationToken.None ? In the canceled use of the user case CancellationToken, I would expect TaskCanceledException.CancellationToken to match this user token.
Edit To make the problem more understandable, with access to the original CancellationTokenSource , it is easy to distinguish the timeout and user cancellation:
origCancellationTokenSource.IsCancellationRequested == true
Getting the CancellationToken from the exception, but gives the wrong answer:
((TaskCanceledException) e.InnerException) .CancellationToken.IsCancellationRequested == false
Here's a minimal example , due to popular demand:
public void foo() { makeRequest().ContinueWith(task => { try { var result = task.Result; // do something with the result; } catch (Exception e) { TaskCanceledException innerException = e.InnerException as TaskCanceledException; bool timedOut = innerException != null && innerException.CancellationToken.IsCancellationRequested == false; // Unfortunately, the above .IsCancellationRequested // is always false, no matter if the request was // cancelled using CancellationTaskSource.Cancel() // or if it timed out } }); } public Task<HttpResponseMessage> makeRequest() { var cts = new CancellationTokenSource(); HttpClient client = new HttpClient() { Timeout = TimeSpan.FromSeconds(10) }; HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "url"); passCancellationTokenToOtherPartOfTheCode(cts); return client.SendAsync(httpRequestMessage, cts.Token); }