httpclient.GetAsync: The underlying connection was closed: An unexpected error occurred on a send

Viewed 11926

I have a simple web api which gets a url and sort of generates a short number as short url. I have created a VS console application where I am calling the web api via the HTTPClient object. When I run the code the first time, it throws the following error:

Error message: One or more errors occurred.

InnerException: System.Net.Http.HttpRequestException: An error occurred while sending the request. ---> System.Net.WebException: The underlying connection was closed: An unexpected error occurred on a send. ---> System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host. ---> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host at System.Net.Sockets.Socket.EndReceive(IAsyncResult asyncResult) at System.Net.Sockets.NetworkStream.EndRead(IAsyncResult asyncResult) --- End of inner exception stack trace --- at System.Net.TlsStream.EndWrite(IAsyncResult asyncResult) at System.Net.ConnectStream.WriteHeadersCallback(IAsyncResult ar) --- End of inner exception stack trace --- at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult) at System.Net.Http.HttpClientHandler.GetResponseCallback(IAsyncResult ar) --- End of inner exception stack trace ---

This is the code that I am using to call the web api. Calling the web api via the browser works just fine.

static void Main(string[] args)
{
    string url = "https://app1/api/AddUrl?longUrl=http://google.com";
    var result = GetTinyUrl(url).Result;
    Console.WriteLine(result.ShortUrl);     
}

protected static async Task<UrlResponse> GetUrl(string baseUrl)
{
    HttpClientHandler handler = new HttpClientHandler();
    handler.UseDefaultCredentials = true;

    var client = new HttpClient(handler);

    var response = client.GetAsync(baseUrl).Result;
    if (response.IsSuccessStatusCode)
    {
        HttpResponseMessage response1 = response;
        response.EnsureSuccessStatusCode();
        var resp = await response.Content.ReadAsStringAsync();
        var result = JsonConvert.DeserializeObject<UrlResponse>(resp);
        return result;
    }

    return null;
}
4 Answers

Just to say I spent hours looking at a similar problem when connecting to an azure service I'm writing.

Couldn't work out why postman worked fine but my code failed miserably. Similarly other URLs I tried in my code worked fine or gave expected failures.

In the end the reason for the failures was that I forgot lower the Minimum TLS Version in the TLS/SSL Settings blade!!

I think older versions of the HttpClient can only deal with TLS v1.0.

Mixing .Result blocking calls with async await can cause deadlocks.

If using async await then go async all the way, and avoid creating a new instance of the HttpClient for each call as this can lead to socked exhaustion.

static Lazy<HttpClient> http = new Lazy<HttpClient>(() => {
    //Handle TLS protocols
    System.Net.ServicePointManager.SecurityProtocol =
        System.Net.SecurityProtocolType.Tls
        | System.Net.SecurityProtocolType.Tls11
        | System.Net.SecurityProtocolType.Tls12;

    var handler = new HttpClientHandler() {
        UseDefaultCredentials = true
    };

    var client = new HttpClient(handler);
    return client;
});

protected static async Task<UrlResponse> GetUrl(string baseUrl) {
    var client = http.Value;

    var response = await client.GetAsync(baseUrl); //Remove .Result and just await
    if (response.IsSuccessStatusCode) {
        HttpResponseMessage response1 = response;
        response.EnsureSuccessStatusCode();
        var resp = await response.Content.ReadAsStringAsync();
        var result = JsonConvert.DeserializeObject<UrlResponse>(resp);
        return result;
    }
    return null;
}

Since this is a console application and there is only one thread then calling the async then you have to change how you call the async method in main

static void Main(string[] args) {
    string url = "https://app1/api/AddUrl?longUrl=http://google.com";
    var result = GetTinyUrl(url).GetAwaiter().GetResult(); //<-- 
    Console.WriteLine(result.ShortUrl);     
}

Reference Async/Await - Best Practices in Asynchronous Programming

Choosing a higher target framework in the solution properties fixed my problem.

I usually don't advise using HttpClient for web requests it sometimes likes to misbehave with various web APIs. I would highly encourage you to use HttpWebRequest instead. You can properly craft the HTTP request with full control and will produce a better overall result.

string uri = "https://api.foo.com/bar";

var httpWebRequest = (HttpWebRequest)WebRequest.Create(uri);
httpWebRequest.ContentType = "application/json";
httpWebRequest.Method = "GET"; // PUT, POST, DELETE, etc
//httpWebRequest.ContentLength = 0; // sometimes you need to send zero (depending on API)
HttpWebResponse httpResponse = (HttpWebResponse)await httpWebRequest.GetResponseAsync();

using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
    var response = streamReader.ReadToEnd();
    // this is your code here...
    var result = JsonConvert.DeserializeObject<UrlResponse>(response);
    return result;
}
Related