14

I'm currently using code that makes HTTP requests using the HttpClient class. Although you can specify a timeout for the request, the value applies to the entirety of the request (which includes resolving the host name, establishing a connection, sending the request and receiving the response).

I need a way to make requests fail fast if they cannot resolve the name or establish a connection, but I also sometimes need to receive large amounts of data, so cannot just reduce the timeout.

Is there a way to achieve this using either a built in (BCL) class or an alternative HTTP client stack?

I've looked briefly at RestSharp and ServiceStack, but neither seems to provide a timeout just for the connection part (but do correct me if I am wrong).

7
  • I am not sure if you looked at the following if so please ignore.. HttpWebRequest.Timeout Commented Nov 16, 2014 at 22:13
  • @DJKRAZE The Timeout on HttpWebRequest "applies to the entire request and response" (from the MSDN page). Commented Nov 16, 2014 at 22:17
  • Is changing the Timeout value after the connection has been established an option? Commented Nov 16, 2014 at 22:19
  • 1
    Why don't you just perform (asynchronous) DNS resolution beforehand, which is the main bugaboo? I would not try establishing connectivity on the port separately as well -- let the regular timeout take care of this, because a good firewall is indistinguishable from a slow server -- but the DNS resolution can just be factored out. Commented Nov 21, 2014 at 12:45
  • 1
    "if they cannot resolve the name or establish a connection" There are two parts to this A) The DNS resolve and B) the Socket open to that port. No DNS resolve usually is because 1) A DNS server or it's other neighbors cannot resolve the name or 2) The DNS server is down. The second part is a desire to send a socket open request, this is what a port scanner does, it see's if the socket (address/port pair) is responding. It does it simply by starting a session via a Socket.Open(). The client either responds or doesn't and the time it takes if you have the DNS resolve is minimal. Commented Nov 26, 2014 at 17:00

7 Answers 7

8
+100

You can use a Timer to abort the request if the connection take too much time. Add an event when the time is elapsed. You can use something like this:

static WebRequest request;
private static void sendAndReceive()
{
    // The request with a big timeout for receiving large amout of data
    request = HttpWebRequest.Create("http://localhost:8081/index/");
    request.Timeout = 100000;

    // The connection timeout
    var ConnectionTimeoutTime = 100;
    Timer timer = new Timer(ConnectionTimeoutTime);
    timer.Elapsed += connectionTimeout;
    timer.Enabled = true;

    Console.WriteLine("Connecting...");
    try
    {
        using (var stream = request.GetRequestStream())
        {
            Console.WriteLine("Connection success !");
            timer.Enabled = false;

            /*
             *  Sending data ...
             */
            System.Threading.Thread.Sleep(1000000);
        }

        using (var response = (HttpWebResponse)request.GetResponse())
        {
            /*
             *  Receiving datas...
             */
        }
    }
    catch (WebException e)
    {
        if(e.Status==WebExceptionStatus.RequestCanceled) 
            Console.WriteLine("Connection canceled (timeout)");
        else if(e.Status==WebExceptionStatus.ConnectFailure)
            Console.WriteLine("Can't connect to server");
        else if(e.Status==WebExceptionStatus.Timeout)
            Console.WriteLine("Timeout");
        else
            Console.WriteLine("Error");
    }
}

static void connectionTimeout(object sender, System.Timers.ElapsedEventArgs e)
{
    Console.WriteLine("Connection failed...");
    Timer timer = (Timer)sender;
    timer.Enabled = false;

    request.Abort();
}

Times here are just for example, you have to adjust them to your needs.

Sign up to request clarification or add additional context in comments.

2 Comments

You're the first with something that would actually solve the problem. It's not exactly pretty, but useful nonetheless!
You can specify AutoReset = false when initializing the timer to remove the timer.Enabled = false in the handler. Also, you can just make the Elapsed look like this: timer.Elapsed += (o, e) => { try { request?.Abort(); } catch { } }; Doing it inline lets you avoid the global request and extra handler method.
2

.NET's HttpWebRequest exposes 2 properties for specifying a Timeout for connecting with a remote HTTP Server:

  • Timeout - Gets or sets the time-out value in milliseconds for the GetResponse and GetRequestStream methods.
  • ReadWriteTimeout - The number of milliseconds before the writing or reading times out. The default value is 300,000 milliseconds (5 minutes).

The Timeout property is the closest to what you're after, but it does suggest that regardless of the Timeout value the DNS resolution may take up to 15 seconds:

A Domain Name System (DNS) query may take up to 15 seconds to return or time out. If your request contains a host name that requires resolution and you set Timeout to a value less than 15 seconds, it may take 15 seconds or more before a WebException is thrown to indicate a timeout on your request.

One way to prempt a lower timeout than 15s for DNS lookups is to lookup the hostname yourself, but many solutions requires P/Invoke to specify low-level settings.

Specifying timeouts in ServiceStack HTTP Clients

