Monday, June 29, 2009

NWSGI 2.0: Dispatching

NWSGI has a fairly simple task: given a URL, call some Python code that produces some output. The devil, as always, is in the details.

What is WSGI?

NWSGI is an implementation of the Python WSGI specification (PEP 333). WSGI is the Web Server Gateway Interface, “a simple and universal interface between web servers and web applications or frameworks”, as defined in PEP 333.

OK, so what does that mean?

The purpose of WSGI is to define how web servers (i.e. Apache, IIS, etc.) talk to Python web application or frameworks (i.e. Trac, Django, etc.). In theory, a web server with a WSGI implementation (such as mod_wsgi for Apache, or NWSGI for IIS) should be able to run any web application that is implemented as a WSGI application (prior to WSGI, many applications/frameworks were server-specific). Of course, that doesn’t always pan out in practice, but it’s a start.

What is a WSGI application?

A WSGI application is surprisingly simple:

def simple_app(environ, start_response):
    """Simplest possible application object"""
    status = '200 OK'
    response_headers = [('Content-type','text/plain')]
    start_response(status, response_headers)
    return ['Hello world!\n']

The simple_app function is the entire application! A WSGI application is a callable (a function, mostly) that takes two arguments and returns an iterable (a list or generator, mostly). There’s some extra goo in there as well (start_response, for example) that’s not really relevant as far as dispatching is concerned. Writing WSGI applications is probably a book’s worth of content, so I’ll leave it at that for now.

Finding Callables in .wsgi Files

Local Files

Now that we have a callable for our application, NWSGI needs to know how to find it. The simplest way is to simply drop the file (let’s call it simple.wsgi) into the application root (i.e. C:\inetpub\wwwroot\, next to web.config). If you make a request to http://example.com/simple.wsgi/, IIS will see that .wsgi is associated with NWSGI and pass the request to it. NWSGI will then open simple.wsgi, try to find a variable called application (which is, by convention, the name of WSGI callables), call it, and return the result to IIS.

In this case, however, our callable is named simple_app, not application. To be able to run our app, NWSGI needs to know this! To do this, there needs to be a script mapping telling NWSGI how to run simple.wsgi.

<wsgi>
    <scriptMappings>
        <scriptMapping scriptName="simple.wsgi" callable="simple_app" />
    </scriptMappings>
</wsgi>

This configuration tells NWSGI that for the script simple.wsgi, it should use simple_app as the callable instead of application. (NOTE: This exact syntax requires 2.0b2 or later).

Other Files

Best practices for WSGI applications say that you should not put your application files in the web-exposed directory. This means that simple.wsgi should really live somewhere else; let’s say C:\simple\simple.wsgi. Of course, now NWSGI doesn’t have the slightest clue where it is, so we have to tell it:

<wsgi>
    <scriptMappings>
        <scriptMapping scriptName="simple.wsgi" physicalPath="C:\simple\simple.wsgi" callable="simple_app" />
    </scriptMappings>
</wsgi>

There is no longer a .wsgi in our application folder, but IIS doesn’t care. If you visit http://example.com/simple.wsgi/ with this configuration, IIS still happily passes the simple.wsgi script onto NWSGI. NWSGI looks at the script mappings and finds that simple.wsgi maps to C:\simple\simple.wsgi, so it loads that file instead (without looking for simple.wsgi in the application directory), looks up simple_app, and calls it.

Using .wsgi files is the simplest way to deploy an application, but NWSGI has a couple of shortcuts to make things easier for certain applications.