My application routinely connects to a third-party server to fetch data via SOAP/WSDL:
$this->soap_client = new SoapClient("https://[the-domain]:443/[path]?wsdl", array(
'trace'=>1,
'login'=>$this->username,
'password'=>$this->password,
'exceptions' => true,
'cache_wsdl' => WSDL_CACHE_NONE
)
Everything has been great for the last year, but they recently updated their WSDL file and now when the application tries to connect I'm getting the following two errors:
SoapClient::SoapClient(http://[the-domain]:80/[path]?xsd=1): failed to open stream: HTTP request failed! HTTP/1.1 401 Unauthorized
and
SoapClient::SoapClient(): I/O warning : failed to load external entity "http://[the-domain]:80/[path]?xsd=1"
When I look at the WSDL XML file, it appears that the offending unloadable file is the document schema file (schemaLocation) that it's trying to import: (from WSDL:)
<types>
<xsd:schema>
<xsd:import namespace="[irrelevant]" schemaLocation="http://[the-domain]:80/[path]?xsd=1"/>
</xsd:schema>
</types>
I've been beating my head against this for a while, and as far as I can tell, the issue is one of two things:
- When I load that schema URL in a browser (after browser authentication) it
302 redirects
to ahttps
url (and drops the port declaration). Is it possible that SOAP call won't follow the redirect when trying to import the schema? - Given the fact that the error message is a 401 error - is it possible that the SOAP call is not passing credentials when trying to import the schema? The schema file requires the same authentication as the WSDL file, but maybe the server is not extending the authentication to the schema when it tries to import?
Assuming that it's the second issue, is there any way that I can force the system to use a different schema URL without downloading the WSDL file, editing it, and storing it/referencing it locally? If so I could try passing the credentials in the URL (http://username:password@domain....
)?
If my only shot is to create a modified copy of both the WSDL and XSD schema file, so be it, but I'd love to hear if anyone has any thoughts that would let me avoid this (as the schema does change from time-to-time).
I'd give cURL a try, it has an option for following redirections
How to post SOAP Request from PHP
It looks like the PHP SoapClient sticks to the same domain policy (including scheme) for sending the Basic Auth username and password in the WSDL request which is done to import the xsd file in the WDSL schema.
So if the WSDL url has a
https
scheme and the import has ahttp
scheme, PHP is not sending the basic authentication info since the connection is not encrypted anymore when requesting thehttp
import url (which would compromise the confidentiality of the authentication info).However it seems that at least for some PHP versions (might be fixed in newer versions) the authentication problem persists even if the
http
url redirects to thehttps
one (on the same domain). After the redirect to a secure URL with the same domain PHP could off course include the given Basic Auth info again.Solution
In the end the only neat way I found to fix this was making the other party change their WSDL content to import a secure URL (
https
one) which has the same scheme, domain and port as the WDSL url itself.Workaround
If that is not an option for you off course you could always go for the workaround which is to save the WSDL as well as the import(s) as local files and refer to the WDSL file instead of the URL. Off course this would also mean you would have to change the WSDL to import the right local file (instead of the
http
URL), and possibly other imports as well. Unfortunately this is the only workaround I know of in this case.PHP Bug?
I also found this PHP bug report which might be related.