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