By the way, for those of you who haven't been keeping up with the inner workings of Python (that would be most of us Windows folks), WSGI (Web Server Gateway Interface) is a Python standard defined in PEP 333 (PEP is an acronym for Python Enhancement Proposals). It details how Python web applications/Python web frameworks should interface with web servers.
Things have change a little in the months since I wrote my original post on running Mercurial on Windows. Someone who has a great deal of dedication has been working on a WSGI interface for IIS since before my last post, and has now gotten it to a point where it works marvelously with Mercurial. Thanks to Jason R. Coombs and his team for all their hard work on the isapi-wsgi project.
On with the show
Installing Mercurial as a server using isapi-wsgi is SO much easier than fiddling with CGI that it was actually a joy. These instructions assume Mercurial 1.5.1. Keep in mind that since you don't have anything on Windows as cool as the FreeBSD Ports and Packages Collection to build stuff and manage dependencies for you that you will have to make sure you match the version of Mercurial to the correct version of Python yourself. IIS needs to be already installed and functioning.
- Install the Python 2.5.6 binary installer package.
- Install the latest of Mark Hammond's indispensable Python for Windows package, for Python 2.6 .
- Install the binary package for isapi-wsgi. 0.4.2 is the one that you want, since it now supports SSL properly. isapi_wsgi.py will be installed in the 'site-packages' directory of your Python installation...assuming you take the defaults. You should take the defaults.
- Go here and follow the instructions in the Testing section to make sure you didn't mess it up.
Go to Selenic and download the source code for Mercurial. Its in a tarball, so you'll probably want to install 7-Zip at this point, because Microsoft obstinately refuses to include tar and gzip support directly in the Windows Explorer...and really why should they since 7-Zip is free, and does exactly that.
Once you've extracted this thing somewhere (or not, you can navigate the archive in 7-Zip and drag-drop the file we need out), find the contrib\win32 directory. Copy hgwebdir_wsgi.py to a directory from which you want to serve Mercurial. I chose C:\inetpub\wwwroot\hg. It doesn't have to be in inetpub, you could use a virtual directory. Don't put your Mercurial repositories in this directory. You'll confuse Mercurial.
On IIS 6 and above, create a new App pool for your Mercurial directory if your IIS installation is doing ANYTHING other than Mercurial. Make sure you make your Mercurial directory into a Application in IIS, and ensure the Application is assigned to the App pool you created for Mercurial. In IIS 5.1, you may want to set the Application Isolation as High "(Isolated)". I mean, the Python interpreter is native code. It could crash and take your entire IIS process down with it. You probably don't want that.
Edit your copy of hgwebdir_wsgi.py. Make the top of it look kinda like this:
# Configuration file location hgweb_config = r'c:\inetpub\wwwroot\hg\hgweb.config' # Global settings for IIS path translation path_strip = 0 # Strip this many path elements off (when using url rewrite) path_prefix = 1 # This many path elements are prefixes (depends on the # virtual path of the IIS application).hgweb_config is pretty simple, it tells the script where to find its config file. path_prefix should be 0 if hgwebdir_wsgi.py is in the root of the web, 1 if its one directory down (server/hg), 2 if you get to it by server/directory/hg... you should get the idea. I don't know what path_strip does, but it sounds risque, so I didn't mess with it.
Run the hgwebdir_wsgi.py scriptpython hgwebdir_wsgi.py
to make a shim dll for Mercurial. All this really does is copy a file from the Python Win32 Extensions package to where the script resides and names it appropriately. Which is nice because you don't have to figure it out for yourself.
Create your hgweb.config file. This is all mine has in it.
[paths] / = c:\repos\*
If you read my last post, then yes, there is just one star this time.
Create an ISAPI script mapping to the shim dll that the script generated. In IIS 5, you just create a script mapping to the dll using .* as the extension (clear the check box that says "Check that files exist", or IIS will generate hate messages). In IIS 6 and above, you will want to create a wildcard script map to that shim dll.
Create a test repository in c:\repos\ (or where ever). If it shows up in the list on http://server/hg, have you a cold one. Good job. If it doesn't, you may have messed up your hgweb.config, maybe your permissions on c:\repos\. Remember, the identity that IIS (or the IIS worker pool) is running under will need full control access to your repository directory. If you don't see anything at all (or 401's and 404's and bears, oh my!), you may have messed up your script map in IIS or the editing the hgwebdir_isapi.py script.
SSL and web security both worked well through IIS. One thing to keep in mind is that the Hg client doesn't care a thing about Windows Integrated Security, so if you want to secure your repos, you'll have to use SSL+Basic Authentication.
If you find yourself having to diagnose your setup, iisreset is your friend. Because you're loading things into the IIS process space, and IIS isn't very likely to reload things just because you changed them, you may find that you have to restart it when you make certain configuration changes.
I found setting up Mercurial with isapi-wsgi to be easier than with CGI. It seems snappier, and less fussy about certain operations that I had trouble with in CGI. I imagine if FastCGI were to ever work with Python on Windows, it would actually perform better than ISAPI, due to the nature of threading within the Python interpreter, but for now, we already have something better than CGI, so that's what I'll stick with.
Issues I've seen
On a 64 bit Windows 2008 R2 server, I ran into a bit of trouble with Mercurial not being able to find its binary bits. See Mercurial has some modules in it that were written in C, because they're really hard core and mathy and they needed some extra speed to make Mercurial as fast as it really should be. In order to fix the "unable to locate module" business, I downloaded the Mercurial source, and lo, the authors had written pure Python versions of the C modules that were giving me grief. I tossed the pure python modules on the PYTHONPATH, and everything worked just fine.I'm going to research this particular issue again, because I've learned a little more about how Python loads C code. It sure would be nice to know how to get Mercurial running at full tilt in IIS 7.5.

This is great stuff. I just finished steps 1 and 2, but I ran into a glitch in step 3. I normally run Windows 7 at the highest UAC setting because I need to know how UAC will affect my installations. Turns out the isapi-wsgi installer can't write to the registry by default. So it creates all the files and directories it needs, but can't create the keys it needs. Also, it never gets put into the Programs and Features registry entry, so you can't uninstall it. Tried again with UAC turned off, and all was well.
ReplyDelete@Bill Blum: It's also possible to simply run the isapi-wsgi installer as administrator (context menu -> Run as administrator). No need to turn UAC off globally.
ReplyDeleteAnother catch: For isapi-wsgi to work under IIS 7, the "IIS 6 Metabase Compatibility" Role Service needs to be installed. Otherwise you only get an unhelpful stacktrace.
ReplyDelete