PHP, SoapClient, SoapServer, and Timeouts

I ran into an extremely frustrating problems while trying to add a test script for some PHP web services. I won’t go into detail on all the different things I went through to debug this problem. But here is what it all boiled down to:

Problem

I have a SOAP Web Service that extends PHP’s internal SoapServer class that serves hundreds of thousands of remote devices. Every now and then, our server will have a hiccup and our Web Service won’t return valid responses. However, nginx will return requests just fine. So, to monitor the Web Service, I wrote a PHP script using SoapClient to read the WSDL, make several SOAP calls, and make sure the responses are valid.

The problem is each SOAP request my PHP Script would call it would take almost exactly 10 seconds. This didn’t make sense, since my iPhone, and SoapUI (a testing tool) took under a second. So it seemed for some reason my SoapClient was taking the extra 10 seconds, like something wasn’t working quite right and would timeout for 10 seconds.

Solution

After extensive debugging (Wireshark, strace, and a bunch of other tools) I found the problem. SoapServer’s handle function not only generates an XML response, but is also sets two http headers, Content-Type and Content-Length. Now, when I setup the web services, I wanted a way to debug requests and responses to files based on IPs, I did it with output buffers. Here is an example:

[php]
$server = new SoapServer(‘webservice.wsdl’, array( /* options */ ));

// Start the output buffer
ob_start();
$server->handle();
// Save the contents of the response
$response = ob_get_contents();
// End the buffer
ob_end_clean();
[/php]

The mistake I made was I didn’t want to on accidentally introduce any extra spaces or characters, so I did:

[php]
$response = trim($response);
[/php]

Why was this a problem? While it seems most SOAP client’s wait for the server’s socket to end, PHP’s SoapClient depends on the http header’s Content-Length. Because I had performed a trim() on $response, it was indeed stripping of an empty space at the end. This caused the content length of the actual response to be one byte less. So PHP’s SoapClient would continue to pull the Socket for 10 seconds until it decided it indeed received the whole message.

So the bottom line is this:

  • SoapServer will set the Content-Length for you, which might be different than what you’re expecting. I assumed nginx would set the Content-Length, not PHP.
  • SoapClient, and probably some other clients in other languages, depend and rely on the http Content-Length, and not what is actually served from the socket. This also is true if you try to output some debug content at the end. At one point I was trying to echo some xml comments at the end of the response, and they wouldn’t show up in SoapClient. But they would show up on other SoapClients.

Moral of the story… SOAP can be very delicate and easy to break. Make sure you realize this when you make changes.

1 thought on “PHP, SoapClient, SoapServer, and Timeouts

  1. SOAP can be very delicate and easy to break

    Quote of the day 🙂

    Like

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.