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