Connection keep-alive not working with System.Net.

2020-02-11 03:57发布

问题:

I'm experimenting with the Heroku API using the .NET System.Net.Http.HttpClient. In particular, I want keep-alive to work so that I can send many HTTP requests using one TCP connection and only do one SSL handshake instead of setting up a TCP connection with SSL handshakes per request.

I'm testing against https://api.heroku.com/status which gives a nice HTTP 200, and using Wireshark to monitor TCP traffic.

Google Chrome, ApacheBench (with -k flag) and curl all seem to be able to keep a TCP connection open and send multiple requests. HttpClient, not so much. I have also tested HttpClient against other hosts (eg. https://google.com/ and there I only see one SSL handshake/TCP setup. So it seems like it's a bad interaction between HttpClient and the Heroku API.

Here's the code I'm testing with:

private static async void TestUrl(string url)
{
    using (var client = GetClient())
    {
        await client.GetAsync(url);
        await client.GetAsync(url);
        await client.GetAsync(url);
        await client.GetAsync(url);
    }
}

private static HttpClient GetClient()
{
    var requestHandler = new HttpClientHandler
    {
        UseCookies = false,
        AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip,
    };

    return new HttpClient(requestHandler);
}

I'm also attaching a Wireshark screenshot showing how HttpClient decides to reset (RST) the connection as soon as it's done receiving data.

Any suggestions for what's going on here?

回答1:

Use WebRequestHandler class instead of HttpClientHandler and set property HttpWebRequest.UnsafeAuthenticatedConnectionSharing to True.



回答2:

Some proxy servers will ignore the keep-alive when doing their authentication. If I recall correctly it's when the proxy server uses NTLM authentication.

In the past, I've had to add a partial class to "help" some web service proxy classes.

I overrode the GetWebRequest(Uri uri) method with

System.Net.HttpWebRequest webRequest = 
        (System.Net.HttpWebRequest)base.GetWebRequest(uri);
webRequest.ProtocolVersion = HttpVersion.Veraion10;  // I know this is strange, but it works
webRequest.ServicePoint.Expect100Continue = false;  // This is where the magic lives
return webRequest;