55

I'm working on an integration with Alternative Payments using their hosted page integration. Their C# SDK does not have this integration available at the moment, but as you can see it's pretty simple and I made a small class to send the post request and get the JSON response.

I tested the json object I'm sending on PostMan and cURL and both work, also the authentication header, so I think they are not the problem. Here is the constructor of my class:

public AlternativePaymentsCli(string apiSecretKey)
{
    this._apiSecretKey = apiSecretKey;

    _httpClient = new HttpClient();
    _httpClient.DefaultRequestHeaders.Accept
        .Add(new MediaTypeWithQualityHeaderValue("application/json"));

    var authInfo = _apiSecretKey;
    authInfo = Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(string.Format("{0}:", _apiSecretKey)));

    // The two line below because I saw in an answer on stackoverflow.
    _httpClient.DefaultRequestHeaders.Add("Connection", "Keep-Alive"); 
    _httpClient.DefaultRequestHeaders.Add("Keep-Alive", "3600");

    _httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("Anything.com custom client v1.0");
    _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authInfo);

}

And the method where I'm posting the data:

public string CreateHostedPageTransaction(HostedPageRequest req) 
{
    var settings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore };

    // I send this same json content on PostMan and it works. The json is not the problem
    var content = new StringContent(JsonConvert.SerializeObject(req, settings), Encoding.UTF8, "application/json");
    var response = _httpClient.PostAsync(this._baseUrl + "/transactions/hosted", content).Result;
    var responseText = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();

    if (response.IsSuccessStatusCode)
        return responseText;

    return "";
}

Then I get this error: An existing connection was forcibly closed by the remote host, at the PostAsync line. This is the error details:

[SocketException (0x2746): An existing connection was forcibly closed by the remote host]
   System.Net.Sockets.Socket.EndReceive(IAsyncResult asyncResult) +8192811
   System.Net.Sockets.NetworkStream.EndRead(IAsyncResult asyncResult) +47

[IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.]
   System.Net.TlsStream.EndWrite(IAsyncResult asyncResult) +294
   System.Net.ConnectStream.WriteHeadersCallback(IAsyncResult ar) +149

[WebException: The underlying connection was closed: An unexpected error occurred on a send.]
   System.Net.HttpWebRequest.EndGetRequestStream(IAsyncResult asyncResult, TransportContext& context) +324
   System.Net.Http.HttpClientHandler.GetRequestStreamCallback(IAsyncResult ar) +137

[HttpRequestException: An error occurred while sending the request.]

I'm using C# 4.5, Asp.Net MVC. I've been reading answers for the same error and none of them solved my issue so far. What am I missing in this code?

Thanks for any help

