Show Contents Previous Page Next Page Chapter 4 - Content Handlers / Handling Errors You already know about using header_out() to set HTTP header fields. A properly formatted HTTP header is sent to the browser when your module explicitly calls send_http_header(), or it is sent for you automatically if you are using Apache::Registry, the PerlSendHeader directive is set to On, and your script prints some text that looks like an HTTP header.
You have to be careful, however, if your module ever returns non-OK status codes. Apache wants to assume control over the header generation process in the case of errors; if your module has already sent the header, then Apache will send a redundant set of headers with unattractive results. This applies both to real HTTP errors, like Consider the following fishy example: package Apache::Crash; # File: Apache/Crash.pm use strict; use Apache::Constants qw(:common); use constant CRASH => 1; sub handler { my $r = shift; $r->content_type('text/plain'); $r->send_http_header; return OK if $r->header_only; return SERVER_ERROR if CRASH; $r->print('Half a haddock is better than none.'); return OK; } 1; __END__
After setting the document MIME type, this module sends off the HTTP header. It then checks a constant named % telnet www.modperl.com 80 Trying 192.168.2.5... Connected to modperl.com. Escape character is '^]'. GET /Crash HTTP/1.0 HTTP/1.1 200 OK Date: Thu, 21 May 1998 11:31:40 GMT Server: Apache/1.3b6 Connection: close Content-Type: text/plain HTTP/1.1 200 OK Date: Thu, 21 May 1998 11:31:40 GMT Server: Apache/1.3b6 Connection: close Content-Type: text/html <HTML><HEAD> <TITLE>500 Internal Server Error</TITLE> </HEAD><BODY> <H1>Internal Server Error</H1> The server encountered an internal error or misconfiguration and was unable to complete your request.<P> </BODY></HTML> Connection closed by foreign host.
Not only are there two HTTP headers here, but both of them indicate a status code of The cardinal rule is that you should never call Apache::send_http_header() until your module has completed all its error checking and has decided to return an OK status code. Here's a better version of Apache::Crash that avoids the problem: package Apache::Crash; # File: Apache/Crash.pm use strict; use Apache::Constants qw(:common); use constant CRASH => 1; sub handler { my $r = shift; return SERVER_ERROR if CRASH; $r->content_type('text/plain'); $r->send_http_header; return OK if $r->header_only; $r->print('Half a haddock is better than none.'); return OK; } 1; __END__ Now when we telnet to the server, the server response looks the way it should: (~) 103% telnet www.modperl.com 80 Trying 192.168.2.5... Connected to modperl.com. Escape character is '^]'. GET /Crash HTTP/1.0 HTTP/1.1 500 Internal Server Error Date: Thu, 21 May 1998 11:40:56 GMT Server: Apache/1.3b6 Connection: close Content-Type: text/html <HTML><HEAD> <TITLE>500 Internal Server Error</TITLE> </HEAD><BODY> <H1>Internal Server Error</H1> The server encountered an internal error or misconfiguration and was unable to complete your request.<P> </BODY></HTML>
Another important detail about error handling is that Apache ignores the fields that you set with header_out() when your module generates an error status or invokes an internal redirect. This is usually not a problem, but there are some cases in which this restriction can be problematic. The most typical case is the one in which you want a module to give the browser a cookie and immediately redirect to a different URI. Or you might want to assign an error document to the For these cases, call the request object's err_header_out() method. It has identical syntax to header_out(), but the fields that you set with it are sent to the browser only when an error has occurred. Unlike ordinary headers, the fields set with err_header_out() persist across internal redirections, and so they are passed to Apache ErrorDocument handlers and other local URIs. This provides you with a simple way to pass information between modules across
internal redirects. Combining the example from this section with the example
from the previous section gives the modules shown in Example 4-18.
Apache::GoFish generates a Figure 4-10. When Apache::GoFish generates a custom error document, it displays the contents of the custom X-Odor header. The code should be fairly self-explanatory. The main point to notice is Apache::GoFish's use of err_header_out() to set the value of the X-Odor field, and Apache::Carp's use of the same function to retrieve it. Like header_out(), when you call err_header_out() with a single argument, it returns the current value of the field and does not otherwise alter the header. When you call it with two arguments, it sets the indicated field. An interesting side effect of this technique is that the X-Odor field is also returned to the browser in the HTTP header. This could be construed as a feature. If you wished to pass information between the content handler and the error handler without leaving tracks in the HTTP header, you could instead use the request object's "notes" table to pass messages from one module to another. Chapter 9 covers how to use this facility (see the description of the notes() method under "Server Core Functions"). Example 4-18. Invoking a Custom Error Handler Document package Apache::GoFish; # File: Apache/GoFish.pm use Apache::Constants qw(:common :response); use constant CRASH=>1; sub handler { my $r = shift; $r->err_header_out('X-Odor'=>"something's rotten in Denmark"); $r->custom_response(SERVER_ERROR, "/Carp"); return SERVER_ERROR if CRASH; $r->content_type('text/plain'); $r->send_http_header; return OK if $r->header_only; $r->print('Half a haddock is better than none.'); return OK; } 1; __END__ Here is a sample configuration entry: <Location /GoFish> SetHandler perl-script PerlHandler Apache::GoFish </Location> Example 4-19. An Error Handler to Complement the Previous Example package Apache::Carp; # File: Apache/Carp.pm use strict; use Apache::Constants qw(:common); use CGI qw(:html); sub handler { my $r = shift; my $odor = $r->err_header_out('X-Odor'); $odor ||= 'unspecified odor'; $r->content_type('text/html'); $r->send_http_header; return OK if $r->header_only; my $original_request = $r->prev; my $original_uri = $original_request ? $original_request->uri : ''; my $admin = $r->server->server_admin; $r->print( start_html(-title => 'Phew!!', -bgcolor => 'white'), h1('Phew!!'), p("Something fishy happened while processing this request."), p("The odor was ", strong($odor), '.'), hr, address(a({-href => "mailto:$admin"}, 'webmaster')), end_html ); return OK; } 1; __END__ Here is a sample configuration entry: <Location /Carp> SetHandler perl-script PerlHandler Apache::Carp </Location>Show Contents Previous Page Next Page Copyright © 1999 by O'Reilly & Associates, Inc. |
HIVE: All information for read only. Please respect copyright! |