Edgewall Software

Running Trac on IIS 6 using Isapi-wsgi

Contributed by:
Seb Patane, Insight Informatics, Brisbane, Australia.

I did this with:

  • Windows Server 2003 SP2
  • Python 2.7
  • IIS 6
  • Trac 0.12

It may work for other versions.

I also haven't tested standard authentication as I use the AccountManagerPlugin. I also found this to be significantly faster than the TracOnWindowsIisAjp.

The only downside is that whenever a plugin is manually installed/code is changed a full iisreset needs to be done to reload the ISAPI dll. Restarting the IIS Site alone doesn't seem to be enough.

  1. Download & install the latest Isapi-wsgi (I used 0.4.2 exe), depends on pywin32 (I used Build 216 exe)
  2. Generate a trac.wsgi script using trac-admin <env> deploy <dir> (see TracInstall#cgi-bin)
  3. copy trac.wsgi into a new file called trac_wsgi.py for Isapi-wsgi. Replace the dot with the underscore!
  4. Append these extra bits so Isapi-wsgi can generate an ISAPI DLL:
    import isapi_wsgi
    # The entry points for the ISAPI extension.
    def __ExtensionFactory__():
        # can also be isapi_wsgi.ISAPISimpleHandler
        return isapi_wsgi.ISAPIThreadPoolHandler(application)
    
    if __name__=='__main__':
        # If run from the command-line, install ourselves.
        from isapi.install import *
        params = ISAPIParameters()
        # Setup the virtual directories - this is a list of directories our
        # extension uses - in this case only 1.
        # Each extension has a "script map" - this is the mapping of ISAPI
        # extensions.
        sm = [
            ScriptMapParams(Extension="*", Flags=0)
        ]
        # To serve from root, just set Name="/"
        vd = VirtualDirParameters(Name="/trac",
                                  Description = "ISAPI-WSGI Trac",
                                  ScriptMaps = sm,
                                  ScriptMapUpdate = "replace"
                                  )
        params.VirtualDirs = [vd]
        HandleCommandLine(params)
    
  5. Ensure everything has the right Permissions for the IIS Site user.
    • READ/EXECUTE: imports from site-packages (including eggs), generated ISAPI dll, trac directory & subdirectories
    • WRITE/MODIFY: trac/conf/trac.ini, trac/log directory, trac/plugins directory, trac/attachments directory (?), trac/db directory (?), .htpasswd file (if using Account Manager)
  6. Generate and Install the ISAPI dll using python trac_wsgi.py install [--server=<IIS Web Site Name>]. Don't forget to do an iisreset whenever the filter is reinstalled.
    • This will automatically create the Virtual Directory, install the ISAPI filter and allow it in Web Service Extensions
  7. That's it, everything should work!

If there are problems you can enable debugging by inserting the following at the top of trac_wsgi.py, reinstalling the ISAPI dll and running python -m win32traceutil (will also display TracLogging to stderr).

import sys

if hasattr(sys, "isapidllhandle"):
    import win32traceutil

TracModWSGI is probably a good starting point as well, even though it deals with Apache.

Broken Authentication

I had a problem with the AccountManagerPlugin always returning Invalid Username or Password. This is because the REMOTE_USER environment variable is set (I'm not sure why, since it seems to be empty).

In any event, putting del environ['REMOTE_USER'] in trac_wsgi.py solved the problem.

Appendix A - My working trac_wsgi.py (with useful extras)

# -*- coding: utf-8 -*-
#
# Copyright (C)2008-2009 Edgewall Software
# Copyright (C) 2008 Noah Kantrowitz <noah@coderanger.net>
# All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at http://trac.edgewall.org/wiki/TracLicense.
#
# This software consists of voluntary contributions made by many
# individuals. For the exact contribution history, see the revision
# history and logs, available at http://trac.edgewall.org/log/.
#
# Author: Noah Kantrowitz <noah@coderanger.net>
#
# IMPORTANT: when you make changes to this file you need to
#            update the ISAPI filter with the following commands -
#   C:\Inetpub\trac\cgi-bin> python trac_wsgi.py install --server=Trac
#   C:\Inetpub\trac\cgi-bin> iisreset
#
import os

"""
# Uncommenting this enables debug output
# you can see it by opening up a command prompt and executing
#       > python -m win32traceutil
import sys

if hasattr(sys, "isapidllhandle"):
    import win32traceutil
"""

def application(environ, start_request):
    if not 'trac.env_parent_dir' in environ:
        environ.setdefault('trac.env_path', 'C:\\Inetpub\\trac')
    if 'PYTHON_EGG_CACHE' in environ:                                           
        os.environ['PYTHON_EGG_CACHE'] = environ['PYTHON_EGG_CACHE']
    elif 'trac.env_path' in environ:
        os.environ['PYTHON_EGG_CACHE'] = \
            os.path.join(environ['trac.env_path'], '.egg-cache')
    elif 'trac.env_parent_dir' in environ:
        os.environ['PYTHON_EGG_CACHE'] = \
            os.path.join(environ['trac.env_parent_dir'], '.egg-cache')

    # Required for Account Manager logins to work
    del environ['REMOTE_USER']

    from trac.web.main import dispatch_request
    return dispatch_request(environ, start_request)

"""
# Uncommenting this enables middleware profiling at /__profile__
# Requires the repoze.profiler egg to be installed
from repoze.profile.profiler import AccumulatingProfileMiddleware
application = AccumulatingProfileMiddleware(application,
                                            log_filename='C:\\repoze.log',
                                            discard_first_request=True,
                                            flush_at_shutdown=True,
                                            path='/__profile__')
"""

import isapi_wsgi
# The entry points for the ISAPI extension.
def __ExtensionFactory__():
    return isapi_wsgi.ISAPIThreadPoolHandler(application)

if __name__=='__main__':
    # If run from the command-line, install ourselves.
    from isapi.install import *
    params = ISAPIParameters()
    # Setup the virtual directories - this is a list of directories our
    # extension uses - in this case only 1.
    # Each extension has a "script map" - this is the mapping of ISAPI
    # extensions.
    sm = [
        ScriptMapParams(Extension="*", Flags=0)
    ]
    # To serve from root, just set Name="/"
    vd = VirtualDirParameters(Name="/trac",
                              Description = "ISAPI-WSGI Trac",
                              ScriptMaps = sm,
                              ScriptMapUpdate = "replace"
                              )
    params.VirtualDirs = [vd]
    HandleCommandLine(params)
Last modified 2 years ago Last modified on Nov 27, 2011 10:28:56 PM