MasonRecipe » History » Revision 12
Revision 11 (EvanCarroll, 2007-08-21 18:17) → Revision 12/20 (EvanCarroll, 2007-08-21 21:03)
= Using Mason with lighttpd (via FastCGI) = Mason can be used with Lighttpd and is (arguably :-) easier to setup that Apache + mod_perl. Here's my current setup. Requirements: * Mason handles files ending with '/' or '.html' or '.css'. * Other files statically served by lighttpd * Load perl modules at fastcgi process start * Disallow access to .mhtml files First we need a mason handler, that lighttpd will use via FastCGI. I've called it mason_lighttpd_handler.fcgi. It does not need to be in any particular location. {{{ #!perl #!/usr/bin/perl use CGI::Fast; strict; use HTML::Mason::CGIHandler; use URI; CGI::Fast; { package HTML::Mason::Commands; # anything you want available to components # use Storable qw(freeze thaw); DBI; # use HTTP::BrowserDetect; Other::Module; } # lazily-instantiated variables my $cgi; my $h; ## We or-set these so you can optionally set them in your lighttpd.conf ## if you want more than one mason site to use the wrapper $ENV{MASON_COMP_ROOT} ||= '/your/comp/root/goes/here'; $ENV{MASON_DATA_ROOT} ||= '/your/data/root/goes/here'; while ($cgi = new CGI::Fast()) CGI::Fast) { # this seems to be necessary as lighttpd does not provide the # PATH_INFO and the QUERY_STRING environment variables # Note that the below seems to work but I have no idea if it's the # 'right' way to parse the REQUEST_URI out into PATH_INFO and QUERY_STRING my $uri = URI->new( $ENV{REQUEST_URI} ); $uri->path( $uri->path . 'index.html' ) $ENV{REQUEST_URI}; if $uri->path ($uri =~ /\/$/ ; /\?/) { $uri =~ /^(.*?)\?(.*)/; $ENV{PATH_INFO} = $uri->path; $1; $ENV{QUERY_STRING} = $uri->query; $ENV{REQUEST_URI} $2; } else { $ENV{PATH_INFO} = "$uri"; $uri; $ENV{QUERY_STRING} = ""; } # this is lazily instantiated because %ENV is not set at startup time if (! $h) { $h = HTML::Mason::CGIHandler->new( comp_root HTML::Mason::CGIHandler->new(comp_root => $ENV{MASON_COMP_ROOT} , $ENV{MASON_COMP_ROOT}, data_dir => $ENV{MASON_DATA_ROOT} , $ENV{MASON_DATA_ROOT}, error_mode => 'fatal' , 'fatal', allow_globals => [qw($dbh $user $test)], error_format => 'line' , allow_globals => [qw/$dbh $U $S/] ); 'html'); } # hand off to mason #eval { $h->handle_request }; eval { $h->handle_cgi_object($cgi) }; if ( my (my $raw_error = $@ ) $@) { warn $raw_error; # print out a pretty system error page and log $raw_error print $raw_error; } } exit 0; }}} Next we need to tell Lighttpd to process requests for this site and process them via this script and FastCGI. Here is the relevant fragment. I'm using a regexp for the hostname expression but you may need something less complicated - see the Lighttpd docs for more information: {{{ #!python $HTTP["host"] =~ "hostnameexpression" { server.document-root = "/path/to/your/document/root" fastcgi.map-extensions = ( ".css" => ".html", "/" => ".html" ) # map .css and '/' to .html so they will be handled by FastCGI index-file.names = ( "index.html" ) url.access-deny = ( ".mhtml" ) fastcgi.server = ( ".html" => (( "socket" => "/tmp/fastcgi.socket", "bin-path" => "/path/to/the/mason_lighttpd_handler.fcgi", "check-local" => "disable" )) ) bin-environment = ( MASON_COMP_ROOT => '/your/comp/root', MASON_DATA_ROOT => '/your/data/root' ) } }}} That's it! When you start lighttpd it will start several copies of your mason handler script, passing requests to them as appropriate. One nice side-effect of this is that if you change a perl module (not a mason component) that would require you to restart/reload an apache mod_perl server, in this case you can just kill the perl fastcgi processes - lighttpd will notice and restart them, of course with your new module code in place. Add debugging (via 'warn') to your handler script if you are getting unexpected results, you can see the output from it in the lighttpd eror log. = Known issues = $m->abort does not work correctly, see this page for details on why: http://www.masonhq.com/docs/manual/1.28/CGIHandler.html#calling_abort___under_cgihandler It looks like this is something that has to be fixed in Mason. == Migration from Apache/mod_* (mod_perl) == First a prequil, mod_* (lang) embeeds the .so for the language in Apache. In doing so it knows things that Fastcgi doesn't, and can do things Fastcgi can't. Some of this behaviors users might be utilizing without knowing, here is a prime example: In Apache, (mod_* not fastcgi) a request to '/' would be processed by:[[BR]] - first looking for the DirectoryIndex (apache would do this.)[[BR]] - call the wrapper with the found DirectoryIndex; or, call the wrapper, (which would call a dhandler) on the unfound file. In Lighttpd, or just cgi in general, the resolution to the index-file is missing, so the wrapper will either be called on the requested file or not. See the "check-local" directive in lighttpd. In cgi, the index-file (Apache's DirectoryIndex) has no influence. For comparison Lighttpd/fastcgi looks like this:[[BR]] - call the wrapper with the found file; or, call the wrapper with the un-found file dependent on check-local being disabled.[[BR]] One horrible solution to this is to redirect all directories to the directory's index.html. A less-horrible solution would be to write the translation process in the wrapper (this only works if check-local is disabled). Another problem -- expanding on the previously mentioned problem is an address with no path, just a query string. ie: http://foo.com/?test.html or /?test.html, or /bar/?test.html In Apache, the resolution looks like this:[[BR]] - first looking for the DirectoryIndex (Apache would do this.)[[BR]] - calling the wrapper with the found DirectoryIndex; with the query.[[BR]] In Lighttpd, (i.e. cgi) this isn't so. Bottom line: index-files don't matter if you're sending everything to the wrapper (not checking for files existence.) If you app makes use of Apache's resolution this can sting. If you don't send everything to the wrapper, you'll completely lose the dhandler functionality. = Credits = Let me know (Justin Hawkins <justin@hawkins.id.au>) if there are problems with this, particular with the REQUEST_URI parsing. This is very much in the category of "seems to work" at this stage.