blob: 0e94bfe8baf88b852eca7b38781563ff142d9e80 [file] [log] [blame]
=head1 NAME
Mod_perl_cgi - running CGI scripts under mod_perl ($Date$)
=head1 DESCRIPTION
This part of the mod_perl FAQ deals with questions surrounding CGI
scripts.
=head1 Why doesn't my CGI script work at all under mod_perl?
What are the symptoms? Here are some possibilities.
=head2 File not found
Have you made the correct entries in Apache's configuration files? You
need to add the C<Alias /perl/ ...> and C<E<lt>Location /perlE<gt>...>
directives to access.conf as described in mod_perl.pod. And of course the
script must be in the directory specified by the Alias directive and it
must be readable and executable by the user that the web server runs as.
=head2 Forbidden
You don't have permission to access /perl/foo on this server.
chmod 755 /path/to/my/mod_perl/scripts
chmod 755 /path/to/my/mod_perl/scripts/foo
=head2 Internal Server Error
The script died with an execution error. There should be an error message
in the server's error.log saying why. Provided you are using CGI.pm, you
can also see what happens by running the script at a shell prompt.
If the error.log claims there are syntax errors in your script,
but
perl -c /path/to/my/mod_perl/scripts/foo
says it is OK, you have probably used __END__ or __DATA__. Sorry.
Mod_perl's Apache::Registry can't deal with that.
=head1 The script runs but the headers are mangled
You have a script that works fine under mod_cgi but the browser
displays "Content-Type: text/html" or similar headers at the top of
the page when it is run under mod_perl. There are two possible
causes.
Something, either your script or mod_perl or CGI.pm (if you are using
it) has to trigger Apache to send the response header. This happens
when you call the CGI.pm $q->header method or mod_perl's
$r->send_http_header. But if your script just prints out one or more
header lines followed by a blank line and the page content, you need
to set "PerlSendHeader On" in the configuration for the location of
the script. This tells mod_perl to parse the stuff that the script
prints and call $r->send_http_header for you when it sees the blank
line.
This parsing only happens if PerlSendHeader is on and the header has
not been sent yet. Even so, it is costly and mod_perl makes the
assumption that individual headers are not split across print
statements, to simplify the parser and avoid having to retain
fragments of headers between calls to print(). So the following does
not work:
print "Content-type: text/html\n";
print "Set-Cookie: iscookietext\; ";
print "expires=Wednesday, 09-Nov-1999 00:00:00 GMT\; ";
print "path=\/\; domain=\.mmyserver.com\; \n\n";
print "hello";
because the Set-Cookie header is split across multiple print's.
You need to print each header (or group of headers) in one go,
possibly after building it up in a temporary variable.
print "Content-type: text/html\n";
my $cookie = "Set-Cookie: iscookietext; ";
$cookie .= "expires=Wednesday, 09-Nov-1999 00:00:00 GMT; ";
$cookie .= "path=/; domain=.mmyserver.com; \n\n";
print $cookie;
print "hello";
=head1 My CGI script behaves strangely under mod_perl. Why?
Remember that a conventional CGI script always starts up a fresh perl
interpreter, whereas a mod_perl script is reused in the same process
context many times. This means that certain categories of variables can
survive from one invocation of the script to the next. You can make that
work to your advantage, but you can also be caught out by it.
When diagnosing a problem that might be caused by variable lifetimes,
always start the web server in single process mode. Apache normally
spawns a number of child processes to handle queries, and they get used in
round-robin fashion, which makes test results unpredictable.
The command
# ./httpd -X
will start a single-process server with its default configuration.
You can specify a different configuration with the C<-f> flag (and
thus use a different port number for testing, for instance).
Now try executing your script from a browser. A non-graphical browser
is often much better for diagnosing low-level problems. Install lynx
(http://lynx.browser.org/) if you haven't already got it and use
lynx -mime_header http://localhost/perl/myscript
to see the response that the web server produces when it GETs your
script, and
lynx -head -dump http://localhost/perl/myscript
to see the response to a HEAD request. The GET and HEAD commands that
come with libwww-perl are similar but slower.
Here are some of the effects that you might see.
=head2 The server terminates after processing the first request
Your script is calling the CORE perl C<exit()> function. That is not
a problem in a conventional CGI script, provided that query processing
is complete. But you almost certainly don't want to exit in a
mod_perl script. It kills the server process that handled the
request, meaning that the advantage of using mod_perl to avoid startup
overhead is lost.
The best way to avoid calling C<exit()> is to restructure the script so
that all execution paths return to a common point at the end of the
script. If this seems impractical you can force the same effect by
placing a label after the last executable statement and replacing calls to
C<exit()> with C<goto label;>
See also what mod_perl_traps says about C<Apache::exit()> and the way
that Apache::Registry causes it to terminate the script but not the
httpd child.
There may be exceptional circumstances in which you explicitly want to
terminate the httpd child at the end of the current request. In this
case C<Apache-E<gt>exit(-2)> should be used.
=head2 Variables retain their value from one request to the next
The so-called sticky query effect happens when the CGI query object, or
another request-specific variable, has a lifetime longer than a single
execution of your script and does not get reinitialised each time the
script is invoked.
This does not matter in a conventional CGI script, because the script
starts with a clean slate for each new request. But a mod_perl script
gets compiled into a subroutine by the Apache::Registry handler and then
processes an arbitrary number of requests. To make sure that both you and
the perl interpreter have the same idea about the meaning of your script,
make sure it starts like this:
#!/usr/bin/perl -w
use strict;
It is good for you! It will make perl point out all variables that you
have not explicitly declared. You can then think about whether they need
to be global or if they can be lexical. Try to declare things lexically,
with my(). These variables disappear when the block they are declared in
ends, so they don't occupy memory when they are not in use and they also
do not need a run-time symbol table entry.
Beware, though, of referring to a lexical variable indirectly from within a
subroutine. To quote L<perlsub/"Private Variables via my()">, the
variable "... now becomes unreachable by the outside world, but retains
its value between calls to ..." the subroutine. You will see classic
"sticky query" symptoms if your code looks like this:
#!/usr/bin/perl -w
use strict;
use CGI;
my $q = CGI->new();
doit();
sub doit {
print($q->header(), $q->start_html());
print('Value is ', $q->param('val')) if $q->param;
$q->print('<p>', $q->startform, 'Value? ',
$q->textfield(-name=>'val', -size=>20), ' ',
$q->submit('enter'), $q->endform);
print($q->end_html());
}
Because you remembered to put the C<-w> switch on the first line, the
error log will tell you that "Variable $q will not stay shared"
(provided you are using perl5.004 or higher).
You must either pass the variable to the subroutine as a parameter,
doit($q)
sub doit {
my($q) = @_;
....
or declare this variable to be global,
use vars qw($q);
$q = CGI->new();
The reason why Perl works this way is explained in a news posting by
Mike Guy that is included with this FAQ (mjtg-news.txt).
=for html
<a href="mjtg-news.txt">mjtg-news.txt</a>
=head2 Variables B<still> retain their value from one request to the next
CGI.pm must pull some extra tricks when it is being used via
Apache::Registry. Versions of CGI.pm before 2.35 did not know this,
and Apache::Registry will complain if you try to use an earlier
version.
CGI.pm detects that it is running under Apache::Registry by looking
for an environment variable. This test can fail if C<use CGI> is
evaluated too early, before the environment has been set up. That can
happen if you have C<use CGI> in a script and pull the script in with
a C<PerlRequire> directive in httpd.conf. Replacing C<use CGI> with
C<require CGI> will fix it.
=head2 Do I have to rewrite my legacy code for mod_perl?
If you have CGI code that seems to be fundamentally at odds with
mod_perl's "compile once, run many" environment, you may find that
it will work if run under the module C<Apache::PerlRun>. See the
documentation of that module, which is included with recent versions
of mod_perl.
=head1 How can my script continue running after sending the response?
If the client submits a form that will take some time to process, you
may want to say "Thanks for submitting the form" and close the
connection, before processing it.
You can achieve this by registering the subroutine that processes the
form as a cleanup handler:
if($ENV{GATEWAY_INTERFACE} =~ /^CGI-Perl/) {
Apache->request->register_cleanup(sub { doProcess($query) });
}