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