Show Contents Previous Page Next Page Chapter 9 - Perl API Reference Guide / The Apache::File Class Apache's http_core module already has a default handler to send
files straight from disk to the client. Such files include static HTML, plain
text, compressed archives, and image files in a number of different formats.
A bare-bones handler in Perl only requires a few lines of code, as Example 9-1
shows. After the standard preamble, the handler() function attempts
to open Example 9-1. A Simple but Flawed Way to Send Static Files package Apache::EchoFile; use strict; use Apache::Constants qw(:common); use Apache::File (); sub handler { my $r = shift; my $fh = Apache::File->new($r->filename) or return FORBIDDEN; $r->send_fd($fh); close $fh; return OK; } 1; __END__ While this works well in most cases, there is more involved in sending a file over HTTP than you might think. To fully support the HTTP/1.1 protocol, one has to handle the PUT and OPTIONS methods, handle GET requests that contain a request body, and provide support for If-modified-since requests. Example 9-2 is the Apache::SendFile
module, a Perl version of the http_core module default handler. It
starts off as before by loading the Apache::Constants module. However,
it brings in more constants than usual. The :response group pulls
in the constants we normally see using the :common tag, plus a few
more including the We next bring in the Apache::File module in order to open and read the contents of the file to be sent and to load the HTTP/1.1-specific file-handling methods. The first step we take upon entering the handler() function is to call the discard_request_body() method. Unlike HTTP/1.0, where only POST and PUT requests may contain a request body, in HTTP/1.1 any method may include a body. We have no use for it, so we throw it away to avoid potential problems. We now check the request method by calling the request object's method_number()
method. Like the http_core handler, we only handle GET requests (method
numbers The PUT method is applicable even if the resource doesn't exist, but we
don't support it, so we return Provided the request has passed all these checks, we attempt to open the
requested file with Apache::File. If the file cannot be opened, the
handler logs an error message and returns At this point, we know that the request method is valid and the file exists and is accessible. But this doesn't mean we should actually send the file because the client may have cached it previously and has asked us to transmit it only if it has changed. The update_mtime(), set_last_modified(), and set_etag() methods together set up the HTTP/1.1 headers that indicate when the file was changed and assign it a unique entity tag that changes when the file changes. We then call the meets_conditions() method to find out if the file
has already been cached by the client. If this is the case, or some other
condition set by the client fails, meets_conditions() returns a response
code other than Otherwise we call the set_content_length() method to set the outgoing
Content-length header to the length of the file, then call send_http_header()
to send the client the full set of HTTP headers. The return value of header_only()
is tested to determine whether the client has requested the header only; if
the method returns false, then the client has requested the body of the file
as well as the headers, and we send the file contents using the send_fd()
method. Lastly, we tidy up by closing the filehandle and returning The real default handler found in http_core.c actually does a bit
more work than this. It includes logic for sending files from memory via mmap()
if After reading through this you'll probably be completely happy to return
Example 9-2. A 100-Percent Pure Perl Implementation of the Default http_core Content Handler package Apache::SendFile; use strict; use Apache::Constants qw(:response :methods :http); use Apache::File (); use Apache::Log (); sub handler { my $r = shift; if ((my $rc = $r->discard_request_body) != OK) { return $rc; } if ($r->method_number == M_INVALID) { $r->log->error("Invalid method in request ", $r->the_request); return NOT_IMPLEMENTED; } if ($r->method_number == M_OPTIONS) { return DECLINED; #http_core.c:default_handler() will pick this up } if ($r->method_number == M_PUT) { return HTTP_METHOD_NOT_ALLOWED; } unless (-e $r->finfo) { $r->log->error("File does not exist: ", $r->filename); return NOT_FOUND; } if ($r->method_number != M_GET) { return HTTP_METHOD_NOT_ALLOWED; } my $fh = Apache::File->new($r->filename); unless ($fh) { $r->log->error("file permissions deny server access: ", $r->filename); return FORBIDDEN; } $r->update_mtime(-s $r->finfo); $r->set_last_modified; $r->set_etag; if((my $rc = $r->meets_conditions) != OK) { return $rc; } $r->set_content_length; $r->send_http_header; unless ($r->header_only) { $r->send_fd($fh); } close $fh; return OK; } 1; __END__Show Contents Previous Page Next Page Copyright © 1999 by O'Reilly & Associates, Inc. |
HIVE: All information for read only. Please respect copyright! |