CommunicationException: An error occurred on client side while making the HTTP request (HTTP.SYS) to third-party WebService

1

I'm from Brazil and our government released a WebService meant to inform employees' payroll that's called eSocial. I'm developing a solution to communicate with this service for about a year now and I found this error in some customers' machines while trying to send information to the government WebService:

System.ServiceModel.CommunicationException:
An error occurred while making the HTTP request to 'https://webservices.producaorestrita.esocial.gov.br/servicos/empregador/enviarloteeventos/WsEnviarLoteEventos.svc'. This could be due to the fact that the server certificate is not configured properly with HTTP.SYS in the HTTPS case. This could also be caused by a mismatch of the security binding between the client and the server.

  ---> System.Net.WebException: The underlying connection was closed: The connection was closed unexpectedly.
    ---> 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.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags)...

In most machines our program is working perfectly, the error arises just on some computers. My target platform is .NET Framework 4.7.

During my researches I've found two solutions for the problem:

  1. Set the property ServicePointManager.SecurityProtocol (https://stackoverflow.com/a/33084791/8133067);
  2. Let Windows Update install the updates needed (https://stackoverflow.com/a/45494549).

In case of the first solution: eSocial WebService uses the TLS 1.2 security protocol, but this is not always the default machine configuration. I already did this in the code and did not solve in most cases:

 ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls |
                                        SecurityProtocolType.Tls11 |
                                        SecurityProtocolType.Tls12;

In case of the second solution: In the research I did the problem could happen on computers with an outdated Windows 7, and theoretically the update via Windows Update would solve, but in most cases did not solve, and I have even caught some cases of this error in Windows 10.

I even did a test program, with the message log and trace log turned on, to see if I could get any more information, but the only other thing I found out (because of tracing) is that the error occurs when the message is sent by the channel (communication channel with the WebService).

Here is the code I'm using to instantiate and call the service:

 const string UrlSvcBase = @"https://webservices.{0}.esocial.gov.br/servicos/empregador/{1}";
 string urlServico = String.Format(UrlSvcBase, "producaorestrita", @"enviarloteeventos/WsEnviarLoteEventos.svc");

 var address = new EndpointAddress(urlServico);
 //var binding = new BasicHttpBinding(BasicHttpsSecurityMode.Transport);
 var binding = new BasicHttpsBinding();  //Available since .NET Framework 4.5
 binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
 // Manual de Orientação do Desenvolvedor v1.4, página 39, item '5.4.5. Validações aplicadas':
 // "O tamanho limite da mensagem SOAP é 750 kbytes."
 // O valor padrão da propriedade MaxReceivedMessageSize é 65.536,
 // que é alterado então para 750 KB * 1024 = 768.000 bytes.
 // Caso contrário, ocorre a exceção:
 //   Exception: System.ServiceModel.CommunicationException
 //   InnerException: System.Exception {System.ServiceModel.QuotaExceededException}
 //   "The maximum message size quota for incoming messages (65536) has been exceeded.
 //    To increase the quota, use the MaxReceivedMessageSize property on the appropriate binding element."  
 //   HResult: -2146233087 (0x80131501)
 binding.MaxReceivedMessageSize = 768000;

 ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls |
                                        SecurityProtocolType.Tls11 |
                                        SecurityProtocolType.Tls12;

 var wsClient = new WsEnviar.ServicoEnviarLoteEventosClient(binding, address);
 // 'certificado' variable's type is X509Certificate2.
 wsClient.ClientCredentials.ClientCertificate.Certificate = certificado;

 wsClient.Open();
 // 'lote' variable's type is XElement.
 var retorno = wsClient.EnviarLoteEventos(lote);
 wsClient.Close();

Does anyone have any idea what might cause this error, or any idea of what else I can do to try to figure out the cause of the problem?

[EDIT]
My question was flagged as a possible duplicate of that one:

WCF Error "This could be due to the fact that the server certificate is not configured properly with HTTP.SYS in the HTTPS case"

But, I had already tried almost all those solutions without success (actually, you can see that question already linked on my original post), that's why I've posted a new question, and there is a fundamental difference because in that question the service was made by the user and in my case the service is third-party. Fortunately I've got the answer somewhere else, so I'll post it here.

c#
wcf
asked on Stack Overflow Jul 27, 2018 by Pedro Gaspar • edited Oct 9, 2018 by Pedro Gaspar

1 Answer

1

I've posted that question on Stack Overflow in Portuguese too:

https://pt.stackoverflow.com/q/318351/86952

And there I've got the answer from the user EProgrammerNotFound (https://stackoverflow.com/users/2087187), in the comments section.

After some testing using Fiddler, he found out that there seems to be a bug in .NET Framework because in some scenarios it was not doing a fallback to a previous version of security protocol.

So, even if I've designated the security protocol property to use TLS versions 1.2, 1.1 and 1.0:

 ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls |
                                        SecurityProtocolType.Tls11 |
                                        SecurityProtocolType.Tls12;

if the connection using TLS 1.2 fails for some reason (in that specific environment that I could not replicate, but occurred in some customers), it stops without trying TLS 1.1 and after that TLS 1.0, and then we get that error.

So, I've changed my code to use only TLS 1.0 directly, and it solved the case on the problematic machines:

 ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;

On my question at SOPT I've also got an answer from the user Jonas Giehl (https://pt.stackoverflow.com/a/324254/86952) that suggested a code that would try each security protocol manually, something that I thought of also but didn't had the time to try.

There goes his code (modified a little bit):

// Stack with protocols we are going to try.
Stack<SecurityProtocolType> protocolsToTry = new Stack<SecurityProtocolType>();
protocolsToTry.Push(SecurityProtocolType.Tls);
protocolsToTry.Push(SecurityProtocolType.Tls11);
protocolsToTry.Push(SecurityProtocolType.Tls12);
bool tryAgain = false;

do
{
   // Set the protocol we are going to try now.
   ServicePointManager.SecurityProtocol = protocolsToTry.Pop();

   try
   {
      // Tries to call the service using the current protocol.
      // If no exceptions are threw, we are ready to exit the loop.
      CallService();
      tryAgain = false;
   }
   catch (CommunicationException ex)
   {
      tryAgain = true;
   }
} while(tryAgain && protocolsToTry.Count() > 0);
answered on Stack Overflow Oct 9, 2018 by Pedro Gaspar

User contributions licensed under CC BY-SA 3.0