Introducing SOAP

SOAP is something you may find a use for, even if you're not intersted in three-tier web applications.
Testing Our Server

Now that our standalone SOAP server is running, we should test it to see if it works. In order to do that, we must create a SOAP request, send it to the server and then parse through the XML-encoded SOAP response that it returns. Luckily, SOAP::Lite includes such a utility, This small program allows us to create and send SOAP requests interactively, displaying the results. alone justifies the download of SOAP::Lite, even if you are planning to work with another SOAP library for Perl.

If we are running our standalone SOAP server on localhost (i.e., on the same computer as we run, and if we are running it on port 8080, we can invoke it as follows:

perl http://localhost:8080/ Text/Caps

Notice how the first argument to is the URL of the SOAP server, and the second argument is the object that we want to invoke. You can avoid a lot of grief by remembering that the second argument must be passed using a URL-style object hierarchy divider, namely a slash (/). Typing “Text::Caps” rather than “Text/Caps” will confuse the SOAP server and result in hard-to-debug errors.

If your invocation of succeeds, you will see the following prompt:

Usage: method[(parameters)]

The “>” sign indicates that it's your turn to type and you can invoke any method for the object to which you've connected. You may now call any method that the object supports, including any parameters. So to capitalize a word, I simply type:

> capitalize('abc')
Because my SOAP client and server are both on the same computer, the response is nearly instantaneous. prints out:
$VAR1 = 'ABC';
Hey, that's pretty great! I just invoke an object method across the network. That wasn't so hard, was it?

SOAP would be nice if we could send simple scalars back and forth. But we can send and receive a variety of data types. For example, we can invoke capitalize_array, sending a list of arguments:

> capitalize_array('abc', 'def', >'GHi')

The return value is an array reference:

$VAR1 = bless( [
                         ], 'Array' );
The returned array reference looks a bit funny because it has been turned into a format that SOAP::Lite can send and retrieve. We will soon see how our programs can ignore this intermediate format, seamlessly exchanging complex data structures over the Internet.

Examining the SOAP

As you can see, it's possible to work with SOAP without understanding the underlying XML-encoded data. However, debugging SOAP problems often requires that you look at the XML as well as the HTTP headers that are sent in the request and the response.

SOAP::Lite objects support the on_debug( ) method, which takes a subroutine reference as an argument. This subroutine is invoked for each SOAP transaction, meaning that we can log information to the disk or screen. The simplest use of on_debug( ) is as follows:

on_debug(sub{print STDERR @_})

In other words, we ask SOAP::Lite to send a copy of everything to STDERR. This provides us with a marvelous opportunity to see what happens behind the scenes. After we invoke this method, reminds us that we invoked a local method rather than a SOAP method:

With debugging turned on, our invocation of capitalize(abc) from before gets translated into a SOAP request (see Listing 3)

Listing 3. SOAP Request

As you can see, the request is divided into a header and a body, as with all HTTP requests. And as with a normal HTTP request, we indicate an action (“POST”) along with a URL, as a Content-Length (indicating the number of bytes in the request) and the Content-Type (which is always going to be “text/xml”).

Then the fun begins: the final header is SOAPAction, which names the object and method that are being invoked. The SOAPAction header is designed to allow corporate firewalls to filter out dangerous objects and methods from being invoked. Currently, however, it would seem that support for SOAPAction is relatively hard to find. Besides, information about both the object and its method are buried inside of the XML request and response themselves, making the header unnecessary for parsing purposes.

The XML itself begins with an XML declaration and then a SOAP envelope. Inside the envelope is an optional header (not shown in this particular invocation) and a mandatory body. The body names the object and method that we wish to invoke, as well as any arguments that we might have passed.

This XML is parsed into the native operating system and language format and is then passed along to the target object. The object returns a response value to the SOAP server which then creates a SOAP response in XML as seen in Listing 4.

Listing 4. SOAP Response in XML

The response, like the request, uses HTTP and HTTP headers to pass some metadata, including the server type, date, content length, type (“text/xml”) and even the type of SOAP server being run.

The envelope for this particular response, like the request, contains no header. However, it does contain a body, in which the return value (of type “xsd:string”) is returned. While the request uses a namespace of “namesp3:capitalize”, the response uses a namespace of “namesp1:capitalizeResponse”. This is standard in SOAP; XML namespaces are used to identify whether the message contains a request or a response and for which method the response is being sent.

Without any explanation, Listing 5 is the similar debugging output from a call to capitalize_array(reuven, shira, atara):

Listing 5. Debugging Output