On Sun, 08 Jun 2003 10:47, Rocco Caputo wrote:
> Nice standard, if seemingly baroque. Is it possible to write a
> POE::Filter::FastCGI? If so, you could probably use it just about
> everywhere POE::Filter::Line fits. That would let you write clients and
> servers fairly trivially.
POE::Filter::FastCGI - I wrote that a while ago and gave up finishing
it; I got kind of deadlocked when I considered that POE is kind of
fundamentally incompatible with Tangram; with Tangram, dereferencing a
reference may involve a database query, auto-vivifying a tied scalar
which is distinctly not co-operatively multi-threading :-).
It's not an unsurmountable problem, but it would involve making some
fairly fundamental changes to Tangram to allow it to use
POE::Component::DBI. On the list, but quite far down.
I've attached what I've got as an idea; it doesn't work, but it's a
substantial starting point for those interested. I've implemented it
from the Specs, not the C implementation. I figure that the
implementation would end up looking less like a hack that way.
I've implemented just the packet layer so far - no FastCGI session
management or tracking yet.
Perhaps it was a little ambitious for a starting POE project ;-)
--
Sam Vilain, [EMAIL PROTECTED]
The greatness of a nation and its moral progress can be judged by the
way its animals are treated.
-- Mahatma Gandhi
package FastCGI::Record;
use POE::Preprocessor ( isa => "POE::Filter::FastCGI" );
use base qw(Class::Tangram);
our $schema =
{
fields => {
int => [ qw(RequestId Type) ],
string => { Data => { sql => "BLOB" }, },
},
};
sub Chunks {
my $self = shift;
# pad packets to 8 byte boundaries
my $pad_len = (8 - length $self->{Data}) & 7;
return (# header
pack ("CCvvCC",
FCGI_VERSION_1
$self->Type,
$self->RequestId,
length $self->Data,
$pad_len,
0),
# data
$self->{Data},
# padding
'\0' x $pad_len);
}
1;
package POE::Filter::FastCGI;
use strict;
use POE::Preprocessor;
use FastCGI::Record;
# is it really 1?
const FCGI_VERSION_1 1
# states states states
const WAIT_HEADER 0
const WAIT_DATA 1
const HEADER_LENGTH 8
# start a new filter
sub new { bless { buffer => "",
length => 0,
min_length => HEADER_LENGTH,
state => WAIT_HEADER }, shift }
# positions in the header
const VERSION 0
const TYPE 1
const REQUESTID 2
const LENGTH 3
const PAD_LEN 4
const RESERVED 5
# new data in
sub get {
my $self = shift;
my $stream = shift;
$self->{records} ||= [];
# for a laugh, let's code this like a C programmer would, using
# iterators instead of slurping everything together (this will
# need to be ported to XS or PASM later)
while ( my $chunk = shift @$stream ) {
# add a chunk to the buffer
$self->{buffer} .= $chunk;
$self->{length} += length $chunk;
# and build into records
$self->{wanted} = -1;
$self->get_one();
delete $self->{wanted};
}
# return as many as we can
return delete $self->{records};
}
# slurp in some data
sub get_one_start {
my $self = shift;
my $stream = shift;
while ( my $chunk = shift @$stream ) {
$self->{buffer} .= $chunk;
$self->{length} += length $chunk;
}
}
# fetch a single record
sub get_one {
my $self = shift;
$self->{wanted} ||= 1;
while ( $self->{length} > $self->{min_length}
and (
$self->{wanted} == -1 or
@{$self->{records}} == $self->{wanted}
)
){
# here we have complete sub-chunks
if ( $self->{state} == WAIT_HEADER ) {
# extract info from the header
$self->{header} =
[
unpack "CCvvCC",
substr $self->{buffer}, 0, HEADER_LENGTH
];
# type must be correct
($self->{header}->[VERSION] == FCGI_VERSION_1)
or die "FastCGI protocol error";
# expect data
$self->{state} = WAIT_DATA;
$self->{min_length} = (HEADER_LENGTH +
$self->{header}->[LENGTH] +
$self->{header}->[PAD_LEN]);
} elsif ( $self->{state} == WAIT_DATA ) {
# got the data, eat a packet
$self->{data} = substr ($self->{buffer}, HEADER_LENGTH,
$self->{header}->[LENGTH], "");
# throw away the padding
substr $self->{buffer}, 0, $self->{header}->[PAD_LEN], "";
# go back to initial state
$self->{state} = WAIT_HEADER;
$self->{min_length} = HEADER_LENGTH;
# create a new FastCGI record
push @{ $self->{records} },
FastCGI::Record->new
(
RequestId => $self->{header}->[REQUESTID],
Type => $self->{header}->[TYPE],
Data => $self->{data},
);
}
}
}
sub put {
my $self = shift;
my $records = shift;
return [ map { $_->Chunks } @$records ];
}
sub get_pending {
my $self = shift;
return $self->{buffer};
}