André Luiz
  • 4,670
  • 6
  • 41
  • 76
  • What, if anything do you see in the response in Fiddler? – DiskJunky Sep 14 '17 at 15:31
  • 5
    Just a nice tip, using `var` for everything will make your coworkers hate you. Only use `var` when the type is apparent (i.e `var date = new DateTime();`) its _very_ clearly a `DateTime`. However `var response = _httpClient.PostAsync(this._baseUrl + "/transactions/hosted", content).Result;` is _not clear_ because `.Result` is a property and not implicit what type it is. – maccettura Sep 14 '17 at 15:36
  • 7
    @maccettura on the contrary, using `var` makes the code a lot cleaner. There is no ambiguity about what the type is unless you write really long methods, in which case you should really, really break them apart. Coworkers will hate you if you write methods that are so long they can't see what the types are – Panagiotis Kanavos Sep 14 '17 at 15:42
  • @PanagiotisKanavos `var` only makes code cleaner when _the type is implied_, using `var` everywhere (even in cases where the type is not implied) is simply _poor programming_. – maccettura Sep 14 '17 at 15:45
  • 4
    @maccettura as for `.Result`, coworkers will hate anyone that blocks an asynchronous call with `.Wait()` or `.Result`. The return type is known anyway. It's HttpResponseMessage. Seeing the name won't help you as you'll have to use intellisense anyway to find which methods to use – Panagiotis Kanavos Sep 14 '17 at 15:45
  • @maccettura which, for example, is why functional languages use type inference instead of specifying the type explicitly You may not be *familiar* with type inference. That doesn't make it poor programming. On the other hand a call to `.Result` should raise anyone's hackles – Panagiotis Kanavos Sep 14 '17 at 15:46
  • @PanagiotisKanavos is it _really_ known? Ignoring prior experience in any library of .NET, what is the easiest way for me to know the type? (Its by declaring it in cases where it's **not redundantly implied**. – maccettura Sep 14 '17 at 15:48
  • @PanagiotisKanavos and yes, I agree that OP should not be blocking async calls (if it wasnt already know, the code I posted is straight from OP's question and not my own). – maccettura Sep 14 '17 at 15:49
  • Totally off. I don't know which IDe you use, I use Visual Studio and when you hover the variable it tells you the type. Thanks – André Luiz Sep 14 '17 at 15:58
  • Possible duplicate of [An existing connection was forcibly closed by the remote host](https://stackoverflow.com/questions/2582036/an-existing-connection-was-forcibly-closed-by-the-remote-host) – Liam Feb 15 '19 at 11:53

5 Answers5

157

I don't see in your code sample where you are setting the value of _baseUrl, but I'm assuming that is being done somewhere. I'm also assuming that since this related to payments, the URL is HTTPS. If the remote host has disabled TLS 1.0 and your connection is coming in as TLS 1.0, it could cause that behavior. I know C# 4.6 has TLS 1.0/1.1/1.2 support enabled by default, but I think C# 4.6 still defaults to only SSL3/TLS 1.0 even though TLS 1.1 and 1.2 are supported. If this is the cause of the issue, you can manually add TLS 1.1 and 1.2 to the enabled values using the following code.

System.Net.ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
Paul Pearce
  • 1,892
  • 1
  • 8
  • 11
  • thank you very much Sir, you solved my issue. Locally I run a fake HTTPS, chrome warn me that's the site is unsafe etc.. that must be it. – André Luiz Sep 14 '17 at 16:00
  • 1
    You do not have to alter code to enable this for applications that are currently running in prod: https://docs.microsoft.com/en-us/officeonlineserver/enable-tls-1-1-and-tls-1-2-support-in-office-online-server – Sean Anderson Jan 25 '18 at 17:51
  • yeah, is this only needed within the app domain (using in a worker role) or do I need to set this per HttpClient request? – Nathan Tregillus Jun 17 '18 at 06:51
  • 1
    @Ruchira and NathanTregillus You can set it per request (adding the code before you send your request), or you can set it once for the entire application by setting it Application_Start() in Global.asax.cs – Paul Pearce Jun 18 '18 at 13:38
  • 1
    Thanks @PaulPearce. I ended up adding this to Global.asax. Used "using System.Net;" add the top to support ServicePointManager and SecurityProtocolType. – Ruchira Jun 20 '18 at 04:59
  • Thanks. My problem was Server 2012. I moved an old non SSL API to SSL'ed Azure Functions and this started happening. Your solution fixed it. Thanks allot! – Piotr Kula Dec 04 '18 at 23:09
  • Struggling for 3 days with this exception and this fixed it! – Rush Frisby Jun 24 '19 at 14:16
  • I added this line of code. But it is not working for me. I am using .NET Framework 4.6.1. I can make the same api call in Postman or in Powershell script without this error. But when I call the api using HttpClient, I am getting the same error. – user3293338 Aug 07 '19 at 23:13
  • @user3293338 - did you add the code to the calling client or to the receiving service? It should be added to the calling client to allow it to connect using the more secure TLS versions. If you could include a sample of your code, that would be helpful with identifying the issue. – Paul Pearce Aug 08 '19 at 13:40
  • I added the code before making the API call. The code looks as following: var stream = new FileStream(filePath, FileMode.Open); var content = new StreamContent(stream); client.DefaultRequestHeaders.Add("X-Atlassian-Token", "nocheck"); System.Net.ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12; var response = client.PostAsync(apiUrl, content).Result; – user3293338 Aug 08 '19 at 17:14
  • This answer is very good, also it is worth the update to use Ssl3 too: "...| SecurityProtocolType.Ssl3" – Sxntk Mar 18 '20 at 23:43
  • Thank you, your answer resolve my problem. It happen when my service using Azure and client don't enable it by default – Pham Lai Jun 23 '20 at 03:51
15

If you are using .Net 4.0 then SecurityProtocolType.Tls11 and SecurityProtocolType.Tls2 are not defined so instead you can use the hard coded value below.

ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;

dferenc
  • 7,163
  • 12
  • 36
  • 42
Tim Kempster
  • 161
  • 1
  • 4
8

It is possible to solve the issue without any changes in the code, as described in this excellent answer to a similar question:

Retarget the web project to .Net 4.6+, then update web.config as the following:

<system.web>
  <compilation targetFramework="4.6" /> 
  <httpRuntime targetFramework="4.6" /> 
</system.web>
d_f
  • 3,735
  • 2
  • 15
  • 25
1

This worked for me, the first line ensures the protocols ssl3 and TLS1.2, and the second line ignores any potential certificate errors (ignore and continue - like expired certs.):

ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback +=  (sender, certificate, chain, sslPolicyErrors) => true;
Fabio Lolli
  • 749
  • 3
  • 22
sao
  • 11
  • 1
  • Hello and welcome to the community, try using the formatting tools in the text editor to make your answer look better, and easier to read. The Code formatter is especially useful and it will work wonders. – Fabio Lolli Feb 24 '20 at 18:37
0

For me this error was caused by forgetting to configure the proxy server. Using the following code to construct the HttpClient solved it for my .NET 4.7.2 application:

var httpClientHandler = new HttpClientHandler { Proxy = WebRequest.GetSystemWebProxy() };
var httpClient = new HttpClient(httpClientHandler);

Hope this helps someone.