View file File name : Responder.pm Content :# File: Repsonder.pm # based heavily on Ken MacLeod's Frontier::Daemon # Author: Joe Johnston 7/2000 # Revisions: # 11/2000 - Cleaned/Add POD. Took out 'use CGI'. # # Meant to be called from a CGI process to answer client # requests and emit the appropriate reponses. See POD for details. # # LICENSE: This code is released under the same licensing # as Perl itself. # # Use the code where ever you want, but due credit is appreciated. package Frontier::Responder; use strict; use vars qw/@ISA/; use Frontier::RPC2; my $snappy_answer = "Hey, I need to return true, don't I?"; # Class constructor. # Input: (expects parameters to be passed in as a hash) # methods => hashref, keys are API procedure names, values are # subroutine references # # Output: blessed reference sub new { my $class = shift; my %args = @_; my $self = bless {}, (ref $class ? ref $class : $class); # Store the dispatch table away for future use. $self->{methods} = $args{methods}; $self->{_decode} = Frontier::RPC2->new(); return $self; } # Grabs input from CGI "stream", makes request # if possible, packs up the response in purddy # XML # Input: None # Output: A XML string suitable for printing from a CGI process sub answer{ my $self = shift; # fetch the xml message sent my $request = get_cgi_request(); unless( defined $request ){ print "Content-Type: text/txt\n\n"; exit; } # Let's figure out the method to execute # along with its arguments my $response = $self->{_decode}->serve( $request, $self->{methods} ); # Ship it! return "Content-Type: text/xml \n\n" . $response; } # private function. No need to advertise this. # Remember, this is just XML. # CGI.pm doesn't grok this. sub get_cgi_request{ my $in; if( $ENV{REQUEST_METHOD} eq 'POST' ){ my $len = $ENV{CONTENT_LENGTH}; unless ( read( STDIN, $in, $len ) == $len ){ return; } }else{ $in = $ENV{QUERY_STRING}; } return $in; } =pod =head1 NAME Frontier::Responder - Create XML-RPC listeners for normal CGI processes =head1 SYNOPSIS use Frontier::Responder; my $res = Frontier::Responder->new( methods => { add => sub{ $_[0] + $_[1] }, cat => sub{ $_[0] . $_[1] }, }, ); print $res->answer; =head1 DESCRIPTION Use I<Frontier::Responder> whenever you need to create an XML-RPC listener using a standard CGI interface. To be effective, a script using this class will often have to be put a directory from which a web server is authorized to execute CGI programs. An XML-RPC listener using this library will be implementing the API of a particular XML-RPC application. Each remote procedure listed in the API of the user defined application will correspond to a hash key that is defined in the C<new> method of a I<Frontier::Responder> object. This is exactly the way I<Frontier::Daemon> works as well. In order to process the request and get the response, the C<answer> method is needed. Its return value is XML ready for printing. For those new to XML-RPC, here is a brief description of this protocol. XML-RPC is a way to execute functions on a different machine. Both the client's request and listeners response are wrapped up in XML and sent over HTTP. Because the XML-RPC conversation is in XML, the implementation languages of the server (here called a I<listener>), and the client can be different. This can be a powerful and simple way to have very different platforms work together without acrimony. Implicit in the use of XML-RPC is a contract or API that an XML-RPC listener implements and an XML-RPC client calls. The API needs to list not only the various procedures that can be called, but also the XML-RPC datatypes expected for input and output. Remember that although Perl is permissive about datatyping, other languages are not. Unforuntately, the XML-RPC spec doesn't say how to document the API. It is recommended that the author of a Perl XML-RPC listener should at least use POD to explain the API. This allows for the programmatic generation of a clean web page. =head1 METHODS =over 4 =item new( I<OPTIONS> ) This is the class constructor. As is traditional, it returns a blessed reference to a I<Frontier::Responder> object. It expects arguments to be given like a hash (Perl's named parameter mechanism). To be effective, populate the C<methods> parameter with a hashref that has API procedure names as keys and subroutine references as values. See the SYNOPSIS for a sample usage. =item answer() In order to parse the request and execute the procedure, this method must be called. It returns a XML string that contains the procedure's response. In a typical CGI program, this string will simply be printed to STDOUT. =back =head1 SEE ALSO perl(1), Frontier::RPC2(3) <http://www.scripting.com/frontier5/xml/code/rpc.html> =head1 AUTHOR Ken MacLeod <ken@bitsko.slc.ut.us> wrote the underlying RPC library. Joe Johnston <jjohn@cs.umb.edu> wrote an adaptation of the Frontier::Daemon class to create this CGI XML-RPC listener class. =cut