avoid warning under strict
[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';
e09efffb 10# $Id: FCGI.PL,v 1.34 2001/10/04 08:04:16 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
0833402a 3851;
eede4b76 386
0833402a 387=pod
eede4b76 388
0833402a 389=head1 NAME
eede4b76 390
0833402a 391FCGI - Fast CGI module
eede4b76 392
0833402a 393=head1 SYNOPSIS
6b312a77 394
0833402a 395 use FCGI;
6b312a77 396
0833402a 397 my $count = 0;
398 my $request = FCGI::Request();
6b312a77 399
0833402a 400 while($request->Accept() >= 0) {
401 print("Content-type: text/html\r\n\r\n", ++$count);
402 }
eede4b76 403
0833402a 404=head1 DESCRIPTION
eede4b76 405
0833402a 406Functions:
eede4b76 407
0833402a 408=over 4
eede4b76 409
0833402a 410=item FCGI::Request
eede4b76 411
0833402a 412Creates a request handle. It has the following optional parameters:
eede4b76 413
0833402a 414=over 8
eede4b76 415
0833402a 416=item input perl file handle (default: \*STDIN)
eede4b76 417
0833402a 418=item output perl file handle (default: \*STDOUT)
9915cd6d 419
0833402a 420=item error perl file handle (default: \*STDERR)
9915cd6d 421
0833402a 422These filehandles will be setup to act as input/output/error
423on succesful Accept.
9915cd6d 424
0833402a 425=item environment hash reference (default: \%ENV)
9915cd6d 426
0833402a 427The hash will be populated with the environment.
9915cd6d 428
0833402a 429=item socket (default: 0)
9915cd6d 430
0833402a 431Socket to communicate with the server.
432Can be the result of the OpenSocket function.
433For the moment, it's the file descriptor of the socket
434that should be passed. This may change in the future.
9915cd6d 435
dcdf34f5 436You should only use your own socket if your program
437is not started by a process manager such as mod_fastcgi
438(except for the FastCgiExternalServer case) or cgi-fcgi.
439If you use the option, you have to let your FastCGI
440server know which port (and possibly server) your program
441is listening on.
442See remote.pl for an example.
443
0833402a 444=item flags (default: 0)
1d209997 445
0833402a 446Possible values:
1d209997 447
0833402a 448=over 12
9915cd6d 449
0833402a 450=item FCGI::FAIL_ACCEPT_ON_INTR
5baeeca7 451
0833402a 452If set, Accept will fail if interrupted.
453It not set, it will just keep on waiting.
5baeeca7 454
0833402a 455=back
5baeeca7 456
0833402a 457=back
5baeeca7 458
0833402a 459Example usage:
460 my $req = FCGI::Request;
5baeeca7 461
0833402a 462or:
463 my %env;
464 my $in = new IO::Handle;
465 my $out = new IO::Handle;
466 my $err = new IO::Handle;
467 my $req = FCGI::Request($in, $out, $err, \%env);
5baeeca7 468
0833402a 469=item FCGI::OpenSocket(path, backlog)
5baeeca7 470
0833402a 471Creates a socket suitable to use as an argument to Request.
5baeeca7 472
0833402a 473=over 8
eede4b76 474
0833402a 475=item path
eede4b76 476
0833402a 477Pathname of socket or colon followed by local tcp port.
420df423 478Note that some systems take file permissions into account
479on Unix domain sockets, so you'll have to make sure that
480the server can write to the created file, by changing
481the umask before the call and/or changing permissions and/or
482group of the file afterwards.
eede4b76 483
0833402a 484=item backlog
eede4b76 485
0833402a 486Maximum length of the queue of pending connections.
487If a connection
488request arrives with the queue full the client may receive
489an error with an indication of ECONNREFUSED.
eede4b76 490
0833402a 491=back
eede4b76 492
0833402a 493=item FCGI::CloseSocket(socket)
eede4b76 494
0833402a 495Close a socket opened with OpenSocket.
eede4b76 496
0833402a 497=item $req->Accept()
90a18d65 498
0833402a 499Accepts a connection on $req, attaching the filehandles and
500populating the environment hash.
501Returns 0 on success.
502If a connection has been accepted before, the old
503one will be finished first.
1b64d24d 504
0833402a 505Note that unlike with the old interface, no die and warn
506handlers are installed by default. This means that if
507you are not running an sfio enabled perl, any warn or
508die message will not end up in the server's log by default.
509It is advised you set up die and warn handlers yourself.
510FCGI.pm contains an example of die and warn handlers.
1b64d24d 511
0833402a 512=item $req->Finish()
1b64d24d 513
0833402a 514Finishes accepted connection.
515Also detaches filehandles.
516
517=item $req->Flush()
518
519Flushes accepted connection.
520
521=item $req->Detach()
522
523Temporarily detaches filehandles on an accepted connection.
1b64d24d 524
0833402a 525=item $req->Attach()
526
527Re-attaches filehandles on an accepted connection.
528
7fa2de73 529=item $req->LastCall()
530
531Tells the library not to accept any more requests on this handle.
532It should be safe to call this method from signal handlers.
533
534Note that this method is still experimental and everything
535about it, including its name, is subject to change.
536
0833402a 537=item $env = $req->GetEnvironment()
538
539Returns the environment parameter passed to FCGI::Request.
540
541=item ($in, $out, $err) = $req->GetHandles()
542
543Returns the file handle parameters passed to FCGI::Request.
544
545=item $isfcgi = $req->IsFastCGI()
546
547Returns whether or not the program was run as a FastCGI.
548
549=back
550
551=head1 AUTHOR
552
553Sven Verdoolaege <skimo@kotnet.org>
554
555=cut
556
557__END__