L'idea è semplice, ma l'implementazione presenta alcune interessanti sfumature. Questa è la firma del metodo di estensione che vorrei implementare in .NET 4.Implementazione del metodo di estensione WebRequest.GetResponseAsync con supporto per CancellationToken
public static Task<WebResponse> GetResponseAsync(this WebRequest request, CancellationToken token);
Ecco la mia implementazione iniziale. Da quello che ho letto, la richiesta web potrebbe essere cancelled due to a timeout. Oltre al supporto descritto in questa pagina, desidero chiamare correttamente lo request.Abort()
se viene richiesta la cancellazione tramite lo CancellationToken
.
public static Task<WebResponse> GetResponseAsync(this WebRequest request, CancellationToken token)
{
if (request == null)
throw new ArgumentNullException("request");
return Task.Factory.FromAsync<WebRequest, CancellationToken, WebResponse>(BeginGetResponse, request.EndGetResponse, request, token, null);
}
private static IAsyncResult BeginGetResponse(WebRequest request, CancellationToken token, AsyncCallback callback, object state)
{
IAsyncResult asyncResult = request.BeginGetResponse(callback, state);
if (!asyncResult.IsCompleted)
{
if (request.Timeout != Timeout.Infinite)
ThreadPool.RegisterWaitForSingleObject(asyncResult.AsyncWaitHandle, WebRequestTimeoutCallback, request, request.Timeout, true);
if (token != CancellationToken.None)
ThreadPool.RegisterWaitForSingleObject(token.WaitHandle, WebRequestCancelledCallback, Tuple.Create(request, token), Timeout.Infinite, true);
}
return asyncResult;
}
private static void WebRequestTimeoutCallback(object state, bool timedOut)
{
if (timedOut)
{
WebRequest request = state as WebRequest;
if (request != null)
request.Abort();
}
}
private static void WebRequestCancelledCallback(object state, bool timedOut)
{
Tuple<WebRequest, CancellationToken> data = state as Tuple<WebRequest, CancellationToken>;
if (data != null && data.Item2.IsCancellationRequested)
{
data.Item1.Abort();
}
}
La mia domanda è semplice ma impegnativa. Questa implementazione si comporterà effettivamente come previsto se utilizzata con TPL?
@ 280Z28 Grazie - ho scritto questo senza VS, quindi non potuto effettivamente provare tutto;) @ –
280Z28 Sì - Come ho couldn'te provarlo, non mi rendevo conto che 'Abort' sarebbe ancora attivare la richiamata (ha senso che lo faccia). Ciò farà sì che il comportamento sia un po 'spento, ma comunque funzionale. (Riceverai una WebException invece della corretta cancellazione). –
Ho modificato il post in 1) descrivo correttamente i 2 errori principali nella mia domanda originale (incluso 1 nuovo), 2) contiene l'ultimo codice di lavoro e 3) contengono una classe di test che mostra un comportamento corretto in un caso di successo e 3 diversi casi di fallimento (cancellazione, timeout e errore 404). –