0

A friend is developing a 3rd party soap client with PHP 5.x. The client is already complete, and worked properly on the server (Debian+Apache) connecting to the dev endpoint. Now he wants to change to production endpoint and certifications, but always got the "could not connect to host" error message without any explanation. The same code with the same certs works from localhost or even from Firefox with imported cert. Only the SoapClient from the server does not want to connect to it. According to him the error is almost instant, so it is not some kind of timeout. He tried to telnet the endpoint from the server and it has connected, so it is not a firewall problem. He already mailed to the webservice maintainer to get the logs, but no response yet. Any idea what can cause such environment dependent issues by SOAP?

Here is the code, but I guess it does not contains any info to solve this:

$soapLocation = (DEV_ENV ? 'https://dev.example.com:443' : 'https://prod.example.com:443').$soapLocationUri;  
$soapClient = new SoapClient(  
        './wsdl/'.$wsdlFileName,  
        array(  
                'local_cert'                    => (DEV_ENV ? './cert/***.pem' : './cert/***.pem'),  
                'passphrase'                    => (DEV_ENV ? 'xxx' : 'xxx'),  
                'location'                      => $soapLocation,  
                'trace'                             => true,  
                'exceptions'                    => true,  
                'keep_alive'                    => true,  
                'connection_timeout'    => 10,  
                'cache_wsdl'                    => WSDL_CACHE_NONE,  
                'soap_version'              => SOAP_1_1,  
                'stream_context'            => stream_context_create(array(  
                        'ssl'   => array(  
                                'verify_peer'               => false,  
                                'verify_peer_name'  => false,  
                                'allow_self_signed' => true  
                        )  
                ))  
        )  
); 

and the exception:

object(SoapFault)#2 (9) {  
  ["message":protected]=>  
  string(25) "Could not connect to host"  
  ["code":protected]=>  
  int(0)  
  ["trace":"Exception":private]=>  
  array(2) {  
    [0]=>  
    array(6) {  
      ["function"]=>  
      string(11) "__doRequest"  
      ["class"]=>  
      string(10) "SoapClient"  
      ["args"]=>  
      array(4) {  
        [0]=>  
        string(1188) "...xml..."  
        [1]=>  
        string(53) "https://prod.example.com:443/BLAH/BLAH_NO_SAML"  
        [2]=>  
        string(0) ""  
        [3]=>  
        int(1)  
      }  
    }  
    ...  
  }  
  ["previous":"Exception":private]=>  
  NULL  
  ["faultstring"]=>  
  string(25) "Could not connect to host"  
  ["faultcode"]=>  
  string(4) "HTTP"  
} 
inf3rno
  • 24,976
  • 11
  • 115
  • 197

1 Answers1

0

According to the webservice maintainers, the production environment enforces TLSv1.2, while the dev environment allowed TLSv1.0. We tested with

curl -v --tlsv1 https://prod.example.com:443/BLAH/BLAH_NO_SAML --cert cert.pem

and it failed, so that was the cause.

The PHP SoapClient makes it really hard to debug connection errors. Some people recommend SoapUI to test webservices before developing a client. I agree with that part, but if you use an extension where a bug from 2004 can still exist, then I think the problem is not with the extension, but your choice of tools.

Just to write something relevant about how to debug this kind of errors without relevant error messages. There is always a way to debug if you have a working and a not working solution. I learned this method on my design of experiments course, they might be developed by Taguchi (not sure about that part). First you need to make a list of the parts of the product. In the current case: webservice, network, hardware, opsystem, firewall, webserver, language, code. You need to use the working product and replace these one by one until you got an error by the faulty part(s). So our working product is on a different machine, but has the same network, so we can exclude the webservice and the network. First we need to replace the hardware, so we need to make a vm or docker image and try it out first on our laptop and next on the server. If it works, then the opsystem is the next. We need to move the working solution to the same opsystem. Sometimes this is not easy, but we can manage it for example we can use nginx instead of apache (to have a different webserver) after we tested it on our laptop. If it does not work on the server, then the opsystem settings or the firewall settings are wrong. If it works, then we need to try on the same webserver. We can use a different extension for our different php version or we can use a totally different language, if it works on the laptop. After that the code would be the next, but it is identical in this case. With this method we would get a failure by the language (if libcurl comes with that), so we will know that some extension or some php.ini settings or the php version is wrong in this case. Another 3+ parts to check...

inf3rno
  • 24,976
  • 11
  • 115
  • 197