2012-12-12 11 views
8

All'interno di una libreria di classi portatili, ho il seguente metodo che invia i dati a un URL specifico. Il metodo funziona alla grande. Tuttavia mi piacerebbe specificare un timeout più aggressivo (il valore predefinito è 100 secondi).Come definire un timeout più aggressivo per HttpWebRequest?

Considerando che non esiste una proprietà Timeout sulla classe HttpWebRequest dalla libreria delle classi portatili, come posso verificare che la chiamata venga abbandonata se impiega più di qualche secondo?

public async Task<HttpResponse> PostAsync(Uri uri, string data) 
{ 
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri); 
    request.Method = "POST"; 
    request.ContentType = "application/x-www-form-urlencoded"; 

    using (Stream requestStream = await request.GetRequestStreamAsync()) 
    { 
     byte[] postBytes = Encoding.UTF8.GetBytes(data); 
     requestStream.Write(postBytes, 0, postBytes.Length); 
    } 

    HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync(); 
    return new HttpResponse(response.StatusCode, await new StreamReader(response.GetResponseStream()).ReadToEndAsync()); 
} 
+0

HttpWebRequest.Timeout = 20000; ? questo è abbastanza intelligente? –

risposta

20

Il codice riportato di seguito restituirà un HttpWebResponse o null se scaduto.

HttpWebResponse response = await TaskWithTimeout(request.GetResponseAsync(), 100); 
if(response != null) 
{ 
    .... 
} 

Task<HttpWebResponse> TaskWithTimeout(Task<WebResponse> task, int duration) 
{ 
    return Task.Factory.StartNew(() => 
    { 
     bool b = task.Wait(duration); 
     if (b) return (HttpWebResponse)task.Result; 
     return null; 
    }); 
} 

--EDIT--

Creazione di un metodo di estensione sarebbe ancora meglio

public static class SOExtensions 
{ 
    public static Task<T> WithTimeout<T>(this Task<T> task, int duration) 
    { 
     return Task.Factory.StartNew(() => 
     { 
      bool b = task.Wait(duration); 
      if (b) return task.Result; 
      return default(T); 
     }); 
    } 
} 

Uso sarebbe:

var response = (HttpWebResponse)await request.GetResponseAsync().WithTimeout(1000); 

--edit 2--

Un altro modo di farlo

public async static Task<T> WithTimeout<T>(this Task<T> task, int duration) 
{ 
    var retTask = await Task.WhenAny(task, Task.Delay(duration)) 
          .ConfigureAwait(false); 

    if (retTask is Task<T>) return task.Result; 
    return default(T); 
} 
+0

Wow! Idea interessante! Mi piace perché è elegante e relativamente facile da capire. Ma mi chiedo se task.Wait è un'operazione di blocco (cioè un thread verrà messo in attesa fino a quando non ritorna). – Martin

+1

@Martin Sì, si tratta di un'operazione di blocco, ma viene eseguita in un'altra attività (vedere: 'Task.Factory.StartNew') e non sarà diversa dal richiamo di' await request.GetResponseAsync() ' –

+0

Accetto! È solo un altro thread sul ThreadPool. Mi chiedo se potremmo renderlo non-bloccante. Bloccando o no, questa è una soluzione molto intelligente. – Martin

0
// Abort the request if the timer fires. 
private static void TimeoutCallback(object state, bool timedOut) { 
    if (timedOut) { 
     HttpWebRequest request = state as HttpWebRequest; 
      if (request != null) { 
       request.Abort(); 
     } 
    } 
} 

Sì, è la responsabilità dell'applicazione client di eseguire il proprio meccanismo di time-out. Puoi farlo dal codice sopra il quale imposta il timeout e usa il metodo ThreadPool.RegisterWaitForSingleObject. Vedi http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.abort.aspx per tutti i dettagli. In sostanza si interrompe da GetResponse, BeginGetResponse, EndGetResponse, GetRequestStream, BeginGetRequestStream o EndGetRequestStream.

Problemi correlati