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.


Jeremy Mattke, Minneapolis, USA. I was able to do this with:

  • Windows 8.1
  • Python 2.7
  • IIS 8.5
  • Trac 1.0

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! NOTE: if you have a problem with trac.web.main being found you need to unzip your eggs in the python/lib/site-packages directory for Trac, Genshi and setuptools

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 4 days ago Last modified on Jan 28, 2015 3:49:11 AM