如果您查看内部HttpClient.SendAsync
(这是用于发送所有请求的内部方法),您会发现Task
正在创建的是一个简单的TaskCompletionSource<HttpResponseMessage>
. 在调用方法内部,它设置this.SetTaskFaulted
了多次,但总是在一个if-else
块内。可能发生的情况是,当设置异常时,它可能会引发另一个异常:SetTaskFaulted
private void SetTaskFaulted(HttpRequestMessage request, CancellationTokenSource cancellationTokenSource, TaskCompletionSource<HttpResponseMessage> tcs, Exception e, TimerThread.Timer timeoutTimer)
{
this.LogSendError(request, cancellationTokenSource, "SendAsync", e);
tcs.TrySetException(e);
HttpClient.DisposeCancellationTokenAndTimer(cancellationTokenSource, timeoutTimer);
}
DisposeCancellationTokenAndTimer
内部配置CancellationToken
,finally
块内部配置定时器:
private static void DisposeCancellationTokenAndTimer(CancellationTokenSource cancellationTokenSource, TimerThread.Timer timeoutTimer)
{
try
{
cancellationTokenSource.Dispose();
}
catch (ObjectDisposedException)
{
}
finally
{
HttpClient.DisposeTimer(timeoutTimer);
}
}
计时器可能会从其Dispose
方法中抛出异常。虽然我确信这是非常罕见的。
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken)
{
if (request == null)
{
throw new ArgumentNullException("request");
}
this.CheckDisposed();
HttpClient.CheckRequestMessage(request);
this.SetOperationStarted();
this.PrepareRequestMessage(request);
CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, this.pendingRequestsCts.Token);
TimerThread.Timer timeoutTimer = this.SetTimeout(linkedCts);
TaskCompletionSource<HttpResponseMessage> tcs = new TaskCompletionSource<HttpResponseMessage>();
try
{
base.SendAsync(request, linkedCts.Token).ContinueWithStandard(delegate(Task<HttpResponseMessage> task)
{
try
{
this.DisposeRequestContent(request);
if (task.IsFaulted)
{
this.SetTaskFaulted(request, linkedCts, tcs, task.Exception.GetBaseException(), timeoutTimer);
}
else
{
if (task.IsCanceled)
{
this.SetTaskCanceled(request, linkedCts, tcs, timeoutTimer);
}
else
{
HttpResponseMessage result = task.Result;
if (result == null)
{
this.SetTaskFaulted(request, linkedCts, tcs, new InvalidOperationException(SR.net_http_handler_noresponse), timeoutTimer);
}
else
{
if (result.Content == null || completionOption == HttpCompletionOption.ResponseHeadersRead)
{
this.SetTaskCompleted(request, linkedCts, tcs, result, timeoutTimer);
}
else
{
this.StartContentBuffering(request, linkedCts, tcs, result, timeoutTimer);
}
}
}
}
}
catch (Exception ex)
{
if (Logging.On)
{
Logging.Exception(Logging.Http, this, "SendAsync", ex);
}
tcs.TrySetException(ex);
}
});
}
catch
{
HttpClient.DisposeTimer(timeoutTimer);
throw;
}
return tcs.Task;