8.5 Creating New Transport Modules


Even with the wide array of transport support code offered by SOAP::Lite , it's possible that newer protocols or even specialized needs based on existing protocols will arise. Writing new protocol suites isn't as difficult as it might seem at first glance.

8.5.1 Writing a Server Transport

In effect, a new server transport was written at the start of the chapter. The WishListCustomer::Transport module may not initially seem like a new transport, because it only piggy -backs on an existing protocol. Despite this, it qualifies because it acts in place of any of the prepackaged server classes. And while the version as presented in the text expects the application to handle loading the "parent" class that it inherits from, it can be written to better handle automatically determining what module to load without application-level intervention.

Reasons for writing a new server class aren't limited to supporting new transport methods . The earlier example was written to handle the authentication layer. If an application wants to create transaction logs using more information than is available to the on_dispatch hook, the ideal approach is to subclass the desired server class and step in at the appropriate point (most likely the handle method). For those cases in which the actual need is to support a different protocol not yet in the core , the best approach is to look at what changes are needed from the vantage of the new and handle methods.

8.5.1.1 Specialized constructors

If the new server class is just doing things like authentication, there is probably no need to provide a specialized constructor. In cases in which a new protocol is accommodated, the chances are good that a class-specific new will have to be written.

Certain conventions have to be followed to maintain full interoperability with the rest of the SOAP::Lite suite. Unless the new class is going to completely reimplement the entire SOAP::Server interface, it should inherit from the SOAP::Server class (or from an existing subclass of it). Also, the local constructor should start out by calling the superclass new method, then making whatever local adjustments are needed to the resulting object:

 # Assumes that $class and @args were initialized from @_ my $self = $class->SUPER::new(@args); 

The SOAP::Server class (as well as the various subclasses in SOAP::Lite ) uses a hash table reference as the basis for the object. Thus, the new class will need to design its structure around this datatype, as well. The SOAP::Lite classes all follow pretty standard data-abstraction and object-oriented methodologies. A leading underscore in the key name ( _transport and _proxy , for example) indicates instance data on the hash reference.

How the new class avoids key collision is up to the developer, though choosing a clear prefix (such as the package name) generally works. And because the SOAP::Server class is designed to be subclassed, it isn't necessary to re- bless the object before returning it; it will already be blessed into the local namespace.

8.5.1.2 Writing a localized handle method

Looking at the code for the various protocol-specific modules in the SOAP::Lite distribution, it is clear that the handle method is the one most-often overloaded. This method represents the core of the server functionality, from an external interface's perspective.

How a new server class chooses to implement the handle method (if at all) is dependent on the planned nature of how the protocol views requests. Protocols such as POP3 and MQ expect requests to have been delivered to a defined location by some other means (email and a message-queue manager, respectively), whereas protocols such as HTTP and TCP expect to await notification from the operating system that incoming data is ready on a readable socket (generally by way of the accept system call). These architectural issues define how the handle method gets used in applications built using the class. The sample TCP server's handle method acted as an infinite loop, where the POP3 handle was wrapped in an explicit loop construct.

It is within the handle method that the server object is expected to retrieve one or more requests, using the protocol it is written to support. In many cases, once the request has been read and stored into memory, it is usually directly usable by the handle method of the superclass. Many of the derived server classes simply call the SOAP::Server handle method once they've read the request:

 my $response = $self->SUPER::handle($request); 

Once the response is given back, it is then up to the new code to properly transmit it back to the client. This is assuming that a client response is part of the design; the POP3 server doesn't send a client reply because there is no client connection available to it.

8.5.1.3 Other server methods

There are other methods within SOAP::Server that may be overloaded, not to mention hooks such as on_dispatch . The documentation for SOAP::Lite and Appendix B of this book may be of help in such tasks .

8.5.2 Writing a Client Transport

Writing an actual new client transport module is trickier than writing a server component. The WishListCustomer::Client class developed earlier in this chapter isn't an actual transport class. It was a subclass of SOAP::Lite itself, designed to interject a consistent header element into every outgoing request.

A class intended for use as a client's transport object is very different.

8.5.2.1 Naming the client class

