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