0

I have followed the example here and here to handle timeouts successfully with the C# HttpClient but I just can't make it work!

var urls = new List<string> { "/success", "/willtimeout", "/success" };
var baseAddress = "http://endpoint";
var httpClient = new HttpClient();
httpClient.Timeout = new TimeSpan(0, 0, 30);
httpClient.BaseAddress = new Uri(baseAddress);
foreach (var url in urls)
{
    try
    {
        var cs = CancellationTokenSource.CreateLinkedTokenSource(CancellationToken.None);
        cs.CancelAfter(new TimeSpan(0, 0, 3));
        var result = await httpClient.GetAsync(urls, cs.Token);
        Console.WriteLine("Success");
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}

This code will print

Success
A task was canceled.
A task was canceled.

How can I make this work properly without creating a new HttpClient every time?

jsgoupil
  • 3,788
  • 3
  • 38
  • 53
  • I can't produce the same result, though I don't have your endpoint. Is the endpoint responsive on the third call? – dkackman Jul 15 '17 at 12:45
  • @dkackman After much more investigation with some WireShark packet sniffing, if I re-use the httpClient, the SYN/ACK is not being sent to the server on 2nd call. The 2nd call times out, the httpClient sends a SYN/ACK for the 3rd call but it is not answered by the server, which creates a time out. When I don't re-use the httpClient, it sends a SYN/ACK for each every single request no problems happen. So this seems to be a server issue? Surprisingly, this is served by a IIS7.5 server, which makes me believe it would be a code issue... But I am really wondering how such lock would be coded. – jsgoupil Jul 15 '17 at 20:04
  • whatever "/willtimeout" does sounds like it really does a number if it makes the entire service unresponsive. Is that a service under your control or someone else's code? – dkackman Jul 15 '17 at 22:31
  • No, I have not control over it. I had tried to create a server which has an infinite Task.Delay for /willtimeout, but I could not reproduce the exact behavior that the target server was doing. I also tried Connection close. It didn't work. I had to work around this problem and use one HttpClient per in flight request. If the request is not in flight anymore, I can re-use it. If the request has timed out, I throw the HttpClient away. – jsgoupil Jul 15 '17 at 22:56

1 Answers1

0

CreateLinkedTokenSource Creates a CancellationTokenSource that will be in the canceled state when any of the source tokens are in the canceled state.

So I guess this is the problem, just create new token source every time and don't link it to anything:

try
{
    var cs = new CancellationTokenSource();
    // ...