Practical mod_perl, from O'Reilly. | 35

Practical mod_perl: Chapter 6: Coding with mod_perl in Mind

Handling Server Timeout Cases and Working with $SIG{ALRM}

Similar to the case where a user aborts the script execution by pressing the Stop button, the browser itself might abort the script if it hasn't returned any output after a certain timeout period (usually a few minutes).

Sometimes scripts perform very long operations that might take longer than the client's timeout.

This can happen when performing full searches of a large database with no full search support. Another example is a script interacting with external applications whose prompt reponse time isn't guaranteed. Consider a script that retrieves a page from another site and does some processing on it before it gets presented to the user. Obviously, nothing guarantees that the page will be retrieved fast, if at all.

In this situation, use $SIG{ALRM} to prevent the timeouts:

my $timeout = 10; # seconds
eval {
  local $SIG{ALRM} =
      sub { die "Sorry, timed out. Please try again\n" };
  alarm $timeout;
  # some operation that might take a long time to complete
  alarm 0;
};
die $@ if $@;

In this code, we run the operation that might take a long time to complete inside an eval block. First we initialize a localized ALRM signal handler, which resides inside the special %SIG hash. If this handler is triggered, it will call die( ), and the eval block will be aborted. You can then do what you want with it--in our example, we chose to abort the execution of the script. In most cases, you will probably want to report to the user that the operation has timed out.

The actual operation is placed between two alarm( ) calls. The first call starts the clock, and the second cancels it. The clock is running for 10 seconds in our example. If the second alarm( ) call doesn't occur within 10 seconds, the SIGALRM signal is sent and the handler stored in $SIG{ALRM} is called. In our case, this will abort the eval block.

If the operation between the two alarm( )s completes in under 10 seconds, the alarm clock is stopped and the eval block returns successfully, without triggering the ALRM handler.

Notice that only one timer can be used at a given time. alarm( )'s returned value is the amount of time remaining in the previous timer. So you can actually roughly measure the execution time as a side effect.

It is usually a mistake to intermix alarm( ) and sleep( ) calls. sleep( ) may be internally implemented in your system with alarm( ), which will break your original alarm( ) settings, since every new alarm( ) call cancels the previous one.

Finally, the actual time resolution may be imprecise, with the timeout period being accurate to plus or minus one second. You may end up with a timeout that varies between 9 and 11 seconds. For granularity finer than one second, you can use Perl's four-argument version of select( ), leaving the first three arguments undefined. Other techniques exist, but they will not help with the task in question, in which we use alarm( ) to implement timeouts.

Generating Correct HTTP Headers

An HTTP response header consists of at least two fields: HTTP response and MIME-type header Content-Type:

HTTP/1.0 200 OK
Content-Type: text/plain

After adding a newline, you can start printing the content. A more complete response includes the date timestamp and server type. For example:

HTTP/1.0 200 OK
Date: Tue, 10 Apr 2001 03:01:36 GMT
Server: Apache/1.3.19 (Unix) mod_perl/1.25
Content-Type: text/plain

To notify clients that the server is configured with KeepAlive Off, clients must be told that the connection will be closed after the content has been delivered:

Connection: close

There can be other headers as well, such as caching control headers and others specified by the HTTP protocol. You can code the response header with a single print( ) statement:

print qq{HTTP/1.1 200 OK
  Date: Tue, 10 Apr 2001 03:01:36 GMT
  Server: Apache/1.3.19 (Unix) mod_perl/1.25
  Connection: close
  Content-Type: text/plain
 
  };

or with a "here"-style print( ):

print <<'EOT';
  HTTP/1.1 200 OK
  Date: Tue, 10 Apr 2001 03:01:36 GMT
  Server: Apache/1.3.19 (Unix) mod_perl/1.25
  Connection: close
  Content-type: text/plain
 
  EOT

Don't forget to include two newlines at the end of the HTTP header. With the help of Apache::Util::ht_time( ), you can get the right timestamp string for the Date: field.

If you want to send non-default headers, use the header_out( ) method. For example:

$r->header_out("X-Server" => "Apache Next Generation 10.0");
$r->header_out("Date" => "Tue, 10 Apr 2001 03:01:36 GMT");

When the headers setting is completed, the send_http_header( ) method will flush the headers and add a newline to designate the start of the content.

$r->send_http_header;

Some headers have special aliases. For example:

$r->content_type('text/plain');

is the same as:

$r->header_out("Content-Type" => "text/plain");

but additionally sets some internal flags used by Apache. Whenever special-purpose methods are available, you should use those instead of setting the header directly.


Created: March 27, 2003
Revised: July 23, 2003

URL: http://webreference.com/programming/perl/mod_perl/chap6/4