http://qs321.pair.com?node_id=569820


in reply to How to debug CGI scripts?

Here's a trick I've been using for a while for this very purpose:

On the client side, send over an extra parameter 'CHECKPOINT=1' with the rest of the data. In my case, the client is a command-line script that GETs or POSTs to the server with a bunch of data. If you're just using an HTML page with a form on it, add a hidden field named 'CHECKPOINT' with value '1'.

On the server side (in your CGI script), check for the CHECKPOINT parameter. If it is given, write out the current parameters. In my case, I am using CGI.pm and have a query object stored in $cgi, so I use:

if ($cgi->param('CHECKPOINT')) { local *STATE; open(STATE, ">/tmp/CGI-CHECKPOINT-".basename($0)) || open(STATE, ">/tmp/CGI-CHECKPOINT"); $cgi->save(*STATE); close STATE; }
I forget why I used a fallback filename. Might be related to mod_perl. The effect of the above code is to write out all query parameters to a file in /tmp when a request is received with CHECKPOINT set.

Now, to replay the request, run your CGI script from the command line with the environment variable CHECKPOINT set to 1. (So 'CHECKPOINT' is actually used for two completely different purposes: saving the state and replaying it. You could use different names if you like.) For replaying, you have to construct your query object differently. This code will come before the preceding code (because it's for setting up the query object):

my $cgi; if ($ENV{'CHECKPOINT'}) { local *STATE; open(STATE, "/tmp/CGI-CHECKPOINT-".basename($0)) || open(STATE, "/tmp/CGI-CHECKPOINT"); $cgi = CGI->new(*STATE); close STATE; $cgi->delete('CHECKPOINT'); } else { $cgi = CGI->new(); }
Now you can repeatedly run the CGI script from the command line, under the debugger, or whatever:
CHECKPOINT=1 perl -d MyScript.cgi
The reason why I named the checkpoint file after the CGI script is because I'm using scripts to make the HTTP requests on the client side, and many times they kick off a series of requests. I may want to debug an earlier one rather than the last one that happened (which is typically a little stub reporting success or failure to the server).

One problem to watch out for: your CGI might easily behave differently if run as a different user than the one used when running under the control of the web server.

The above logic is all wrapped up in a pair of modules that I use for all my CGI scripts and client-side scripts. As a result, whenever I have a problem with any of my scripts, I can run it with CHECKPOINT=1 and be stepping through the debugger in seconds; no need to modify the script in order to enable this replay mode. Also, this means that the functionality is available in the production scripts, so when a problem arises I have a straightforward way to capture a trace and debug it. (Depending on what your CGIs are doing, you might want to just use the checkpoint capture half on the production servers, then copy the resulting /tmp/CGI-CHECKPOINT* file over to your test server. Much less likely to screw up the production server by hanging onto a lock file or creating a file as the wrong user and thereby breaking every subsequent request.) (Not that I'm saying that has ever happened to me.) (But you'll notice I didn't say that it hadn't.)