NAME

Mod_perl_cgi - running CGI scripts under mod_perl ($Date: 2000/03/05 11:57:55 $)

DESCRIPTION

This part of the mod_perl FAQ deals with questions surrounding CGI scripts.

Why doesn't my CGI script work at all under mod_perl?

What are the symptoms? Here are some possibilities.

File not found

Have you made the correct entries in Apache's configuration files? You need to add the Alias /perl/ ... and <Location /perl>... 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.

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

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.

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";

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 -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.

The server terminates after processing the first request

Your script is calling the CORE perl 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 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 exit() with goto label;

See also what mod_perl_traps says about 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 Apache->exit(-2) should be used.

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 "Private Variables via my()" in perlsub, 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 -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).

mjtg-news.txt

Variables 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 use CGI is evaluated too early, before the environment has been set up. That can happen if you have use CGI in a script and pull the script in with a PerlRequire directive in httpd.conf. Replacing use CGI with require CGI will fix it.

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 Apache::PerlRun. See the documentation of that module, which is included with recent versions of mod_perl.

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) });
  }