NAME¶
POE::Filter::HTTPD - parse simple HTTP requests, and serialize HTTP::Response
SYNOPSIS¶
#!perl
use warnings;
use strict;
use POE qw(Component::Server::TCP Filter::HTTPD);
use HTTP::Response;
POE::Component::Server::TCP->new(
Port => 8088,
ClientFilter => 'POE::Filter::HTTPD', ### <-- HERE WE ARE!
ClientInput => sub {
my $request = $_[ARG0];
# It's a response for the client if there was a problem.
if ($request->isa("HTTP::Response")) {
my $response = $request;
$request = $response->request;
warn "ERROR: ", $request->message if $request;
$_[HEAP]{client}->put($response);
$_[KERNEL]->yield("shutdown");
return;
}
my $request_fields = '';
$request->headers()->scan(
sub {
my ($header, $value) = @_;
$request_fields .= (
"<tr><td>$header</td><td>$value</td></tr>"
);
}
);
my $response = HTTP::Response->new(200);
$response->push_header( 'Content-type', 'text/html' );
$response->content(
"<html><head><title>Your Request</title></head>" .
"<body>Details about your request:" .
"<table border='1'>$request_fields</table>" .
"</body></html>"
);
$_[HEAP]{client}->put($response);
$_[KERNEL]->yield("shutdown");
}
);
print "Aim your browser at port 8088 of this host.\n";
POE::Kernel->run();
exit;
DESCRIPTION¶
POE::Filter::HTTPD interprets input streams as HTTP 0.9, 1.0 or 1.1 requests. It
returns a HTTP::Request objects upon successfully parsing a request.
On failure, it returns an HTTP::Response object describing the failure. The
intention is that application code will notice the HTTP::Response and send it
back without further processing. The erroneous request object is sometimes
available via the "$r->request" in HTTP::Response method. This is
illustrated in the "SYNOPSIS".
For output, POE::Filter::HTTPD accepts HTTP::Response objects and returns their
corresponding streams.
Please see HTTP::Request and HTTP::Response for details about how to use these
objects.
PUBLIC FILTER METHODS¶
POE::Filter::HTTPD implements the basic POE::Filter interface.
CAVEATS¶
Some versions of libwww are known to generate invalid HTTP. For example, this
code (adapted from the HTTP::Request::Common documentation) will cause an
error in a POE::Filter::HTTPD daemon:
NOTE: Using this test with libwww-perl/5.834 showed that it added the proper
HTTP/1.1 data! We're not sure which version of LWP fixed this. This example is
valid for older LWP installations, beware!
use HTTP::Request::Common;
use LWP::UserAgent;
my $ua = LWP::UserAgent->new();
$ua->request(POST 'http://example.com', [ foo => 'bar' ]);
By default, HTTP::Request is HTTP version agnostic. It makes no attempt to add
an HTTP version header unless you specifically declare a protocol using
"$request->protocol('HTTP/1.0')".
According to the HTTP 1.0 RFC (1945), when faced with no HTTP version header,
the parser is to default to HTTP/0.9. POE::Filter::HTTPD follows this
convention. In the transaction detailed above, the Filter::HTTPD based daemon
will return a 400 error since POST is not a valid HTTP/0.9 request type.
Upon handling a request error, it is most expedient and reliable to respond with
the error and shut down the connection. Invalid HTTP requests may corrupt the
request stream. For example, the absence of a Content-Length header signals
that a request has no content. Requests with content but not that header will
be broken into a content-less request and invalid data. The invalid data may
also appear to be a request! Hilarity will ensue, possibly repeatedly, until
the filter can find the next valid request. By shutting down the connection on
the first sign of error, the client can retry its request with a clean
connection and filter.
It is possible to use POE::Filter::HTTPD for streaming content, but an
application can use it to send headers and then switch to POE::Filter::Stream.
From the input handler (the InputEvent handler if you're using wheels, or the
ClientInput handler for POE::Component::Server::TCP):
my $response = HTTP::Response->new(200);
$response->push_header('Content-type', 'audio/x-mpeg');
$_[HEAP]{client}->put($response);
$_[HEAP]{client}->set_output_filter(POE::Filter::Stream->new());
Then the output-flushed handler (FlushEvent for POE::Wheel::ReadWrite, or
ClientFlushed for POE::Component::Server::TCP) can
put() chunks of the
stream as needed.
my $bytes_read = sysread(
$_[HEAP]{file_to_stream}, my $buffer = '', 4096
);
if ($bytes_read) {
$_[HEAP]{client}->put($buffer);
}
else {
delete $_[HEAP]{file_to_stream};
$_[KERNEL]->yield("shutdown");
}
SEE ALSO¶
Please see POE::Filter for documentation regarding the base interface.
The SEE ALSO section in POE contains a table of contents covering the entire POE
distribution.
HTTP::Request and HTTP::Response explain all the wonderful things you can do
with these classes.
BUGS¶
Many aspects of HTTP 1.0 and higher are not supported, such as keep-alive. A
simple I/O filter can't support keep-alive, for example. A number of more
feature-rich POE HTTP servers are on the CPAN. See
<
http://search.cpan.org/search?query=POE+http+server&mode=dist>
AUTHORS & COPYRIGHTS¶
POE::Filter::HTTPD was contributed by Artur Bergman. Documentation is provided
by Rocco Caputo.
Please see POE for more information about authors and contributors.