Project

General

Profile

MasonRecipe » History » Revision 18

Revision 17 (EvanCarroll, 2007-08-26 00:41) → Revision 18/20 (eryretqwewrqr, 2009-09-21 20:37)

h1. 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. 


 


 <pre> 

 

 #!perl 
 #!/usr/bin/perl 
 use CGI::Fast; 
 use HTML::Mason::CGIHandler; 
 use URI; 

 { 
	 package HTML::Mason::Commands; 

	 ## anything you want available to components 
	 use Storable qw(freeze thaw); 
	 use HTTP::BrowserDetect; 

	 ## An example of how to keep a $dbh persistent 
	 ##our $dbh = DBI->connect_cached( 
	 ## 	 'dbi:Pg:dbname=dbname' 
	 ## 	 , 'username' 
	 ## 	 , 'password' 
	 ## 	 , {AutoCommit=>0, RaiseError=>1, PrintError=>1} 
	 ##) || die "Could not Connect to DB".$dbi::errstr ; 

 } 

 # lazily-instantiated variables 
 my $cgi; 
 my $h; 

 while ($cgi = new CGI::Fast()) 
 { 
         ## make sure it is alive! (if not it will reconnect) 
         ## $HTML::Mason::Commands::dbh->ping;  

	 my $uri = URI->new( $ENV{REQUEST_URI} ); 

         ## this is a hack, that emulates mod_perl behavior see notes at bottom 
         ## You might not want this hack, and it might be worse than not having it 
	 $uri->path( $uri->path . 'index.html' ) 
		 if $uri->path =~ /\/$/ 
	 ; 

	 $ENV{PATH_INFO}      = $uri->path; 
	 $ENV{QUERY_STRING} = $uri->query; 
	 $ENV{REQUEST_URI}    = "$uri"; 

	 # this is lazily instantiated because %ENV is not set at startup time 
	 if (! $h) { 
		 $h = HTML::Mason::CGIHandler->new( 
			 comp_root         => $ENV{MASON_COMP_ROOT} 
			 , data_dir        => $ENV{MASON_DATA_ROOT} 
			 , error_mode      => 'fatal' 
			 , error_format    => 'line' 
			 ## Three good globals dbh user and session 
			 , allow_globals => [qw/$dbh $U $S/] 
		 ); 
	 } 

	 ## hand off to mason 
	 eval { $h->handle_cgi_object($cgi) }; 

         ## catch error 
	 if ( my $raw_error = $@ ) { 
		 $HTML::Mason::Commands::dbh->rollback; ## roll back 
		 warn $raw_error; 
		 # print out a pretty system error page and log $raw_error 
	 } 

	 ## things went well 
	 else { 
		 $HTML::Mason::Commands::dbh->commit; 
	 } 

 } 

 exit 0; 

 </pre> 



 



 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: 


 


 <pre> 

 

 #!python 
 $HTTP["host"] =~ "hostnameexpression" { 
         server.document-root     = "/path/to/your/document/root" 
         ## map .css and '/' to .html so they will be handled by FastCGI 
         fastcgi.map-extensions     = ( ".css" => ".html", "/" => ".html" )     
         index-file.names           = ( "index.html" ) 
         url.access-deny            = ( ".mhtml" ) ## add autohandler/dhandler if you'd like 
         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' 
         ) 
 } 
 </pre> 


 


 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. 


 


 h1. 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. 


 


 h2. 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: 

 

 - first looking for the DirectoryIndex (apache would do this.) 

 

 - 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: 

 

 - call the wrapper with the found file; or, call the wrapper with the un-found file dependent on check-local being disabled. 



 



 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: 

 

 - first looking for the DirectoryIndex (Apache would do this.) 

 

 - calling the wrapper with the found DirectoryIndex; with the query. 



 



 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. 


 


 h1. Credits 


 


 Latest rewrite (Evan Carroll <me@evancarroll.com>) 
   - added env variables for comp/data root 
   - added example of DBI persistence with fastcgi 
   - removed original query-parsing regex, used URI instead. 
   - added some globals to be shared 

 (Justin Hawkins <justin@hawkins.id.au>) First Draft. 

 This is very much in the category of "seems to work" at this stage.    (initial note still applies)