1

I've observed some unusual SSL tunnel behavior when asynchronously sending multiple requests from System.Net.Http.HttpClient. I've isolated the problem to the following minimal working example (MWE):

public async Task MinimalExample()
{
    var c = new HttpClient
    {
        BaseAddress = new Uri("https://www.google.com")
    };
    var l = new List<Task<HttpResponseMessage>>();
    for (int i = 0; i < 5; i++)
    {
        l.Add(c.GetAsync(""));
    }
    await Task.WhenAll(l);
}

This code hits Google five times in parallel.

Running this code and inspecting traffic in Fiddler shows the following:

enter image description here

The client issues five SSL tunnels, one for each request. This is unusual, since typically HttpClient reuses SSL tunnels to the same host. Indeed, running the synchronous version of the MWE:

public void MinimalExampleSynchronous()
{
    var c = new HttpClient
    {
        BaseAddress = new Uri("https://www.google.com")
    };
    var l = new List<HttpResponseMessage>();
    for (int i = 0; i < 5; i++)
    {
        l.Add(c.GetAsync("").Result);
    }
}

... shows the following in Fiddler:

enter image description here

A single SSL tunnel is established, and used to transmit the 5 requests (which is typically how well-behaving HTTP clients and browsers act).

My question is:

  • Why does the parallel version not create and reuse a single SSL tunnel?

My MWE only issues 5 requests, but for larger tasks HttpClient ends up creating thousands of redundant SSL tunnels, which significantly slows things down and creates lots of waste. Is this a bug, or is my parallel implementation just using HttpClient stupidly?

DumpsterDoofus
  • 1,132
  • 12
  • 30
  • If T1 is the time it takes to enter your loop and *start* issuing the network request, and T2 is the time that it takes to actually issue the request and establish the tunnel, then perhaps T2 is much greater than T1? This would mean that a new request is created, and it is decided that it needs a new tunnel before the first request can establish the tunnel and statically register it in shared memory. I'm not familiar with the HttpClient's tunnels, but could you explicitly create one and share it? – Sam Dec 19 '16 at 18:48
  • @Sam Yeah my gut suspicion is that the parallel version creates the requests at the same time, so each tasks is like "I need to issue a tunnel because one didn't exist the moment I was created". But I have no idea if this is true, or how to explicitly create/share tunnels :( – DumpsterDoofus Dec 19 '16 at 18:55
  • Potential workaround: Issue one single request first to initialize, then issue the rest of the requests once that one is complete? – Sam Dec 19 '16 at 21:30

0 Answers0