Created
October 31, 2011 21:50
-
-
Save samoshkin/1329102 to your computer and use it in GitHub Desktop.
WCF-Web-API/SendAsync-nightmare
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
protected internal sealed override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, | |
CancellationToken cancellationToken) | |
{ | |
if (request == null) | |
{ | |
throw new ArgumentNullException("request", SR.net_http_handler_norequest); | |
} | |
// ProcessRequest() and ProcessResponse() are supposed to be fast, so we call ProcessRequest() on the same | |
// thread SendAsync() was invoked to avoid context switches. However, if ProcessRequest() throws, we have | |
// to catch the exception since the caller doesn't expect exceptions when calling SendAsync(): The | |
// expectation is that the returned task will get faulted on errors, but the async call to SendAsync() | |
// should complete. | |
TaskCompletionSource<HttpResponseMessage> tcs = new TaskCompletionSource<HttpResponseMessage>(); | |
try | |
{ | |
HttpRequestMessage newRequestMessage = ProcessRequest(request, cancellationToken); | |
Task<HttpResponseMessage> sendAsyncTask = base.SendAsync(newRequestMessage, cancellationToken); | |
// We schedule a continuation task once the inner handler completes in order to trigger the response | |
// processing method. ProcessResponse() is only called if the task wasn't canceled before. | |
sendAsyncTask.ContinueWith(task => | |
{ | |
if (task.IsFaulted) | |
{ | |
tcs.TrySetException(task.Exception.GetBaseException()); | |
return; | |
} | |
if (task.IsCanceled) | |
{ | |
tcs.TrySetCanceled(); | |
return; | |
} | |
if (task.Result == null) | |
{ | |
tcs.TrySetException(new InvalidOperationException(SR.net_http_handler_noresponse)); | |
return; | |
} | |
try | |
{ | |
HttpResponseMessage responseMessage = ProcessResponse(task.Result, cancellationToken); | |
tcs.TrySetResult(responseMessage); | |
} | |
catch (OperationCanceledException e) | |
{ | |
// If ProcessResponse() throws an OperationCanceledException check wheter it is related to | |
// the cancellation token we received from the user. If so, cancel the Task. | |
HandleCanceledOperations(cancellationToken, tcs, e); | |
} | |
catch (Exception e) | |
{ | |
tcs.TrySetException(e); | |
} | |
// We don't pass the cancellation token to the continuation task, since we want to get called even | |
// if the operation was canceled: We'll set the Task returned to the user to canceled. Passing the | |
// cancellation token here would result in the continuation task to not be called at all. I.e. we | |
// would never complete the task returned to the caller of SendAsync(). | |
}, TaskContinuationOptions.ExecuteSynchronously); | |
} | |
catch (OperationCanceledException e) | |
{ | |
HandleCanceledOperations(cancellationToken, tcs, e); | |
} | |
catch (Exception e) | |
{ | |
tcs.TrySetException(e); | |
} | |
return tcs.Task; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment