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