Apply patch from RT#57697
[catagits/fcgi2.git] / perl / FCGI.PL
CommitLineData
1b64d24d 1use Config;
fa01dc1c 2use ExtUtils::MakeMaker;
1b64d24d 3
0833402a 4do 'FCGI.cfg' or die "no FCGI.cfg";
1b64d24d 5
0833402a 6open OUT, ">FCGI.pm";
7
8print "Generating FCGI.pm\n";
9print OUT <<'EOP';
9aa25e66 10# $Id: FCGI.PL,v 1.37 2002/12/15 20:02:48 skimo Exp $
0833402a 11
12package FCGI;
13
14require Exporter;
15require DynaLoader;
16
17@ISA = qw(Exporter DynaLoader);
18# Items to export into callers namespace by default. Note: do not export
19# names by default without a very good reason. Use EXPORT_OK instead.
20# Do not simply export all your public functions/methods/constants.
21@EXPORT = qw(
562f0c66 22
0833402a 23);
24
0833402a 25EOP
26
21b4f5ec 27print OUT '$VERSION = q{'.MM->parse_version('version.pm')."};\n\n";
4945bfbe 28
0833402a 29print OUT "bootstrap FCGI;\n" unless ($pure);
30
21b4f5ec 31print OUT '$VERSION = eval $VERSION;';
32
0833402a 33print OUT <<'EOP' if ($pure);
34use Symbol;
35use POSIX 'ENOTCONN';
36
37use constant VERSION_1 => 1;
38
39use constant BEGIN_REQUEST => 1;
40use constant PARAMS => 4;
41use constant FCGI_STDIN => 5;
42use constant FCGI_STDOUT => 6;
43use constant FCGI_STDERR => 7;
44
45use constant RESPONDER => 1;
46use constant AUTHORIZER => 2;
47use constant FILTER => 3;
48
49%FCGI::rolenames = (RESPONDER, "RESPONDER",
50 AUTHORIZER, "AUTHORIZER",
51 FILTER, "FILTER",
52 );
ef8432ef 53
54# This only works on Unix; anyone familiar with Windows is welcome
55# to give a hand here
0833402a 56sub IsFastCGI {
57 my ($req) = @_;
58 $req->{isfastcgi} =
59 (!defined getpeername shift->{listen_sock}) && $! == ENOTCONN
60 unless exists $req->{isfastcgi};
61 return $req->{isfastcgi};
62}
63
ef8432ef 64sub GetEnvironment {
65 return shift->{'env'};
66}
67
0833402a 68sub read_nv_len {
69 my ($stream) = @_;
70 my $buf;
71 return undef unless read $stream, $buf, 1, 0;
72 my ($len) = unpack("C", $buf);
73 if ($len & 0x80) {
152da6b7 74 $buf = pack("C", $len & 0x7F);
0833402a 75 return undef unless read $stream, $buf, 3, 1;
76 $len = unpack("N", $buf);
1b64d24d 77 }
0833402a 78 $len;
1b64d24d 79}
0833402a 80
81sub RequestX {
82 my $self = {
83 in => shift,
84 out => shift,
85 err => shift,
86 env => shift,
87 socket => shift,
88 flags => shift,
7fa2de73 89 last => 0,
0833402a 90 };
91 open $self->{listen_sock}, "<&=0";
92 bless $self, "FCGI";
93}
94
95my $run_once = 0;
96
97sub Accept {
98 my ($req) = @_;
99
100 unless ($req->IsFastCGI()) {
101 return -1 if $run_once;
102
103 $run_once = 1;
104 return 0;
105 }
106 $req->Finish();
107 $req->{socket} = gensym();
7fa2de73 108 if ($req->{last} || !accept($req->{socket}, $req->{listen_sock})) {
0833402a 109 $req->{error} = "accept";
110 return -1;
111 }
112 my ($type, $id, $body) = $req->read_record();
113 if ($type != BEGIN_REQUEST) {
114 $req->{error} = "begin request";
115 return -1;
116 }
117 my ($role, $flags) = unpack("nC", $body);
118 $req->{role} = $role;
119 $req->{flags} = $flags;
120 $req->{id} = $id;
121
122 %{$req->{env}} = ();
123 $req->{env}{FCGI_ROLE} = $FCGI::rolenames{$req->{role}};
124 my $param = FCGI::Stream->new($req, PARAMS);
125 my ($nlen, $vlen);
562f0c66 126 while (defined($nlen = read_nv_len($param)) &&
0833402a 127 defined($vlen = read_nv_len($param))) {
128 my ($name, $val);
129 read $param, $name, $nlen;
130 read $param, $val, $vlen;
131 $req->{env}{$name} = $val;
132 }
133 $req->Bind;
134 $req->{accepted} = 1;
135
1b64d24d 136 return 0;
137}
0833402a 138
139sub UndoBindings {
140 my ($req) = @_;
141 untie ${$req->{in}};
142 untie ${$req->{out}};
143 untie ${$req->{err}};
144 $req->{bound} = 0;
145}
146
147sub Bind {
148 my ($req) = @_;
149 tie ${$req->{in}}, 'FCGI::Stream', $req, FCGI_STDIN;
150 tie ${$req->{out}}, 'FCGI::Stream', $req, FCGI_STDOUT;
151 tie ${$req->{err}}, 'FCGI::Stream', $req, FCGI_STDERR;
152 $req->{bound} = 1;
153}
154
155sub Attach {
156 my ($req) = @_;
157 $req->Bind() if ($req->{accepted} && !$req->{bound});
158}
159
160sub Detach {
161 my ($req) = @_;
162 $req->UndoBindings() if ($req->{accepted} && $req->{bound});
163}
164
165sub Finish {
166 my ($req) = @_;
167 return unless $req->{accepted};
168 if ($req->{bound}) {
169 $req->UndoBindings();
9aa25e66 170 # apparently these are harmful
171 # close ${$req->{out}};
172 # close ${$req->{err}};
1b64d24d 173 }
0833402a 174 $req->{accepted} = 0;
175}
176
7fa2de73 177sub LastCall {
178 shift->{last} = 1;
179}
180
0833402a 181sub DESTROY {
182 shift->Finish();
183}
184
185sub read_record {
186 my ($self) = @_;
187 my ($header, $body);
188
189 read($self->{socket}, $header, 8);
190 my ($version, $type, $id, $clen, $plen) = unpack("CCnnC", $header);
191 read($self->{socket}, $body, $clen+$plen);
192 $body = undef if $clen == 0;
193 ($type, $id, $body);
194}
195
196sub read {
197 my ($self, $rtype, $len) = @_;
198 while (length $self->{buf} < $len) {
199 my ($type, $id, $buf) = $self->read_record();
200 return undef unless defined $buf;
201 if ($type != $rtype) {
202 $self->{error} = "unexpected stream type";
203 return 0;
b716743a 204 }
0833402a 205 $self->{buf} .= $buf;
b716743a 206 }
562f0c66 207 my ($newbuf, $result) = (substr($self->{buf}, $len),
0833402a 208 substr($self->{buf}, 0, $len));
209 $self->{buf} = $newbuf;
210 $result;
211}
212
213sub Flush {
214 my ($req) = @_;
215}
216
217sub write {
218 my ($self, $type, $content, $len) = @_;
219 return unless $len > 0;
220 $self->write_record($type, $content, $len);
34bfd355 221}
222
0833402a 223sub write_record {
4f396a01 224 my ($self, $type, $content, $length) = @_;
225 my $offset = 0;
226 while ($length > 0) {
227 my $len = $length > 32*1024 ? 32*1024 : $length;
228 my $padlen = (8 - ($len % 8)) % 8;
229 my $templ = "CCnnCxa${len}x$padlen";
562f0c66 230 my $data = pack($templ,
231 VERSION_1, $type, $self->{id}, $len, $padlen,
4f396a01 232 substr($content, $offset, $len));
233 syswrite $self->{socket}, $data;
234 $length -= $len;
235 $offset += $len;
236 }
0833402a 237}
238
239{ package FCGI::Stream;
240
241sub new {
242 my ($class, $src, $type) = @_;
243 my $handle = do { \local *FH };
244 tie($$handle, $class, $src, $type);
245 $handle;
246}
247
248sub TIEHANDLE {
249 my ($class, $src, $type) = @_;
250 bless { src => $src, type => $type }, $class;
251}
5baeeca7 252
0833402a 253sub READ {
254 my ($stream, undef, $len, $offset) = @_;
255 my ($ref) = \$_[1];
256 my $buf = $stream->{src}->read($stream->{type}, $len);
257 return undef unless defined $buf;
258 substr($$ref, $offset, 0, $buf);
259 length $buf;
260}
261
262sub PRINT {
263 my ($stream) = shift;
264 for (@_) {
02e74f89 265 $stream->{src}->write($stream->{type}, $_, length($_));
1d209997 266 }
02e74f89 267 return 1;
0833402a 268}
1d209997 269
0833402a 270sub CLOSE {
271 my ($stream) = @_;
272 $stream->{src}->write_record($stream->{type}, undef, 0);
1d209997 273}
5baeeca7 274
0833402a 275}
276
277EOP
278print OUT while <DATA>;
279close OUT;
280__END__
281
282# Preloaded methods go here.
794c66be 283
0833402a 284# Autoload methods go after __END__, and are processed by the autosplit program.
1d209997 285
0833402a 286*FAIL_ACCEPT_ON_INTR = sub() { 1 };
287
ef8432ef 288sub Request(;***$*$) {
0833402a 289 my @defaults = (\*STDIN, \*STDOUT, \*STDERR, \%ENV, 0, 0);
e09efffb 290 $_[4] = fileno($_[4]) if defined($_[4]) && defined(fileno($_[4]));
0833402a 291 splice @defaults,0,@_,@_;
292 RequestX(@defaults);
293}
1d209997 294
0833402a 295sub accept() {
296 warn "accept called as a method; you probably wanted to call Accept" if @_;
03e35f7f 297 if (%FCGI::ENV) {
0833402a 298 %ENV = %FCGI::ENV;
1d209997 299 } else {
0833402a 300 %FCGI::ENV = %ENV;
1b64d24d 301 }
0833402a 302 my $rc = Accept($global_request);
303 for (keys %FCGI::ENV) {
304 $ENV{$_} = $FCGI::ENV{$_} unless exists $ENV{$_};
305 }
306
307 # not SFIO
308 $SIG{__WARN__} = $warn_handler if (tied (*STDIN));
309 $SIG{__DIE__} = $die_handler if (tied (*STDIN));
310
311 return $rc;
1b64d24d 312}
313
0833402a 314sub finish() {
315 warn "finish called as a method; you probably wanted to call Finish" if @_;
b89243f1 316 %ENV = %FCGI::ENV if %FCGI::ENV;
b716743a 317
0833402a 318 # not SFIO
319 if (tied (*STDIN)) {
320 delete $SIG{__WARN__} if ($SIG{__WARN__} == $warn_handler);
321 delete $SIG{__DIE__} if ($SIG{__DIE__} == $die_handler);
1b64d24d 322 }
5baeeca7 323
0833402a 324 Finish ($global_request);
325}
90a18d65 326
0833402a 327sub flush() {
328 warn "flush called as a method; you probably wanted to call Flush" if @_;
329 Flush($global_request);
330}
331
332sub detach() {
333 warn "detach called as a method; you probably wanted to call Detach" if @_;
334 Detach($global_request);
335}
90a18d65 336
0833402a 337sub attach() {
338 warn "attach called as a method; you probably wanted to call Attach" if @_;
339 Attach($global_request);
1b64d24d 340}
341
0833402a 342# deprecated
343sub set_exit_status {
1b64d24d 344}
345
0833402a 346sub start_filter_data() {
347 StartFilterData($global_request);
348}
349
350$global_request = Request();
351$warn_handler = sub { print STDERR @_ };
352$die_handler = sub { print STDERR @_ unless $^S };
34bfd355 353
0833402a 354package FCGI::Stream;
e40fb02c 355
0833402a 356sub PRINTF {
357 shift->PRINT(sprintf(shift, @_));
34bfd355 358}
359
420df423 360sub BINMODE {
361}
362
0833402a 363sub READLINE {
364 my $stream = shift;
365 my ($s, $c);
366 my $rs = $/ eq '' ? "\n\n" : $/;
367 my $l = substr $rs, -1;
368 my $len = length $rs;
369
370 $c = $stream->GETC();
371 if ($/ eq '') {
562f0c66 372 while ($c eq "\n") {
0833402a 373 $c = $stream->GETC();
374 }
375 }
376 while (defined $c) {
377 $s .= $c;
378 last if $c eq $l and substr($s, -$len) eq $rs;
379 $c = $stream->GETC();
380 }
381 $s;
382}
1b64d24d 383
0833402a 384sub OPEN {
385 $_[0]->CLOSE;
386 if (@_ == 2) {
387 return open($_[0], $_[1]);
388 } else {
389 my $rc;
390 eval("$rc = open($_[0], $_[1], $_[2])");
391 die $@ if $@;
392 return $rc;
393 }
394}
1b64d24d 395
0bbb6895 396# Some things (e.g. IPC::Run) use fileno to determine if a filehandle is open,
397# so we return a defined, but meaningless value. (-1 being the error return
398# value from the syscall in c, meaning it can never be a valid fd no)
399# Probably a better alternative would be to return the fcgi stream fd.
400sub FILENO { -1 }
497802b1 401
0833402a 4021;
eede4b76 403
0833402a 404=pod
eede4b76 405
0833402a 406=head1 NAME
eede4b76 407
0833402a 408FCGI - Fast CGI module
eede4b76 409
0833402a 410=head1 SYNOPSIS
6b312a77 411
0833402a 412 use FCGI;
6b312a77 413
0833402a 414 my $count = 0;
415 my $request = FCGI::Request();
6b312a77 416
0833402a 417 while($request->Accept() >= 0) {
418 print("Content-type: text/html\r\n\r\n", ++$count);
419 }
eede4b76 420
0833402a 421=head1 DESCRIPTION
eede4b76 422
0833402a 423Functions:
eede4b76 424
0833402a 425=over 4
eede4b76 426
0833402a 427=item FCGI::Request
eede4b76 428
0833402a 429Creates a request handle. It has the following optional parameters:
eede4b76 430
0833402a 431=over 8
eede4b76 432
0833402a 433=item input perl file handle (default: \*STDIN)
eede4b76 434
0833402a 435=item output perl file handle (default: \*STDOUT)
9915cd6d 436
0833402a 437=item error perl file handle (default: \*STDERR)
9915cd6d 438
0833402a 439These filehandles will be setup to act as input/output/error
88665260 440on successful Accept.
9915cd6d 441
0833402a 442=item environment hash reference (default: \%ENV)
9915cd6d 443
0833402a 444The hash will be populated with the environment.
9915cd6d 445
0833402a 446=item socket (default: 0)
9915cd6d 447
0833402a 448Socket to communicate with the server.
449Can be the result of the OpenSocket function.
450For the moment, it's the file descriptor of the socket
451that should be passed. This may change in the future.
9915cd6d 452
dcdf34f5 453You should only use your own socket if your program
454is not started by a process manager such as mod_fastcgi
562f0c66 455(except for the FastCgiExternalServer case) or cgi-fcgi.
dcdf34f5 456If you use the option, you have to let your FastCGI
457server know which port (and possibly server) your program
458is listening on.
459See remote.pl for an example.
460
0833402a 461=item flags (default: 0)
1d209997 462
0833402a 463Possible values:
1d209997 464
0833402a 465=over 12
9915cd6d 466
0833402a 467=item FCGI::FAIL_ACCEPT_ON_INTR
5baeeca7 468
0833402a 469If set, Accept will fail if interrupted.
470It not set, it will just keep on waiting.
5baeeca7 471
0833402a 472=back
5baeeca7 473
0833402a 474=back
5baeeca7 475
0833402a 476Example usage:
477 my $req = FCGI::Request;
5baeeca7 478
0833402a 479or:
480 my %env;
481 my $in = new IO::Handle;
482 my $out = new IO::Handle;
483 my $err = new IO::Handle;
484 my $req = FCGI::Request($in, $out, $err, \%env);
5baeeca7 485
0833402a 486=item FCGI::OpenSocket(path, backlog)
5baeeca7 487
0833402a 488Creates a socket suitable to use as an argument to Request.
5baeeca7 489
0833402a 490=over 8
eede4b76 491
0833402a 492=item path
eede4b76 493
0833402a 494Pathname of socket or colon followed by local tcp port.
420df423 495Note that some systems take file permissions into account
496on Unix domain sockets, so you'll have to make sure that
497the server can write to the created file, by changing
498the umask before the call and/or changing permissions and/or
499group of the file afterwards.
eede4b76 500
0833402a 501=item backlog
eede4b76 502
0833402a 503Maximum length of the queue of pending connections.
504If a connection
505request arrives with the queue full the client may receive
506an error with an indication of ECONNREFUSED.
eede4b76 507
0833402a 508=back
eede4b76 509
0833402a 510=item FCGI::CloseSocket(socket)
eede4b76 511
0833402a 512Close a socket opened with OpenSocket.
eede4b76 513
0833402a 514=item $req->Accept()
90a18d65 515
0833402a 516Accepts a connection on $req, attaching the filehandles and
517populating the environment hash.
518Returns 0 on success.
519If a connection has been accepted before, the old
520one will be finished first.
1b64d24d 521
0833402a 522Note that unlike with the old interface, no die and warn
523handlers are installed by default. This means that if
524you are not running an sfio enabled perl, any warn or
525die message will not end up in the server's log by default.
526It is advised you set up die and warn handlers yourself.
527FCGI.pm contains an example of die and warn handlers.
1b64d24d 528
0833402a 529=item $req->Finish()
1b64d24d 530
0833402a 531Finishes accepted connection.
532Also detaches filehandles.
533
534=item $req->Flush()
535
536Flushes accepted connection.
537
538=item $req->Detach()
539
540Temporarily detaches filehandles on an accepted connection.
1b64d24d 541
0833402a 542=item $req->Attach()
543
544Re-attaches filehandles on an accepted connection.
545
7fa2de73 546=item $req->LastCall()
547
548Tells the library not to accept any more requests on this handle.
549It should be safe to call this method from signal handlers.
550
551Note that this method is still experimental and everything
552about it, including its name, is subject to change.
553
0833402a 554=item $env = $req->GetEnvironment()
555
556Returns the environment parameter passed to FCGI::Request.
557
558=item ($in, $out, $err) = $req->GetHandles()
559
560Returns the file handle parameters passed to FCGI::Request.
561
562=item $isfcgi = $req->IsFastCGI()
563
564Returns whether or not the program was run as a FastCGI.
565
566=back
567
568=head1 AUTHOR
569
570Sven Verdoolaege <skimo@kotnet.org>
571
572=cut
573
574__END__