facebook/tornado — pull request #525 by alekstorm (2012-06-03 07:45:59+00:00)
Title: Implement SPDY support

### Introduction ###

To my knowledge, this patch makes Tornado the first Python web framework to support SPDY (v2), and the second web framework in any language to support pushed streams (Node.js is the other). Websocket-over-SPDY support is coming soon; see below.

If you're not excited about SPDY yet, I recommend reading [Google's whitepaper](http://dev.chromium.org/spdy/spdy-whitepaper) and the [specification](http://dev.chromium.org/spdy/spdy-protocol/spdy-protocol-draft2).

Do not use this in production systems, as it has only been tested on its own test suite.

This introduces minor, but fundamental incompatibilities with current Tornado applications, so merging it should be postponed until Tornado 3.0 gives us carte blanche to break backward compatibility. Until then, I'll maintain it as a separate branch - please submit bug reports and other issues there.

In most cases, users will be able to upgrade their applications to support SPDY simply by swapping out `HTTPServer` for `SPDYServer`, since connection framing is transparent to the application layer. `AsyncSPDYClient`, a client that uses SPDY when available, and HTTP otherwise, is provided as well.

### Implementation ###

See commit message, which covers many changes not mentioned here.

### Dependencies ###

Unfortunately, SPDY will probably not be immediately deployable in production systems, since most load balancers don't support it. Nginx, whose support is due out in the next few weeks, will only support it on the client-facing side of the reverse proxy in the first release, not the backend.

In addition, SPDY framing on the open Internet is negotiated through the NPN extension to TLS, which is only supported in OpenSSL 1.0.1 and Python 3.3. `SPDYServer` with NPN is tested and confirmed working against both Chrome and Firefox (latest), and `AsyncSPDYClient` works with both Google's and Twitter's servers.

Still, I highly recommend doing internal testing in anticipation of wider SPDY support. Both `SPDYServer` and `AsyncSPDYClient` can be configured to speak SPDY without NPN, and Chrome can be started with the `--use-spdy=no-ssl` flag.

### Server push ###

`UIModule`s can now push their Javascript and CSS files, and the `static_url` function available to templates can push the returned URL. Since the default behavior for both of these is easily overridden and users are aware that this patch introduces incompatibilities, I am confident enough to have both push by default, but could be convinced otherwise.

Resources can also be pushed manually with the new `RequestHandler.push` method, which takes the relative URI of the resource as a parameter. If the request doesn't use SPDY framing, this is a no-op.

### Miscellaneous ###

Previously, TCP connection timeout, premature connection termination, and any HTTP parsing errors were handled in the client by executing the request callback with a fake 599 response; users would then call `response.rethrow()` to raise an exception. Now, rather than returning a fake response, an exception will be automatically raised in the stack context of the callback. For example:

```
@contextlib.contextmanager
def die_on_error():
try:
yield
except Exception:
logging.error("exception in asynchronous operation",exc_info=True)
sys.exit(1)

with StackContext(die_on_error):
http_client.fetch(url, callback)
```

`HTTPResponse.rethrow` has been removed, but the `error` attribute has been converted to a boolean representing whether the status code isn't 2xx or 3xx.

Since the `websocket` module has been updated to use the new `HTTPConnection` interface, it should, in theory, support SPDY framing. However, Chrome 19 crashes whenever I start it with `--enable-websocket-over-spdy`, so we'll need to work with the Chromium team to get this fixed. If there were unit tests for the `websocket` module, I would have updated them.

I borrowed (stole) Mark Nottingham's `c_zlib` module from his old [nbhttp](https://github.com/mnot/nbhttp/tree/spdy/src) project, since the Python standard library's `zlib` module lacks pre-set dictionary support, which SPDY requires. It works fine on my Ubuntu machine; reports on which systems it doesn't are more than welcome.

### Future work ###

Next up are implementing a prioritization algorithm and v3 support. The most significant additions to v3 are flow control and the CREDENTIALS frame; time will tell how difficult these are to implement. The code is modularized in such a way that supporting new versions should be transparent to the user.