The name of a client class is defined by the protocol itself and the transport class it is expected to work with. All the client classes provided by SOAP::Lite have names that fit the following form:

 ${transport_class}::${protocol_uc}::Client 

By default, SOAP::Lite objects have a transport object instantiated from the SOAP::Transport class. Using Jabber as an example, the class name becomes:

 SOAP::Transport::JABBER::Client 

SOAP::Lite attempts to load protocol code using the transport object's class name and the uppercased protocol string. Keeping with the ongoing example, this means that when a client tries to set a proxy with a protocol specification of jabber:// , SOAP::Lite ( specifically , the proxy method of SOAP::Transport ) tries to load a package called SOAP::Transport::JABBER , and expects a client class called SOAP::Transport::JABBER::Client within it.

This becomes important when choosing the name for the new client class. To work with the loading mechanism already in place, the class has to either use SOAP::Transport as the leading elements of its name or offer a different transport class that can be plugged in to the SOAP::Lite object.

Subclassing SOAP::Transport isn't difficult. The constructor isn't designed to take any arguments when called, and the only class method provided is proxy , which is responsible for either returning the current proxy object (when no arguments are passed to it) or setting a new one (when given an endpoint string as an argument). An empty subclass can provide the base elements of the class name for specifying a transport.

8.5.2.2 Constructors

A client transport class is expected to inherit from either SOAP::Client or some other subclass of it. As such, the client class may not need to overload the new method at all. As with the server class details earlier, there are plenty of cases in which a client might subclass an existing transport class just to overload a different method.

In another similarity to the server classes, a client new should build up from an object created by calling the superclass new method. These objects are also based on hash-table references, so the new client class should design for this.

When a constructor is called, it's usually called by the proxy method of a transport object. Such calls start with two parameters in the list of arguments: the string endpoint followed by the endpoint itself as passed to the proxy method. Any other arguments to the proxy method follow these two. The general design is such that arguments are expected to be name/value pairs that get converted to a hash table. The key endpoint is just one element of the hash table. If the new class constructor is expected to function in tandem with SOAP::Transport , this behavior must be supported.

8.5.2.3 The send_receive method

Just as handle is the core of a server class, the send_receive method is the core of a client-transport class. This method is called when the SOAP::Lite object has turned an ordinary method call into a serialized request.

When called, the send_receive method receives a hash table with four keys:

action

The "action" specifier for the request, usually the SOAPAction header in an HTTP environment.

encoding

The SOAP encoding URN, often dependent on the version of SOAP that serializes the request.

endpoint

The URN describing the endpoint that SOAP::Lite expects the client transport object to send the request to.

envelope

The actual XML text that comprised the request itself.

Which of these the method acts on is up to the class itself. Some of the provided transports use only the envelope.

The responsibility of the send_receive method is to get the envelope to the endpoint and return any response from the server to the caller as a SOAP::SOM object. In the case of the SOAP::Transport::MAILTO module, the response is based solely on whether the SMTP session indicated that the message was sent or whether the send failed.

In the two-way protocols, such as TCP or HTTP, the returned SOAP::SOM object is the deserialized response from the endpoint. In the SOAP::Transport::LOCAL::Client class, the send_receive method passes the envelope directly to the handle method of SOAP::Server and returns the response from that method unchanged because the value is already a SOAP::SOM object.

8.5.2.4 Other client methods

There are other client-level methods that a new class might need to overload, such as options or endpoint . The endpoint method is generally used to either fetch the current endpoint to which messages are being sent or to set a new endpoint without having to search for and load a new protocol module. A new client transport might need to do more with the value when an endpoint is changed, particularly if it involves passwords or other such data.

The SOAP::Lite documentation and Appendix B of this book cover the other methods in additional detail.

8.5.3 Example Transport Module

As a last example for the chapter, Example 8-12 illustrates how a transport module might look. The module provides three classes, one each for server and client, and one to act as a transport class for SOAP::Lite objects. The purpose of the package is to allow one-way applications (such as a POP3-based server and a MAILTO-based client) to use some of the block-oriented encryption modules available in Perl. This is done by overloading the proxy method to look for URN values of this format:

 des-ftp://server.domain/path 

