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