Project

General

Profile

Using Request Tracker with lighttpd (via FastCGI, using Mason)

Best Practical Solutions' Request Tracker (RT), an enterprise-grade ticketing system which enables a group of people to intelligently and efficiently manage tasks, issues, and requests submitted by a community of users, can be used with Lighttpd.

Setting up RT3 is somewhat similar to using Mason with Lighttpd and FastCGI, apart from some minor configuration differences.

Setting up RT on Lighttpd requires the following:
  • Satisfy all Perl dependencies for RT
  • Mason handles the files ending with '/' or '.html' or '.css' or '.js'.
  • Mason handles 'mail-gateway', required for the RT mail gateway
  • Mason handles 'Search/Chart', required for charting search results
  • Mason handles 'Search/Results.rdf', required for RSS feed of search results
  • Mason handles 'Search/Results.tsv', required for spreadsheet download of search results
  • Other files statically served by Lighttpd
  • Required Perl modules load when the FastCGI process starts
  • Disallow access to .mhtml files

This document assumes you install your Request Tracker in /opt/rt3/

First we need a Mason handler, which is a patched version of the handler that comes with the RT installation. Lighttpd will use this handler via FastCGI. In my setup the handler is called mason_lighttpd_handler.fcgi, the handler does not need to be in any particular location.


#!perl
#!/usr/bin/perl
# BEGIN BPS TAGGED BLOCK {{{
# 
# COPYRIGHT:
#  
# This software is Copyright (c) 1996-2006 Best Practical Solutions, LLC 
#                                          <jesse@bestpractical.com>
# 
# (Except where explicitly superseded by other copyright notices)
# 
# 
# LICENSE:
# 
# This work is made available to you under the terms of Version 2 of
# the GNU General Public License. A copy of that license should have
# been provided with this software, but in any event can be snarfed
# from www.gnu.org.
# 
# This work is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
# 
# 
# CONTRIBUTION SUBMISSION POLICY:
# 
# (The following paragraph is not intended to limit the rights granted
# to you to modify and distribute this software under the terms of
# the GNU General Public License and is only of importance to you if
# you choose to contribute your changes and enhancements to the
# community by submitting them to Best Practical Solutions, LLC.)
# 
# By intentionally submitting any modifications, corrections or
# derivatives to this work, or any other work intended for use with
# Request Tracker, to Best Practical Solutions, LLC, you confirm that
# you are the copyright holder for those contributions and you grant
# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
# royalty-free, perpetual, license to use, copy, create derivative
# works based on those contributions, and sublicense and distribute
# those contributions and any derivatives thereof.
# 
# END BPS TAGGED BLOCK }}}
package RT::Mason;

use strict;
use vars '$Handler';
use File::Basename;
require ('/opt/rt3/bin/webmux.pl');

# Enter CGI::Fast mode, which should also work as a vanilla CGI script.
require CGI::Fast;

my $h;

RT::Init();

while ( my $cgi = CGI::Fast->new ) {
    # the whole point of fastcgi requires the env to get reset here..
    # So we must squash it again
    $ENV{'PATH'}   = '/bin:/usr/bin';
    $ENV{'CDPATH'} = '' if defined $ENV{'CDPATH'};
    $ENV{'SHELL'}  = '/bin/sh' if defined $ENV{'SHELL'};
    $ENV{'ENV'}    = '' if defined $ENV{'ENV'};
    $ENV{'IFS'}    = '' if defined $ENV{'IFS'};

    my $uri = $ENV{REQUEST_URI};
    if ($uri =~ /\?/) {
      $uri =~ /^(.*?)\?(.*)/;
      $ENV{PATH_INFO} = $1;
      $ENV{QUERY_STRING} = $2;
    } else {
      $ENV{PATH_INFO} = $uri;
      $ENV{QUERY_STRING} = "";
    }

    Module::Refresh->refresh if $RT::DevelMode;
    RT::ConnectToDatabase();

    if ( ( !$Handler->interp->comp_exists( $cgi->path_info ) )
        && ( $Handler->interp->comp_exists( $cgi->path_info . "/index.html" ) ) ) {
        $cgi->path_info( $cgi->path_info . "/index.html" );
    }

    eval { $Handler->handle_cgi_object($cgi); };
    if ($@) {
        $RT::Logger->crit($@);
    }
    RT::Interface::Web::Handler->CleanupRequest(); 

}

1;

Now we set up the host that we'll use for our RT installation, telling Lighttpd to pass all necessary requests to the site through FastCGI.


#!python
$HTTP["host"] =~ "site1\.example\.com" {
  # Specify the documentroot
  server.document-root = "/opt/rt3/share/html" 

  # Map appropriate files and extensions
  fastcgi.map-extensions = ( ".css" => ".html", ".js" => ".html", "/" => ".html", "mail-gateway" => ".html", "Search/Chart" => ".html", "Search/Results.rdf" => ".html", "Search/Results.tsv" => ".html" )

  # Set Lighttpd to check for an index.html file for each directory
  index-file.names = ( "index.html" )

  # Disallow access to .mhtml files
  url.access-deny = ( ".mhtml" )

  setenv.add-environment = (
       "SCRIPT_NAME" => "/",
  )

  # Set up an alias for the /NoAuth/images location
  url.rewrite-once = (
       "^/(?!NoAuth/images/)(.*)" => "/$1",
  )

  # Add trailing slash so attachment downloads work
  url.rewrite-once = (
        "^(.*)/Ticket/Attachment/(.*)" => "/$1/Ticket/Attachment/$2/" 
  )

  # Set up FastCGI handler
  fastcgi.server = ( ".html" =>
     ((
        "socket"        => "/tmp/rt-fcgi.socket",
        "bin-path"      => "/opt/rt3/bin/mason_lighttpd_handler.fcgi",
        "check-local"   => "disable",
        "min-procs"     => 2,
        "max-procs"     => 2
      ))
  )
}

That's it!

The above Lighttpd configuration will start two instances of the Mason handler script, passing requests to them as appropriate.

Credits

Justin Hawkins <> for the initial Using Mason with Lighttpd (via FastCGI) write-up.

Alternative version

The above Lighttpd configuration breaks all the RT features that depend on Mason's default handler (dhandler), such as ticket dependency graphs and richtext edit. Basically, requests that should be handled through FastCGI are not because the query doesn't end in ".html". In some cases this can be worked around using map-extensions and/or URL rewriting like the attachments, in others it's not possible.

The real fix would be to make a mechanism like map-extensions that can match on the start of the query string (or better, on a regex). In the meantime, it's possible to achieve a similar result with several instances of fastcgi.server that reference one and provide their own prefix, but then there is a conflict on which instance should start the FastCGI backend.

Instead, I've adapted the Apache configuration to make something much simpler, where all requests go through Mason. It's of course a tad slower, but this static content is cached on the client side anyway.

# server.{modules, port, pid-file} statements skipped

var.root = "/somewhere" 

# Adjust according to your installation path
server.document-root = root + "/rt/share/html" 

# Set Lighttpd to check for an index.html file for each directory
index-file.names = ( "index.html" )

# Handle ALL requests through Mason
fastcgi.server = ( "/" =>
     ((
        "socket"        => root + "/rt-fcgi.socket",
        "bin-path"      => root + "/rt_mason_handler.fcgi",
        "check-local"   => "disable",
        "min-procs"     => 4,
        "max-procs"     => 8
      )),
  )

- Thomas Equeter