Title: Prevent "underlying connection was closed" errors in C#
This example shows how to fix the error "The underlying connection was closed" when downloading a response from a web page.
Background
The basic approach for downloading a result from a web page is simple. Create a new WebClient object and use its DownloadString to download a URL result as a string. The following code snippet shows this technique.
using (WebClient client = new WebClient())
{
return client.DownloadString(url);
}
This works fine for some web sites, but for others you get one of the following error messages.
This seems to be caused by the web site using one of the newer security protocols. The solution is to set the ServicePointManager class's static SecurityProtocol property to an appropriate value.
In older versions of .NET, that enumeration has two values, Ssl3 (Secure Socket Layer 3.0) and Tls (Transport Layer Security 1.0). Newer versions of the enumeration also include the values Tls11 (TLS 1.1), Tls12 (TLS 1.2), and Tls13 (TLS 1.3).
In Visual Studio 2019 with .NET Framework 4.5, I managed to get the earlier code snippet to work by setting SecurityProtocol to SecurityProtocolType.SystemDefault. That value has the numeric value 0, but for some reason setting this property to 0 doesn't seem to work in Visual Studio 2008. Presumably that's because of some difference in the Framework versions.
Fortunately explicitly setting the value to allow the TLS 1.1 and TLS 1.2 protocols seems to work.
A Fake Enumeration
To avoid using magic numbers, I wanted to create an enumeration holding the possible protocol values. Unfortunately the SecurityProtocol property expects a value of type SecurityProtocolType. If you set it equal to an enumeration that you define, Visual Studio complains that it cannot convert your enumeration into the necessary type.
In C# you can specify an enumeration's type, but it must be an integral type such as int, uint, or short, so you cannot make it have the type SecurityProtocolType.
To work around that, I created the following class to define the possible values.
public class Protocols
{
public const SecurityProtocolType
protocol_SystemDefault = 0,
protocol_Ssl3 = (SecurityProtocolType)48,
protocol_Tls = (SecurityProtocolType)192,
protocol_Tls11 = (SecurityProtocolType)768,
protocol_Tls12 = (SecurityProtocolType)3072;
}
This class simply defines the protocol values listed on Microsoft's web page SecurityProtocolType Enum. (Interestingly this is one of the pages that will not return a result unless you set the SecurityProtocol property.)
The example's Form1_Load event handler uses the following statement to set the SecurityProtocol.
ServicePointManager.SecurityProtocol =
Protocols.protocol_Tls11 | Protocols.protocol_Tls12;
The SecurityProtocol is a mask, so you can use the | operator to allow multiple protocols. This code allows TLS 1.2 and TLS 1.3.
Conclusion
So that's the technique. Set ServicePointManager.SecurityProtocol to a combination of the protocols that you want to allow when downloading a web result. The Protocols class defines values that you can use almost as if Protocols were an enumeration.
Download the example to experiment with it and to see additional details.
|