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