SOAP client timeouts during function execution - php

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.

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.

Having problems creating a functionin SOAP header in php

I'm trying to use a webform to create incidents in ServiceNow. The ServiceNow API uses basic authentication with username, password and an apikey.
The authentication always fails as the apikey isn't recognized correctly. When using a SOAPClient like Boomerang it works, but I just don't get it correctly in php.
The following information are in the header in Boomerang (which works perfectly fine):
Authorization - Basic SU5UU0RQTVI6TWFudWZhY3R1cmluZ1czYmYwcm0=
Content-Type - text/xml; charset=utf-8
SOAPAction - http://www.service-now.com/hlr_incident_api/createIncident
Api-Key - 97c97bd4-5226-43c4-9909-adee8332d1e0
The code that I'm using (which always fails) looks like this:
$wsdl ='sns.wsdl';
$client = new SoapClient($wsdl, array('login' => 'USER',
'password' => 'PASSWORD',
'soap_version' => SOAP_1_2));
$ns = 'http://www.service-now.com/hlr_incident_api';
$header = new SoapHeader($ns, '97c97bd4-5226-43c4-9909-adee8332d1e0');
$client->__setSoapHeaders($header);
Can you help me setting up the correct SOAP header in php?
Thank you very much!!

PHP - SOAP Client Unable to Call HTTPS Endpoint

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

How to make PHP Soap 1.1 call with non-default Content-Type

I need to make call to SOAP 1.1 web service in PHP. However, one of requirements in order to work is that I must send Content-Type=application/soap+xml. Now, I know that these are differences:
SOAP 1.2 -> Content-Type: application/soap+xml
SOAP 1.1 -> Content-Type: text/xml
I need to use SoapClient with WSDL to do this. However, I couldn't find how to set Content-Type after I set version to SoapClient to 1.1
Could someone provide example or code snippet?
Thank you!
There are a number of headers that are ignored when you try to set them in a stream_context, and will never be used.
Check the underlying C source file soap/php_http.c and search for "skip some predefined headers" for a list.
This is the reason some people report problems trying to set Host, Authorization, Content-Type and other headers in certain situations.
You can supply a stream context in the SoapClient options.
$ctx_opts = array(
'http' => array(
'header' => 'Content-Type: application/soap+xml'
)
);
$ctx = stream_context_create($ctx_opts);
$soapClient = new SoapClient('your.wsdl', array('stream_context' => $ctx));

XML-RPC pinging (google and others)

I am trying to ping (SEO tactic called "ping" is used for new content to get robots index it faster) Google in PHP. Only thing I know is that I need to send my request to following url:
http://blogsearch.google.com/ping/RPC2
Probably I can use PHP XML-RPC functions. I don't know how to format my request and which method to use.
As far as you're concerned to do the XML-RPC request (Example #1).
If you follow the specification of a pingback, it would look like this:
$sourceURI = 'http://example.com/';
$targetURI = 'http://example.com/';
$service = 'http://blogsearch.google.com/ping/RPC2';
$request = xmlrpc_encode_request("pingback.ping", array($sourceURI, $targetURI));
$context = stream_context_create(array('http' => array(
'method' => "POST",
'header' => "Content-Type: text/xml",
'content' => $request
)));
$file = file_get_contents($service, false, $context);
$response = xmlrpc_decode($file);
if ($response && xmlrpc_is_fault($response)) {
trigger_error("xmlrpc: $response[faultString] ($response[faultCode])");
} else {
print_r($response);
}
Which would give you the following output:
Array
(
[flerror] =>
[message] => Thanks for the ping.
)
Generally, if you don't know which method you call, you can also try XML-RPC Introspection - but not all XML-RPC servers offer that.
You asked in a comment:
According to specs, $targetURI should be: "The target of the link on the source site. This SHOULD be a pingback-enabled page". How can I make pingback enabled page, or more important, what is that actually?
A pingback enabled site is a website that announces an XML-RPC pinbback service as well. That's done with the HTMl <link> element in the <head> section. Example:
<link rel="pingback" href="http://hakre.wordpress.com/xmlrpc.php" />
The href points to an XML-RPC endpoint that has the pingback.ping method available.
Or it's done by sending a specifc HTTP response header:
X-Pingback: http://charlie.example.com/pingback/xmlrpc
See pingback-enabled resource.
So if you ping others, others should be able to ping you, too.
Ummm, I think we should use weblogUpdates.ping or weblogUpdates.extendedPing instead of pingback.ping to ping a site about new content.
pingback.ping is for a new link from one site to another, not for new content.
I think you should use weblogUpdates.extendedPing with google blog search, weblogs, Pingomatic and weblogUpdates.ping for other server. I created a ping tool but some server return an error with http and https, i cannot give a bug Online rpc xml ping

Categories