Archive for March, 2008

PHP

How I PHP: X-SendFile

It’s time for a new article in the ‘How I PHP’ series, which are about methods I use, but aren’t commonly known. This time it is about an Apache module ‘mod_xsendfile’.

Normally when want to let a user download a file, you simply stick it in a dir under the document root and let Apache do the rest.

However in some cases that is not good enough. You might need to do some authenticate first or you need to lookup the actual file name. In that case you would use PHP, which would result in a script looking like this:

authenticate(); # authenticate and authorize, redirect/exit if failed
$file = determine_file();
 
if (!file_exists($file)) trigger_error("File '$file' doesn't exist.", E_USER_ERROR);
 
header("Content-type: application/octet-stream");
header('Content-Disposition: attachment; filename="' . basename($file) . '"');
header("Content-Length: ". filesize($file));
readfile($file);

This means PHP has to read in the file, which goes through the output buffer, is flushed to Apache and processed before send to client. In this small I didn’t specify any other headers Apache normally sends like last-modified. If I want to actually make the caching based on last-modified work, I need to check the if-modified-since request header, check the mtime of the file and send a 304 result header. (I’m to lazy right now to write a code example for that, sorry)

Wouldn’t it be nicer to tell Apache, please send that file, and be done with it. Well, you can. When you enable mod_xsendfile in Apache, you can send an X-SendFile header, which is processed by Apache.

authenticate(); # authenticate and authorize, redirect/exit if failed
$file = determine_file();
 
header("X-Sendfile: $somefile");
header("Content-type: application/octet-stream");
header('Content-Disposition: attachment; filename="' . basename($file) . '"');

Note that this technique was copied from Lighttp, so if you’re using that it will also work.

Javeline / Ajax.org, PHP

Authentication without sessions in PHP

The normal way to save a state, like the fact that a user is logged in, is to use sessions. Session work really nice, you can save all kind of data on the server (in a temp file) which is identified by a hash which is known on the client in a cookie or by url rewriting.

However if you only need to save a small bit of login info, like a username and timestamp, using sessions is a bit of an overkill. Also, if your system would grow beyond to a size where you need load balancing over multiple servers, having a solution with sessions would be somewhat of a burden. You would need to store them in a centralized place, like a DB server.

Another way to do this, is to create an authentication hash. The idea is fairly simple: Join the information you want to known into a single string and append an md5 key of all the info + a secret word. On each request, check if the hash is available and correct, otherwise redirect the user to a login form. You can use the hash the same way PHP uses the session hash, using cookies or URL rewriting.

[snip php-source]code/authhash/authhash.php[/snip]
Download the code