tozevv
tozevv

Reputation: 784

.Net WebRequest Timeout versus TCP Timeout

Question

How to configure the TCP timeout for a single WebRequest?

Context

According to the docs, WebRequest.Timeout:

The length of time, in milliseconds, until the request times out, or the value Timeout.Infinite to indicate that the request does not time out. The default value is defined by the descendant class.

Requesting web resource from an non-existent endpoint (IIS without service bindings on the requested port) fails with a different and constant timeout of about 21 seconds.

According to this serverfault answer this appears to be a connect TCP timeout.

Sample Code

 public static async Task<string> WebRequestToString(string requestUri, int timeoutMilliseconds)
 {
     var request = WebRequest.Create(requestUri) as HttpWebRequest;
     request.KeepAlive = false;
     request.Timeout = timeoutMilliseconds;
     request.ReadWriteTimeout = timeoutMilliseconds;
     using (var response = await request.GetResponseAsync() as HttpWebResponse)
     {
         // Get the response stream
         using (var reader = new StreamReader(response.GetResponseStream()))
         {
             var responseBody = await reader.ReadToEndAsync();
             return responseBody;
         }
     }
 }

 static void Main(string[] args)
 {
     string uri = "http://10.15.1.24:8081/thiservicedoesnotexist/";
     int timeoutMilliseconds = 10;

     Stopwatch sw = new Stopwatch();
     sw.Start();
     try
     {
         WebRequestToString(uri, timeoutMilliseconds).Wait();
     }
     catch (AggregateException ex)
     {
         Console.WriteLine(ex.ToString());
     }
     sw.Stop();

     Console.WriteLine("Elaped {0}ms", sw.ElapsedMilliseconds);
     Console.ReadKey();
 }

Example Output

  System.AggregateException: One or more errors occurred. ---> System.Net.WebExcep
  tion: Unable to connect to the remote server ---> System.Net.Sockets.SocketExcep
  tion: A connection attempt failed because the connected party did not properly r
  espond after a period of time, or established connection failed because connecte
  d host has failed to respond 10.15.1.24:8081
     at System.Net.Sockets.Socket.EndConnect(IAsyncResult asyncResult)
     at System.Net.ServicePoint.ConnectSocketInternal(Boolean connectFailure, Sock
  et s4, Socket s6, Socket& socket, IPAddress& address, ConnectSocketState state,
  IAsyncResult asyncResult, Exception& exception)
     --- End of inner exception stack trace ---
     at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
     at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar,
  Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchron
  ization)
  --- End of stack trace from previous location where exception was thrown ---
     at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
     at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
  ification(Task task)
     at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
     at TimeoutTests.Program.<WebRequestToString>d__0.MoveNext() in \\vmware-host\
  shared folders\Documents\Visual Studio 2012\Projects\TimeoutTests\TimeoutTests\P
  rogram.cs:line 20
     --- End of inner exception stack trace ---
     at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceled
  Exceptions)
     at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationTo
  ken cancellationToken)
     at System.Threading.Tasks.Task.Wait()
     at TimeoutTests.Program.Main(String[] args) in \\vmware-host\shared folders\D
  ocuments\Visual Studio 2012\Projects\TimeoutTests\TimeoutTests\Program.cs:line 4
  0
  ---> (Inner Exception #0) System.Net.WebException: Unable to connect to the remo
  te server ---> System.Net.Sockets.SocketException: A connection attempt failed b
  ecause the connected party did not properly respond after a period of time, or e
  stablished connection failed because connected host has failed to respond 10.15.
  1.24:8081
     at System.Net.Sockets.Socket.EndConnect(IAsyncResult asyncResult)
     at System.Net.ServicePoint.ConnectSocketInternal(Boolean connectFailure, Sock
  et s4, Socket s6, Socket& socket, IPAddress& address, ConnectSocketState state,
  IAsyncResult asyncResult, Exception& exception)
     --- End of inner exception stack trace ---
     at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
     at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar,
  Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchron
  ization)
  --- End of stack trace from previous location where exception was thrown ---
     at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
     at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
  ification(Task task)
     at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
     at TimeoutTests.Program.<WebRequestToString>d__0.MoveNext() in \\vmware-host\
  shared folders\Documents\Visual Studio 2012\Projects\TimeoutTests\TimeoutTests\P
  rogram.cs:line 20<---

  Elaped 21169ms

Upvotes: 3

Views: 918

Answers (2)

tozevv
tozevv

Reputation: 784

I ended up fixing this by creating a GetResponseAsync method extension based on approach and seems to work fine:

public static class WebRequestExtensions
{
    public static async Task<WebResponse> GetResponseAsyncWithTimeout(this WebRequest request)
    {
        var timeoutCancellationTokenSource = new CancellationTokenSource();

        var responseTask = request.GetResponseAsync();

        var completedTask = await Task.WhenAny(responseTask, Task.Delay(request.Timeout, timeoutCancellationTokenSource.Token));
        if (completedTask == responseTask)
        {
            timeoutCancellationTokenSource.Cancel();
            return await responseTask;
        }
        else
        {
            request.Abort();
            throw new TimeoutException("The operation has timed out.");
        }
    }
}

Upvotes: 1

Philip Stuyck
Philip Stuyck

Reputation: 7467

I am very doubtful that this timeout is actually the same as the TCP connection timeout. The documentation of the webrequest timeout is as follows :

The length of time, in milliseconds, until the request times out, or the value Timeout.Infinite to indicate that the request does not time out. The default value is defined by the descendant class.

That is a timeout on the level of your request, ie HTTP. It might be that a connection to the server is establised but that creating the response takes a long time. Also that time can time out.

How the webrequest handles this and maps it on TCP is implementation dependent. A TCP timeout for connection establishment cannot be infinite.

I also read the following : The Timeout property indicates the length of time, in milliseconds, until the request times out and throws a WebException. The Timeout property affects only synchronous requests made with the GetResponse method. To time out asynchronous requests, use the Abort method.

You are asynchronous!

Upvotes: 2

Related Questions