This month, we're going to dig deeply into my bag of tricks and learn how to debug a Perl CGI script. All the tips and techniques here are straightforward; most of them are things you probably know from your first course in programming-that is, if you had one! And that just may be your problem: if you are a self-taught Perl programmer, you may not have received any formal education in debugging.
So, let's set the stage. You've just written your magnum Perlus, a 1000-line masterpiece CGI script that takes orders, processes credit cards, tracks inventory, and calculates satellite orbits. You upload it to the server, and fire up your browser to put it through its paces. And you get something like what appears below:
|
500 Internal Server Error
The server encountered an internal error or misconfiguration and was unable to complete your request. Please contact the server administrator, webmaster@foo.com and inform them of the time the error occurred, and anything you might have done that may have caused the error. More information about this error may be available in the server error log.
|
Um, oops. Now what?
An excellent resource for beginning (and even experienced) CGI programmers is The Idiot's Guide to Solving Perl CGI Problems. And don't be offended by the name: we're all idiots, at times. Let's try a few of their suggestions, and some others as we go, to see what's wrong.
Our first step: verify that the script is available for execution.
cgi-bin or cgi-local. Sometimes the
scripts have to be in a central CGI repository, like
/usr/local/etc/httpd/cgi-bin.
.cgi; some require .pl;
others have no such requirement.
$ chmod 0755 myscript.cgi
or you may be able to use your FTP client software to change the
permissions after you upload the script.
This will serve to let you know that Perl's working, and the web server is set up correctly to handle Perl.#!/usr/bin/perl -w use diagnostics; print 'Content-type: text/html Hello, world.';
You may want to take this a bit further though, and for that I can recommend nothing better than perldiver. It's a very useful script that you upload and run to get a comprehensive report on the server: where Perl is, which modules are installed, and a wealth of other information. (You should probably remove this script once you are finished checking your site, as it provides a lot of information that an unscrupulous user might use to break in.)
So, you've taken these steps, and you still can't figure out what's wrong? The next logical step is to verify that the script compiles cleanly. You should as a matter of course start ALL your perl scripts this way:
These two little lines will save you a heap of trouble, but you have to use them every time to ensure that they'll be there to help you when you need them. The first line is the standard signal to the operating system that the Perl interpreter gets the rest of this file as source code. But the addition of the "-w" switch turns on compile-time and run-time warnings. The second line turns on a "pragma" (a compile-time option, if you will) that causes all errors and warnings to be spelled out in newbie-level, painfully explicit detail. Such detailed warnings sometimes get in the way if you are debugging from the command line, as your output scrolls off the screen, chased by a hundred lines of diagnostic output. But in the CGI world, we'll put up with this, nay even welcome it, as you will soon see.#!/usr/bin/perl -w use diagnostics;
There's a third line that you may wish to add to your code.
I hesitate to push this on anyone because I don't hold myself to this standard (which most Perl experts would say reflects poorly on me, and I have no defense for that). All I'll say is that you should try it, until such point as you understand what it does: then, make up your own mind. Basically, this pragma makes Perl more like other languages that require you to pre-declare variables. (There's much more to this, but for our purposes this will do.)use strict;
So, now you've added these lines, and perhaps run the code from the command line (if available) using
Perhaps now you'll see a few syntax errors, or other problems, that point you toward your problem. (The most common one I face here is lack of a particular optional library element, like Mail::Mailer.) If so, great: you have more information than you did when the dreaded "500" popped up on your browser.$ perl -c myscript.cgi
But what if you don't have access to a Perl interpreter at your command prompt? (This is becoming less and less likely, as Perl is available for almost any commonly used platform, and differences between native versions are likely to be slight or esoteric. But perhaps you are developing on a Windows system, and haven't taken the plunge to put Perl on your Microsoft slavebox yet. Or perhaps you are quick-fixing a script using a borrowed computer, and you don't want to inflict a huge Perl installation for a few minutes work.
This is where we get into the meat of what I do for a living: debugging Perl scripts with nothing more than an FTP client and a browser. First, we have to make some alterations to your script:
use CGI qw(:standard);
use CGI::Carp qw(fatalsToBrowser);
BEGIN {
print CGI::header();
open (STDERR, '>myscript.log');
}
These lines, or ones very much like them, go into just about every CGI
script I write. The first imports the standard CGI.pm package. Yes,
it's a large package, and it can be a bit like navigating an aircraft
carrier around the local marina. But it saves so much of your time,
it's worth it. Unless your web server is running on a 486, the couple
of extra microseconds you need to load this package will never be
missed. And if your script is part of a site that's being hit like
Yahoo!, you can still use CGI.pm or similar add-ons, but you just have
to learn how to do it with mod_perl. (Which is out of scope for this
column, and so we'll wave hands in the air and dismiss all this. Use
CGI.pm, it's good for you. Trust me, I'm a professional.)
The second line imports another package called "CGI::Carp", which is an
error handling package that replaces and extends the more frequently
seen Carp. We grab the CGI family's Carp because we want
nicely-formatted error messages, but even more we want the
error-trapping facilities provided by the internal routine
fatalsToBrowser. This routine will intercept control of
our script in the event of a fatal error, and display that error
(instead of the "500") in the browser.
Think of it as a "fail-safe".
The third line signals a BEGIN block. This is a syntactic element of Perl programs that lets you insert code blocks anywhere in your program (say, in include files, or modules). These blocks only get executed once, as they are parsed (so, they are compile-time code). In what I've shown, the code will redirect any further output via STDERR to a log file. This is quite handy in the CGI environment, because many times your CGI program's errors are either suppressed, or written to a centralized site error log that is very large and may not be readable by you.
There's another thing slipped into the BEGIN block which is intended
to save you grief. That's the print CGI::header
statement.
This forces a compliant HTTP header back to the browser before
anything else can slip in there and spoil your day. If you
aren't aware of it, CGI scripts must return a proper header at the top
of their "output document" so the browser knows what to do with the
output. The most common header looks like
(including that empty line), and that's what we produce. If your script already produces a header, then you'll see an "extra" one appear as plain text at the top of your document. Ignore it; when you finish debugging, you can remove theContent-type: text/html
print from the BEGIN
block and go back to using the regular header.
Of course, if your script produces a header other than Content-type (say, a cookie, or a redirection), that header will be ignored, pushed down into the document by our BEGIN block header. Again, we'll remove the BEGIN's header at some point when we're convinced the bugs are all dead.
Now, with these lines in place, you have a very good start towards
catching and even preventing those frustrating "500"s.
The author, an independent
consultant and
freelance CGI
programmer, has been a professional programmer for 20 years,
and a Perl programmer for 5 years.
Once upon a time, but long before Perl was even a gleam in Larry's eye,
he even did a little teaching at the
college level, breaking in
brand-new programmers. Learn
more about him. Write him,
if you have questions or comments about these articles.