With a .NET/C# application I am trying to download data from an HTTP API. Even though the timeout of the HttpClient instance is set to 30 minutes, the request will time out much faster. Today I learned this is due to the fact that the .NET HttpClient does not send any TCP Keep Alive packets. (This is why I can download data from that API in Chrome, as proven with Wireshark, Chrome does send these packets while HttpClient does not.)
This is how I had hoped to get the JSON data from the API:
this.httpclient = new HttpClient();
[...]
result = await this.httpclient.GetAsync(url);
Now I did some research but I could not find out how to send these Keep Alive pings. The HttpClient does not seem to support them. So my guess is these need to be enabled on the socket when opening the connection. Is there any workaround for this, maybe avoiding HttpClient? Can I enable the Keep-Alive pings somehow on the socket? Is this a feature that needs to be enabled through the OS?
Please note: This is NOT about the HTTP "Connection: Keep-Alive" header! You can enable this for the HttpClient class but for obvious reasons it won't trigger any TCP Keep Alive packets.
Edit:
The requests always work when these Keep-Alive packets are being sent by the client, they will fail if not. This can be reproduced by using different clients/browsers. Best example: The Postman Chrome extension does send Keep-Alive packets, hence the request works. Using the almost identical Postman standalone client the request fails (or does not return any data) because, funnily enough, the standalone client does NOT send any Keep-Alive packets which can easily be checked with Wireshark.
Edit: I have found a very simple solution for this problem, see my answer below.
So now I end up answering my own question as I just have found a super simple solution that actually works:
this.httpclient = new HttpClient();
var sp = ServicePointManager.FindServicePoint(new Uri(url));
sp.SetTcpKeepAlive(true, 30000, 30000);
result = await this.httpclient.GetAsync(url);
In fact I used
var sp = ServicePointManager.FindServicePoint(new Uri(baseUrl));
where baseUrl is the common document root of all my requests I am sending through a for loop.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With