PHP - SOAP Client Unable to Call HTTPS Endpoint - php

I have a problem that I can't call the webservice from other server and it shows error like this :
Error Fetching http headers
When I looked into more detailed error, I realized that the endpoint is not right. For example, I wrote this in my php code
$client=new SoapClient('https://www.example.com:17819/wsdl/sample.php?wsdl', array("trace" => 1, "connection_timeout" => 5, "exceptions" => 1));
The endpoint supposed to be in https, but the error shows that the endpoint begins with http --> http: //www.example.com:17819/wsdl/sample.php?wsdl
What am I missing in my php client code?
Thanks

I just solved this issue.
I used nusoap_client to solve this issue.
And I change the request format too, like this
$msg = "<soapenv:Envelope xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/' xmlns:urn='urn:sj_service'>";
And so on..
Thanks

Related

NTML Authentication with PHP to Navision SOAP webservice

I'm currently developing a payment gateway that has to send the order to Navision where it will be managed. In the webservice the NTML authentication is enabled so first it is necessary to extend the native class SoapClient. For it I have found enough documentation in the web https://thomas.rabaix.net/articles/using-soap-php-with-ntlm-authentication that allows to extend this native class.
Now the code exposed in that post does not return me the xslm first.
In this case this would be my code
define("USERPWD", "user:password");
require_once("NTLMStream.php");
require_once("NTLMSoapClient.php");
stream_wrapper_unregister('http');
stream_wrapper_register('http', 'NTLMStream') or die("Failed to register protocol");
// Initialize Soap Client
$url = "http://ipaddress:port/DynamicsNAV1_test/WS/enterprise/Codeunit/SalesEnterprise?WSDL";
$uri = "urn:microsoft-dynamics-schemas/codeunit/SalesEnterprise";
$params = [
'stream_context' => stream_context_create([
'ssl' => [
'verify_peer'=>false,
'verify_peer_name'=>false,
'allow_self_signed'=>true,
]]),
'cache_wsdl'=> WSDL_CACHE_NONE,
'trace' => 1,
];
$client = new NTLMSoapClient($url, $params);
stream_wrapper_restore('http');
As you can see I have dispensed with the classes used by this author to define the credentials.
inally the code returns the following error:
SOAP Fault: (faultcode: WSDL, faultstring: SOAP-ERROR: Parsing WSDL: Couldn't load from 'http://ipaddress:port/DynamicsNAV1_test/WS/Enterprise/Codeunit/SalesEnterprise?WSDL' : Document is empty).
I will be happy to provide more information if needed. Many thanks in advance!
The first 2 responses you get from an NTLM handshake are 401's with no body, the 3rd response if successfully authenticated will contain the 200 and response.
https://techcommunity.microsoft.com/t5/iis-support-blog/windows-authentication-http-request-flow-in-iis/ba-p/324645
Update 30/1/21
Its been a while since ive used Php, but id start with this post that shows how to manually do the NTLM curl from the shell then how to repeat that same command from php curl - curl with ntlm authentication works in command line but not inside php
Also there are some nuggets in the top few answers of this SO search: https://stackoverflow.com/search?q=NTLM+PHP+curl
I had this issue with the same SOAP exception, but the code similar to yours was working fine with NAV2013. The company I'm working for upgraded to NAV2020 and then the SOAP Exception appeared. After countless hours of back and forward of refactoring, and researching the web, the solution is on the Microsoft documentation... The issue is that in the original code for NTLMSoapClient class you have to change the following line:
'Content-Type: text/xml; charset=UTF-8'
Which is under the variable $headers in the method processRequest to:
'Content-Type: text/xml; charset=ISO-8859-1'
At some point Dynamics API is no longer accepting credentials with Unicode characters. I hope this works for you as it did for me.

PHP SoapClient returning null when server sends back an error response

I am using PHP SoapClient to send requests to a 3rd party using their WSDL. I have working functionality when I send requests that contain all required data. However, if a required field is missing (or some other minor error) all I'm getting back in my catch is a SoapFault with the message 'Internal Server Error'. Just to make it clear, I have zero control over the server side of the request.
Using SoapUI with the same request I get the following response.
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<s:Fault>
<faultcode>s:Client</faultcode>
<faultstring xml:lang="en-US">Request validation failed.</faultstring>
<detail>
<RequestValidationFault xmlns="http://schemas.datacontract.org/2004/07/OCCM.Phoenix.EWF.Shared.Modules.ERM.Common.BusinessEntities.Exceptions" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Domain>Request validation</Domain>
<Message>Request validation failed.</Message>
<ValidationResult xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<a:string>Patient: Last name is required.</a:string>
</ValidationResult>
</RequestValidationFault>
</detail>
</s:Fault>
</s:Body>
</s:Envelope>
This is exactly what I need to be able to tell the a user of the system on my end to go in and enter/fix the needed data. In this case the patients last name.
I have exceptions and tracing turned on and when a successful request is made I can use $obj->__getLastResponse() and see the expected data. However when an error comes back the response is '', or 'NULL' if I var_dump() it.
Looking closer in SoapUI I can see that the response has the header:
status HTTP/1.1 500 Internal Server Error
My assumption is that SoapClient is seeing this and just immediately throwing an exception based on the http error. Has anyone had a similar experience with an issue like this? If so, is there a way to tell SoapClient to return the full response?
If that's not possible does anyone know if there is a function I can override to save the raw response text before the exception is thrown? I had an issue earlier where the success XML response was wrapped in meta data. By overriding __doRequest() and manually stripping the meta data I got SoapClient to stop throwing exceptions. This won't work for my current problem as the response is blank there as well.
I was unable to find a solution using SoapClient. I ended up using curl to resend the request when there is an error. Then I manually parsing the actual response.

SoapClient call returns 500 Internal Server Error

I am trying to connect to an API using PHP and its built-in SoapClient. I have checked against the url I was given through the ill-formatted documents the client gave and $client->__getFunctions() returns a list of three functions. HelloWorld($name), which responds with Hello ~name~, shows me that I am communicating with the server through the SoapClient call and the URL is correct.
However, when I try to access one of the other methods that __getFunctions() gives me, even after copy/pasting the XML from the docs and putting in my own credentials, I am still being given an Internal Server Error faultstring and 500 as faultcode from the SoapFault object.
I am sure that it is my own XML string that is causing the issue but I cannot for the life of me figure out how. Reaching out to the API provider directly hasn't proven helpful. This is my first time dealing with Soap/Web Services so I am unsure of where to go from here.
I did wget http//xxx.xxx.xxx?wsdl and it returned me what looks like a valid XML response, the same one I get when I go directly to the url in the browser. What should I be looking into in order to solve this issue? All of the past API's I've dealt with have been JSON/RESTful so I feel out of my element trying to debug PHP errors.
Edit
I have slowly deleted parts of my method call and parts of my XML string, trying to trigger a different error or something in order to find what I need to fix. What I have found is that by not passing in my XML string, I get a valid response from the $client->FunctionCall(...). It's an "this isn't right" message but it's a message! In fact, passing that function ANYTHING for the xml parameter causes the 500 http faultcode/faultstring. Does this mean that my XMl is poorly formatted or does it mean that there is an issue on their end handling requests?
Second Edit
If I make my $client decleration as follows, I get the faultstring Could not connect to host
$opts = array(
'ssl' => array('ciphers'=>'RC4-SHA')
);
$client = new SoapClient($CREDS['orderingWSDL'], array (
"encoding"=>"ISO-8859-1",
'stream_context' => stream_context_create($opts),
'exceptions'=>true,
));
I am getting more confused the longer I try to fix this.
Sometimes a 500 status coming from a SOAP service could be a SoapFault exception being thrown. To help your troubleshooting, you'll want to be able to inspect both your request XML, and the response XML.
Put your code in try/catch blocks, and use $client->__getLastRequest() and $client->__getLastResponse() to inspect the actual XML.
Example:
$client = new SoapClient('http//xxx.xxx.xxx?wsdl', array('soap_version'=>SOAP_1_1,'trace' => 1,'exceptions' => true));
try {
$response = $client->someFunction();
var_dump($response);
} catch (Exception $e) {
var_dump($e->getMessage());
var_dump($client->__getLastRequest());
var_dump($client->__getLastResponse());
}

SOAP client timeouts during function execution

I need to use SOAP to retrieve some data from a database. I'm not an experienced PHP programmer, that's why I need some help. The company which provides the webservice (WSDL) gave me login info and links to the svc and wsdl files. They also gave me an example in C# of how to connect:
var proxy = new ChannelFactory<ServiceReferenceWCF.IWebService2>("custom");
proxy.Credentials.UserName.UserName = login;
proxy.Credentials.UserName.Password = pass;
var result = proxy.CreateChannel();
var logged_in = result.loggedIn();
Here's my PHP code:
$wsdl_proto = 'https';
$wsdl_host = 'their_wsdl_host';
$wsdl_host_path = 'their_wsdl_path';
$namespace_proto = 'https';
$namespace_host = 'their_namespace_host';
$namespace_path = 'their_namespace_path';
$location = $namespace_proto.'://'.$namespace_host.$namespace_path;
$wsdl_url = $wsdl_proto.'://'.$wsdl_host.$wsdl_host_path;
$connection = new SoapClient($wsdl_url, array('location' => $location, 'soap_version' => SOAP_1_1, 'connection_timeout'=> 600,
'proxy_login' => "my_login", 'proxy_password' => "my_password"));
$functions = $connection->__getFunctions();
var_dump($functions);
$logged_in = $connection->loggedIn();
It hangs during the loggedIn() function call. This function is listed in the $functions variable, so it is valid. I tried some other functions provided by the service - the result is always the same: the script simply freezes. And by that I mean there is no response from the service and PHP waits for the loggedIn() function to finish. After it exceeds the timeout, I get an error: Error Fetching http headers in...
What am I doing wrong? How can I debug it?
UPDATE:
I tried every single thing you guys suggested. But I still didn't manage to solve the problem. I don't use a proxy. You can find the results below:
1. I installed the SoapUI. After configuring the request for the some_method function (creating a basic Auth with credentials) I received a response: An error occurred when verifying security for the message.
Ticking the Authenticate pre-emptively option didn't help. I searched for a solution to this error, but I didn't find anything.
2. I tried almost every imaginable combination of options for the SoapClient class. Here are some of them:
$connection = new SoapClient($wsdl_url, array(
'login' => "login",
'password' => "pass",
'trace' => 1,
));
The response headers are empty. The request headers:
REQUEST HEADERS:
POST /file.svc HTTP/1.1
Host: host
Connection: Keep-Alive
User-Agent: PHP-SOAP/5.6.16
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://tempuri.org/file/some_method"
Content-Length: 221
Authorization: Basic HASH
REQUEST:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://tempuri.org/"><SOAP-ENV:Body><ns1:some_method/></SOAP-ENV:Body></SOAP-ENV:Envelope>
Next combination:
$connection = new SoapClient($wsdl,array(
'login' => "login",
'password' => "pass",
'trace' => 1,
'connection_timeout' => 500000,
'cache_wsdl' => WSDL_CACHE_BOTH,
'keep_alive' => false,
));
The response headers are empty. The request headers are the same, as before.
3. Using this:
$connection->__setLocation('https://host.org/file.svc');
doesn't help. However when I set the location to the WSDL file instead of the SVC file, I get the following response:
HTTP/1.1 405 Method Not Allowed
Connection: Keep-Alive
Content-Length: 1293
Date: Wed, 23 Dec 2015 14:28:53 GMT
Content-Type: text/html
Server: Microsoft-IIS/7.5
Allow: GET, HEAD, OPTIONS, TRACE
X-Powered-By: ASP.NET
I'm sure that the WSDL service is not slow enough, to exceed the timeout (Ricardo Velhote suggested it).
4. I've got an XML configuration file provided with the C# example I mentioned earlier:
<client>
<endpoint address="https://host/file.svc" binding="customBinding" bindingConfiguration="custom" contract="ServiceReference1.file" name="custom" />
</client>
<bindings>
<customBinding>
<binding name="custom">
<security defaultAlgorithmSuite="Default" authenticationMode="UserNameOverTransport" requireDerivedKeys="true" includeTimestamp="true" messageSecurityVersion="WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10">
<localClientSettings detectReplays="false" />
<localServiceSettings detectReplays="false" />
</security>
<textMessageEncoding messageVersion="Soap11WSAddressing10" />
<httpsTransport maxReceivedMessageSize="2147483647" maxBufferPoolSize="2147483647" />
</binding>
</customBinding>
</bindings>
I tried to extend the SoapClient class as suggested here, but as you can guess it didn't work - the behaviour of the script is still the same.
According to your update and the XML configuration file provided with the example this Webservice appears to be using WS-Addressing so the regular SOAPClient will not work and requires it to be extended in order to support WS-Addressing.
This is the line that gives it away
<textMessageEncoding messageVersion="Soap11WSAddressing10" />
I have yet to use WS-Addressing but I was recently analysing an API for project we will be working on in the future that requires it.
Please take note of this project or this other project which may be useful (it's easy to search for more PHP WS-Addressing).
Again, I have only done research and do not have any hands-on experience to help you with actual code :)
[EDIT: Obsolete answer after the update]
First of all, you might be being mislead by the use of the variable proxy in the example code. They are probably referring to HTTP Basic Authentication and not a proxy.
Try replacing proxy_login and proxy_password with login and password.
However, having said that, if you are getting the WSDL it means that at least it's connecting and obtaining the information about the service (which is good).
In normal situations you do not need to specify location in SoapClient as it should be defined by the WSDL file. By setting the location parameter you are overriding what is set in the WSDL file and you may be pointing it to a location that does not exist.
Try ommiting the location and soap_version from the SoapClient constructor and let the library handle those parameter automatically:
$connection = new SoapClient($wsdl_url, array('connection_timeout'=> 600,
'proxy_login' => "my_login", 'proxy_password' => "my_password"));
On the other hand, perhaps you are dealing with an extremely slow Web Service. There are many parameters in PHP that may be affecting the time it takes to timeout and most likely they are well below your connection_timeout parameter:
default_socket_timeout
max_execution_time
You have missed proxy_host and proxy_port in the options... If you need proxy to work provide theese parameters:
....
$connection = new SoapClient($wsdl_url, array(
'location' => $location,
'soap_version' => SOAP_1_1,
'connection_timeout'=> 600,
'proxy_host' => '....', // Your proxy host
'proxy_port' => 8080, // Your proxy port
'proxy_login' => "my_login",
'proxy_password' => "my_password"
));
Your first action when coding in the language you don't really know should be using manuals and examples.
Here is a link to PHP Manual on SoapClient - http://php.net/manual/en/soapclient.soapclient.php
For debugging SoapClient you can pass "trace" argument. Quote from the manual: Setting the boolean trace option enables use of the methods SoapClient->__getLastRequest, SoapClient->__getLastRequestHeaders, SoapClient->__getLastResponse and SoapClient->__getLastResponseHeaders.
$client = new SoapClient("some.wsdl", array('trace' => true));
So, in your case if you want to see what is going wrong do this:
$client = SoapClient($wsdl_url, array('trace' => 1));
$result = $client->SomeFunction();
echo "REQUEST HEADERS:\n" . $client->__getLastRequestHeaders() . "\n";
echo "REQUEST:\n" . $client->__getLastRequest() . "\n";
echo "Response headers:\n" . $client->__getLastResponseHeaders() . "\n";
echo "Response:\n" . $client->__getLastResponse() . "\n";
Also, as was noted in another answer - you are setting proxy_login and proxy_password parameters in the request, which should only be used if you are using proxy server to connect to that WSDL service.

Invoking WCF service with PHP (with federated security)

I’m trying to invoke a WCF service (.NET) from PHP. It’s a little more complicated than just using a SoapClient since the service uses a WS2007FederationHttpBinding to authenticate.
Here’s the code I’m using at the moment. I haven’t even added credentials as I’m not sure how, but regardless, I’m not even at the point where I’m getting access denied errors.
$wsdl = "https://slc.centershift.com/sandbox40/StoreService.svc?wsdl";
$client = new SoapClient($wsdl,array(
//'soap_version'=>SOAP_1_2 // default 1.1, but this gives 'Uncaught SoapFault exception: [HTTP] Error Fetching http headers'
));
$params = array();
$params['SiteID'] = 123;
$params['GetPromoData'] = false;
$ret = $client->GetSiteUnitData(array('GetSiteUnitData_Request'=>$params));
print_r($ret);
Which WSDL should I be pointing to?
https://slc.centershift.com/Sandbox40/StoreService.svc?wsdl
Seems to be very short, but includes a reference to (note the wsdl0) https://slc.centershift.com/Sandbox40/StoreService.svc?wsdl=wsdl0
https://slc.centershift.com/Sandbox40/StoreService.svc?singleWsdl
Seems to have everything in it.
Do I need to specify SOAP 1.2? When I do, I get a connection timeout ([HTTP] Error Fetching http headers). When I don’t, the default of SOAP 1.1 is used and I get a [HTTP] Cannot process the message because the content type 'text/xml; charset=utf-8' was not the expected type 'application/soap+xml; charset=utf-8'. Is this because I’m not authenticated yet, or because I’m using the wrong SOAP version?
How to authenticate in PHP? Here’s the corresponding .NET/C# code. Do I need to somehow put these as SOAP headers? Or am I thinking about it all wrong, and I need to do some kind of authentication before I even call the method (from what I read, I’m supposed to get a token back and then use it for all future method calls – I think I see an example of this in an answer here on Stack Overflow.
If I call $client->__getFunctions(), using either WSDL and either SOAP version, I’m getting a valid list of all functions, so I assume either of these is fine and my real issue is the authentication.
Other programmers I’ve talked to had spent time trying to get this to work, but gave up and instead implemented a proxy in .NET. They pass their parameters from PHP to their own unsecured .NET service, which in turn calls this secure service. It works, but seems crazily inefficient to me, and counter-productive, as the purpose of WCF is to support all types of clients (even non-HTTP ones!).
I’ve read How to: Create a WSFederationHttpBinding on MSDN, but it didn’t help.
You can use this URL for WSDL https://slc.centershift.com/Sandbox40/StoreService.svc?singleWsdl. This WSDL has all definitions.
You have to use 1.2 because this webservice works with SOAP 1.2 version. I tried it with 1.1 and 1.2 and both of them gived error. 1.1 is version error, 1.2 is timeout error. I think there is an error at this test server. I used it with svcutil to generate code but it gived error too. Normaly it should get information and generate the code example to call service.
Normally you can add authenticate parameters with SoapHeader or directly add to options in SoapClient consruct (if service authentication is basic authentication). I write below code according to your screenshot. But it gives timeout after long wait.
$wsdl = "https://slc.centershift.com/sandbox40/StoreService.svc?wsdl";
$client = new SoapClient($wsdl,array('trace' => 1,'soap_version' => SOAP_1_2));
$security = array(
'UserName' => array(
'UserName'=>'TestUser',
'Password'=>'TestPassword',
'SupportInteractive'=>false
)
);
$header = new SoapHeader('ChannelFactory','Credentials',$security, false);
$client->__setSoapHeaders($header);
$params = array();
$params['SiteID'] = 100000000;
$params['Channel'] = 999;
try {
$ret = $client->GetSiteUnitData($params);
print_r($ret);
}catch(Exception $e){
echo $e->getMessage();
}
__getFunctions works, because it prints functions defined in WSDL. There is no problem with getting WSDL information at first call. But real problem is communication. PHP gets WSDL, generates required SOAP request then sends to server, but server is not responding correctly. SOAP server always gives a response even if parameters or request body are not correct.
You should communicate with service provider, I think they can give clear answer to your questions.
Having worked with consuming .NET WS from PHP before I believe you would need to create objects from classes in PHP that matches the names that .NET is expecting. The WSDL should tell you the types it is expecting. I hope this assist with your path forward!
If the SOAP call works from a C# application, you could use Wireshark (with the filter ip.dst == 204.246.130.80) to view the actual request being made and then construct a similar request from php.
Check this answer to see how you can do a custom SOAP call.
There's also the option of doing raw curl requests, since it might be easier to build your xml body, but then you would have to parse the response yourself with simplexml.

Categories