Bump version
[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;
29829dde 264 my $bytes = 0;
0833402a 265 for (@_) {
29829dde 266 my $len = length($_);
267 $stream->{src}->write($stream->{type}, $_, $len);
268 $bytes += $len;
1d209997 269 }
29829dde 270 return $bytes;
0833402a 271}
1d209997 272
0833402a 273sub CLOSE {
274 my ($stream) = @_;
275 $stream->{src}->write_record($stream->{type}, undef, 0);
1d209997 276}
5baeeca7 277
0833402a 278}
279
280EOP
281print OUT while <DATA>;
282close OUT;
283__END__
284
285# Preloaded methods go here.
794c66be 286
0833402a 287# Autoload methods go after __END__, and are processed by the autosplit program.
1d209997 288
0833402a 289*FAIL_ACCEPT_ON_INTR = sub() { 1 };
290
ef8432ef 291sub Request(;***$*$) {
0833402a 292 my @defaults = (\*STDIN, \*STDOUT, \*STDERR, \%ENV, 0, 0);
e09efffb 293 $_[4] = fileno($_[4]) if defined($_[4]) && defined(fileno($_[4]));
0833402a 294 splice @defaults,0,@_,@_;
295 RequestX(@defaults);
296}
1d209997 297
0833402a 298sub accept() {
299 warn "accept called as a method; you probably wanted to call Accept" if @_;
300 if (defined %FCGI::ENV) {
301 %ENV = %FCGI::ENV;
1d209997 302 } else {
0833402a 303 %FCGI::ENV = %ENV;
1b64d24d 304 }
0833402a 305 my $rc = Accept($global_request);
306 for (keys %FCGI::ENV) {
307 $ENV{$_} = $FCGI::ENV{$_} unless exists $ENV{$_};
308 }
309
310 # not SFIO
311 $SIG{__WARN__} = $warn_handler if (tied (*STDIN));
312 $SIG{__DIE__} = $die_handler if (tied (*STDIN));
313
314 return $rc;
1b64d24d 315}
316
0833402a 317sub finish() {
318 warn "finish called as a method; you probably wanted to call Finish" if @_;
319 %ENV = %FCGI::ENV if (defined %FCGI::ENV);
b716743a 320
0833402a 321 # not SFIO
322 if (tied (*STDIN)) {
323 delete $SIG{__WARN__} if ($SIG{__WARN__} == $warn_handler);
324 delete $SIG{__DIE__} if ($SIG{__DIE__} == $die_handler);
1b64d24d 325 }
5baeeca7 326
0833402a 327 Finish ($global_request);
328}
90a18d65 329
0833402a 330sub flush() {
331 warn "flush called as a method; you probably wanted to call Flush" if @_;
332 Flush($global_request);
333}
334
335sub detach() {
336 warn "detach called as a method; you probably wanted to call Detach" if @_;
337 Detach($global_request);
338}
90a18d65 339
0833402a 340sub attach() {
341 warn "attach called as a method; you probably wanted to call Attach" if @_;
342 Attach($global_request);
1b64d24d 343}
344
0833402a 345# deprecated
346sub set_exit_status {
1b64d24d 347}
348
0833402a 349sub start_filter_data() {
350 StartFilterData($global_request);
351}
352
353$global_request = Request();
354$warn_handler = sub { print STDERR @_ };
355$die_handler = sub { print STDERR @_ unless $^S };
34bfd355 356
0833402a 357package FCGI::Stream;
e40fb02c 358
0833402a 359sub PRINTF {
360 shift->PRINT(sprintf(shift, @_));
34bfd355 361}
362
420df423 363sub BINMODE {
364}
365
0833402a 366sub READLINE {
367 my $stream = shift;
368 my ($s, $c);
369 my $rs = $/ eq '' ? "\n\n" : $/;
370 my $l = substr $rs, -1;
371 my $len = length $rs;
372
373 $c = $stream->GETC();
374 if ($/ eq '') {
562f0c66 375 while ($c eq "\n") {
0833402a 376 $c = $stream->GETC();
377 }
378 }
379 while (defined $c) {
380 $s .= $c;
381 last if $c eq $l and substr($s, -$len) eq $rs;
382 $c = $stream->GETC();
383 }
384 $s;
385}
1b64d24d 386
0833402a 387sub OPEN {
388 $_[0]->CLOSE;
389 if (@_ == 2) {
390 return open($_[0], $_[1]);
391 } else {
392 my $rc;
393 eval("$rc = open($_[0], $_[1], $_[2])");
394 die $@ if $@;
395 return $rc;
396 }
397}
1b64d24d 398
0bbb6895 399# Some things (e.g. IPC::Run) use fileno to determine if a filehandle is open,
400# so we return a defined, but meaningless value. (-1 being the error return
401# value from the syscall in c, meaning it can never be a valid fd no)
402# Probably a better alternative would be to return the fcgi stream fd.
403sub FILENO { -1 }
497802b1 404
0833402a 4051;
eede4b76 406
0833402a 407=pod
eede4b76 408
0833402a 409=head1 NAME
eede4b76 410
0833402a 411FCGI - Fast CGI module
eede4b76 412
0833402a 413=head1 SYNOPSIS
6b312a77 414
0833402a 415 use FCGI;
6b312a77 416
0833402a 417 my $count = 0;
418 my $request = FCGI::Request();
6b312a77 419
0833402a 420 while($request->Accept() >= 0) {
421 print("Content-type: text/html\r\n\r\n", ++$count);
422 }
eede4b76 423
0833402a 424=head1 DESCRIPTION
eede4b76 425
0833402a 426Functions:
eede4b76 427
0833402a 428=over 4
eede4b76 429
0833402a 430=item FCGI::Request
eede4b76 431
0833402a 432Creates a request handle. It has the following optional parameters:
eede4b76 433
0833402a 434=over 8
eede4b76 435
0833402a 436=item input perl file handle (default: \*STDIN)
eede4b76 437
0833402a 438=item output perl file handle (default: \*STDOUT)
9915cd6d 439
0833402a 440=item error perl file handle (default: \*STDERR)
9915cd6d 441
0833402a 442These filehandles will be setup to act as input/output/error
443on succesful Accept.
9915cd6d 444
0833402a 445=item environment hash reference (default: \%ENV)
9915cd6d 446
0833402a 447The hash will be populated with the environment.
9915cd6d 448
0833402a 449=item socket (default: 0)
9915cd6d 450
0833402a 451Socket to communicate with the server.
452Can be the result of the OpenSocket function.
453For the moment, it's the file descriptor of the socket
454that should be passed. This may change in the future.
9915cd6d 455
dcdf34f5 456You should only use your own socket if your program
457is not started by a process manager such as mod_fastcgi
562f0c66 458(except for the FastCgiExternalServer case) or cgi-fcgi.
dcdf34f5 459If you use the option, you have to let your FastCGI
460server know which port (and possibly server) your program
461is listening on.
462See remote.pl for an example.
463
0833402a 464=item flags (default: 0)
1d209997 465
0833402a 466Possible values:
1d209997 467
0833402a 468=over 12
9915cd6d 469
0833402a 470=item FCGI::FAIL_ACCEPT_ON_INTR
5baeeca7 471
0833402a 472If set, Accept will fail if interrupted.
473It not set, it will just keep on waiting.
5baeeca7 474
0833402a 475=back
5baeeca7 476
0833402a 477=back
5baeeca7 478
0833402a 479Example usage:
480 my $req = FCGI::Request;
5baeeca7 481
0833402a 482or:
483 my %env;
484 my $in = new IO::Handle;
485 my $out = new IO::Handle;
486 my $err = new IO::Handle;
487 my $req = FCGI::Request($in, $out, $err, \%env);
5baeeca7 488
0833402a 489=item FCGI::OpenSocket(path, backlog)
5baeeca7 490
0833402a 491Creates a socket suitable to use as an argument to Request.
5baeeca7 492
0833402a 493=over 8
eede4b76 494
0833402a 495=item path
eede4b76 496
0833402a 497Pathname of socket or colon followed by local tcp port.
420df423 498Note that some systems take file permissions into account
499on Unix domain sockets, so you'll have to make sure that
500the server can write to the created file, by changing
501the umask before the call and/or changing permissions and/or
502group of the file afterwards.
eede4b76 503
0833402a 504=item backlog
eede4b76 505
0833402a 506Maximum length of the queue of pending connections.
507If a connection
508request arrives with the queue full the client may receive
509an error with an indication of ECONNREFUSED.
eede4b76 510
0833402a 511=back
eede4b76 512
0833402a 513=item FCGI::CloseSocket(socket)
eede4b76 514
0833402a 515Close a socket opened with OpenSocket.
eede4b76 516
0833402a 517=item $req->Accept()
90a18d65 518
0833402a 519Accepts a connection on $req, attaching the filehandles and
520populating the environment hash.
521Returns 0 on success.
522If a connection has been accepted before, the old
523one will be finished first.
1b64d24d 524
0833402a 525Note that unlike with the old interface, no die and warn
526handlers are installed by default. This means that if
527you are not running an sfio enabled perl, any warn or
528die message will not end up in the server's log by default.
529It is advised you set up die and warn handlers yourself.
530FCGI.pm contains an example of die and warn handlers.
1b64d24d 531
0833402a 532=item $req->Finish()
1b64d24d 533
0833402a 534Finishes accepted connection.
535Also detaches filehandles.
536
537=item $req->Flush()
538
539Flushes accepted connection.
540
541=item $req->Detach()
542
543Temporarily detaches filehandles on an accepted connection.
1b64d24d 544
0833402a 545=item $req->Attach()
546
547Re-attaches filehandles on an accepted connection.
548
7fa2de73 549=item $req->LastCall()
550
551Tells the library not to accept any more requests on this handle.
552It should be safe to call this method from signal handlers.
553
554Note that this method is still experimental and everything
555about it, including its name, is subject to change.
556
0833402a 557=item $env = $req->GetEnvironment()
558
559Returns the environment parameter passed to FCGI::Request.
560
561=item ($in, $out, $err) = $req->GetHandles()
562
563Returns the file handle parameters passed to FCGI::Request.
564
565=item $isfcgi = $req->IsFastCGI()
566
567Returns whether or not the program was run as a FastCGI.
568
569=back
570
571=head1 AUTHOR
572
573Sven Verdoolaege <skimo@kotnet.org>
574
575=cut
576
577__END__