NAME
POEx::HTTP::Server - HTTP server in pure POE
SYNOPSIS
use POEx::HTTP::Server;
POEx::HTTP::Server->spawn(
inet => {
LocalPort => 80
},
handlers => [
'^/$' => 'poe:my-alias/root',
'^/static' => 'poe:my-alias/static',
'' => 'poe:my-alias/error'
]
);
# events of session my-alias:
sub root {
my( $heap, $req, $resp ) = @_[HEAP,ARG0,ARG1];
$resp->content_type( 'text/html' );
$resp->content( << HTML );
<html>...</html>
HTML
$resp->done;
}
sub static {
my( $heap, $req, $resp ) = @_[HEAP,ARG0,ARG1];
my $file = File::Spec->catfile( $heap->{root}, $req->path );
$resp->sendfile( $file );
}
sub error {
my( $heap, $req, resp ) = @_[HEAP,ARG0,ARG1];
$resp->error( 404, "Nothing to do for ".$req->path );
}
DESCRIPTION
POEx::HTTP::Server is a clean reimplementation of an HTTP server. It uses POEx::URI to simplify event specification. It allows limiting connection concurrency.
POEx::HTTP::Server differs from POE::Component::Server::HTTP by having a cleaner code base and still being maintained.
POEx::HTTP::Server differs from POE::Component::Server::SimpleHTTP by not using Moose and not using the YELLING-STYLE of parameter passing.
METHODS
spawn
POEx::HTTP::Server->spawn( %CONFIG );
Spawns the server session. %CONFIG
contains one or more of the following parameters:
inet
POEx::HTTP::Server->spawn( inet => $HASHREF );
Specify the parameters handed to POE::Wheel::SocketFactory when creating the listening socket.
As a convenience, LocalAddr
is changed into BindAddress
and LocalPort
into BindPort
.
Defaults to:
POEx::HTTP::Server->spawn( inet => { Listen=>1, BindPort=> 80 } );
handlers
POEx::HTTP::Server->spawn( handlers => $HASHREF );
POEx::HTTP::Server->spawn( handlers => $ARRAYREF );
Set the events that handle a request. Keys to $HASHREF
are regexes which match on all or part of the request path. Values are url to the events that will handle the request.
The regexes are not anchored. This means that /foo
will match the path /something/foo
. Use ^
if that's what you mean; ^/foo
.
Specifiying an $ARRAYREF
allows you to control the order in which the regexes are matched:
POEx::HTTP::Server->spawn( handlers => [
'foo' => 'poe:my-session/foo',
'onk' => 'poe:my-session/onk',
'honk' => 'poe:my-session/honk',
] );
The handler for onk
will always match before honk
can.
Use ''
if you want a catchall handler.
See HANDLERS below.
handler
POEx::HTTP::Server->spawn( handler => $uri );
Syntatic sugar for
POEx::HTTP::Server->spawn( handler => [ '' => $uri ] );
alias
POEx::HTTP::Server->spawn( alias => $ALIAS );
Sets the server session's alias. The alias defaults to 'HTTPd'.
concurrency
POEx::HTTP::Server->spawn( concurrency => $NUM );
Sets the request concurrency level; this is the number of requests that may be serviced in parallel. Defaults to (-1) infinit concurrency.
error
headers
POEx::HTTP::Server->spawn( concurrency => $HASHREF );
All the key/value pairs in $HASHREF
will be set as HTTP headers on all responses.
keepalive
POEx::HTTP::Server->spawn( keepalive => $N );
keepalivetimeout
POEx::HTTP::Server->spawn( keepalivetimeout => $TIME );
options
POEx::HTTP::Server->spawn( options => $HASHREF );
Options passed POE::Session->create. You may also specify debug
to turn on some debugging output.
retry
POEx::HTTP::Server->spawn( retry => $SECONDS );
If binding to the port fails, the server will wait $SECONDS
to retry the operation.
Defaults to 60. Use 0 to turn retry off.
HANDLERS
A handler is a POE event that will handle a given HTTP request. ARG0
is a POEx::HTTP::Server::Request object. ARG1
is a POEx::HTTP::Server::Response object. The handler should query the request object for details or parameters of the request.
my $req = $_[ARG0];
my $file = File::Spec->catfile( $doc_root, $req->uri->path );
my $query = $req->uri->query_form;
my $conn = $req->connection;
my $ip = $conn->remote_ip;
my $port = $conn->remote_port;
The handler must populate the response object with necessary headers and content. If the handler wishes to send an error to the browser, it may set the response code. A default code of RC_OK (200) is used. The response is the send to the browser with either respond
or send
. When the handler is done, done
is called on the response object.
my $resp = $_[ARG1];
$resp->content_type( 'text/plain' );
$resp->content( "Hello world\n" );
$resp->respond;
$resp->done;
use HTTP::Status;
$resp->code( RC_FOUND );
$resp->header( 'Location' => $new_uri );
$resp->content_type( 'text/plain' );
my $io = IO::File->new( $file );
while( <$io> ) {
$resp->send( $_ );
}
$resp->done;
The last example is silly. It would be better to use sendfile
like so:
$resp->content_type( 'image/gif' );
$resp->sendfile( $file );
# Don't call ->done after sendfile
Handlers may chain to other event handlers, using normal POE events. You must keep track of at least the request handler so that you may call done
when the request is finished.
Here is an example of an unrolled loop:
sub handler {
my( $heap, $resp ) = $_[HEAP,ARG1];
$heap->{todo} = [ qw( one two three ) ];
$poe_kernel->yield( next_handler => $resp );
}
sub next_handler {
my( $heap, $resp ) = $_[HEAP,ARG0];
# Get the request object from the response
my $req = $resp->request;
# And you can get the connection object from the request
my $h = shift @{ $heap->{todo} };
if( $h ) {
# Send the content returned by event handlers in another session
$resp->send( $poe_kernel->call( $heap->{session}, $h, $req ) );
$poe_kernel->yeild( next_handler => $resp );
}
else {
$poe_kernel->yield( 'last_handler', $resp );
}
}
sub last_handler {
my( $heap, $resp ) = $_[HEAP,ARG0];
$resp->done;
}
Handler parameters
POE URIs are allowed to have their own parameter. If you use them, they will appear as a hashref in ARG0
with the request and response objects as ARG1
and ARG2
respectively.
POEx::HTTP::Server->spawn( handler => 'poe:my-session/handler?honk=bonk' );
sub handler {
my( $args, $req, $resp ) = @_[ARG0, ARG1, ARG2];
# $args = { honk => 'bonk' }
}
Special handlers
There are 5 special handlers that are invoked when a browser connection is opened and closed, before and after each request and when an error occurs.
The note about "handler parameters" also aplies to special handlers.
on_connect
Invoked when a new connection is made to the server. ARG0
is a POEx::HTTP::Server::Connection object that may be queried for information. This connection object is shared by all requests objects that use this connection.
POEx::HTTP::Server->spawn(
handlers => { on_connect => 'poe:my-session/on_connect' }
);
sub on_connect {
my( $object, $connection ) = @_[OBJECT, ARG0];
# ...
}
It goes without saying that if you use "keepalive" "pre_request" will be invoked more often then on_connect
.
on_disconnect
Invoked when a connection is closed. ARG0
is the same POEx::HTTP::Server::Connection object that was passed to on_connect.
pre_request
Invoked after a request is read from the browser but before it is processed. ARG0
is a POEx::HTTP::Server::Request object. There is no ARG1
.
POEx::HTTP::Server->spawn(
handlers => { pre_request => 'poe:my-session/pre' }
);
sub pre {
my( $object, $request ) = @_[OBJECT, ARG0];
my $connection = $request->connection;
# ...
}
post_request
Invoked after a response has been sent to the browser. ARG0
is a POEx::HTTP::Server::Request object. ARG1
is a POEx::HTTP::Server::Response object, with it's content
cleared.
POEx::HTTP::Server->spawn(
handlers => { pre_request => 'poe:my-session/post' }
);
sub post {
my( $object, $request, $response ) = @_[OBJECT, ARG0, ARG1];
my $connection = $request->connection;
# ...
}
on_error
TODO
EVENTS
shutdown
$poe_kernel->signal( $poe_kernel => 'shutdown' );
$poe_kernel->post( HTTPd => 'shutdown' );
Initiate server shutdown. Note however that any pending requests will stay active. The session will exit when the last of the requests has exited.
handlers_get
TODO
handlers_set
TODO
handlers_add
TODO
handlers_remove
TODO
SEND HEADERS
TO BE WRITEN
STREAMING
TO BE WRITEN
SEE ALSO
AUTHOR
Philip Gwyn, <gwyn -at- cpan.org>
COPYRIGHT AND LICENSE
Copyright (C) 2010 by Philip Gwyn
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.8 or, at your option, any later version of Perl 5 you may have available.