Burre Ifort
Burre Ifort

Reputation: 713

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

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;
}

Upvotes: 5

Views: 17810

Answers (4)

Jorn van der Wal
Jorn van der Wal

Reputation: 21

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

Upvotes: 2

Richard Nixon
Richard Nixon

Reputation: 847

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.

Upvotes: 12

Nkosi
Nkosi

Reputation: 246998

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

Upvotes: 5

Svek
Svek

Reputation: 12848

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;
}

Upvotes: 0

Related Questions