The transport takes the encryption algorithm part of the protocol string, the des-ftp in the example, and removes it from the endpoint. It saves the algorithm for later reference and creates an object using the rest of the endpoint string. On the server side, the server class is designed in a very similar way to WishListCustomer::Transport from earlier in this chapter. It allows dynamic specification of what server class it should base itself on, then overloads calls to handle to decrypt the message before passing it to the parent class.

Much of the critical functionality takes place in the class called Crypt::SOAP::Transport , which is meant to be used as a transport handler in place of the SOAP::Transport class. It manages attribute methods for all aspects of creating objects from the Crypt::CBC class, [2] including dynamic selection of whether to treat data to hex-string treatment (in which data is converted to printable hex representation after encryption and presumed to be in such a format when being decrypted).

[2] It is available from CPAN and requires one or more cipher implementations (such as Crypt::DES , Crypt::IDEA , Crypt:: Blowfish , etc.) for operation.

The classes are limited to the one-way transports due to the fact that not all the two-way packages have the same hook points in their request flow. The HTTP module can be employed because it uses clearly identified request and response methods to get and set content. By contrast, the TCP server class writes the result of the handle method directly to the client socket, with no intermediate step available for overloading.

The code is shown in Example 8-12. It isn't shown in entirety due to the length, but the full source is listed in Appendix D.

Understand that this is meant as an illustrative example only. Not all of the block-cipher algorithms the code refers to are considered secure enough for critical data. This code should not used in a production setting.

Example 8-12. The abbreviated Crypt::SOAP module
 package Crypt::SOAP;     use strict; use vars qw(%known_cbc);     use SOAP::Lite; use Crypt::CBC;     # A mapping table of the ciphers that can be used with the # Crypt::CBC module. The key is the lc'd name for matching # and the value is what must get passed to Crypt::CBC::new %known_cbc = ( des      => 'DES',                idea     => 'IDEA',                blowfish => 'Blowfish',                rc6      => 'RC6',                rijndael => 'Rijndael' );     package Crypt::SOAP::Transport;     @ISA = qw(SOAP::Transport);     sub proxy {     my $self = shift->new;     my $class = ref $self;         return $self->{_proxy} unless @_;         my ($cipher, $proto);     my $endpoint = shift;     if ($endpoint =~ /^(\w+):/) {         ($cipher, $proto) = split(/-/, );         $endpoint =~ s/^$cipher-//;     } else {         die "$class: No transport protocol in proxy";     }     if ($cipher = $Crypt::SOAP::known_cbc{lc $cipher}) {         $self->cipher($cipher);     } else {         die "$class: Cipher $cipher unknown or unsupported "             . 'in proxy';     }         $self->SUPER::proxy($endpoint, @_);     $self->{_proxy} =         Crypt::SOAP::Client->new($self, $self->{_proxy}); }     package Crypt::SOAP::Server;     sub new {     my ($class, %args) = @_;     return $class if ref $class;         die "$class: Cannot create objects without a parent " .         'class specified first'             unless (@ISA);         my $transport_args;     if ($args{transport}) {         $transport_args = $args{transport};         delete $args{transport};     }     my $self = $class->SUPER::new(%args);     $self->{_CSS_transport} =         Crypt::SOAP::Transport->new($transport_args ?                                     @$transport_args : ( ));         $self; }     sub handle {     my ($self, $message) = @_;         $message = $self->{_CSS_transport}->decrypt($message);     $self->SUPER::handle($message); }     package Crypt::SOAP::Client;     sub new {     my ($class, $transport, $client) = @_;     return $class if ref $class;         die "$class: new( ) must be called with a transport " .         'object and an existing client object'             unless (UNIVERSAL::can($transport, 'new') &&                     UNIVERSAL::can($client, 'new'));     $client->{_CSC_transport} = $transport;     @ISA = (ref $client);     bless $client, $class; }     sub send_receive {     my $self = shift;     my %args = @_;         $args{envelope} = $self->{_CSC_transport}                            ->encrypt($args{envelope});     $self->SUPER::send_receive(%args); }     1; 


Programming Web Services with Perl
Programming Web Services with Perl
ISBN: 0596002068
EAN: 2147483647
Year: 2000
Pages: 123

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net