Search code examples
c#.nethttpwebrequestasync-awaitportable-class-library

How to define a more aggressive timeout for HttpWebRequest?


Inside a Portable Class Library, I've the following method which post data to a specific Url. The method works great. However I'd like to specify a more aggressive timeout (the default is 100 seconds).

Considering that there's no Timeout property on the HttpWebRequest class from the Portable Class Library, how can I make sure that the call is abandoned if it takes longer than a few seconds?

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());
}

Solution

  • Below code either will return a HttpWebResponse or null if timed out.

    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--

    Creating an extension method would be even better

    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);
            });
        }
    }
    

    Usage would be:

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

    --EDIT 2--

    Another way of doing it

    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);
    }