Overview

WSGI

The official definition is that the wsgi protocol is the protocol for a web server and application servers to communicate. PEP 333 describes this protocol in detail. But for most developers all you really need to know is that it is a low level standard for programs/processes/objects/ and functions to communicate via HTTP . If you don’t know what HTTP is, well, use google and read up, that stuff is important if you are developing web applications.

WSGI Application

A wsgi application is an application that implements the wsgi protocol. Sounds enterprisey doesn’t it? It’s really quite simple to implement. It’s a callable(or function) that takes 2 parameters and returns a list.

Here is the simplest wsgi application you can make.

"""
wsgi_app.py
"""


def application(environ, start_response):
    status = "200 OK"  # HTTP messages have a status
    body = "Hello World" # HTTP messages have a body

    # HTTP messages have headers to describe various things, at a
    # minimum describing the type(Content-Type) and the length of the
    # content(Content-Length)
    headers = [("Content-Type","text/plain"),
                        ("Content-Length",str(len(body)))]

    start_response(status, headers) # calling the function passed in
                                    # with the status and headers of
                                    # the HTTP Response Message

    return [body] # returning a list containing the body of the HTTP
                  # Response Message

THAT’S IT!!!!! NO FRAMEWORK NEEDED, other than the standard library.

Though this application can recieve and return messages that conform to the HTTP protocol, this doesn’t mean you can now magically point your web browser at this function and get “Hello World” displayed in the page. For that you need a WSGI Web Server.

WSGI Web Server

So what is a web server? it’s a application that communicates via HTTP typically over TCP/IP. We will not cover TCP/IP here. There’s plenty of information elsewhere. What you need to know is that anything that can communicate via TCP/IP is generally reachable on a port at an IP address.

So in order to get the above application to be reachable by your web browser, it needs a web server to listen on a port at a specific IP address and when an HTTP Request Message comes in, it would call our application.

The python standard library comes with several web servers, but not all of them can be told to call our application. Here’s an example

"""
wsgi_server.py
"""
from wsgi_app import application
from wsgiref.simple_server import make_server

 # listen on the local address so that it's only reachable by the
 # machine it's running on. on port 8080, when a HTTP Request is
 # recieved, call application(environ, start_response)
server = make_server("localhost", 8080, application)

# after you recieve one HTTP Request, handle it than quit.
server.handle_request()

Running wsgi_server.py and pointing your browser at http://localhost:8080 would cause the WSGI Application to spit out a HTTP Response Message who’s body contains the text “Hello World”

Calling WSGI Application’s(they’re all just python callables)

Typically a wsgi application is made available to call over TCPI/IP by a webserver that knows the wsgi protocol. But as was stated earlier a wsgi application is just a callable that takes 2 parameters and returns a list (after calling the second parameter “start_response”).

So as you may have guessed a wsgi application can be called just like any other function or callable if provided the input it expects. Though calling them this way might be a little more complicated than necessary for general functions. Still, it is important to remember that in the end, a wsgi application is nothing more than a python callable that you likely use everytime you write code in python.

In the rest of the documentation we will show how to exploit the fact that a wsgi application is just a python callable, and what cool things you can do.

Middleware

Now that you know that a WSGI application is nothing more than a python callable/function that takes 2 parameters and returns a list, we need to cover the concept of middleware. Yet another concept that sounds enterprisey but it’s really simple.

Middleware is a python callable that is given a WSGI application that returns a WSGI application. That’s it.

"""
middleware.py
"""


def do_nothing(application):
    def m(environ, start_response):
        # could do something cool here before calling the application
        return application(environ, start_response)
    return m


def do_nothing_2(application):

    def m(environ, start_response):
        #setup someway to capture the inner applications status and
        #headers
        inner_status = None
        inner_headers = []

        def inner_start_response(status, headers):
            inner_status = status
            inner_headers = headers

        # before the application is called
        application_body = application(environ, inner_start_response)

        # could do something after the application is called, but
        # instead we will pass things along
        start_response(inner_status, inner_headers)
        return application_body
    return m

Let your imagination run wild for a second, think of what you could do if you could manipulate the HTTP Request message before calling another application, or think what you could do if you could manipulate the HTTP Response message before it is returned to the caller(which could be another function, middleware, or even the browser).

Here are things that could be good uses for middleware.

  1. log the incoming HTTP Request message and the outgoing HTTP Response message
  2. You could replace words in the HTTP Response message body
  3. Add or remove headers from the HTTP Request message or HTTP response message.
  4. return a completely fabricated HTTP Response message and not even call the inner application

So as you can see, there’s some pretty cool things you can do to the HTTP Request and Response messages in middleware. But, the example above looks pretty intimidating.

And we haven’t even gotten into any examples of how to manipulate form input on an HTTP Request message yet because these first few sections we wanted to emphasize what the interface actually is.

Unless you are an uber hacker with a lot of time on their hands, or you are bored, you would likely never write WSGI applications <wsgi_application> or middleware this way. Especially given there are so many libraries and frameworks that make it easier to do.

Still, like it was stated at the beginning, it is beneficial to know what’s going on because the abstractions will fail you, and when they do, you need to know what’s going on. Heck even WSGI is an abstraction and it’s not without it’s faults.

In the next section we cover what webob is, what webob has to do with wsgi, and what you can do with it.

WebOb

If you were to write a significant application using just wsgi, inevitably you would become intimately familiar with HTTP. If you are a good software developer, eventually you would come up with your own representation of a HTTP Request message and a HTTP Response message given enough time and energy.

You would do this so that all of the little quirks and edge cases of dealing with HTTP could be held in once place in your code rather than repeated all over you code base.

Well guess what, likely every web framework in existence has eventually grown it’s own representation of a HTTP Request message and HTTP Response message, or they used a library such as webob.

The WebOb library makes it easier for you to deal with wsgi and HTTP Request and Response messages.

Here’s an example of a wsgi application, wsgi middleware and wsgi web server that we have previously discussed.

"""
webob_wsgi.py
"""
from webob import Request, Response
from wsgiref.simple_server import make_server


def wsgi_hello_app(environ, start_response):
    response = Response("Hello World")

    #did you know that Response is a wsgi application
    return response(environ, start_response)

# or this one-liner, because Response is a wsgi application
wsgi_hello_app = Response("Hello World")


def wsgi_hello_middleware(app):
    def m(environ, start_response):
        request = Request(environ)

        # request is a lot easier to manipulate than a dictionary
        # let's add a header to prove a point
        request.headers["WSGI-Hello-Middleware"] = "Say Hello Application"

        # call a wsgi app and convert what it returns into a
        # webob.Response which is easier to manipulate
        response = request.get_response(app)

        response.headers["WSGI-Application-Middleware"] = "Say Hello Caller"
        return response(environ, start_response)
    return m


application = wsgi_hello_middleware(wsgi_hello_app)
server = make_server("localhost", 8080, application)
server.handle_request()

And WebOb is well tested, well documented and supports python 2.6 through python 3.x. And thank goodness for the docs, because now if you have questions on how to use WebOb, you can just go read. :)