The underlying HttpWebRequest Timeout and ReadWriteTimeout properties can also be specified in ServiceStack's high-level HTTP Clients, i.e. in C# Service Clients with:

var client = new JsonServiceClient(BaseUri) {
    Timeout = TimeSpan.FromSeconds(30)
};

Or using ServiceStack's HTTP Utils with:

var timeoutMs = 30 * 1000;
var response = url.GetStringFromUrl(requestFilter: req => 
    req.Timeout = timeoutMs);

4 Comments

Thank you for your response. However, unless I misunderstand something, all you do is point out the limitations of HttpWebRequest. What I'm really looking for are alternatives that do not have these limitations.
@MortenMertner I'm suggesting out of the only Timeout options available, Timeout is the closest to what you're looking for. In addition all clients built on .NET's underlying HttpWebRequest is also only going to have the same API's at best as the only workaround for finer-grained control is to P/Invoke into Win32 system API's.
Right. I'm already using Timeout, but am not happy with it, so still looking for an alternative .NET library. I could end up writing one myself (using the system APIs), but that is more work than I can squeeze into the current cycle.
@MortenMertner Have you looked at using HttpWebRequest asynchronously and implementing your own timeout logic?
1

I believe RestSharp does have timeout properties in RestClient.

        var request = new RestRequest();
        var client = new RestClient
        {
            Timeout = timeout, //Timeout in milliseconds to use for requests made by this client instance
            ReadWriteTimeout = readWriteTimeout //The number of milliseconds before the writing or reading times out.
        };

        var response = client.Execute(request);
        //Handle response

3 Comments

RestSharp uses HttpWebRequest internally, so the Timeout behaves no differently there than it does with HttpClient.
@MortenMertner, HttpWebRequest has two different Timeouts, one Timeout is for entire connection life cycle and ReadWriteTimeout for Stream flush timeout. It is design flaw that HttpClient does not specify both, however HttpClient is asynchronous so you can and you should implement your own Cancellation Token Source to supply cancelling (timing out) of read/write stream operation.
@AkashKava It is not so much a problem that is does not implement both (see 2nd answer to this SO question: stackoverflow.com/questions/1500955/…), but rather that neither has a ConnectionTimeout. I'll probably go for using timers and abort the request manually (per Ludovic's suggestion), but it smells.
0

You right, you are unable to set this specific timeout. I don't have enough information about how the libraries were built, but for the purpose they are meant to, I believe they fit. Someone wants to do a request and set a timeout for everything.

I suggest you take a different approach. You are trying to do two different things here that HttpRequest do at once:

  1. Try to find the host/stabblish a connection;
  2. Transfer data;

You could try to separate this in two stages.

  1. Use Ping class (check this out) to try to get to your host and set a timeout for it;
  2. Use the HttpRequest IF it works for your needs (of timeout,

This process should not slow down everything, since part of resolving names/routes would be done at the first stage. This would not be totally disposable.

There's a drawback on this solution: your remote host must accept pings. Hope this helps.

1 Comment

Don't use PING for network wide client or host availability, because Routers often disallow ICMP packets.
0

I used this method to check if the connection can be established. This however doesn't guarantee that the connection can be established by the subsequent call in HttpWebRequest.

private static bool CanConnect(string machine)
{
    using (TcpClient client = new TcpClient())
    {
        if (!client.ConnectAsync(machine, 443).Wait(50)) // Check if we can connect in 50ms
        {
            return false;
        }
    }

    return true;
}

Comments

-1

if timeouts does not suits your need - don't use them. you can use a handler which waits for the operation to complete. when you get a response - stop the handler and proceed. that way you will get short time requests when failing and long time requests for large amounts of data.

something like this maybe:

 var handler = new ManualResetEvent(false);

 request = (HttpWebRequest)WebRequest.Create(url)
 {
    // initialize parameters such as method
 }

 request.BeginGetResponse(new AsyncCallback(delegate(IAsyncResult result)
 {
      try
      {
          var request = (HttpWebRequest)result.AsyncState;

          using (var response = (HttpWebResponse)request.EndGetResponse(result))
          {
              using (var stream = response.GetResponseStream())
              {
                  // success 
              }

              response.Close();
          }
      }
      catch (Exception e)
      {
          // fail operations go here
      }
      finally
      {
          handler.Set(); // whenever i succeed or fail
      }
 }), request);

 handler.WaitOne(); // wait for the operation to complete

Comments

-1

What about asking for only the header at first, and then the usual resource if it is successful,

webRequest.Method = "HEAD";

2 Comments

To see why this is not an answer to the question, consider the case where the requests are already all HEAD requests. The original problem of having the timeout apply to making the connection would not disappear -- they would not "fail fast".
I did not see that the OP was about all head requests. To solve the OP of "I need a way to make requests fail fast if they cannot resolve the name or establish a connection, but I also sometimes need to receive large amounts of data, so cannot just reduce the timeout". Head only would let you know if you have a 404, 500 or something you are not able to program around.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.