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