You are viewing a plain text version of this content. The canonical link for it is here.
Posted to modperl@perl.apache.org by Steve Fink <st...@fink.com> on 2002/04/12 23:48:21 UTC
Reading Transfer-Encoding: chunked requests
I have a workable solution, but am hoping somebody knowledgeable about
mod_perl, CGI.pm, and the 'chunked' Transfer-Encoding could critique
it.
I am using sendmail's milter API to capture email messages and pass
them through a perl script that compares the messages against a text
index. I originally used a server written in perl to process the
messages, but then decided to switch over the using Apache as the
server so I wouldn't have to deal with keeping yet another server
alive all the time. Also, I have preexisting mod_perl CGI code that
handles similar documents submitted via a browser.
However, it turns out to be difficult and just a generally bad idea to
buffer messages on the sendmail side before sending them over to the
server, so I'm using the 'chunked' transfer encoding to send a piece
at a time, as soon as each piece arrives at sendmail. Unfortunately,
mod_perl and CGI.pm currently do not support the chunked encoding for
requests.
So I wrote a subclass of CGI.pm that buffers the input and hands it
over to CGI::new(). It seems to work, but I'm calling initialization
routines in a strange way and may be bypassing some of the normal
setup. Is there a recommended way of doing things like this? (Maybe
one that eliminates the need for IO::String?)
Oh, and yes, I know I should set timeouts while reading chunks. And
that I shouldn't assume that all chunked requests are multipart forms.
(Is there an easy way to fix that one?)
Thanks in advance!
Here is my module:
package Apache::ChunkableCGI;
use base 'CGI';
use IO::String;
use constant REQUEST_NO_BODY => 0;
use constant REQUEST_CHUNKED_ERROR => 1;
use constant REQUEST_CHUNKED_DECHUNK => 2;
use constant REQUEST_CHUNKED_PASS => 3;
sub new {
my $self = shift;
my $r = Apache->request;
if ($r->header_in('Transfer-Encoding') ne 'chunked') {
return $self->SUPER::new(@_);
}
my $status = $r->setup_client_block(REQUEST_CHUNKED_DECHUNK);
warn "setup status: $status";
my $data;
if ($r->should_client_block()) {
my $buf;
while ($status = $r->get_client_block($buf, 3 << 20)) {
# warn "get block status: $status";
$data .= $buf;
}
} else {
warn "client should not block";
}
my ($boundary) = $ENV{CONTENT_TYPE} =~ /boundary= \"? ([^\";,]+) \"?/x;
my $fh = IO::String->new(\$data);
# I don't remember why I didn't just use $self->SUPER::new
my $cgi = (ref($self) || $self)->SUPER::new({});
$cgi->read_multipart($boundary, length($data), $fh);
return $cgi;
}
1;