NAME
POE::Filter::HTTPD::Chunked - Drop-in replacement for POE::Filter::HTTPD that also support HTTP1.1 chunked transfer-encoding.
SYNOPSIS
#!perl
use warnings;
use strict;
use POE qw(Component::Server::TCP Filter::HTTPD::Chunked);
use HTTP::Response;
POE::Component::Server::TCP->new(
Port => 8088,
ClientFilter => 'POE::Filter::HTTPD::Chunked',
ClientInput => sub {
my $request = $_[ARG0];
# It's a response for the client if there was a problem.
if ($request->isa("HTTP::Response")) {
$_[HEAP]{client}->put($request);
$_[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>" .
"<tr><td>Body</td><td>" . $request->content . "</td>" .
"</body></html>"
);
$_[HEAP]{client}->put($response);
$_[KERNEL]->yield("shutdown");
}
);
print "Aim your browser at port 8088 of this host.\n";
POE::Kernel->run();
exit;
For detail and an example of handling partial chunks, see "Handling of partial chunked data" below.
DESCRIPTION
POE::Filter::HTTPD::Chunked interprets input streams as HTTP requests. It returns a HTTP::Request object 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. This is illustrated in the SYNOPSIS.
For output, this module accepts HTTP::Response objects and returns their corresponding streams.
Please see HTTP::Request and HTTP::Response for details about how to use these objects.
The following are the major differences between this module and the core POE POE::Filter::HTTPD:
- handling of incoming chunked data
-
POE::Filter::HTTPD has no support for handling 'chunked' requests (part of HTTP1.1 spec), and would return an error (in the form of an HTTP::Response object returned to the POE session). For many applications, this may not be a problem, as they can put an HTTP1.1 proxy in front of the application, that will de-chunk the request, and return the normal HTTP/1.0 content-length. This method, however, causes issues with applications that either a/ want to handle partial content for a request as it comes in, b/ don't want to have to artificially adjust request timeouts whilst waiting for the proxy to get the full request or c/ don't want the additional system complexity of having to use a proxy to dechunk.
- support for any request type
-
POE::Filter::HTTPD didn't handle all request types (ie, DELETE). This restriction has been removed in this module.
PUBLIC FILTER METHODS
POE::Filter::HTTPD::Chunked implements the basic POE::Filter interface.
Handling of partial chunked data.
In order to allow for partial handling of data, if an optional constructor argument of 'event_on_chunk' is passed in with a true value, and a partial chunked request has been received since the last time the wheel causes a 'get' call to be emitted, the partial chunked data is returned back. This is wrapped in a class of HTTP::Request::Chunked, which is just a marker sub-class of HTTP::Request, with the following detail set:
- content
-
Will be set to the partial content that has been received, since the last HTTP::Request::Chunked packet was returned.
- x-chunk-offset header
-
The offset (in bytes) as to where the current partial content starts.
- x-chunk-offset-size header
-
The number of bytes in this partial chunk.
Note that the final chunk will never be returned as an HTTP::Request::Chunked object. Instead, the full request will be returned as an HTTP::Request object instead.
An example usage of how partial chunks is as-follows:
my $filter = POE::Filter::HTTPD::Chunked->new( event_on_chunk => 1 );
sub input_event {
my ( $kernel, $heap, $request ) = @_[ KERNEL, HEAP, ARG0 ];
if ( $request->isa( 'HTTP::Response' ) ) {
# if we get an HTTP::Response object in, this was due to an error
# in parsing the request. Just return to client.
$heap->{ wheel }->put( $request );
} elsif ( $request->isa( 'HTTP::Request::Chunked' ) ) {
# more data has come in for the given request. Can just be used
# to reset timeout, or to deal with the data within it.
$kernel->alarm( 'reset_timer' => time() + 30 );
printf(
"got partial data of '%s' from byte offset %i, with size of %i",
$request->content,
$request->header( 'x-chunk-offset' ),
$request->header( 'x-chunk-offset-size' ),
);
} else {
# got a completed request. This will be an HTTP::Request object,
# populated with all data from the original request, converted to
# non-chunked format. If any body was included in the request, be
# it chunked or not, a content-length header will be set, giving
# the number of bytes in the body.
printf(
"got complete request of '%s', with size of '%i'",
$request->content,
$request->header( 'content-length' )
);
}
}
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
SEE ALSO
POE::Filter::HTTPD - Original basis for this class. Note that much of the original POE::Filter::HTTPD code is redone for this class, due to different requirements. Assume that any errors that occour in POE::Filter::HTTPD::Chunked are mine, and not based on this module.
POE::Filter - Superclass, for general Filter API.
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.
AUTHORS AND LICENSE
Mark Morgan <makk384@gmail.com>
Tom Clark <tom@woot.co.uk>
This modules is bassed off of POE::Filter::HTTPD module, contributed by Arthur Bergman, with documentation provided by Rocco Caputo.
Thanks to trutap (www.trutap.com) for paying us whilst developing this code, and allowing it to be released.
Copyright (c) 2008-2010 Mark Morgan. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.