WebClient - connection from ASP.Net site forcibly closed but not from console app
I have an ASP.Net site on .Net Framework 4.6.x. When it makes a request to a certain web service, I get back “Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.”
The call works on the test and production environments, just not on developer machines.
Thoughts:
- Is it a cert chain issue?
- Is it an SSL/Cipher issue?
- Is it firewall at their end? IP allow-list?
Then I tried it in Postman, and it worked fine. So we can rule out cert chain and firewall.
A place to try things
I made a new .Net Fx 4.6.1 solution to figure out what’s going on, with a console app and an MVC site, and roughly the same code snippet in both.
The console app got a healthy response from the web service! 😨 So I narrowed it down to web projects, or more likely, IISExpress.
var url = "https://webservice.example.org/thing/api.php";
var host = "webservice.example.org";
var xml = @"<?xml version=""1.0"" encoding=""utf-16""?>
<request xmlns...>
<someXml />
</request>";
//1. First thing to try:
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
//2. Second thing to try:
ServicePointManager.Expect100Continue = false;
//3. Check if DNS resolves the same for both - add a watch:
var hostEntry = Dns.GetHostEntry(host);
var webClient = new WebClient();
//4. This fixed it for me, but we can do it in web.config instead:
webClient.Proxy = WebRequest.GetSystemWebProxy();
var responseXml = webClient.UploadString(url, xml);
ViewBag.Message = responseXml;
// or
Console.WriteLine(responseXml);
Potential solutions for you
Most of these are answers to WebClient from Asp.net gives “An existing connection was forcibly closed by the remote host” Error
1. SecurityProtocolType.Tls12
The internet consensus was that this would fix it, but it didn’t for me, and wasn’t necessary in my eventual end state. I think this might fix it if you are running a .Net Framework version prior to 4.6.
In my environment, SecurityProtocol
has a default value of Tls | Tls11 | Tls12 | Tls13
. You probably don’t want to needlessly limit this to TLS 1.2 only, as it precludes future upgrades.
The code to try:
// Before using your WebClient:
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
2. Don’t Expect100Continue
Some other answers mentioned this. By default in both the console app and the MVC site, the property ServicePointManager.Expect100Continue
defaults to true.
// Before using your WebClient:
ServicePointManager.Expect100Continue = false;
This did nothing for me.
3. Did you check DNS?
One commenter points out that on the server, they were getting a different DNS resolution to on their local dev. Now, that wouldn’t make sense for my console app being able to work just fine locally. But you can check with the code above and putting a watch on hostEntry
.
4. GetSystemWebProxy
This was the fix for me! We have loads of weird VPNish proxy stuff at work I don’t understand. I don’t know why this stops the ASP.Net site working when console works fine. Perhaps the console app knows to use the system default proxy and IISExpress doesn’t.
You can either configure the webClient.Proxy
in code:
webClient.Proxy = WebRequest.GetSystemWebProxy();
…or in web.config:
<system.net>
<defaultProxy useDefaultCredentials="true" />
</system.net>
And then some care may be needed to check that this config doesn’t leak into Production if not needed.
The SO question and answers: C# webclient and proxy server
The searches that did and didn’t do it
This is a limited selection of the better searches from a misspent morning 🙃🙃🙃
In DuckDuckGo:
- “iisexpress webclient use TLS 1.2” - barking up the wrong tree
- “iisexpress webclient tls connection forcibly closed” - this led me to the answer (after adapting the code from HttpClient to WebClient)
- “asp.net requires proxy for webclient but console” - looking in more detail into why, and this got me the web.config solution.