Cleaned up OS_CreateLocalIpcFd() and OS_FcgiConnect().
[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';
c620a324 9# $Id: FCGI.PL,v 1.22 2000/12/31 21:56:10 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
c620a324 24$VERSION = '0.59';
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
0833402a 334sub READLINE {
335 my $stream = shift;
336 my ($s, $c);
337 my $rs = $/ eq '' ? "\n\n" : $/;
338 my $l = substr $rs, -1;
339 my $len = length $rs;
340
341 $c = $stream->GETC();
342 if ($/ eq '') {
343 while ($c eq "\n") {
344 $c = $stream->GETC();
345 }
346 }
347 while (defined $c) {
348 $s .= $c;
349 last if $c eq $l and substr($s, -$len) eq $rs;
350 $c = $stream->GETC();
351 }
352 $s;
353}
1b64d24d 354
0833402a 355sub OPEN {
356 $_[0]->CLOSE;
357 if (@_ == 2) {
358 return open($_[0], $_[1]);
359 } else {
360 my $rc;
361 eval("$rc = open($_[0], $_[1], $_[2])");
362 die $@ if $@;
363 return $rc;
364 }
365}
1b64d24d 366
0833402a 3671;
eede4b76 368
0833402a 369=pod
eede4b76 370
0833402a 371=head1 NAME
eede4b76 372
0833402a 373FCGI - Fast CGI module
eede4b76 374
0833402a 375=head1 SYNOPSIS
6b312a77 376
0833402a 377 use FCGI;
6b312a77 378
0833402a 379 my $count = 0;
380 my $request = FCGI::Request();
6b312a77 381
0833402a 382 while($request->Accept() >= 0) {
383 print("Content-type: text/html\r\n\r\n", ++$count);
384 }
eede4b76 385
0833402a 386=head1 DESCRIPTION
eede4b76 387
0833402a 388Functions:
eede4b76 389
0833402a 390=over 4
eede4b76 391
0833402a 392=item FCGI::Request
eede4b76 393
0833402a 394Creates a request handle. It has the following optional parameters:
eede4b76 395
0833402a 396=over 8
eede4b76 397
0833402a 398=item input perl file handle (default: \*STDIN)
eede4b76 399
0833402a 400=item output perl file handle (default: \*STDOUT)
9915cd6d 401
0833402a 402=item error perl file handle (default: \*STDERR)
9915cd6d 403
0833402a 404These filehandles will be setup to act as input/output/error
405on succesful Accept.
9915cd6d 406
0833402a 407=item environment hash reference (default: \%ENV)
9915cd6d 408
0833402a 409The hash will be populated with the environment.
9915cd6d 410
0833402a 411=item socket (default: 0)
9915cd6d 412
0833402a 413Socket to communicate with the server.
414Can be the result of the OpenSocket function.
415For the moment, it's the file descriptor of the socket
416that should be passed. This may change in the future.
9915cd6d 417
0833402a 418=item flags (default: 0)
1d209997 419
0833402a 420Possible values:
1d209997 421
0833402a 422=over 12
9915cd6d 423
0833402a 424=item FCGI::FAIL_ACCEPT_ON_INTR
5baeeca7 425
0833402a 426If set, Accept will fail if interrupted.
427It not set, it will just keep on waiting.
5baeeca7 428
0833402a 429=back
5baeeca7 430
0833402a 431=back
5baeeca7 432
0833402a 433Example usage:
434 my $req = FCGI::Request;
5baeeca7 435
0833402a 436or:
437 my %env;
438 my $in = new IO::Handle;
439 my $out = new IO::Handle;
440 my $err = new IO::Handle;
441 my $req = FCGI::Request($in, $out, $err, \%env);
5baeeca7 442
0833402a 443=item FCGI::OpenSocket(path, backlog)
5baeeca7 444
0833402a 445Creates a socket suitable to use as an argument to Request.
5baeeca7 446
0833402a 447=over 8
eede4b76 448
0833402a 449=item path
eede4b76 450
0833402a 451Pathname of socket or colon followed by local tcp port.
eede4b76 452
0833402a 453=item backlog
eede4b76 454
0833402a 455Maximum length of the queue of pending connections.
456If a connection
457request arrives with the queue full the client may receive
458an error with an indication of ECONNREFUSED.
eede4b76 459
0833402a 460=back
eede4b76 461
0833402a 462=item FCGI::CloseSocket(socket)
eede4b76 463
0833402a 464Close a socket opened with OpenSocket.
eede4b76 465
0833402a 466=item $req->Accept()
90a18d65 467
0833402a 468Accepts a connection on $req, attaching the filehandles and
469populating the environment hash.
470Returns 0 on success.
471If a connection has been accepted before, the old
472one will be finished first.
1b64d24d 473
0833402a 474Note that unlike with the old interface, no die and warn
475handlers are installed by default. This means that if
476you are not running an sfio enabled perl, any warn or
477die message will not end up in the server's log by default.
478It is advised you set up die and warn handlers yourself.
479FCGI.pm contains an example of die and warn handlers.
1b64d24d 480
0833402a 481=item $req->Finish()
1b64d24d 482
0833402a 483Finishes accepted connection.
484Also detaches filehandles.
485
486=item $req->Flush()
487
488Flushes accepted connection.
489
490=item $req->Detach()
491
492Temporarily detaches filehandles on an accepted connection.
1b64d24d 493
0833402a 494=item $req->Attach()
495
496Re-attaches filehandles on an accepted connection.
497
498=item $env = $req->GetEnvironment()
499
500Returns the environment parameter passed to FCGI::Request.
501
502=item ($in, $out, $err) = $req->GetHandles()
503
504Returns the file handle parameters passed to FCGI::Request.
505
506=item $isfcgi = $req->IsFastCGI()
507
508Returns whether or not the program was run as a FastCGI.
509
510=back
511
512=head1 AUTHOR
513
514Sven Verdoolaege <skimo@kotnet.org>
515
516=cut
517
518__END__