package Gantry::Server; use strict; use warnings; use base qw( HTTP::Server::Simple::CGI ); use Symbol; my $engine_object; my $net_server; sub set_engine_object { my $self = shift; $engine_object = shift; } sub set_net_server { my $self = shift; $net_server = shift; } sub handler { my $self = shift; eval { $self->handle_request() }; if ( $@ ) { warn "$@\n"; } } sub handle_request_test_xml { my ( $self, $location, $xml ) = @_; $engine_object->{__POST_BODY__} = $xml; $ENV{ CONTENT_LENGTH } = 0; $ENV{ REQUEST_METHOD } = 'POST'; $ENV{ URI } = $location; $ENV{ PATH_INFO } = $location; return $self->_test_helper(); } sub handle_request_test_post { my ( $self, $request ) = @_; my $method = 'POST'; # always GET for tests $request =~ s/^(POST|GET)\://; my( $uri, $args ) = split( /\?/, $request ); $ENV{PATH_INFO} = $uri || $request; $ENV{REQUEST_METHOD} = $method; $ENV{CONTENT_LENGTH} = 0; $ENV{QUERY_STRING} = ( defined $args ? $args : '' ); $ENV{SCRIPT_NAME} = ""; return $self->_test_helper(); } sub handle_request_test { my ( $self, $request ) = @_; my $method = 'GET'; # always GET for tests $request =~ s/^(POST|GET)\://; my( $uri, $args ) = split( /\?/, $request ); $ENV{PATH_INFO} = $uri || $request; $ENV{REQUEST_METHOD} = $method; $ENV{CONTENT_LENGTH} = 0; $ENV{QUERY_STRING} = ( defined $args ? $args : '' ); $ENV{SCRIPT_NAME} = ""; return $self->_test_helper(); } sub _test_helper { my $self = @_; # divert STDOUT to another handle that stores the returned data my $out_handle = gensym; my $out = tie *$out_handle, "Gantry::Server::Tier"; my $original_handle = select $out_handle; # dispatch to the gantry engine my $status; eval { $status = $engine_object->dispatch(); }; if ( $@ ) { return( '401', ( "($@)" . ( $out->get_output() ) ) ); } return( $status, $out->get_output() ); } sub net_server { $net_server ? $net_server : ''; } sub setup_server_url { $ENV{SERVER_URL} ||= ( "http://" . ( $ENV{SERVER_NAME} || '' ) . ":" . $ENV{SERVER_PORT} . "/" ); } sub handle_request { my ( $self ) = @_; # divert STDOUT to another handle that stores the returned data my $out_handle = gensym; my $out = tie *$out_handle, "Gantry::Server::Tier"; my $original_handle = select $out_handle; # dispatch to the gantry engine my $status; eval { $status = $engine_object->dispatch(); }; if ( $@ ) { select $original_handle; print <<"EO_FAILURE_RESPONSE"; HTTP/1.0 401 Not Found Content-type: text/html

Not Found

The requested URL $ENV{PATH_INFO} was not found on this server.
$@ EO_FAILURE_RESPONSE return; } select $original_handle; print "HTTP/1.0 $status\n" . $out->get_output(); } package Gantry::Server::Tier; use strict; sub get_output { my $self = shift; return $self->[1] || ''; } sub TIEHANDLE { my $class = shift; my $self = [ shift() ]; return bless $self, $class; } sub PRINT { my $self = shift; no warnings; $self->[1] .= join '', @_; } 1; =head1 NAME Gantry::Server - HTTP::Server::Simple::CGI subclass providing stand alone server =head1 SYNOPSIS #!/usr/bin/perl use strict; use Gantry::Server; use lib '/home/myhome/lib'; use YourApp qw{ -Engine=CGI -TemplateEngine=Default }; my $cgi_engine = Gantry::Engine::CGI->new(); $cgi_engine->add_location( '/', 'YourApp' ); my $server = Gantry::Server->new(); # pass a port number to the above constructor if you don't want 8080. $server->set_engine_object( $cgi_engine ); $server->run(); =head1 DESCRIPTION This module subclasses HTTP::Server::Simple::CGI to provide a stand alone server for any Gantry app. Pretend you are deploying to a CGI environment, but replace $cgi_engine->dispatch(); with use Gantry::Server; my $server = Gantry::Server->new(); $server->set_engine_object( $cgi_engine ); $server->run(); Note that you must call set_engine_object before calling run, and you must pass it a valid Gantry::Engine::CGI object with the proper locations and config definitions. By default, your server will start on port 8080. If you want a different port, pass it to the constructor. You can generate the above script, with port control, in bigtop by doing this in your config section: config { engine CGI; CGI Gantry { with_server 1; } #... } app YourApp { #... } =head1 METHODS =over 4 =item set_engine_object You must call this before calling run. Pass it a Gantry::Engine::CGI object. =item run This starts the server and never returns. =item handler This method overrides the parent version to avoid taking form parameters prematurely. =item handle_request This method functions as a little web server processing http requests (but it leans heavily on HTTP::Server::Simple::CGI). =item handle_request_test This method pretends to be a web server, but only handles a single request before returning. This is useful for testing your Gantry app without having to use sockets. =item handle_request_test_post This is the same as handle_request_test, but it treats the request as a POST. This is mainly used for form testing. =item handle_request_test_xml This method is like C, but for SOAP packets. Call it with the location you want to hit and the XML packet to PUT there. Returns whatever the server returns. =item net_server Retrieves the defined Net::Sever engine type =item set_net_server optionaly you can set a Net::Sever engine type ( see Net::Server ). $server->set_net_server( 'Net::Server::PreForkSimple' ); =item setup_server_url Builds and sets the SERVER_URL environment variable. =back =head1 AUTHOR Phil Crow =head1 COPYRIGHT and LICENSE Copyright (c) 2006, Phil Crow. This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.6 or, at your option, any later version of Perl 5 you may have available. =cut