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