diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 02:57:58 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 02:57:58 +0000 |
commit | be1c7e50e1e8809ea56f2c9d472eccd8ffd73a97 (patch) | |
tree | 9754ff1ca740f6346cf8483ec915d4054bc5da2d /web/server/h2o/libh2o/misc/p5-net-fastcgi/lib | |
parent | Initial commit. (diff) | |
download | netdata-be1c7e50e1e8809ea56f2c9d472eccd8ffd73a97.tar.xz netdata-be1c7e50e1e8809ea56f2c9d472eccd8ffd73a97.zip |
Adding upstream version 1.44.3.upstream/1.44.3upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'web/server/h2o/libh2o/misc/p5-net-fastcgi/lib')
9 files changed, 3102 insertions, 0 deletions
diff --git a/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI.pm b/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI.pm new file mode 100644 index 00000000..4126b219 --- /dev/null +++ b/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI.pm @@ -0,0 +1,12 @@ +package Net::FastCGI; + +use strict; +use warnings; + +our $VERSION = '0.14'; + +use Net::FastCGI::Constant; +use Net::FastCGI::Protocol; + +1; + diff --git a/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI.pod b/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI.pod new file mode 100644 index 00000000..65725b95 --- /dev/null +++ b/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI.pod @@ -0,0 +1,170 @@ +=head1 NAME + +Net::FastCGI - FastCGI Toolkit + +=head1 DESCRIPTION + +This distribution aims to provide a complete API for working with the FastCGI +protocol. + +The primary goal is to provide a function oriented and object oriented API which +are not tied to a specific I/O model or framework. + +Secondary goal is to provide higher level tools/API which can be used for debugging +and interoperability testing. + +=head1 PROGRESS + +The function oriented API is considered feature complete. L<Net::FastCGI::Protocol> +provides functions to build and parse all FastCGI v1.0 messages, also provided is a +few convenient higher level functions such as C<build_begin_request()>, +C<build_end_request()>, C<parse_record()> and C<dump_record()>. + +Work has begun on object oriented implementation and a simple blocking I/O class which is +intended for testing and debugging. + +=head1 PACKAGES + +=over 4 + +=item L<Net::FastCGI::Constant> + +FastCGI protocol constants. + +=item L<Net::FastCGI::IO> + +Provides functions to read and write FastCGI messages. + +=item L<Net::FastCGI::Protocol> + +Provides functions to build and parse FastCGI messages. + +=back + +=head1 ENVIRONMENT + +Environment variable C<NET_FASTCGI_PP> can be set to a true value before loading +this package to disable usage of XS implementation. + +=head1 PREREQUISITES + +=head2 Run-Time + +=over 4 + +=item L<perl> 5.6 or greater. + +=item L<Carp>, core module. + +=item L<Exporter>, core module. + +=back + +=head2 Build-Time + +In addition to Run-Time: + +=over 4 + +=item L<Test::More> 0.47 or greater, core module since 5.6.2. + +=item L<Test::Exception>. + +=item L<Test::HexString>. + +=back + +=head1 SEE ALSO + +=head2 Community + +=over 4 + +=item Official FastCGI site + +L<http://www.fastcgi.com/> + +=back + +=head2 Standards + +=over 4 + +=item FastCGI Specification Version 1.0 + +L<http://www.fastcgi.com/devkit/doc/fcgi-spec.html> + +=item RFC 3875 - The Common Gateway Interface (CGI) Version 1.1 + +L<http://tools.ietf.org/html/rfc3875> + +=back + +=head2 White papers + +=over 4 + +=item FastCGI: A High-Performance Web Server Interface + +L<http://www.fastcgi.com/devkit/doc/fastcgi-whitepaper/fastcgi.htm> + +=item FastCGI - The Forgotten Treasure + +L<http://cryp.to/publications/fastcgi/> + +=back + +=head2 Perl implementations + +=over 4 + +=item L<AnyEvent::FCGI> + +Application server implementation, built on top of L<AnyEvent>. Supports Responder role. +Capable of multiplexing. + +=item L<FCGI> + +Application server implementation, built on top of C<libfcgi> (reference implementation). +Supports all FastCGI roles. Responds to management records. Processes requests synchronously. + +=item L<FCGI::Async> + +Application server implementation, built on top of L<IO::Async>. Supports Responder role. +Responds to management records. Capable of multiplexing. + +=item L<FCGI::Client> + +Client (Web server) implementation. Supports Responder role. + +=item L<FCGI::EV> + +Application server implementation, built on top of L<EV>. Supports Responder role. + +=item L<Mojo::Server::FastCGI> + +Application server implementation. Supports Responder role. Processes requests synchronously. + +=item L<POE::Component::FastCGI> + +Application server implementation, built on top of L<POE>. Supports Responder role. +Capable of multiplexing. + +=back + +=head1 SUPPORT + +Please report any bugs or feature requests to C<bug-net-fastcgi@rt.cpan.org>, or through +the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Net-FastCGI> + +=head1 AUTHOR + +Christian Hansen C<chansen@cpan.org> + +=head1 COPYRIGHT + +Copyright 2008-2010 by Christian Hansen. + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself. + diff --git a/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/Constant.pm b/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/Constant.pm new file mode 100644 index 00000000..1e86dbf1 --- /dev/null +++ b/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/Constant.pm @@ -0,0 +1,179 @@ +package Net::FastCGI::Constant; + +use strict; +use warnings; + +BEGIN { + our $VERSION = '0.14'; + my @common = qw[ FCGI_MAX_CONTENT_LEN + FCGI_MAX_LEN + FCGI_HEADER_LEN + FCGI_VERSION_1 + FCGI_NULL_REQUEST_ID ]; + + my @type = qw[ FCGI_BEGIN_REQUEST + FCGI_ABORT_REQUEST + FCGI_END_REQUEST + FCGI_PARAMS + FCGI_STDIN + FCGI_STDOUT + FCGI_STDERR + FCGI_DATA + FCGI_GET_VALUES + FCGI_GET_VALUES_RESULT + FCGI_UNKNOWN_TYPE + FCGI_MAXTYPE ]; + + my @role = qw[ FCGI_RESPONDER + FCGI_AUTHORIZER + FCGI_FILTER ]; + + my @flag = qw[ FCGI_KEEP_CONN ]; + + my @protocol_status = qw[ FCGI_REQUEST_COMPLETE + FCGI_CANT_MPX_CONN + FCGI_OVERLOADED + FCGI_UNKNOWN_ROLE ]; + + my @value = qw[ FCGI_MAX_CONNS + FCGI_MAX_REQS + FCGI_MPXS_CONNS ]; + + my @pack = qw[ FCGI_Header + FCGI_BeginRequestBody + FCGI_EndRequestBody + FCGI_UnknownTypeBody ]; + + my @name = qw[ @FCGI_TYPE_NAME + @FCGI_RECORD_NAME + @FCGI_ROLE_NAME + @FCGI_PROTOCOL_STATUS_NAME ]; + + our @EXPORT_OK = ( @common, + @type, + @role, + @flag, + @protocol_status, + @value, + @pack, + @name ); + + our %EXPORT_TAGS = ( all => \@EXPORT_OK, + common => \@common, + type => \@type, + role => \@role, + flag => \@flag, + protocol_status => \@protocol_status, + value => \@value, + pack => \@pack ); + + our @FCGI_TYPE_NAME = ( + undef, # 0 + 'FCGI_BEGIN_REQUEST', # 1 + 'FCGI_ABORT_REQUEST', # 2 + 'FCGI_END_REQUEST', # 3 + 'FCGI_PARAMS', # 4 + 'FCGI_STDIN', # 5 + 'FCGI_STDOUT', # 6 + 'FCGI_STDERR', # 7 + 'FCGI_DATA', # 8 + 'FCGI_GET_VALUES', # 9 + 'FCGI_GET_VALUES_RESULT', # 10 + 'FCGI_UNKNOWN_TYPE' # 11 + ); + + our @FCGI_RECORD_NAME = ( + undef, # 0 + 'FCGI_BeginRequestRecord', # 1 + 'FCGI_AbortRequestRecord', # 2 + 'FCGI_EndRequestRecord', # 3 + 'FCGI_ParamsRecord', # 4 + 'FCGI_StdinRecord', # 5 + 'FCGI_StdoutRecord', # 6 + 'FCGI_StderrRecord', # 7 + 'FCGI_DataRecord', # 8 + 'FCGI_GetValuesRecord', # 9 + 'FCGI_GetValuesResultRecord', # 10 + 'FCGI_UnknownTypeRecord', # 11 + ); + + our @FCGI_ROLE_NAME = ( + undef, # 0 + 'FCGI_RESPONDER', # 1 + 'FCGI_AUTHORIZER', # 2 + 'FCGI_FILTER', # 3 + ); + + our @FCGI_PROTOCOL_STATUS_NAME = ( + 'FCGI_REQUEST_COMPLETE', # 0 + 'FCGI_CANT_MPX_CONN', # 1 + 'FCGI_OVERLOADED', # 2 + 'FCGI_UNKNOWN_ROLE', # 3 + ); + + if (Internals->can('SvREADONLY')) { # 5.8 + Internals::SvREADONLY(@FCGI_TYPE_NAME, 1); + Internals::SvREADONLY(@FCGI_RECORD_NAME, 1); + Internals::SvREADONLY(@FCGI_ROLE_NAME, 1); + Internals::SvREADONLY(@FCGI_PROTOCOL_STATUS_NAME, 1); + Internals::SvREADONLY($_, 1) for @FCGI_TYPE_NAME, + @FCGI_RECORD_NAME, + @FCGI_ROLE_NAME, + @FCGI_PROTOCOL_STATUS_NAME; + } + + require Exporter; + *import = \&Exporter::import; +} + + +sub FCGI_LISTENSOCK_FILENO () { 0 } + +# common +sub FCGI_MAX_CONTENT_LEN () { 0xFFFF } +sub FCGI_MAX_LEN () { 0xFFFF } # deprecated +sub FCGI_HEADER_LEN () { 8 } +sub FCGI_VERSION_1 () { 1 } +sub FCGI_NULL_REQUEST_ID () { 0 } + +# type +sub FCGI_BEGIN_REQUEST () { 1 } +sub FCGI_ABORT_REQUEST () { 2 } +sub FCGI_END_REQUEST () { 3 } +sub FCGI_PARAMS () { 4 } +sub FCGI_STDIN () { 5 } +sub FCGI_STDOUT () { 6 } +sub FCGI_STDERR () { 7 } +sub FCGI_DATA () { 8 } +sub FCGI_GET_VALUES () { 9 } +sub FCGI_GET_VALUES_RESULT () { 10 } +sub FCGI_UNKNOWN_TYPE () { 11 } +sub FCGI_MAXTYPE () { FCGI_UNKNOWN_TYPE } + +# role +sub FCGI_RESPONDER () { 1 } +sub FCGI_AUTHORIZER () { 2 } +sub FCGI_FILTER () { 3 } + +# flags +sub FCGI_KEEP_CONN () { 1 } + +# protocol status +sub FCGI_REQUEST_COMPLETE () { 0 } +sub FCGI_CANT_MPX_CONN () { 1 } +sub FCGI_OVERLOADED () { 2 } +sub FCGI_UNKNOWN_ROLE () { 3 } + +# value +sub FCGI_MAX_CONNS () { 'FCGI_MAX_CONNS' } +sub FCGI_MAX_REQS () { 'FCGI_MAX_REQS' } +sub FCGI_MPXS_CONNS () { 'FCGI_MPXS_CONNS' } + +# pack +sub FCGI_Header () { 'CCnnCx' } +sub FCGI_BeginRequestBody () { 'nCx5' } +sub FCGI_EndRequestBody () { 'NCx3' } +sub FCGI_UnknownTypeBody () { 'Cx7' } + +1; + diff --git a/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/Constant.pod b/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/Constant.pod new file mode 100644 index 00000000..d0ca04c1 --- /dev/null +++ b/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/Constant.pod @@ -0,0 +1,264 @@ +=head1 NAME + +Net::FastCGI::Constant - FastCGI protocol constants. + +=head1 DESCRIPTION + +FastCGI protocol constants. + +=head1 CONSTANTS + +Constants can either be imported individually or in sets grouped by tag names. +The tag names are: + +=head2 C<:common> + +=over 4 + +=item C<FCGI_MAX_CONTENT_LEN> + +Maximum number of octets that the content component of the record can hold. (C<65535>) + +=item C<FCGI_HEADER_LEN> + +Number of octets in C<FCGI_Header>. (C<8>) + +=item C<FCGI_VERSION_1> + +Value for C<version> component of C<FCGI_Header>. (C<1>) + +=item C<FCGI_NULL_REQUEST_ID> + +Value for C<request_id> component of C<FCGI_Header>. (C<0>) + +=back + +=head2 C<:type> + +Values for C<type> component of C<FCGI_Header>. + +=over 4 + +=item C<FCGI_BEGIN_REQUEST> + +=item C<FCGI_ABORT_REQUEST> + +=item C<FCGI_END_REQUEST> + +=item C<FCGI_PARAMS> + +=item C<FCGI_STDIN> + +=item C<FCGI_STDOUT> + +=item C<FCGI_STDERR> + +=item C<FCGI_DATA> + +=item C<FCGI_GET_VALUES> + +=item C<FCGI_GET_VALUES_RESULT> + +=item C<FCGI_UNKNOWN_TYPE> + +=item C<FCGI_MAXTYPE> + +=back + +=head2 C<:flag> + +Mask for C<flags> component of C<FCGI_BeginRequestBody>. + +=over 4 + +=item C<FCGI_KEEP_CONN> + +=back + +=head2 C<:role> + +Values for C<role> component of C<FCGI_BeginRequestBody>. + +=over 4 + +=item C<FCGI_RESPONDER> + +=item C<FCGI_AUTHORIZER> + +=item C<FCGI_FILTER> + +=back + +=head2 C<:protocol_status> + +Values for C<protocol_status> component of C<FCGI_EndRequestBody>. + +=over 4 + +=item C<FCGI_REQUEST_COMPLETE> + +=item C<FCGI_CANT_MPX_CONN> + +=item C<FCGI_OVERLOADED> + +=item C<FCGI_UNKNOWN_ROLE> + +=back + +=head2 C<:value> + +Variable names for C<FCGI_GET_VALUES> / C<FCGI_GET_VALUES_RESULT> records. + +=over 4 + +=item C<FCGI_MAX_CONNS> + +=item C<FCGI_MAX_REQS> + +=item C<FCGI_MPXS_CONNS> + +=back + +=head2 C<:pack> + +C<pack()> / C<unpack()> templates + +=over 4 + +=item C<FCGI_Header> + + Octet/ 0 | 1 | + / | | + | 0 1 2 3 4 5 6 7 | 0 1 2 3 4 5 6 7 | + +-----------------+-----------------+ + 0 | Version | Type | + +-----------------+-----------------+ + 2 | Request ID | + +-----------------+-----------------+ + 4 | Content Length | + +-----------------+-----------------+ + 6 | Padding Length | Reserved | + +-----------------+-----------------+ + Total 8 octets + + Template: CCnnCx + + my ($version, $type, $request_id, $content_length, $padding_length) + = unpack(FCGI_Header, $octets); + +=item C<FCGI_BeginRequestBody> + + Octet/ 0 | 1 | + / | | + | 0 1 2 3 4 5 6 7 | 0 1 2 3 4 5 6 7 | + +-----------------+-----------------+ + 0 | Role | + +-----------------+-----------------+ + 2 | Flags | | + +-----------------+ + + 4 | | + + Reserved + + 6 | | + +-----------------+-----------------+ + Total 8 octets + + Template: nCx5 + + my ($role, $flags) = unpack(FCGI_BeginRequestBody, $octets); + +=item C<FCGI_EndRequestBody> + + Octet/ 0 | 1 | + / | | + | 0 1 2 3 4 5 6 7 | 0 1 2 3 4 5 6 7 | + +-----------------+-----------------+ + 0 | | + + Application Status + + 2 | | + +-----------------+-----------------+ + 4 | Protocol Status | | + +-----------------+ Reserved + + 6 | | + +-----------------+-----------------+ + Total 8 octets + + Template: NCx3 + + my ($app_status, $protocol_status) + = unpack(FCGI_EndRequestBody, $octets); + +=item C<FCGI_UnknownTypeBody> + + Octet/ 0 | 1 | + / | | + | 0 1 2 3 4 5 6 7 | 0 1 2 3 4 5 6 7 | + +-----------------+-----------------+ + 0 | Unknown Type | | + +-----------------+ + + 2 | | + + + + 4 | Reserved | + + + + 6 | | + +-----------------+-----------------+ + Total 8 octets + + Template: Cx7 + + my $unknown_type = unpack(FCGI_UnknownTypeBody, $octets); + +=back + +=head2 C<:name> + +Arrays containing names of value components. These are read-only. + +=over 4 + +=item C<@FCGI_TYPE_NAME> + + print $FCGI_TYPE_NAME[FCGI_BEGIN_REQUEST]; # FCGI_BEGIN_REQUEST + +=item C<@FCGI_ROLE_NAME> + + print $FCGI_ROLE_NAME[FCGI_RESPONDER]; # FCGI_RESPONDER + +=item C<@FCGI_PROTOCOL_STATUS_NAME> + + print $FCGI_PROTOCOL_STATUS_NAME[FCGI_OVERLOADED]; # FCGI_OVERLOADED + +=back + +I<Note> + +It's not safe to assume that C<exists> works for validation purposes, index C<0> +might be C<undef>. + +Use boolean context instead: + + ($FCGI_TYPE_NAME[$type]) + || die; + +=head1 EXPORTS + +None by default. All functions can be exported using the C<:all> tag or individually. + +=head1 SEE ALSO + +=over 4 + +=item L<http://www.fastcgi.com/devkit/doc/fcgi-spec.html> + +=back + +=head1 AUTHOR + +Christian Hansen C<chansen@cpan.org> + +=head1 COPYRIGHT + +Copyright 2008-2010 by Christian Hansen. + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself. + diff --git a/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/IO.pm b/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/IO.pm new file mode 100644 index 00000000..15583fb5 --- /dev/null +++ b/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/IO.pm @@ -0,0 +1,227 @@ +package Net::FastCGI::IO; +use strict; +use warnings; +use warnings::register; + +use Carp qw[]; +use Errno qw[EBADF EINTR EPIPE]; +use Net::FastCGI::Constant qw[FCGI_HEADER_LEN]; +use Net::FastCGI::Protocol qw[build_header build_record build_stream + parse_header parse_record]; + +BEGIN { + our $VERSION = '0.14'; + our @EXPORT_OK = qw[ can_read + can_write + read_header + read_record + write_header + write_record + write_stream ]; + + our %EXPORT_TAGS = ( all => \@EXPORT_OK ); + + require Exporter; + *import = \&Exporter::import; + + eval q<use Time::HiRes 'time'>; +} + +*throw = \&Carp::croak; + +sub read_header { + @_ == 1 || throw(q/Usage: read_header(fh)/); + my ($fh) = @_; + + my $len = FCGI_HEADER_LEN; + my $off = 0; + my $buf; + + while ($len) { + my $r = sysread($fh, $buf, $len, $off); + if (defined $r) { + last unless $r; + $len -= $r; + $off += $r; + } + elsif ($! != EINTR) { + warnings::warn(qq<FastCGI: Could not read FCGI_Header: '$!'>) + if warnings::enabled; + return; + } + } + if ($len) { + $! = $off ? EPIPE : 0; + warnings::warn(q<FastCGI: Could not read FCGI_Header: Unexpected end of stream>) + if $off && warnings::enabled; + return; + } + return parse_header($buf); +} + +sub write_header { + @_ == 5 || throw(q/Usage: write_header(fh, type, request_id, content_length, padding_length)/); + my $fh = shift; + + my $buf = &build_header; + my $len = FCGI_HEADER_LEN; + my $off = 0; + + while () { + my $r = syswrite($fh, $buf, $len, $off); + if (defined $r) { + $len -= $r; + $off += $r; + last unless $len; + } + elsif ($! != EINTR) { + warnings::warn(qq<FastCGI: Could not write FCGI_Header: '$!'>) + if warnings::enabled; + return undef; + } + } + return $off; +} + +sub read_record { + @_ == 1 || throw(q/Usage: read_record(fh)/); + my ($fh) = @_; + + my $len = FCGI_HEADER_LEN; + my $off = 0; + my $buf; + + while ($len) { + my $r = sysread($fh, $buf, $len, $off); + if (defined $r) { + last unless $r; + $len -= $r; + $off += $r; + if (!$len && $off == FCGI_HEADER_LEN) { + $len = vec($buf, 2, 16) # Content Length + + vec($buf, 6, 8); # Padding Length + } + } + elsif ($! != EINTR) { + warnings::warn(qq<FastCGI: Could not read FCGI_Record: '$!'>) + if warnings::enabled; + return; + } + } + if ($len) { + $! = $off ? EPIPE : 0; + warnings::warn(q<FastCGI: Could not read FCGI_Record: Unexpected end of stream>) + if $off && warnings::enabled; + return; + } + return parse_record($buf); +} + +sub write_record { + @_ == 4 || @_ == 5 || throw(q/Usage: write_record(fh, type, request_id [, content])/); + my $fh = shift; + + my $buf = &build_record; + my $len = length $buf; + my $off = 0; + + while () { + my $r = syswrite($fh, $buf, $len, $off); + if (defined $r) { + $len -= $r; + $off += $r; + last unless $len; + } + elsif ($! != EINTR) { + warnings::warn(qq<FastCGI: Could not write FCGI_Record: '$!'>) + if warnings::enabled; + return undef; + } + } + return $off; +} + +sub write_stream { + @_ == 4 || @_ == 5 || throw(q/Usage: write_stream(fh, type, request_id, content [, terminate])/); + my $fh = shift; + + my $buf = &build_stream; + my $len = length $buf; + my $off = 0; + + while () { + my $r = syswrite($fh, $buf, $len, $off); + if (defined $r) { + $len -= $r; + $off += $r; + last unless $len; + } + elsif ($! != EINTR) { + warnings::warn(qq<FastCGI: Could not write FCGI_Record stream: '$!'>) + if warnings::enabled; + return undef; + } + } + return $off; +} + +sub can_read (*$) { + @_ == 2 || throw(q/Usage: can_read(fh, timeout)/); + my ($fh, $timeout) = @_; + + my $fd = fileno($fh); + unless (defined $fd && $fd >= 0) { + $! = EBADF; + return undef; + } + + my $initial = time; + my $pending = $timeout; + my $nfound; + + vec(my $fdset = '', $fd, 1) = 1; + + while () { + $nfound = select($fdset, undef, undef, $pending); + if ($nfound == -1) { + return undef unless $! == EINTR; + redo if !$timeout || ($pending = $timeout - (time - $initial)) > 0; + $nfound = 0; + } + last; + } + $! = 0; + return $nfound; +} + +sub can_write (*$) { + @_ == 2 || throw(q/Usage: can_write(fh, timeout)/); + my ($fh, $timeout) = @_; + + my $fd = fileno($fh); + unless (defined $fd && $fd >= 0) { + $! = EBADF; + return undef; + } + + my $initial = time; + my $pending = $timeout; + my $nfound; + + vec(my $fdset = '', $fd, 1) = 1; + + while () { + $nfound = select(undef, $fdset, undef, $pending); + if ($nfound == -1) { + return undef unless $! == EINTR; + redo if !$timeout || ($pending = $timeout - (time - $initial)) > 0; + $nfound = 0; + } + last; + } + $! = 0; + return $nfound; +} + +1; + diff --git a/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/IO.pod b/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/IO.pod new file mode 100644 index 00000000..84a9f097 --- /dev/null +++ b/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/IO.pod @@ -0,0 +1,391 @@ +=head1 NAME + +Net::FastCGI::IO - Provides functions to read and write FastCGI messages. + +=head1 SYNOPSIS + + # FCGI_Header + @values = read_header($fh); + $header = read_header($fh); + $result = write_header($fh, $type, $request_id, $content_length, $padding_length); + + # FCGI_Record + @values = read_record($fh); + $record = read_record($fh); + $result = write_record($fh, $type, $request_id); + $result = write_record($fh, $type, $request_id, $content); + + # FCGI_Record Stream + $result = write_stream($fh, $type, $request_id, $content); + $result = write_stream($fh, $type, $request_id, $content, $terminate); + + # I/O ready + $result = can_read($fh, $timeout); + $result = can_write($fh, $timeout); + +=head1 DESCRIPTION + +Provides unbuffered blocking I/O functions to read and write FastCGI messages. + +=head1 FUNCTIONS + +=head2 read_header + +Attempts to read a C<FCGI_Header> from file handle C<$fh>. + +I<Usage> + + ($type, $request_id, $content_length, $padding_length) + = read_header($fh); + + $header = read_header($fh); + say $header->{type}; + say $header->{request_id}; + say $header->{content_length}; + say $header->{padding_length}; + +I<Arguments> + +=over 4 + +=item C<$fh> + +The file handle to read from. Must be a file handle with a file descriptor. File handle +mode should be set to binary. + +=back + +I<Returns> + +Upon successful completion, the return value of L<Net::FastCGI::Protocol/parse_header>. +Otherwise, a false value (C<undef> in scalar context and an empty list in list context). + +If C<read_header> reaches end-of-file before reading any octets, it returns a +false value. If unsuccessful, C<read_header> returns a false value and C<$!> +contains the error from the C<sysread> call. If C<read_header> encounters +end-of-file after some but not all of the needed octets, the function returns +a false value and sets C<$!> to C<EPIPE>. + +I<Implementation> + +The implementation calls C<sysread> in a loop, restarting if C<sysread> +returns C<undef> with C<$!> set to C<EINTR>. If C<sysread> does not provide +all the requested octets, C<read_header> continues to call C<sysread> until +either all the octets have been read, reaches end-of-file or an error occurs. + +=head2 read_record + +Attempts to read a C<FCGI_Record> from file handle C<$fh>. + +I<Usage> + + ($type, $request_id, $content) + = read_record($fh); + + $record = read_record($fh); + say $record->{type}; + say $record->{request_id}; + +I<Arguments> + +=over 4 + +=item C<$fh> + +The file handle to read from. Must be a file handle with a file descriptor. +File handle mode should be set to binary. + +=back + +I<Returns> + +Upon successful completion, the return value of L<Net::FastCGI::Protocol/parse_record>. +Otherwise, a false value (C<undef> in scalar context and an empty list in list context). + +If C<read_record> reaches end-of-file before reading any octets, it returns a +false value. If unsuccessful, C<read_record> returns a false value and C<$!> +contains the error from the C<sysread> call. If C<read_record> encounters +end-of-file after some but not all of the needed octets, the function returns +a false value and sets C<$!> to C<EPIPE>. + +I<Implementation> + +The implementation calls C<sysread> in a loop, restarting if C<sysread> +returns C<undef> with C<$!> set to C<EINTR>. If C<sysread> does not provide +all the requested octets, C<read_record> continues to call C<sysread> until +either all the octets have been read, reaches end-of-file or an error occurs. + +=head2 write_header + +Attempts to write a C<FCGI_Header> to file handle C<$fh>. + +I<Usage> + + $result = write_header($fh, $type, $request_id, $content_length, $padding_length); + +I<Arguments> + +=over 4 + +=item C<$fh> + +The file handle to write to. Must be a file handle with a file descriptor. File handle +mode should be set to binary. + +=item C<$type> + +An unsigned 8-bit integer. + +=item C<$request_id> + +An unsigned 16-bit integer. + +=item C<$content_length> + +An unsigned 16-bit integer. + +=item C<$padding_length> + +An unsigned 8-bit integer. + +=back + +I<Returns> + +=over 4 + +=item C<$result> + +Upon successful completion, the number of octets actually written. Otherwise, +C<undef> and C<$!> contains the error from the C<syswrite> call. + +=back + +I<Implementation> + +The implementation calls C<syswrite> in a loop, restarting if C<syswrite> +returns C<undef> with C<$!> set to C<EINTR>. If C<syswrite> does not output +all the requested octets, C<write_header> continues to call C<syswrite> until +all the octets have been written or an error occurs. + +=head2 write_record + +Attempts to write a C<FCGI_Record> to file handle C<$fh>. + +I<Usage> + + $result = write_record($fh, $type, $request_id); + $result = write_record($fh, $type, $request_id, $content); + +I<Arguments> + +=over 4 + +=item C<$fh> + +The file handle to write to. Must be a file handle with a file descriptor. File handle +mode should be set to binary. + +=item C<$type> + +An unsigned 8-bit integer. + +=item C<$request_id> + +An unsigned 16-bit integer. + +=item C<$content> (optional) + +A string of octets containing the content, cannot exceed 65535 octets in length. + +=back + +I<Returns> + +=over 4 + +=item C<$result> + +Upon successful completion, the number of octets actually written. Otherwise, +C<undef> and C<$!> contains the error from the C<syswrite> call. + +=back + +I<Implementation> + +The implementation calls C<syswrite> in a loop, restarting if C<syswrite> +returns C<undef> with C<$!> set to C<EINTR>. If C<syswrite> does not output +all the requested octets, C<write_record> continues to call C<syswrite> until +all the octets have been written or an error occurs. + +=head2 write_stream + +Attempts to write a C<FCGI_Record> stream to file handle C<$fh>. + +I<Usage> + + $result = write_stream($fh, $type, $request_id, $content); + $result = write_stream($fh, $type, $request_id, $content, $terminate); + +I<Arguments> + +=over 4 + +=item C<$fh> + +The file handle to write to. Must be a file handle with a file descriptor. File handle +mode should be set to binary. + +=item C<$type> + +An unsigned 8-bit integer. + +=item C<$request_id> + +An unsigned 16-bit integer. + +=item C<$content> + +A string of octets containing the stream content. + +=item C<$terminate> (optional) + +A boolean indicating whether or not the stream should be terminated. +Defaults to false. + +=back + +I<Returns> + +=over 4 + +=item C<$result> + +Upon successful completion, the number of octets actually written. Otherwise, +C<undef> and C<$!> contains the error from the C<syswrite> call. + +=back + +I<Implementation> + +The implementation calls C<syswrite> in a loop, restarting if C<syswrite> +returns C<undef> with C<$!> set to C<EINTR>. If C<syswrite> does not output +all the requested octets, C<write_stream> continues to call C<syswrite> until +all the octets have been written or an error occurs. + +=head2 can_read + +Determines wheter or not the given file handle C<$fh> is ready for reading +within the given timeout C<$timeout>. + +I<Usage> + + $result = can_read($fh, $timeout); + +I<Arguments> + +=over 4 + +=item C<$fh> + +The file handle to test for readiness. Must be a file handle with a file descriptor. + +=item C<$timeout> + +Maximum interval to wait. Can be set to either a non-negative numeric value or +C<undef> for infinite wait. + +=back + +I<Returns> + +Upon successful completion, C<0> or C<1>. Otherwise, C<undef> and C<$!> contains +the C<select> error. + +I<Implementation> + +The implementation calls C<select> in a loop, restarting if C<select> returns +C<-1> with C<$!> set to C<EINTR> and C<$timeout> has not elapsed. + +=head2 can_write + +Determines wheter or not the given file handle C<$fh> is ready for writing +within the given timeout C<$timeout>. + +I<Usage> + + $result = can_write($fh, $timeout); + +I<Arguments> + +=over 4 + +=item C<$fh> + +The file handle to test for readiness. Must be a file handle with a file descriptor. + +=item C<$timeout> + +Maximum interval to wait. Can be set to either a non-negative numeric value or +C<undef> for infinite wait. + +=back + +I<Returns> + +Upon successful completion, C<0> or C<1>. Otherwise, C<undef> and C<$!> contains +the C<select> error. + +I<Implementation> + +The implementation calls C<select> in a loop, restarting if C<select> returns +C<-1> with C<$!> set to C<EINTR> and C<$timeout> has not elapsed. + +=head1 EXPORTS + +None by default. All functions can be exported using the C<:all> tag or individually. + +=head1 DIAGNOSTICS + +=over 4 + +=item B<(F)> Usage: %s + +Subroutine called with wrong number of arguments. + +=item B<(W Net::FastCGI::IO)> FastCGI: Could not read %s + +=item B<(W Net::FastCGI::IO)> FastCGI: Could not write %s + +=back + +=head1 SEE ALSO + +=over 4 + +=item FastCGI Specification Version 1.0 + +L<http://www.fastcgi.com/devkit/doc/fcgi-spec.html> + +=item The Common Gateway Interface (CGI) Version 1.1 + +L<http://tools.ietf.org/html/rfc3875> + +=item L<Net::FastCGI::Constant> + +=item L<Net::FastCGI::Protocol> + +=back + +=head1 AUTHOR + +Christian Hansen C<chansen@cpan.org> + +=head1 COPYRIGHT + +Copyright 2008-2010 by Christian Hansen. + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself. + + diff --git a/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/Protocol.pm b/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/Protocol.pm new file mode 100644 index 00000000..0c4210e9 --- /dev/null +++ b/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/Protocol.pm @@ -0,0 +1,203 @@ +package Net::FastCGI::Protocol; + +use strict; +use warnings; + +use Carp qw[croak]; +use Net::FastCGI qw[]; +use Net::FastCGI::Constant qw[:type :common FCGI_KEEP_CONN]; + +BEGIN { + our $VERSION = '0.14'; + our @EXPORT_OK = qw[ build_begin_request + build_begin_request_body + build_begin_request_record + build_end_request + build_end_request_body + build_end_request_record + build_header + build_params + build_record + build_stream + build_unknown_type_body + build_unknown_type_record + check_params + parse_begin_request_body + parse_end_request_body + parse_header + parse_params + parse_record + parse_record_body + parse_unknown_type_body + get_record_length + get_type_name + get_role_name + get_protocol_status_name + is_known_type + is_management_type + is_discrete_type + is_stream_type ]; + + our %EXPORT_TAGS = ( all => \@EXPORT_OK ); + + my $use_pp = $ENV{NET_FASTCGI_PP} || $ENV{NET_FASTCGI_PROTOCOL_PP}; + + if (!$use_pp) { + eval { + require Net::FastCGI::Protocol::XS; + }; + $use_pp = !!$@; + } + + if ($use_pp) { + require Net::FastCGI::Protocol::PP; + Net::FastCGI::Protocol::PP->import(@EXPORT_OK); + } + else { + Net::FastCGI::Protocol::XS->import(@EXPORT_OK); + } + + # shared between XS and PP implementation + push @EXPORT_OK, 'dump_record', 'dump_record_body'; + + require Exporter; + *import = \&Exporter::import; +} + +our $DUMP_RECORD_MAX = 78; # undocumented +our $DUMP_RECORD_ALIGN = !!0; # undocumented + +my %ESCAPES = ( + "\a" => "\\a", + "\b" => "\\b", + "\t" => "\\t", + "\n" => "\\n", + "\f" => "\\f", + "\r" => "\\r", +); + +sub dump_record { + goto \&dump_record_body if (@_ == 2 || @_ == 3); # deprecated + @_ == 1 || croak(q/Usage: dump_record(octets)/); + + my $len = &get_record_length; + ($len && $len <= length $_[0] && vec($_[0], 0, 8) == FCGI_VERSION_1) + || return '{Malformed FCGI_Record}'; + + return dump_record_body(&parse_record); +} + +sub dump_record_body { + @_ == 2 || @_ == 3 || croak(q/Usage: dump_record_body(type, request_id [, content])/); + my ($type, $request_id) = @_; + + my $content_length = defined $_[2] ? length $_[2] : 0; + + my $max = $DUMP_RECORD_MAX > 0 ? $DUMP_RECORD_MAX : FCGI_MAX_CONTENT_LEN; + my $out = ''; + + if ( $type == FCGI_PARAMS + || $type == FCGI_GET_VALUES + || $type == FCGI_GET_VALUES_RESULT) { + if ($content_length == 0) { + $out = q[""]; + } + elsif (check_params($_[2])) { + my ($off, $klen, $vlen) = (0); + while ($off < $content_length) { + my $pos = $off; + for ($klen, $vlen) { + $_ = vec($_[2], $off, 8); + $_ = vec(substr($_[2], $off, 4), 0, 32) & 0x7FFF_FFFF + if $_ > 0x7F; + $off += $_ > 0x7F ? 4 : 1; + } + + my $head = substr($_[2], $pos, $off - $pos); + $head =~ s/(.)/sprintf('\\%.3o',ord($1))/egs; + $out .= $head; + + my $body = substr($_[2], $off, $klen + $vlen); + for ($body) { + s/([\\\"])/\\$1/g; + s/([\a\b\t\n\f\r])/$ESCAPES{$1}/g; + s/([^\x20-\x7E])/sprintf('\\x%.2X',ord($1))/eg; + } + $out .= $body; + $off += $klen + $vlen; + last if $off > $max; + } + substr($out, $max - 5) = ' ... ' + if length $out > $max; + $out = qq["$out"]; + } + else { + $out = 'Malformed FCGI_NameValuePair(s)'; + } + } + elsif ( $type == FCGI_BEGIN_REQUEST + || $type == FCGI_END_REQUEST + || $type == FCGI_UNKNOWN_TYPE) { + if ($content_length != 8) { + my $name = $type == FCGI_BEGIN_REQUEST ? 'FCGI_BeginRequestBody' + : $type == FCGI_END_REQUEST ? 'FCGI_EndRequestBody' + : 'FCGI_UnknownTypeBody'; + $out = sprintf '{Malformed %s (expected 8 octets got %d)}', $name, $content_length; + } + elsif ($type == FCGI_BEGIN_REQUEST) { + my ($role, $flags) = parse_begin_request_body($_[2]); + if ($flags != 0) { + my @set; + if ($flags & FCGI_KEEP_CONN) { + $flags &= ~FCGI_KEEP_CONN; + push @set, 'FCGI_KEEP_CONN'; + } + if ($flags) { + push @set, sprintf '0x%.2X', $flags; + } + $flags = join '|', @set; + } + $out = sprintf '{%s, %s}', get_role_name($role), $flags; + } + elsif($type == FCGI_END_REQUEST) { + my ($astatus, $pstatus) = parse_end_request_body($_[2]); + $out = sprintf '{%d, %s}', $astatus, get_protocol_status_name($pstatus); + } + else { + my $unknown_type = parse_unknown_type_body($_[2]); + $out = sprintf '{%s}', get_type_name($unknown_type); + } + } + elsif ($content_length) { + my $looks_like_binary = do { + my $count = () = $_[2] =~ /[\r\n\t\x20-\x7E]/g; + ($count / $content_length) < 0.7; + }; + $out = substr($_[2], 0, $max + 1); + for ($out) { + if ($looks_like_binary) { + s/(.)/sprintf('\\x%.2X',ord($1))/egs; + } + else { + s/([\\\"])/\\$1/g; + s/([\a\b\t\n\f\r])/$ESCAPES{$1}/g; + s/([^\x20-\x7E])/sprintf('\\x%.2X',ord($1))/eg; + } + } + substr($out, $max - 5) = ' ... ' + if length $out > $max; + $out = qq["$out"]; + } + else { + $out = q[""]; + } + + my $name = get_type_name($type); + my $width = 0; + $width = 27 - length $name # length("FCGI_GET_VALUES_RESULT") == 22 + if $DUMP_RECORD_ALIGN; # + length(0xFFFF) == 5 + return sprintf '{%s, %*d, %s}', $name, $width, $request_id, $out; +} + +1; + diff --git a/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/Protocol.pod b/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/Protocol.pod new file mode 100644 index 00000000..64f6a7e6 --- /dev/null +++ b/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/Protocol.pod @@ -0,0 +1,1227 @@ +=head1 NAME + +Net::FastCGI::Protocol - Provides functions to build and parse FastCGI messages. + +=head1 SYNOPSIS + + # FCGI_Header + $octets = build_header($type, $request_id, $content_length, $padding_length); + @values = parse_header($octets); + $header = parse_header($octets); + + # FCGI_BeginRequestBody + $octets = build_begin_request_body($role, $flags); + @values = parse_begin_request_body($octets); + + # FCGI_EndRequestBody + $octets = build_end_request_body($app_status, $protocol_status); + @values = parse_end_request_body($octets); + + # FCGI_UnknownTypeBody + $octets = build_unknown_type_body($type); + @values = parse_unknown_type_body($octets); + + # FCGI_BeginRequestRecord + $octets = build_begin_request_record($request_id, $role, $flags); + + # FCGI_EndRequestRecord + $octets = build_end_request_record($request_id, $app_status, $protocol_status); + + # FCGI_UnknownTypeRecord + $octets = build_unknown_type_record($type); + + # FCGI_NameValuePair's + $octets = build_params($params); + $params = parse_params($octets); + $bool = check_params($octets); + + # FCGI_Record + $octets = build_record($type, $request_id); + $octets = build_record($type, $request_id, $content); + @values = parse_record($octets); + $record = parse_record($octets); + $record = parse_record_body($type, $request_id, $content); + + # FCGI_Record Debugging / Tracing + $string = dump_record($octets); + $string = dump_record_body($type, $request_id, $content); + + # FCGI_Record Stream + $octets = build_stream($type, $request_id, $content); + $octets = build_stream($type, $request_id, $content, $terminate); + + # Begin Request + $octets = build_begin_request($request_id, $role, $flags, $params); + $octets = build_begin_request($request_id, $role, $flags, $params, $stdin); + $octets = build_begin_request($request_id, $role, $flags, $params, $stdin, $data); + + # End Request + $octets = build_end_request($request_id, $app_status, $protocol_status); + $octets = build_end_request($request_id, $app_status, $protocol_status, $stdout); + $octets = build_end_request($request_id, $app_status, $protocol_status, $stdout, $stderr); + +=head1 DESCRIPTION + +Provides functions to build and parse FastCGI messages. + +=head1 FUNCTIONS + +Please note that all functions in this package expects octets, not unicode strings. +It's the callers responsibility to ensure this. If any of theese functions is called +with unicode strings containing code points above 255, they will most likely produce +malformed messages. + +=head2 build_begin_request + +Builds a Begin Request message. + +I<Usage> + + $octets = build_begin_request($request_id, $role, $flags, $params); + $octets = build_begin_request($request_id, $role, $flags, $params, $stdin); + $octets = build_begin_request($request_id, $role, $flags, $params, $stdin, $data); + +I<Arguments> + +=over 4 + +=item C<$request_id> + +An unsigned 16-bit integer. Identifier of the request. + +=item C<$role> + +An unsigned 16-bit integer. This should be set to either C<FCGI_RESPONDER>, +C<FCGI_AUTHORIZER> or C<FCGI_FILTER>. + +=item C<$flags> + +An unsigned 8-bit integer. This should be set to either C<0> or contain the +mask C<FCGI_KEEP_CONN> if a persistent connection is desired. + +=item C<$params> + +A hash reference containing name-value pairs. This is the CGI environ that the +application expects. + +=item C<$stdin> (optional) + +A string of octets containing the C<FCGI_STDIN> content. This should only be +provided if C<$role> is set to either C<FCGI_RESPONDER> or C<FCGI_FILTER>. The +C<FCGI_STDIN> stream is terminated if provided. + +=item C<$data> (optional) + +A string of octets containing the C<FCGI_DATA> content. This should only be +provided if C<$role> is set to C<FCGI_FILTER>. The C<FCGI_DATA> stream is +terminated if provided. + +=back + +I<Returns> + +=over 4 + +=item C<$octets> + +A string of octets containing the message. + +=back + +=head2 build_begin_request_body + +Builds a C<FCGI_BeginRequestBody>. + +I<Usage> + + $octets = build_begin_request_body($role, $flags); + +I<Arguments> + +=over 4 + +=item C<$role> + +An unsigned 16-bit integer. + +=item C<$flags> + +An unsigned 8-bit integer. + +=back + +I<Returns> + +=over 4 + +=item C<$octets> + +A string of octets containing the body. String is 8 octets in length. + +=back + +=head2 build_begin_request_record + +Builds a C<FCGI_BeginRequestRecord>. + +I<Usage> + + $octets = build_begin_request_record($request_id, $role, $flags); + +I<Arguments> + +=over 4 + +=item C<$request_id> + +An unsigned 16-bit integer. + +=item C<$role> + +An unsigned 16-bit integer. + +=item C<$flags> + +An unsigned 8-bit integer. + +=back + +I<Returns> + +=over 4 + +=item C<$octets> + +A string of octets containing the record. String is 16 octets in length. + +=back + +=head2 build_end_request + +Builds a End Request message + +I<Usage> + + $octets = build_end_request($request_id, $app_status, $protocol_status); + $octets = build_end_request($request_id, $app_status, $protocol_status, $stdout); + $octets = build_end_request($request_id, $app_status, $protocol_status, $stdout, $stderr); + +I<Arguments> + +=over 4 + +=item C<$request_id> + +An unsigned 16-bit integer. Identifier of the request. + +=item C<$app_status> + +An unsigned 32-bit integer. Application status code of the request. + +=item C<$protocol_status> + +An unsigned 8-bit integer. This should be set to either C<FCGI_REQUEST_COMPLETE>, +C<FCGI_CANT_MPX_CONN>, C<FCGI_OVERLOADED> or C<FCGI_UNKNOWN_ROLE>. + +=item C<$stdout> (optional) + +A string of octets containing the C<FCGI_STDOUT> content. The C<FCGI_STDOUT> +stream is terminated if provided. + +=item C<$stderr> (optional) + +A string of octets containing the C<FCGI_STDERR> content. The C<FCGI_STDERR> +stream is terminated if provided. + +=back + +I<Returns> + +=over 4 + +=item C<$octets> + +A string of octets containing the message. + +=back + +I<Note> + +This function is equivalent to C<build_end_request_record()> if called without +C<$stdout> and C<$stderr>. + +=head2 build_end_request_body + +Builds a C<FCGI_EndRequestBody>. + +I<Usage> + + $octets = build_end_request_body($app_status, $protocol_status); + +I<Arguments> + +=over 4 + +=item C<$app_status> + +An unsigned 32-bit integer. + +=item C<$protocol_status> + +An unsigned 8-bit integer. + +=back + +I<Returns> + +=over 4 + +=item C<$octets> + +A string of octets containing the body. String is 8 octets in length. + +=back + +=head2 build_end_request_record + +Builds a C<FCGI_EndRequestRecord>. + +I<Usage> + + $octets = build_end_request_record($request_id, $app_status, $protocol_status); + +I<Arguments> + +=over 4 + +=item C<$request_id> + +An unsigned 16-bit integer. + +=item C<$app_status> + +An unsigned 32-bit integer. + +=item C<$protocol_status> + +An unsigned 8-bit integer. + +=back + +I<Returns> + +=over 4 + +=item C<$octets> + +A string of octets containing the record. String is 16 octets in length. + +=back + +=head2 build_header + +Builds a C<FCGI_Header>. + +I<Usage> + + $octets = build_header($type, $request_id, $content_length, $padding_length); + +I<Arguments> + +=over 4 + +=item C<$type> + +An unsigned 8-bit integer. + +=item C<$request_id> + +An unsigned 16-bit integer. + +=item C<$content_length> + +An unsigned 16-bit integer. + +=item C<$padding_length> + +An unsigned 8-bit integer. + +=back + +I<Returns> + +=over 4 + +=item C<$octets> + +A string of octets containing the header. String is 8 octets in length. + +=back + +=head2 build_params + +Builds C<FCGI_NameValuePair>'s. + +I<Usage> + + $octets = build_params($params); + +I<Arguments> + +=over 4 + +=item C<$params> + +A hash reference containing name-value pairs. + +=back + +I<Returns> + +=over 4 + +=item C<$octets> + +=back + +=head2 build_record + +Builds a C<FCGI_Record>. + +I<Usage> + + $octets = build_record($type, $request_id); + $octets = build_record($type, $request_id, $content); + +I<Arguments> + +=over 4 + +=item C<$type> + +An unsigned 8-bit integer. + +=item C<$request_id> + +An unsigned 16-bit integer. + +=item C<$content> (optional) + +A string of octets containing the content, cannot exceed 65535 octets in length. + +=back + +I<Returns> + +=over 4 + +=item C<$octets> + +A string of octets containing the record. + +=back + +I<Note> + +Follows the recommendation in specification and pads the record by +8-(content_length mod 8) zero-octets. + +=head2 build_stream + +Builds a series of stream records. + +I<Usage> + + $octets = build_stream($type, $request_id, $content); + $octets = build_stream($type, $request_id, $content, $terminate); + +I<Arguments> + +=over 4 + +=item C<$type> + +An unsigned 8-bit integer. + +=item C<$request_id> + +An unsigned 16-bit integer. + +=item C<$content> + +A string of octets containing the stream content. + +=item C<$terminate> (optional) + +A boolean indicating whether or not the stream should be terminated. +Defaults to false. + +=back + +I<Returns> + +=over 4 + +=item C<$octets> + +A string of octets containing the stream. + +=back + +I<Note> + +Stream is not terminated if C<$content> is empty unless C<$terminate> is set. + +C<$content> is split in segment sizes of 32760 octets (32768 - FCGI_HEADER_LEN). + +=head2 build_unknown_type_body + +Builds a C<FCGI_UnknownTypeBody>. + +I<Usage> + + $octets = build_unknown_type_body($type); + +I<Arguments> + +=over 4 + +=item C<$type> + +An unsigned 8-bit integer. + +=back + +I<Returns> + +=over 4 + +=item C<$octets> + +A string of octets containing the body. String is 8 octets in length. + +=back + +=head2 build_unknown_type_record + +Builds a C<FCGI_UnknownTypRecord>. + +I<Usage> + + $octets = build_unknown_type_record($type); + +I<Arguments> + +=over 4 + +=item C<$type> + +An unsigned 8-bit integer. + +=back + +I<Returns> + +=over 4 + +=item C<$octets> + +A string of octets containing the record. String is 16 octets in length. + +=back + +=head2 check_params + +Determine wheter or not params is well-formed. + +I<Usage> + + $boolean = check_params($octets); + +I<Arguments> + +=over 4 + +=item C<$octets> + +A string of octets containing C<FCGI_NameValuePair>'s. + +=back + +I<Returns> + +=over 4 + +=item C<$boolean> + +A boolean indicating whether or not C<$octets> consist of well-formed C<FCGI_NameValuePair>'s. + +=back + +=head2 dump_record + +Dump a C<FCGI_Record>. + +I<Usage> + + $string = dump_record($octets); + +I<Arguments> + +=over 4 + +=item C<$octets> + +A string of octets containing at least one record. + +=back + +I<Returns> + +=over 4 + +=item C<$string> + +A short (less than 100 characters) string representation of the record in printable US-ASCII. + +=back + +=head2 dump_record_body + +Dump a C<FCGI_Record>. + +I<Usage> + + $string = dump_record_body($type, $request_id); + $string = dump_record_body($type, $request_id, $content); + +I<Arguments> + +=over 4 + +=item C<$type> + +An unsigned 8-bit integer. + +=item C<$request_id> + +An unsigned 16-bit integer. + +=item C<$content> (optional) + +A string of octets containing the content. + +=back + +I<Returns> + +=over 4 + +=item C<$string> + +A short (less than 100 characters) string representation of the record in printable US-ASCII. + +=back + +=head2 parse_begin_request_body + +Parses a C<FCGI_BeginRequestBody>. + +I<Usage> + + ($role, $flags) = parse_begin_request_body($octets); + +I<Arguments> + +=over 4 + +=item C<$octets> + +A string of octets containing the body, must be greater than or equal to 8 octets in length. + +=back + +I<Returns> + +=over 4 + +=item C<$role> + +An unsigned 16-bit integer. + +=item C<$flags> + +An unsigned 8-bit integer. + +=back + +=head2 parse_end_request_body + +Parses a C<FCGI_EndRequestBody>. + +I<Usage> + + ($app_status, $protocol_status) = parse_end_request_body($octets); + +I<Arguments> + +=over 4 + +=item C<$octets> + +A string of octets containing the body, must be greater than or equal to 8 octets in length. + +=back + +I<Returns> + +=over 4 + +=item C<$app_status> + +An unsigned 32-bit integer. + +=item C<$flags> + +An unsigned 8-bit integer. + +=back + +=head2 parse_header + +Parses a C<FCGI_Header>. + +I<Usage> + + ($type, $request_id, $content_length, $padding_length) + = parse_header($octets); + + $header = parse_header($octets); + say $header->{type}; + say $header->{request_id}; + say $header->{content_length}; + say $header->{padding_length}; + +I<Arguments> + +=over 4 + +=item C<$octets> + +A string of octets containing the header, must be greater than or equal to 8 octets in length. + +=back + +I<Returns> + +In list context: + +=over 4 + +=item C<$type> + +An unsigned 8-bit integer. + +=item C<$request_id> + +An unsigned 16-bit integer. + +=item C<$content_length> + +An unsigned 16-bit integer. + +=item C<$padding_length> + +An unsigned 8-bit integer. + +=back + +In scalar context a hash reference containing above variable names as keys. + +=head2 parse_params + +Parses C<FCGI_NameValuePair>'s. + +I<Usage> + + $params = parse_params($octets); + +I<Arguments> + +=over 4 + +=item C<$octets> + +A string of octets containing C<FCGI_NameValuePair>'s. + +=back + +I<Returns> + +=over 4 + +=item C<$params> + +A hash reference containing name-value pairs. + +=back + +=head2 parse_record + +Parses a C<FCGI_Record>. + +I<Usage> + + ($type, $request_id, $content) + = parse_record($octets); + + $record = parse_record($octets); + say $record->{type}; + say $record->{request_id}; + +I<Arguments> + +=over 4 + +=item C<$octets> + +A string of octets containing at least one record. + +=back + +I<Returns> + +In list context: + +=over 4 + +=item C<$type> + +An unsigned 8-bit integer. + +=item C<$request_id> + +An unsigned 16-bit integer. + +=item C<$content> + +A string of octets containing the record content. + +=back + +In scalar context a hash reference containing the C<FCGI_Record> components. +See L</parse_record_body>. + +=head2 parse_record_body + +Parses a C<FCGI_Record>. + +I<Usage> + + $record = parse_record_body($type, $request_id, $content); + say $record->{type}; + say $record->{request_id}; + +I<Arguments> + +=over 4 + +=item C<$type> + +An unsigned 8-bit integer. + +=item C<$request_id> + +An unsigned 16-bit integer. + +=item C<$content> + +A string of octets containing the record content. + +=back + +I<Returns> + +A hash reference which represents the C<FCGI_Record>. The content depends on the +type of record. All record types have the keys: C<type> and C<request_id>. + +=over 4 + +=item C<FCGI_BEGIN_REQUEST> + +=over 8 + +=item C<role> + +An unsigned 16-bit integer. + +=item C<flags> + +An unsigned 8-bit integer. + +=back + +=item C<FCGI_END_REQUEST> + +=over 8 + +=item C<app_status> + +An unsigned 32-bit integer. + +=item C<protocol_status> + +An unsigned 8-bit integer. + +=back + +=item C<FCGI_PARAMS> + +=item C<FCGI_STDIN> + +=item C<FCGI_DATA> + +=item C<FCGI_STDOUT> + +=item C<FCGI_STDERR> + +=over 8 + +=item C<content> + +A string of octets containing the content of the stream. + +=back + +=item C<FCGI_GET_VALUES> + +=item C<FCGI_GET_VALUES_RESULT> + +=over 8 + +=item C<values> + +A hash reference containing name-value pairs. + +=back + +=item C<FCGI_UNKNOWN_TYPE> + +=over 8 + +=item C<unknown_type> + +An unsigned 8-bit integer. + +=back + +=back + +=head2 parse_unknown_type_body + +Parses a C<FCGI_UnknownTypeBody>. + +I<Usage> + + $type = parse_unknown_type_body($octets); + +I<Arguments> + +=over 4 + +=item C<$octets> + +C<$octets> must be greater than or equal to 8 octets in length. + +=back + +I<Returns> + +=over 4 + +=item C<$type> + +An unsigned 8-bit integer. + +=back + +=head2 get_record_length + +I<Usage> + + $length = get_record_length($octets); + +I<Arguments> + +=over 4 + +=item C<$octets> + +A string of octets containing at least one C<FCGI_Header>. + +=back + +I<Returns> + +=over 4 + +=item C<$length> + +An unsigned integer containing the length of record in octets. If C<$octets> +contains insufficient octets C<(< FCGI_HEADER_LEN)> C<0> is returned. + +=back + +=head2 get_type_name + +I<Usage> + + $name = get_type_name($type); + $name = get_type_name(FCGI_BEGIN_REQUEST); # 'FCGI_BEGIN_REQUEST' + $name = get_type_name(255); # '0xFF' + +I<Arguments> + +=over 4 + +=item C<$type> + +An unsigned 8-bit integer. + +=back + +I<Returns> + +=over 4 + +=item C<$name> + +A string containing the name of the type. If C<$type> is not a known v1.0 type, +a hexadecimal value is returned. + +=back + +I<Note> + +See also L<Net::FastCGI::Constant/":name">. + +=head2 get_role_name + +I<Usage> + + $name = get_role_name($type); + $name = get_role_name(FCGI_RESPONDER); # 'FCGI_RESPONDER' + $name = get_role_name(65535); # '0xFFFF' + +I<Arguments> + +=over 4 + +=item C<$role> + +An unsigned 16-bit integer. + +=back + +I<Returns> + +=over 4 + +=item C<$name> + +A string containing the name of the role. If C<$role> is not a known v1.0 role, +a hexadecimal value is returned. + +=back + +I<Note> + +See also L<Net::FastCGI::Constant/":name">. + +=head2 get_protocol_status_name + +I<Usage> + + $name = get_protocol_status_name($protocol_status); + $name = get_protocol_status_name(FCGI_REQUEST_COMPLETE); # 'FCGI_REQUEST_COMPLETE' + $name = get_protocol_status_name(255); # '0xFF' + +I<Arguments> + +=over 4 + +=item C<$protocol_status> + +An unsigned 8-bit integer. + +=back + +I<Returns> + +=over 4 + +=item C<$name> + +A string containing the name of the protocol status. If C<$protocol_status> is +not a known v1.0 protocol status code, a hexadecimal value is returned. + +=back + +I<Note> + +See also L<Net::FastCGI::Constant/:name>. + +=head2 is_known_type + +I<Usage> + + $boolean = is_known_type($type); + +I<Arguments> + +=over 4 + +=item C<$type> + +An unsigned 8-bit integer. + +=back + +I<Returns> + +=over 4 + +=item C<$boolean> + +A boolean indicating whether or not C<$type> is a known FastCGI v1.0 type. + +=back + +=head2 is_management_type + +I<Usage> + + $boolean = is_management_type($type); + +I<Arguments> + +=over 4 + +=item C<$type> + +An unsigned 8-bit integer. + +=back + +I<Returns> + +=over 4 + +=item C<$boolean> + +A boolean indicating whether or not C<$type> is a management type. + +=back + +=head2 is_discrete_type + +I<Usage> + + $boolean = is_discrete_type($type); + +I<Arguments> + +=over 4 + +=item C<$type> + +An unsigned 8-bit integer. + +=back + +I<Returns> + +=over 4 + +=item C<$boolean> + +A boolean indicating whether or not C<$type> is a discrete type. + +=back + +=head2 is_stream_type + +I<Usage> + + $boolean = is_stream_type($type); + +I<Arguments> + +=over 4 + +=item C<$type> + +An unsigned 8-bit integer. + +=back + +I<Returns> + +=over 4 + +=item C<$boolean> + +A boolean indicating whether or not C<$type> is a stream type. + +=back + +=head1 EXPORTS + +None by default. All functions can be exported using the C<:all> tag or individually. + +=head1 DIAGNOSTICS + +=over 4 + +=item B<(F)> Usage: %s + +Subroutine called with wrong number of arguments. + +=item B<(F)> Invalid Argument: %s + +=item B<(F)> FastCGI: Insufficient number of octets to parse %s + +=item B<(F)> FastCGI: Malformed record %s + +=item B<(F)> FastCGI: Protocol version mismatch (0x%.2X) + +=back + +=head1 SEE ALSO + +=over 4 + +=item FastCGI Specification Version 1.0 + +L<http://www.fastcgi.com/devkit/doc/fcgi-spec.html> + +=item The Common Gateway Interface (CGI) Version 1.1 + +L<http://tools.ietf.org/html/rfc3875> + +=item L<Net::FastCGI::Constant> + + +=back + +=head1 AUTHOR + +Christian Hansen C<chansen@cpan.org> + +=head1 COPYRIGHT + +Copyright 2008-2010 by Christian Hansen. + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself. + diff --git a/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/Protocol/PP.pm b/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/Protocol/PP.pm new file mode 100644 index 00000000..bfba2579 --- /dev/null +++ b/web/server/h2o/libh2o/misc/p5-net-fastcgi/lib/Net/FastCGI/Protocol/PP.pm @@ -0,0 +1,429 @@ +package Net::FastCGI::Protocol::PP; +use strict; +use warnings; + +use Carp qw[]; +use Net::FastCGI::Constant qw[:all]; + +BEGIN { + our $VERSION = '0.14'; + our @EXPORT_OK = qw[ build_begin_request + build_begin_request_body + build_begin_request_record + build_end_request + build_end_request_body + build_end_request_record + build_header + build_params + build_record + build_stream + build_unknown_type_body + build_unknown_type_record + check_params + parse_begin_request_body + parse_end_request_body + parse_header + parse_params + parse_record + parse_record_body + parse_unknown_type_body + is_known_type + is_management_type + is_discrete_type + is_stream_type + get_record_length + get_role_name + get_type_name + get_protocol_status_name ]; + + our %EXPORT_TAGS = ( all => \@EXPORT_OK ); + + require Exporter; + *import = \&Exporter::import; +} + +sub TRUE () { !!1 } +sub FALSE () { !!0 } + +sub ERRMSG_OCTETS () { q/FastCGI: Insufficient number of octets to parse %s/ } +sub ERRMSG_MALFORMED () { q/FastCGI: Malformed record %s/ } +sub ERRMSG_VERSION () { q/FastCGI: Protocol version mismatch (0x%.2X)/ } +sub ERRMSG_OCTETS_LE () { q/Invalid Argument: '%s' cannot exceed %u octets in length/ } + +sub throw { + @_ = ( sprintf($_[0], @_[1..$#_]) ) if @_ > 1; + goto \&Carp::croak; +} + +# FCGI_Header + +sub build_header { + @_ == 4 || throw(q/Usage: build_header(type, request_id, content_length, padding_length)/); + return pack(FCGI_Header, FCGI_VERSION_1, @_); +} + +sub parse_header { + @_ == 1 || throw(q/Usage: parse_header(octets)/); + (defined $_[0] && length $_[0] >= 8) + || throw(ERRMSG_OCTETS, q/FCGI_Header/); + (vec($_[0], 0, 8) == FCGI_VERSION_1) + || throw(ERRMSG_VERSION, unpack('C', $_[0])); + return unpack('xCnnCx', $_[0]) + if wantarray; + my %header; + @header{qw(type request_id content_length padding_length)} + = unpack('xCnnCx', $_[0]); + return \%header; +} + +# FCGI_BeginRequestBody + +sub build_begin_request_body { + @_ == 2 || throw(q/Usage: build_begin_request_body(role, flags)/); + return pack(FCGI_BeginRequestBody, @_); +} + +sub parse_begin_request_body { + @_ == 1 || throw(q/Usage: parse_begin_request_body(octets)/); + (defined $_[0] && length $_[0] >= 8) + || throw(ERRMSG_OCTETS, q/FCGI_BeginRequestBody/); + return unpack(FCGI_BeginRequestBody, $_[0]); +} + +# FCGI_EndRequestBody + +sub build_end_request_body { + @_ == 2 || throw(q/Usage: build_end_request_body(app_status, protocol_status)/); + return pack(FCGI_EndRequestBody, @_); +} + +sub parse_end_request_body { + @_ == 1 || throw(q/Usage: parse_end_request_body(octets)/); + (defined $_[0] && length $_[0] >= 8) + || throw(ERRMSG_OCTETS, q/FCGI_EndRequestBody/); + return unpack(FCGI_EndRequestBody, $_[0]); +} + +# FCGI_UnknownTypeBody + +sub build_unknown_type_body { + @_ == 1 || throw(q/Usage: build_unknown_type_body(type)/); + return pack(FCGI_UnknownTypeBody, @_); +} + +sub parse_unknown_type_body { + @_ == 1 || throw(q/Usage: parse_unknown_type_body(octets)/); + (defined $_[0] && length $_[0] >= 8) + || throw(ERRMSG_OCTETS, q/FCGI_UnknownTypeBody/); + return unpack(FCGI_UnknownTypeBody, $_[0]); +} + +# FCGI_BeginRequestRecord + +sub build_begin_request_record { + @_ == 3 || throw(q/Usage: build_begin_request_record(request_id, role, flags)/); + my ($request_id, $role, $flags) = @_; + return build_record(FCGI_BEGIN_REQUEST, $request_id, + build_begin_request_body($role, $flags)); +} + +# FCGI_EndRequestRecord + +sub build_end_request_record { + @_ == 3 || throw(q/Usage: build_end_request_record(request_id, app_status, protocol_status)/); + my ($request_id, $app_status, $protocol_status) = @_; + return build_record(FCGI_END_REQUEST, $request_id, + build_end_request_body($app_status, $protocol_status)); +} + +# FCGI_UnknownTypeRecord + +sub build_unknown_type_record { + @_ == 1 || throw(q/Usage: build_unknown_type_record(type)/); + my ($type) = @_; + return build_record(FCGI_UNKNOWN_TYPE, FCGI_NULL_REQUEST_ID, + build_unknown_type_body($type)); +} + +sub build_record { + @_ == 2 || @_ == 3 || throw(q/Usage: build_record(type, request_id [, content])/); + my ($type, $request_id) = @_; + + my $content_length = defined $_[2] ? length $_[2] : 0; + my $padding_length = (8 - ($content_length % 8)) % 8; + + ($content_length <= FCGI_MAX_CONTENT_LEN) + || throw(ERRMSG_OCTETS_LE, q/content/, FCGI_MAX_CONTENT_LEN); + + my $res = build_header($type, $request_id, $content_length, $padding_length); + + if ($content_length) { + $res .= $_[2]; + } + + if ($padding_length) { + $res .= "\x00" x $padding_length; + } + + return $res; +} + +sub parse_record { + @_ == 1 || throw(q/Usage: parse_record(octets)/); + my ($type, $request_id, $content_length) = &parse_header; + + (length $_[0] >= FCGI_HEADER_LEN + $content_length) + || throw(ERRMSG_OCTETS, q/FCGI_Record/); + + return wantarray + ? ($type, $request_id, substr($_[0], FCGI_HEADER_LEN, $content_length)) + : parse_record_body($type, $request_id, + substr($_[0], FCGI_HEADER_LEN, $content_length)); +} + +sub parse_record_body { + @_ == 3 || throw(q/Usage: parse_record_body(type, request_id, content)/); + my ($type, $request_id) = @_; + + my $content_length = defined $_[2] ? length $_[2] : 0; + + ($content_length <= FCGI_MAX_CONTENT_LEN) + || throw(ERRMSG_OCTETS_LE, q/content/, FCGI_MAX_CONTENT_LEN); + + my %record = (type => $type, request_id => $request_id); + if ($type == FCGI_BEGIN_REQUEST) { + ($request_id != FCGI_NULL_REQUEST_ID && $content_length == 8) + || throw(ERRMSG_MALFORMED, q/FCGI_BeginRequestRecord/); + @record{ qw(role flags) } = parse_begin_request_body($_[2]); + } + elsif ($type == FCGI_ABORT_REQUEST) { + ($request_id != FCGI_NULL_REQUEST_ID && $content_length == 0) + || throw(ERRMSG_MALFORMED, q/FCGI_AbortRequestRecord/); + } + elsif ($type == FCGI_END_REQUEST) { + ($request_id != FCGI_NULL_REQUEST_ID && $content_length == 8) + || throw(ERRMSG_MALFORMED, q/FCGI_EndRequestRecord/); + @record{ qw(app_status protocol_status) } + = parse_end_request_body($_[2]); + } + elsif ( $type == FCGI_PARAMS + || $type == FCGI_STDIN + || $type == FCGI_STDOUT + || $type == FCGI_STDERR + || $type == FCGI_DATA) { + ($request_id != FCGI_NULL_REQUEST_ID) + || throw(ERRMSG_MALFORMED, $FCGI_RECORD_NAME[$type]); + $record{content} = $content_length ? $_[2] : ''; + } + elsif ( $type == FCGI_GET_VALUES + || $type == FCGI_GET_VALUES_RESULT) { + ($request_id == FCGI_NULL_REQUEST_ID) + || throw(ERRMSG_MALFORMED, $FCGI_RECORD_NAME[$type]); + $record{values} = parse_params($_[2]); + } + elsif ($type == FCGI_UNKNOWN_TYPE) { + ($request_id == FCGI_NULL_REQUEST_ID && $content_length == 8) + || throw(ERRMSG_MALFORMED, q/FCGI_UnknownTypeRecord/); + $record{unknown_type} = parse_unknown_type_body($_[2]); + } + else { + # unknown record type, pass content so caller can decide appropriate action + $record{content} = $_[2] if $content_length; + } + + return \%record; +} + +# Reference implementation use 8192 (libfcgi) +sub FCGI_SEGMENT_LEN () { 32768 - FCGI_HEADER_LEN } + +sub build_stream { + @_ == 3 || @_ == 4 || throw(q/Usage: build_stream(type, request_id, content [, terminate])/); + my ($type, $request_id, undef, $terminate) = @_; + + my $len = defined $_[2] ? length $_[2] : 0; + my $res = ''; + + if ($len) { + if ($len < FCGI_SEGMENT_LEN) { + $res = build_record($type, $request_id, $_[2]); + } + else { + my $header = build_header($type, $request_id, FCGI_SEGMENT_LEN, 0); + my $off = 0; + while ($len >= FCGI_SEGMENT_LEN) { + $res .= $header; + $res .= substr($_[2], $off, FCGI_SEGMENT_LEN); + $len -= FCGI_SEGMENT_LEN; + $off += FCGI_SEGMENT_LEN; + } + if ($len) { + $res .= build_record($type, $request_id, substr($_[2], $off, $len)); + } + } + } + + if ($terminate) { + $res .= build_header($type, $request_id, 0, 0); + } + + return $res; +} + +sub build_params { + @_ == 1 || throw(q/Usage: build_params(params)/); + my ($params) = @_; + my $res = ''; + while (my ($key, $val) = each(%$params)) { + for ($key, $val) { + my $len = defined $_ ? length : 0; + $res .= $len < 0x80 ? pack('C', $len) : pack('N', $len | 0x8000_0000); + } + $res .= $key; + $res .= $val if defined $val; + } + return $res; +} + +sub parse_params { + @_ == 1 || throw(q/Usage: parse_params(octets)/); + my ($octets) = @_; + + (defined $octets) + || return +{}; + + my ($params, $klen, $vlen) = ({}, 0, 0); + while (length $octets) { + for ($klen, $vlen) { + (1 <= length $octets) + || throw(ERRMSG_OCTETS, q/FCGI_NameValuePair/); + $_ = vec(substr($octets, 0, 1, ''), 0, 8); + next if $_ < 0x80; + (3 <= length $octets) + || throw(ERRMSG_OCTETS, q/FCGI_NameValuePair/); + $_ = vec(pack('C', $_ & 0x7F) . substr($octets, 0, 3, ''), 0, 32); + } + ($klen + $vlen <= length $octets) + || throw(ERRMSG_OCTETS, q/FCGI_NameValuePair/); + my $key = substr($octets, 0, $klen, ''); + $params->{$key} = substr($octets, 0, $vlen, ''); + } + return $params; +} + +sub check_params { + @_ == 1 || throw(q/Usage: check_params(octets)/); + (defined $_[0]) + || return FALSE; + + my ($len, $off, $klen, $vlen) = (length $_[0], 0, 0, 0); + while ($off < $len) { + for ($klen, $vlen) { + (($off += 1) <= $len) + || return FALSE; + $_ = vec($_[0], $off - 1, 8); + next if $_ < 0x80; + (($off += 3) <= $len) + || return FALSE; + $_ = vec(substr($_[0], $off - 4, 4), 0, 32) & 0x7FFF_FFFF; + } + (($off += $klen + $vlen) <= $len) + || return FALSE; + } + return TRUE; +} + +sub build_begin_request { + (@_ >= 4 && @_ <= 6) || throw(q/Usage: build_begin_request(request_id, role, flags, params [, stdin [, data]])/); + my ($request_id, $role, $flags, $params) = @_; + + my $r = build_begin_request_record($request_id, $role, $flags) + . build_stream(FCGI_PARAMS, $request_id, build_params($params), TRUE); + + if (@_ > 4) { + $r .= build_stream(FCGI_STDIN, $request_id, $_[4], TRUE); + if (@_ > 5) { + $r .= build_stream(FCGI_DATA, $request_id, $_[5], TRUE); + } + } + return $r; +} + +sub build_end_request { + (@_ >= 3 && @_ <= 5) || throw(q/Usage: build_end_request(request_id, app_status, protocol_status [, stdout [, stderr]])/); + my ($request_id, $app_status, $protocol_status) = @_; + + my $r; + if (@_ > 3) { + $r .= build_stream(FCGI_STDOUT, $request_id, $_[3], TRUE); + if (@_ > 4) { + $r .= build_stream(FCGI_STDERR, $request_id, $_[4], TRUE); + } + } + $r .= build_end_request_record($request_id, $app_status, $protocol_status); + return $r; +} + +sub get_record_length { + @_ == 1 || throw(q/Usage: get_record_length(octets)/); + (defined $_[0] && length $_[0] >= FCGI_HEADER_LEN) + || return 0; + return FCGI_HEADER_LEN + vec($_[0], 2, 16) # contentLength + + vec($_[0], 6, 8); # paddingLength +} + +sub is_known_type { + @_ == 1 || throw(q/Usage: is_known_type(type)/); + my ($type) = @_; + return ($type > 0 && $type <= FCGI_MAXTYPE); +} + +sub is_discrete_type { + @_ == 1 || throw(q/Usage: is_discrete_type(type)/); + my ($type) = @_; + return ( $type == FCGI_BEGIN_REQUEST + || $type == FCGI_ABORT_REQUEST + || $type == FCGI_END_REQUEST + || $type == FCGI_GET_VALUES + || $type == FCGI_GET_VALUES_RESULT + || $type == FCGI_UNKNOWN_TYPE ); +} + +sub is_management_type { + @_ == 1 || throw(q/Usage: is_management_type(type)/); + my ($type) = @_; + return ( $type == FCGI_GET_VALUES + || $type == FCGI_GET_VALUES_RESULT + || $type == FCGI_UNKNOWN_TYPE ); +} + +sub is_stream_type { + @_ == 1 || throw(q/Usage: is_stream_type(type)/); + my ($type) = @_; + return ( $type == FCGI_PARAMS + || $type == FCGI_STDIN + || $type == FCGI_STDOUT + || $type == FCGI_STDERR + || $type == FCGI_DATA ); +} + +sub get_type_name { + @_ == 1 || throw(q/Usage: get_type_name(type)/); + my ($type) = @_; + return $FCGI_TYPE_NAME[$type] || sprintf('0x%.2X', $type); +} + +sub get_role_name { + @_ == 1 || throw(q/Usage: get_role_name(role)/); + my ($role) = @_; + return $FCGI_ROLE_NAME[$role] || sprintf('0x%.4X', $role); +} + +sub get_protocol_status_name { + @_ == 1 || throw(q/Usage: get_protocol_status_name(protocol_status)/); + my ($status) = @_; + return $FCGI_PROTOCOL_STATUS_NAME[$status] || sprintf('0x%.2X', $status); +} + +1; + |