HttpClient
имеет встроенную функцию тайм-аута (несмотря на то, что она является асинхронной, то есть таймауты можно считать ортогональными для функции HTTP-запроса и, таким образом, обрабатываться универсальными асинхронными утилитами, но это в стороне), и когда тайм-аут срабатывает, ll throw a TaskCanceledException
(завернутый в AggregateException
).
TCE содержит a CancellationToken
, который равен CancellationToken.None
.
Теперь, если я предоставляю HttpClient
своим CancellationToken
и использую это, чтобы отменить операцию до ее окончания (или таймаута), я получаю то же самое TaskCanceledException
, снова с CancellationToken.None
.
Есть ли еще способ, , посмотрев только на исключение, брошенное, чтобы выяснить, отменяет ли тайм-аут запрос, не делая для моего собственного CancellationToken
доступным для кода, который проверяет исключение?
P.S. Может ли это быть ошибкой, а CancellationToken
каким-то образом неправильно зафиксировано на CancellationToken.None
? В аннулированном использовании пользовательского случая CancellationToken я бы ожидал, что TaskCanceledException.CancellationToken
будет соответствовать этому пользовательскому токену.
Edit
Чтобы сделать проблему более понятной, с доступом к исходному CancellationTokenSource
, легко отличить таймаут и отмену пользователя:
origCancellationTokenSource.IsCancellationRequested == true
Получение CancellationToken
из исключения, но дает неправильный ответ:
((TaskCanceledException) e.InnerException).CancellationToken.IsCancellationRequested == false
Здесь минимальный пример, из-за популярного спроса:
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);
}