3 # CGI::FCGI -- Alternative Pure Perl CGI::Fast and FCGI implementation
4 # Copyright (C) 2015,2016,2017,2020,2021 Kyle J. McKay.
7 # Licensed under the same license as Perl OR GNU GPL v2 or later
8 # There is NO WARRANTY, to the extent permitted by law.
9 # See license below starting with '=head1 LICENSE'
11 # See documentation below starting with '=head1'
13 # MUST be used BEFORE CGI::Fast and FCGI.
15 $CGI::FCGI
::VERSION
= '1.0.1';
32 my $name = $subtoname->(__PACKAGE__
);
33 $INC{$name} = __FILE__
if !$INC{$name};
34 foreach my $pkg (qw(CGI::Fast FCGI)) {
35 my $pkgn = $subtoname->($pkg);
36 croak
"Must `use ".__PACKAGE__
.";` ".
38 if $INC{$pkgn} && $INC{$pkgn} ne $INC{$name};
39 $INC{$pkgn} = $INC{$name};
47 FCGI_MAX_CONNS
=> 1, # Maximum concurrent connections
48 FCGI_MAX_REQS
=> 1, # Max concurrent requests on a connection
49 FCGI_MPXS_CONNS
=> 0, # No multiplexing capabilities here
54 3 => "FILTER", # Not currently supported
59 open my $fh, '+<&0' or return 0;
60 return 0 unless getsockname $fh;
61 defined(my $st = getsockopt($fh, SOL_SOCKET
, SO_TYPE
)) or return 0;
62 $st = unpack("i", $st);
63 defined($st) && $st == SOCK_STREAM
or return 0;
64 return 0 if getpeername $fh;
65 return $!{ENOTCONN
} ?
1 : 0;
69 # Make sure state's not saved
82 return unless !$is_inited;
86 croak
'STDIN_FILENO (0) is not an accept socket' unless IsFCGI
;
88 open ASOCK
, '<&=0' or croak
'STDIN_FILENO (0) is not valid';
89 $fcgi{'asock'} = *ASOCK
;
96 return unless defined $data;
98 my $len = length $data;
100 my $cnt = syswrite $fh, $data, $len, $offset;
101 next if !defined($cnt) && $!{EINTR
};
102 croak
"syswrite failed: $!" unless defined $cnt;
103 croak
"syswrite of $len wrote 0" unless $cnt;
114 return undef unless defined $cnt && $cnt >= 0;
117 while ($len < $cnt) {
118 my $r = sysread $fh, $data, $cnt - $len, $len;
119 next if !defined($r) && $!{EINTR
};
120 croak
"sysread failed: $!" unless defined $r;
121 return '' if $eofok && !$r && !$len; # EOF at beginning okay
122 croak
"sysread of @{[$cnt - $len]} read 0" unless $r;
132 return undef unless defined $cnt && $cnt >= 0;
134 while ($len < $cnt) {
136 my $r = sysread $fh, $data, 32768;
137 next if !defined($r) && $!{EINTR
};
138 croak
"sysread failed: $!" unless defined $r;
139 croak
"sysread of 32768 read 0" unless $r;
148 my $offset = shift || 0;
149 my $discardvalue = shift || 0;
150 my $len = length($data);
151 croak
"_read_one_var invalid input" unless $offset >= 0 && $offset + 3 <= $len;
153 my $nlen = unpack('C', substr($data, $ptr++, 1));
155 croak
"_read_one_var invalid input" unless $ptr + 5 <= $len;
156 my @bytes = unpack('CCC', substr($data, $ptr, 3));
158 $nlen = (($nlen & 0x7f) << 24) | ($bytes[0] << 16) |
159 ($bytes[1] << 8) | $bytes[2];
161 my $vlen = unpack('C', substr($data, $ptr++, 1));
163 croak
"_read_one_var invalid input" unless $ptr + 4 <= $len;
164 my @bytes = unpack('CCC', substr($data, $ptr, 3));
166 $vlen = (($vlen & 0x7f) << 24) | ($bytes[0] << 16) |
167 ($bytes[1] << 8) | $bytes[2];
169 croak
"FCGI out of bounds var name/value length" if $ptr + $nlen + $vlen > $len;
171 _read_discard
($fcgi{'csock'}, $nlen + $vlen);
172 return ("_ZERO_LENGTH_VAR_NAME", "", $ptr - $offset + $nlen + $vlen);
175 _read_discard
($fcgi{'csock'}, $nlen + $vlen);
176 return ("_TOO_LONG_VAR_NAME", "", $ptr - $offset + $nlen + $vlen);
179 _read_discard
($fcgi{'csock'}, $nlen + $vlen);
180 return ("_TOO_LONG_VAR_VAL", "", $ptr - $offset + $nlen + $vlen);
182 return (substr($data, $ptr, $nlen),
183 ($discardvalue ?
undef : substr($data, $ptr+$nlen, $vlen)),
184 $ptr - $offset + $nlen + $vlen);
190 return pack('C', ($l & 0x7f)) if $l < 128;
198 return pack('CCCC', $b3, $b2, $b1, $b0);
201 sub _encode_one_var
{
203 return _encode_length
(length($n)).
204 _encode_length
(length($v)).
211 my $rem = $len & 0x7;
212 return '' unless $rem;
213 return pack('C', 0) x
(8 - $rem);
223 return unless ($type == 6 || $type == 7) && length($data) || $force;
224 while (length($data) > 65535) {
225 _write_all
($fh, pack('CCnnCC', 1, $type, $rid, 32768, 0, 0).substr($data, 0, 32768));
226 substr($data, 0, 32768) = '';
228 my $padding = _get_padding
(length($data));
229 _write_all
($fh, pack('CCnnCC', 1, $type, $rid, length($data), length($padding), 0).$data.$padding);
232 sub _write_end_stream
{
236 return _write_stream
($fh, $type, $rid, '', 1);
239 sub _write_end_request
{
243 my $pstat = shift || 0;
244 my $astat = shift || 0;
245 _write_all
($fh, pack('CCnnCCNCCCC',
246 1, 3, $rid, 8, 0, 0, $astat, $pstat, 0, 0, 0));
249 sub _handle_unknown_packet
{
250 my ($fh, $rid, $clen, $plen) = @_;
251 croak
"FCGI invalid unkown packet content length $clen (must be >= 1)"
253 my $t = _read_all
($fh, 1);
254 _read_discard
($fh, $clen + $plen - 1);
255 _write_all
($fh, pack('CCnnCCCCCCCCCC',
256 1, 11, $rid, 8, 0, 0, unpack('C', $t), 0, 0, 0, 0, 0, 0, 0));
259 sub _handle_get_params
{
260 my ($fh, $rid, $clen, $plen) = @_;
261 my $varnames = _read_all
($fh, $clen);
262 _read_discard
($fh, $plen);
265 my $len = length($varnames);
266 while ($offset < $len) {
267 my ($n, undef, $b) = _read_one_var
($varnames, $offset, 1);
273 map({exists($PARAMS{$_}) ? _encode_one_var
($_, $PARAMS{$_}) : ()} @v))
275 _write_stream
($fh, 10, $rid, $vars, 1);
278 # Can only return types 1, 2, 4, 5, or 8
279 # And for types 1 and 2 the packet is fully read along with its padding
284 my ($type, $rid, $clen, $plen);
286 my $request = _read_all
($fh, 8, $eofok);
287 return undef if !defined($request) && $eofok;
289 ($vers, $type, $rid, $clen, $plen) = unpack('CCnnC', $request);
290 croak
"FCGI bad packet header version $vers (should be 1)"
292 croak
"FCGI invalid packet type $type (should be 1..11)"
293 unless $type >= 1 && $type <= 11;
294 croak
"FCGI unexpected packet type $type (should be 1, 2, 4, 5, 8, 9 or 11)"
295 if $type == 3 || $type == 6 || $type == 7 || $type == 10;
296 _handle_get_params
($fh, $rid, $clen, $plen), redo if $type == 9;
297 _handle_unknown_packet
($fh, $rid, $clen, $plen), redo if $type == 11;
298 croak
"FCGI invalid BEGIN content length $clen (should be 8)"
299 if $type == 1 && $clen != 8;
300 croak
"FCGI invalid ABORT content length $clen (should be 0)"
301 if $type == 2 && $clen != 0;
302 croak
"FCGI invalid requestId == 0 for BEGIN/ABORT/PARAMS/STDIN/DATA"
303 if ($type <= 2 || $type == 4 || $type == 5 || $type == 8) && !$rid;
307 my $begin = _read_all
($fh, $clen);
308 _read_discard
($fh, $plen);
309 ($role, $flags) = unpack('nC', $begin);
312 _read_discard
($fh, $plen);
314 return ($type, $rid, $clen, $plen, $role, $flags);
318 return unless $active{'active'};
319 my $pstat = shift || 0;
320 my $astat = shift || 0;
321 my $done = shift || 0;
322 _write_end_stream
($fcgi{'csock'}, 6, $active{'active'}) if $done;
323 _write_end_request
($fcgi{'csock'}, $active{'active'}, $pstat, $astat);
324 *STDIN
= $active{'STDIN'};
325 *STDOUT
= $active{'STDOUT'};
326 *STDERR
= $active{'STDERR'};
327 %ENV = %{$active{'ENV'}};
329 unless ($active{'keep'}) {
330 close($fcgi{'csock'});
331 undef $fcgi{'csock'};
338 _init
unless $is_inited;
339 _close_active
(0, 0, 1) if $active{'active'};
342 while (!$active{'active'}) {
343 while (!$fcgi{'csock'}) {
345 unless (accept(CSOCK
, $fcgi{'asock'})) {
346 next if $!{EINTR
} || $!{ECONNABORTED
};
347 croak
"accept failed: $!";
349 $fcgi{'csock'} = *CSOCK
;
351 my ($type, $rid, $clen, $plen, $role, $flags) = _read_packet
($fcgi{'csock'}, 1);
353 close($fcgi{'csock'});
354 undef $fcgi{'csock'};
357 _read_discard
($fcgi{'csock'}, $clen+$plen), redo if $type == 5 || $type == 8;
358 croak
"FCGI unexpected packet type $type (expecting 1 -- BEGIN)"
360 if ($role != 1 && $role != 2) {
361 _write_end_request
($fcgi{'csock'}, $rid, 3);
362 unless($flags & 0x01) {
363 close($fcgi{'csock'});
364 undef $fcgi{'csock'};
368 $active{'active'} = $rid;
369 $active{'role'} = $role;
370 $active{'keep'} = 1 if $flags & 0x01;
371 $active{'STDIN'} = *STDIN
;
372 $active{'STDOUT'} = *STDOUT
;
373 $active{'STDERR'} = *STDERR
;
375 $active{'ENV'} = \
%saveenv;
380 my ($type, $rid, $clen, $plen) = _read_packet
($fcgi{'csock'});
382 _write_end_request
($fcgi{'sock'}, $rid, 1)
383 unless $rid == $active{'active'};
386 redo unless $rid == $active{'active'};
391 croak
"FCGI unexpected packet type $type (expecting 4 -- PARAMS)"
394 my $vars = _read_all
($fcgi{'csock'}, $clen);
395 _read_discard
($plen);
397 my $len = length($vars);
398 while ($offset < $len) {
399 my ($n, $v, $b) = _read_one_var
($vars, $offset);
405 _read_discard
($plen);
407 $vars{'FCGI_ROLE'} = $ROLES{$active{'role'}}; # We must add this
409 # Tie the streams, set %ENV and off we go!
411 tie
*TI
, 'CGI::FCGI::IStream' or croak
"CGI::FCGI::IStream tie STDIN failed";
413 tie
*TO
, 'CGI::FCGI::OStream', 6 or croak
"CGI::FCGI::IStream tie STDOUT failed";
415 tie
*TE
, 'CGI::FCGI::OStream', 7 or croak
"CGI::FCGI::IStream tie STDERR failed";
420 $ENV{$n} = $v while ($n,$v) = each %vars;
424 my $min = shift || 1;
425 return '' unless $active{'active'};
427 while (!$active{'eof'} && $min > 0) {
428 my ($type, $rid, $clen, $plen) = _read_packet
($fcgi{'csock'});
430 _write_end_request
($fcgi{'sock'}, $rid, 1)
431 unless $rid == $active{'active'};
434 redo unless $rid == $active{'active'};
439 croak
"FCGI unexpected packet type $type (expecting 5 -- STDIN)"
441 my $input = _read_all
($fcgi{'csock'}, $clen);
442 _read_discard
($fcgi{'sock'}, $plen);
443 $min -= length($input);
444 $active{'eof'} = 1 unless length($input);
453 return unless $active{'active'};
454 _write_stream
($fcgi{'csock'}, $type, $active{'active'}, $data)
455 if defined($data) && length($data);
460 my ($class, $package) = @_;
461 $package or $package = __PACKAGE__
;
462 my $try = ref $class || $class || $package;
463 UNIVERSAL
::isa
($try, $package)
464 or croak
"Cannot call ${package}::new with a class ($try)".
465 " that does not inherit from $package";
470 my $class = _get_new_class
(shift);
472 CGI
->_reset_globals();
473 $class->_setup_symbols(@CGI::SAVED_SYMBOLS
) if @CGI::SAVED_SYMBOLS
;
475 my $self = $CGI::Q
= $class->SUPER::new
(@_);
476 $self->{'.fcgi_generation'} = $generation if $self;
482 $self->SUPER::DESTROY
if $self->can('SUPER::DESTROY');
483 _close_active
(0, 0, 1)
484 if $active{'active'} && $self->{'.fcgi_generation'} == $generation;
487 sub CGI
::FCGI
::Raw
::new
{
488 my $class = _get_new_class
(shift, 'CGI::FCGI::Raw');
492 $self->{'.fcgi_generation'} = $generation if $self;
496 sub CGI
::FCGI
::Raw
::DESTROY
{
498 _close_active
(0, 0, 1)
499 if $active{'active'} && $self->{'.fcgi_generation'} == $generation;
502 sub CGI
::FCGI
::Raw
::ResetCGI
{
504 CGI
->_reset_globals();
505 CGI
->_setup_symbols(@CGI::SAVED_SYMBOLS
) if @CGI::SAVED_SYMBOLS
;
509 package CGI
::FCGI
::IStream
;
516 $self->{'buffer'} = '';
517 return bless $self, $class;
531 if (!$self->{'eof'}) {
532 my $more = CGI
::FCGI
::_read_more
($min);
533 $self->{'eof'} = 1 if !length($more);
534 $self->{'buffer'} .= $more;
541 if !$self->{'eof'} && !length($self->{'buffer'});
542 return $self->{'eof'} ?
1 : 0;
548 if !$self->{'eof'} && !length($self->{'buffer'});
549 return undef unless length($self->{'buffer'});
550 my $c = substr($self->{'buffer'}, 0, 1);
551 substr($self->{'buffer'}, 0, 1) = '';
556 my ($self, undef, $length, $offset) = @_;
558 $offset = 0 unless defined($offset);
559 $$bufref = '' unless defined($$bufref);
560 croak
"CGI::FCGI::IStream::READ invalid length $length"
562 $offset += length($$bufref) if $offset < 0;
563 croak
"CGI::FCGI::IStream::READ invalid read offset"
564 if $offset > length($$bufref);
565 $self->_read_more(length($self->{'buffer'} - $length))
566 if length($self->{'buffer'}) < $length;
567 $length = length($self->{'buffer'}) if $length > length($self->{'buffer'});
568 substr($$bufref, $offset) = substr($self->{'buffer'}, 0, $length);
569 substr($self->{'buffer'}, 0, $length) = '';
575 if (ref($/) eq 'SCALAR') {
576 $self->_read_more(${$/} - length($self->{'buffer'}))
577 if !$self->{'eof'} && length($self->{'buffer'}) < ${$/};
578 my $ans = substr($self->{'buffer'}, 0, ${$/});
579 substr($self->{'buffer'}, 0, ${$/}) = '';
581 } elsif (defined($/)) {
584 my $d = $/ eq '' ? "\n\n" : $/;
585 while (($pos = index($self->{'buffer'}, $d, $offset)) < 0 && !$self->{'eof'}) {
586 $offset += length($self->{'buffer'}) - (length($d) - 1);
594 if !$self->{'eeof'} && length($self->{'buffer'}) <= $pos;
596 if substr($self->{'buffer'}, $pos, 1) eq "\n";
598 my $ans = substr($self->{'buffer'}, 0, $cpos);
599 substr($self->{'buffer'}, 0, $pos) = '';
603 $self->_read_more(32768) while !$self->{'eof'};
605 return undef unless length($self->{'buffer'});
606 my $ans = $self->{'buffer'};
607 $self->{'buffer'} = '';
616 push(@a, $l) while defined($l = $self->_read_line);
619 return $self->_read_line;
630 package CGI
::FCGI
::OStream
;
638 $self->{'type'} = $type;
639 return bless $self, $class;
663 my $template = shift;
664 return $self->PRINT(sprintf $template, @_);
667 sub PRINT
{goto &FCGI
::Stream
::PRINT
}
669 sub FCGI
::Stream
::PRINT
{
671 CGI
::FCGI
::_write_more
($self->{'type'}, join('', @_));
677 my ($scalar, $length, $offset) = @_;
678 $scalar = '' if !defined($scalar);
679 $length = length($scalar) if !defined($length);
680 croak
"CGI::FCGI::OStream::WRITE invalid length $length"
682 $offset = 0 if !defined($offset);
683 $offset += length($scalar) if $offset < 0;
684 croak
"CGI::FCGI::OStream::WRITE invalid write offset"
685 if $offset < 0 || $offset > $length;
686 my $max = length($scalar) - $offset;
687 $length = $max if $length > $max;
688 $self->PRINT(substr($scalar, $offset, $length));
696 sub new
{croak
'FCGI->new is not permitted when `use CGI::FCGI;` is in effect'}
700 our @ISA = ('CGI::FCGI');
702 sub new
{goto &CGI
::FCGI
::new
}
708 CGI::FCGI - Alternative CGI::Fast and FCGI Interface
713 use CGI qw/ :standard /;
717 if (CGI::FCGI::IsFCGI) {
722 while (my $q = $CGI->new) {
724 last unless --$maxrequests;
730 # Perform standard CGI processing for one request
737 CGI::FCGI is intended as a bare bones Pure Perl replacement for
738 both CGI::Fast and FCGI. It's lightweight and supports the most
739 common use case of wanting to support FastCGI but missing one
740 or both of the CGI::Fast and FCGI modules.
742 It only supports 'RESPONDER' and 'AUTHORIZER' roles (the
743 'RESPONDER' role corresponding to standard CGI applications) and it
744 only handles one request at a time on one socket at a time. For
745 compatibility with FCGI.pm, the FCGI_ROLE environment variable is always
746 set to either "RESPONDER" or "AUTHORIZER".
748 Nevertheless, this is quite sufficient to reap a huge performance
749 gain as Perl need not be restarted once for every CGI request.
751 Note that just like FCGI, input/output is always byte oriented so
752 the caller is responsible for decoding/encoding as needed. To
753 facilitate compatibility with the standard FCGI, the tied output
754 streams call a FCGI::Stream::PRINT function which contains the
755 actual PRINT implementation and may be head-patched in exactly
756 the same manner as the standard FCGI::Stream::PRINT implemention
757 to provide UTF-8 encoding on output.
759 Other than CGI::Fast::new and FCGI::Stream::PRINT, B<none> of the
760 other standard CGI::Fast or FCGI functions are provided!
762 Remember, if using CGI::FCGI::Raw to allow custom handling of any
763 'POST' data, the data B<MUST> be read from <STDIN> and B<NOT>
764 the <ARGV> file handle which is also known as <>.
770 =item CGI::FCGI::IsFCGI
772 Returns true if STDIN_FILENO appears to be a FastCGI socket.
774 As there's not a direct equivalent in CGI::Fast or FCGI, don't
775 call this if code is to be interchangeable between CGI::FCGI and
780 Returns a new CGI instance for the next request. Returns undef
781 if the listen socket has been closed. All arguments are passed
786 Convenient alias for CGI::FCGI::new, all arguments are passed
789 =item CGI::FCGI::Raw::new
791 Returns a new instance for the next request that is B<NOT> a CGI instance.
792 This is similar to FCGI::Request()->Accept() but can be used in a loop just the
793 same way CGI::FCGI::new (or the equivalent CGI::Fast::new) can. While the
794 returned instance is alive, STDIN, STDOUT, STDERR and %ENV are directed to the
795 request being serviced (in just the same way as for CGI::FCGI::new), but
796 B<none> of the CGI processing happens (e.g. slurping up 'POST' data, parsing
797 QUERY_STRING, etc.) as the instance is B<NOT> a CGI instance and for the same
798 reason B<NONE> of the standard CGI methods are available on the instance.
799 No arguments are passed up to CGI since the new instance is B<NOT> a CGI.
800 Returns undef if the listen socket has been closed. However, it is possible
801 to explicitly create a new CGI instance (CGI->new) after calling this function
802 provided the CGI global state has first been reset to its normal "empty" state.
803 See the CGI::FCGI::Raw::ResetCGI function for help with this.
805 =item CGI::FCGI::Raw::ResetCGI
807 Resets the global CGI state so that a call to CGI->new will create a new
808 CGI instance from the current environment in %ENV without being influenced by a
809 previously handled request.
811 Do B<NOT> call this unless the "Raw" interface is being used! The regular
812 interface (CGI::FCGI::new or CGI::Fast::new) takes care of this automatically.
814 When using the "Raw" interface (i.e. CGI::FCGI::Raw::new) B<AND> then calling
815 CGI->new directly, this function should be called B<AFTER> CGI::FCGI::Raw::new
816 but B<BEFORE> CGI->new to make sure that CGI->new doesn't return an instance
817 with leftover configuration and/or data from a previously handled request.
819 =item FCGI::Stream::PRINT
821 All stream output passes through this function which may be head-patched
822 to perform custom processing such as UTF-8 encoding. This is the same
823 name used by the standard FCGI module for compatibility.
829 Although the implementation is Pure Perl, it I<does> make heavy use of
830 the socket functions so may not function properly on platforms where the
831 socket functions are not available or only partially supported.
833 This module must be use'd B<before> CGI::Fast and FCGI otherwise it
834 will die with a fatal error.
836 While a request is active, STDIN, STDOUT, STDERR and %ENV are temporarily
837 altered and there's no option to do otherwise.
839 When a new request is started, the current values of the STDIN, STDOUT,
840 STDERR, %ENV and $/ variables are preserved before tying STDIN, STDOUT,
841 STDERR and adding received variables to %ENV.
843 When the request ends (i.e. it's DESTROY'd) those variables are restored
844 to the values they had just prior to the start of the request therefore
845 changes to any of those variables during the processing of a request are,
846 deliberately, nonpersistent.
852 =item CGI::FCGI Copyright (C) 2015,2016,2017,2020,2021 Kyle J. McKay.
854 =item All rights reserved.
858 This library is free software; you can redistribute it and/or modify
859 it under the same terms as Perl itself or under the terms of the
860 GNU General Public License version 2 (or, at your option, any later
863 This program is distributed in the hope that it will be useful,
864 but WITHOUT ANY WARRANTY; without even the implied warranty of
865 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
867 Address feedback to mackyle <at> gmail.com.