Actually update to tornado 2.2.0.

This is the v2.2.0 tag in git://github.com/facebook/tornado, ie.
commit id 02bc76155de5bf4dca243e4d0c019c0ac4c8b3be .  I'm not sure
exactly which version was imported previously, but it wasn't quite this one.
The good news is it doesn't affect the tornado source code, just a bunch of
supporting files we don't really use.

* commit '3f6c64ca9fda3b2bf3f8ca3acb47b3d8cb255cbf':
  Squashed 'tr/vendor/tornado/' changes from 94078a6..3dd205c

Change-Id: I3a2ec0d6bf020e6d883126d1f3d96e10eefe9c6c
diff --git a/.coveragerc b/.coveragerc
new file mode 100644
index 0000000..2db6c2f
--- /dev/null
+++ b/.coveragerc
@@ -0,0 +1,18 @@
+# Test coverage configuration.
+# Usage:
+#   pip install coverage
+#   coverage erase  # clears previous data if any
+#   coverage run -m tornado.test.runtests
+#   coverage report  # prints to stdout
+#   coverage html  # creates ./htmlcov/*.html including annotated source
+[run]
+branch = true
+source = tornado
+omit = 
+    tornado/platform/*
+    tornado/test/*
+    */_auto2to3*
+
+[report]
+# Ignore missing source files, i.e. fake template-generated "files"
+ignore_errors = true
diff --git a/.gitignore b/.gitignore
index 85b6dd3..ba77616 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,13 @@
 *.pyc
 *.so
 *~
-build
-dist/
-tornado.egg-info
+build/
+/dist/
+MANIFEST
+/tornado.egg-info/
 _auto2to3*
+.tox/
+.vagrant
+/.coverage
+/htmlcov/
+/env/
diff --git a/PKG-INFO b/PKG-INFO
deleted file mode 100644
index 1a86eb2..0000000
--- a/PKG-INFO
+++ /dev/null
@@ -1,11 +0,0 @@
-Metadata-Version: 1.0
-Name: tornado
-Version: 2.2
-Summary: Tornado is an open source version of the scalable, non-blocking web server and and tools that power FriendFeed
-Home-page: http://www.tornadoweb.org/
-Author: Facebook
-Author-email: python-tornado@googlegroups.com
-License: http://www.apache.org/licenses/LICENSE-2.0
-Download-URL: http://github.com/downloads/facebook/tornado/tornado-2.2.tar.gz
-Description: UNKNOWN
-Platform: UNKNOWN
diff --git a/maint/README b/maint/README
new file mode 100644
index 0000000..9a9122b
--- /dev/null
+++ b/maint/README
@@ -0,0 +1,3 @@
+This directory contains tools and scripts that are used in the development
+and maintainance of Tornado itself, but are probably not of interest to
+Tornado users.
diff --git a/maint/appengine/README b/maint/appengine/README
new file mode 100644
index 0000000..8d534f2
--- /dev/null
+++ b/maint/appengine/README
@@ -0,0 +1,8 @@
+Unit test support for app engine.  Currently very limited as most of
+our tests depend on direct network access, but these tests ensure that the
+modules that are supposed to work on app engine don't depend on any
+forbidden modules.
+
+The code lives in maint/appengine/common, but should be run from the py25
+or py27 subdirectories (which contain an app.yaml and a bunch of symlinks).
+runtests.py is the entry point; cgi_runtests.py is used internally.
diff --git a/maint/appengine/common/cgi_runtests.py b/maint/appengine/common/cgi_runtests.py
new file mode 100644
index 0000000..a3f1596
--- /dev/null
+++ b/maint/appengine/common/cgi_runtests.py
@@ -0,0 +1,75 @@
+#!/usr/bin/env python
+import sys
+import unittest
+
+# Most of our tests depend on IOLoop, which is not importable on app engine.
+# Run the tests that work, and check that forbidden imports don't sneak
+# in to modules that are supposed to work on app engine.
+TEST_MODULES = [
+    'tornado.httputil.doctests',
+    #'tornado.iostream.doctests',
+    'tornado.util.doctests',
+    #'tornado.test.auth_test',
+    #'tornado.test.curl_httpclient_test',
+    'tornado.test.escape_test',
+    #'tornado.test.gen_test',
+    #'tornado.test.httpclient_test',
+    #'tornado.test.httpserver_test',
+    'tornado.test.httputil_test',
+    #'tornado.test.import_test',
+    #'tornado.test.ioloop_test',
+    #'tornado.test.iostream_test',
+    #'tornado.test.process_test',
+    #'tornado.test.simple_httpclient_test',
+    #'tornado.test.stack_context_test',
+    'tornado.test.template_test',
+    #'tornado.test.testing_test',
+    #'tornado.test.twisted_test',
+    #'tornado.test.web_test',
+    #'tornado.test.wsgi_test',
+]
+
+def import_everything():
+    # import tornado.auth
+    # import tornado.autoreload
+    # import tornado.curl_httpclient  # depends on pycurl
+    # import tornado.database  # depends on MySQLdb
+    import tornado.escape
+    # import tornado.httpclient
+    # import tornado.httpserver
+    import tornado.httputil
+    # import tornado.ioloop
+    # import tornado.iostream
+    import tornado.locale
+    import tornado.options
+    # import tornado.netutil
+    # import tornado.platform.twisted # depends on twisted
+    # import tornado.process
+    # import tornado.simple_httpclient
+    import tornado.stack_context
+    import tornado.template
+    import tornado.testing
+    import tornado.util
+    import tornado.web
+    # import tornado.websocket
+    import tornado.wsgi
+    
+def all():
+    return unittest.defaultTestLoader.loadTestsFromNames(TEST_MODULES)
+
+def main():
+    print "Content-Type: text/plain\r\n\r\n",
+
+    import_everything()
+
+    try:
+        unittest.main(defaultTest="all", argv=sys.argv)
+    except SystemExit, e:
+        if e.code == 0:
+            print "PASS"
+        else:
+            raise
+
+if __name__ == '__main__':
+    main()
+
diff --git a/maint/appengine/common/runtests.py b/maint/appengine/common/runtests.py
new file mode 100644
index 0000000..2db8d1a
--- /dev/null
+++ b/maint/appengine/common/runtests.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python
+from __future__ import with_statement
+
+import contextlib
+import errno
+import os
+import random
+import signal
+import socket
+import subprocess
+import sys
+import time
+import urllib2
+
+if __name__ == "__main__":
+    tornado_root = os.path.abspath(os.path.join(os.path.dirname(__file__),
+                                                '../../..'))
+    # dev_appserver doesn't seem to set SO_REUSEADDR
+    port = random.randrange(10000, 11000)
+    # does dev_appserver.py ever live anywhere but /usr/local/bin?
+    proc = subprocess.Popen([sys.executable,
+                             "/usr/local/bin/dev_appserver.py",
+                             os.path.dirname(os.path.abspath(__file__)),
+                             "--port=%d" % port,
+                             "--skip_sdk_update_check",
+                             ],
+                            cwd=tornado_root)
+            
+    try:
+        for i in xrange(50):
+            with contextlib.closing(socket.socket()) as sock:
+                err = sock.connect_ex(('localhost', port))
+                if err == 0:
+                    break
+                elif err != errno.ECONNREFUSED:
+                    raise Exception("Got unexpected socket error %d" % err)
+                time.sleep(0.1)
+        else:
+            raise Exception("Server didn't start listening")
+
+        resp = urllib2.urlopen("http://localhost:%d/" % port)
+        print resp.read()
+    finally:
+        # dev_appserver sometimes ignores SIGTERM (especially on 2.5),
+        # so try a few times to kill it.
+        for sig in [signal.SIGTERM, signal.SIGTERM, signal.SIGKILL]:
+            os.kill(proc.pid, sig)
+            res = os.waitpid(proc.pid, os.WNOHANG)
+            if res != (0,0):
+                break
+            time.sleep(0.1)
+        else:
+            os.waitpid(proc.pid, 0)
diff --git a/maint/appengine/py25/app.yaml b/maint/appengine/py25/app.yaml
new file mode 100644
index 0000000..8bd7892
--- /dev/null
+++ b/maint/appengine/py25/app.yaml
@@ -0,0 +1,8 @@
+application: tornado-tests-appengine25
+version: 1
+runtime: python
+api_version: 1
+
+handlers:
+- url: /
+  script: cgi_runtests.py
\ No newline at end of file
diff --git a/maint/appengine/py25/cgi_runtests.py b/maint/appengine/py25/cgi_runtests.py
new file mode 120000
index 0000000..a9fc90e
--- /dev/null
+++ b/maint/appengine/py25/cgi_runtests.py
@@ -0,0 +1 @@
+../common/cgi_runtests.py
\ No newline at end of file
diff --git a/maint/appengine/py25/runtests.py b/maint/appengine/py25/runtests.py
new file mode 120000
index 0000000..2cce26b
--- /dev/null
+++ b/maint/appengine/py25/runtests.py
@@ -0,0 +1 @@
+../common/runtests.py
\ No newline at end of file
diff --git a/maint/appengine/py25/tornado b/maint/appengine/py25/tornado
new file mode 120000
index 0000000..13b2e09
--- /dev/null
+++ b/maint/appengine/py25/tornado
@@ -0,0 +1 @@
+../../../tornado
\ No newline at end of file
diff --git a/maint/appengine/py27/app.yaml b/maint/appengine/py27/app.yaml
new file mode 100644
index 0000000..e5dea07
--- /dev/null
+++ b/maint/appengine/py27/app.yaml
@@ -0,0 +1,9 @@
+application: tornado-tests-appengine27
+version: 1
+runtime: python27
+threadsafe: false
+api_version: 1
+
+handlers:
+- url: /
+  script: cgi_runtests.py
\ No newline at end of file
diff --git a/maint/appengine/py27/cgi_runtests.py b/maint/appengine/py27/cgi_runtests.py
new file mode 120000
index 0000000..a9fc90e
--- /dev/null
+++ b/maint/appengine/py27/cgi_runtests.py
@@ -0,0 +1 @@
+../common/cgi_runtests.py
\ No newline at end of file
diff --git a/maint/appengine/py27/runtests.py b/maint/appengine/py27/runtests.py
new file mode 120000
index 0000000..2cce26b
--- /dev/null
+++ b/maint/appengine/py27/runtests.py
@@ -0,0 +1 @@
+../common/runtests.py
\ No newline at end of file
diff --git a/maint/appengine/py27/tornado b/maint/appengine/py27/tornado
new file mode 120000
index 0000000..13b2e09
--- /dev/null
+++ b/maint/appengine/py27/tornado
@@ -0,0 +1 @@
+../../../tornado
\ No newline at end of file
diff --git a/maint/appengine/setup.py b/maint/appengine/setup.py
new file mode 100644
index 0000000..5d2d314
--- /dev/null
+++ b/maint/appengine/setup.py
@@ -0,0 +1,4 @@
+# Dummy setup file to make tox happy.  In the appengine world things aren't
+# installed through setup.py
+import distutils.core
+distutils.core.setup()
diff --git a/maint/appengine/tox.ini b/maint/appengine/tox.ini
new file mode 100644
index 0000000..0970bf8
--- /dev/null
+++ b/maint/appengine/tox.ini
@@ -0,0 +1,19 @@
+# App Engine tests require the SDK to be installed separately.
+# Version 1.6.1 or newer is required (older versions don't work when
+# python is run from a virtualenv)
+#
+# These are currently excluded from the main tox.ini because their
+# logs are spammy and they're a little flaky.
+[tox]
+envlist = py25-appengine, py27-appengine
+
+[testenv]
+changedir = {toxworkdir}
+
+[testenv:py25-appengine]
+basepython = python2.5
+commands = python {toxinidir}/py25/runtests.py {posargs:}
+
+[testenv:py27-appengine]
+basepython = python2.7
+commands = python {toxinidir}/py27/runtests.py {posargs:}
diff --git a/maint/requirements.txt b/maint/requirements.txt
new file mode 100644
index 0000000..30e0d32
--- /dev/null
+++ b/maint/requirements.txt
@@ -0,0 +1,21 @@
+# Frozen pip requirements for tools used in the development of tornado
+
+# Tornado's optional dependencies
+MySQL-python==1.2.3
+Twisted==11.1.0
+pycurl==7.19.0
+
+# Other useful tools
+Sphinx==1.1.2
+coverage==3.5.1
+pyflakes==0.5.0
+tox==1.3
+virtualenv==1.7
+
+# Indirect dependencies
+Jinja2==2.6
+Pygments==1.4
+docutils==0.8.1
+py==1.4.6
+wsgiref==0.1.2
+zope.interface==3.8.0
diff --git a/maint/test/README b/maint/test/README
new file mode 100644
index 0000000..e4e6cd9
--- /dev/null
+++ b/maint/test/README
@@ -0,0 +1,3 @@
+This directory contains additional tests that are not included in the main
+suite (because e.g. they have extra dependencies, run slowly, or produce
+more output than a simple pass/fail)
diff --git a/maint/test/websocket/.gitignore b/maint/test/websocket/.gitignore
new file mode 100644
index 0000000..a9a1bd3
--- /dev/null
+++ b/maint/test/websocket/.gitignore
@@ -0,0 +1 @@
+reports/
diff --git a/maint/test/websocket/client.py b/maint/test/websocket/client.py
new file mode 100644
index 0000000..1679887
--- /dev/null
+++ b/maint/test/websocket/client.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python
+import sys
+from tornado.options import options, define, parse_command_line
+from twisted.python import log
+from twisted.internet import reactor
+from autobahn.fuzzing import FuzzingClientFactory
+
+define('servers', type=str, multiple=True,
+       default=['Tornado=ws://localhost:9000'])
+
+define('cases', type=str, multiple=True,
+       default=["*"])
+define('exclude', type=str, multiple=True,
+       default=["9.*"])
+
+if __name__ == '__main__':
+   parse_command_line()
+   log.startLogging(sys.stdout)
+   servers = []
+   for server in options.servers:
+      name, _, url = server.partition('=')
+      servers.append({"agent": name, "url": url, "options": {"version": 17}})
+   spec = {
+       "options": {"failByDrop": False},
+       "enable-ssl": False,
+       "servers": servers,
+       "cases": options.cases,
+       "exclude-cases": options.exclude,
+       "exclude-agent-cases": {},
+       }
+   fuzzer = FuzzingClientFactory(spec)
+   reactor.run()
diff --git a/maint/test/websocket/run.sh b/maint/test/websocket/run.sh
new file mode 100755
index 0000000..9478687
--- /dev/null
+++ b/maint/test/websocket/run.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+#
+# Runs the autobahn websocket conformance test against tornado in both
+# python2 and python3.  Output goes in ./reports/servers/index.html.
+#
+# The --cases and --exclude arguments can be used to run only part of
+# the suite.  The default is --exclude="9.*" to skip the relatively slow
+# performance tests; pass --exclude="" to override and include them.
+
+set -e
+
+# build/update the virtualenvs
+tox
+
+.tox/py25/bin/python server.py --port=9001 &
+PY25_SERVER_PID=$!
+
+.tox/py27/bin/python server.py --port=9002 &
+PY27_SERVER_PID=$!
+
+.tox/py32/bin/python server.py --port=9003 &
+PY32_SERVER_PID=$!
+
+.tox/pypy/bin/python server.py --port=9004 &
+PYPY_SERVER_PID=$!
+
+sleep 1
+
+.tox/py27/bin/python ./client.py --servers=Tornado/py25=ws://localhost:9001,Tornado/py27=ws://localhost:9002,Tornado/py32=ws://localhost:9003,Tornado/pypy=ws://localhost:9004 "$@" || true
+
+kill $PY25_SERVER_PID
+kill $PY27_SERVER_PID
+kill $PY32_SERVER_PID
+kill $PYPY_SERVER_PID
+wait
+
+echo "Tests complete. Output is in ./reports/servers/index.html"
\ No newline at end of file
diff --git a/maint/test/websocket/server.py b/maint/test/websocket/server.py
new file mode 100644
index 0000000..b44056c
--- /dev/null
+++ b/maint/test/websocket/server.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+
+from tornado.ioloop import IOLoop
+from tornado.options import define, options, parse_command_line
+from tornado.util import bytes_type
+from tornado.websocket import WebSocketHandler
+from tornado.web import Application
+
+define('port', default=9000)
+
+class EchoHandler(WebSocketHandler):
+    def on_message(self, message):
+        self.write_message(message, binary=isinstance(message, bytes_type))
+
+if __name__ == '__main__':
+    parse_command_line()
+    app = Application([
+            ('/', EchoHandler),
+            ])
+    app.listen(options.port, address='127.0.0.1')
+    IOLoop.instance().start()
diff --git a/maint/test/websocket/tox.ini b/maint/test/websocket/tox.ini
new file mode 100644
index 0000000..0709749
--- /dev/null
+++ b/maint/test/websocket/tox.ini
@@ -0,0 +1,12 @@
+# We don't actually use tox to run this test, but it's the easiest way
+# to install autobahn and deal with 2to3 for the python3 version.
+# See run.sh for the real test runner.
+[tox]
+envlist = py27, py32, py25, pypy
+setupdir=../../..
+
+[testenv]
+commands = python -c pass
+
+[testenv:py27]
+deps = autobahn
diff --git a/maint/vm/README b/maint/vm/README
new file mode 100644
index 0000000..7660588
--- /dev/null
+++ b/maint/vm/README
@@ -0,0 +1,23 @@
+This directory contains virtual machine setup scripts for testing Tornado.
+
+Requirements:
+
+Vagrant (http://vagrantup.com) and VirtualBox (http://virtualbox.org).
+Vagrant provides an easy download for Ubuntu 10.04 (aka lucid64); base
+images for other platforms are harder to find and can be built with
+VeeWee (https://github.com/jedi4ever/veewee).
+
+Usage:
+
+cd to the appropriate directory and run `vagrant up`, then `vagrant ssh`.
+From there, simply run `tox` to run the full test suite, or cd to /tornado
+and test manually.  Afterwards, use `vagrant suspend` or `vagrant destroy`
+to clean up.
+
+Notes:
+
+Python distutils (and therefore tox) assume that if the platform
+supports hard links, they can be used in the Tornado source directory.
+VirtualBox's shared folder filesystem does not support hard links (or
+symlinks), so we have to use NFS shared folders instead.  (which has
+the unfortunate side effect of requiring sudo on the host machine)
diff --git a/maint/vm/freebsd/Vagrantfile b/maint/vm/freebsd/Vagrantfile
new file mode 100644
index 0000000..1672492
--- /dev/null
+++ b/maint/vm/freebsd/Vagrantfile
@@ -0,0 +1,27 @@
+require 'vagrant/systems/freebsd'
+
+Vagrant::Config.run do |config|
+    # A freebsd image can be created with veewee
+    # https://github.com/jedi4ever/veewee
+    # 
+    # vagrant basebox define freebsd freebsd-8.2-pcbsd-i386-netboot
+    # vagrant basebox build freebsd
+    # vagrant basebox export freebsd
+    # vagrant box add freebsd freebsd.box
+    config.vm.box = "freebsd"
+
+    config.vm.system = :freebsd
+    
+    # Note that virtualbox shared folders don't work with freebsd, so
+    # we'd need nfs shared folders here even if virtualbox gains
+    # support for symlinks.
+    config.vm.network "172.19.1.3"
+    config.vm.share_folder("tornado", "/tornado", "../../..", :nfs => true)
+
+    # This doesn't seem to get mounted by default for freebsd,
+    # but that's actually a good thing since there are apparently issues
+    # when one nfs export is a subfolder of another.
+    #config.vm.share_folder("v-root", "/vagrant", ".", :nfs => true)
+
+    config.vm.provision :shell, :path => "setup.sh"
+end
\ No newline at end of file
diff --git a/maint/vm/freebsd/setup.sh b/maint/vm/freebsd/setup.sh
new file mode 100644
index 0000000..25003c2
--- /dev/null
+++ b/maint/vm/freebsd/setup.sh
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+chsh -s bash vagrant
+
+# This doesn't get created automatically for freebsd since virtualbox
+# shared folders don't work.
+ln -snf /tornado/maint/vm/freebsd /vagrant
+
+PORTS="
+lang/python27
+devel/py-pip
+devel/py-virtualenv
+ftp/curl
+"
+
+PIP_PACKAGES="
+tox
+pycurl
+"
+
+cd /usr/ports
+
+for port in $PORTS; do
+    make -C $port -DBATCH install
+done
+
+pip install $PIP_PACKAGES
+
+/tornado/maint/vm/shared-setup.sh
+
diff --git a/maint/vm/freebsd/tox.ini b/maint/vm/freebsd/tox.ini
new file mode 100644
index 0000000..ef1c4bc
--- /dev/null
+++ b/maint/vm/freebsd/tox.ini
@@ -0,0 +1,13 @@
+[tox]
+envlist=py27-full, py27
+setupdir=/tornado
+# /home is a symlink to /usr/home, but tox doesn't like symlinks here
+toxworkdir=/usr/home/vagrant/tox-tornado
+
+[testenv]
+commands = python -m tornado.test.runtests {posargs:}
+
+[testenv:py27-full]
+# other dependencies aren't really worth the install time
+deps =
+     pycurl
diff --git a/maint/vm/shared-setup.sh b/maint/vm/shared-setup.sh
new file mode 100755
index 0000000..4493ad1
--- /dev/null
+++ b/maint/vm/shared-setup.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+# Run at the end of each vm's provisioning script
+
+set -e
+
+# Link tox.ini into the home directory so you can run tox immediately
+# after ssh'ing in without cd'ing to /vagrant (since cd'ing to /tornado
+# gets the wrong config)
+ln -sf /vagrant/tox.ini ~vagrant/tox.ini
diff --git a/maint/vm/ubuntu10.04/Vagrantfile b/maint/vm/ubuntu10.04/Vagrantfile
new file mode 100644
index 0000000..63520cb
--- /dev/null
+++ b/maint/vm/ubuntu10.04/Vagrantfile
@@ -0,0 +1,9 @@
+Vagrant::Config.run do |config|
+    config.vm.box = "lucid64"
+    config.vm.box_url = "http://files.vagrantup.com/lucid64.box"
+
+    config.vm.network "172.19.1.2"
+    config.vm.share_folder("tornado", "/tornado", "../../..", :nfs=> true)
+
+    config.vm.provision :shell, :path => "setup.sh"
+end
\ No newline at end of file
diff --git a/maint/vm/ubuntu10.04/setup.sh b/maint/vm/ubuntu10.04/setup.sh
new file mode 100644
index 0000000..d5db904
--- /dev/null
+++ b/maint/vm/ubuntu10.04/setup.sh
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+set -e
+
+apt-get update
+
+# libcurl4-gnutls-dev is the default if you ask for libcurl4-dev, but it
+# has bugs that make our tests deadlock (the relevant tests detect this and
+# disable themselves, but it means that to get full coverage we have to use
+# the openssl version).
+# The oddly-named python-software-properties includes add-apt-repository.
+APT_PACKAGES="
+python-pip
+python-dev
+libmysqlclient-dev
+libcurl4-openssl-dev
+python-software-properties
+"
+
+apt-get -y install $APT_PACKAGES
+
+
+# Ubuntu 10.04 has python 2.6 as default; install more from here.
+# The most important thing is to have both 2.5 and a later version so we
+# test with both tornado.epoll and 2.6+ stdlib's select.epoll.
+add-apt-repository ppa:fkrull/deadsnakes
+apt-get update
+
+DEADSNAKES_PACKAGES="
+python2.5
+python2.5-dev
+python2.7
+python2.7-dev
+python3.2
+python3.2-dev
+"
+apt-get -y install $DEADSNAKES_PACKAGES
+
+
+PIP_PACKAGES="
+virtualenv
+tox
+MySQL-python
+pycurl
+twisted
+"
+
+pip install $PIP_PACKAGES
+
+/tornado/maint/vm/shared-setup.sh
diff --git a/maint/vm/ubuntu10.04/tox.ini b/maint/vm/ubuntu10.04/tox.ini
new file mode 100644
index 0000000..87841ac
--- /dev/null
+++ b/maint/vm/ubuntu10.04/tox.ini
@@ -0,0 +1,32 @@
+[tox]
+envlist = py27-full, py25-full, py32, py25, py26, py26-full, py27
+setupdir=/tornado
+toxworkdir=/home/vagrant/tox-tornado
+
+[testenv]
+commands = python -m tornado.test.runtests {posargs:}
+
+[testenv:py25]
+basepython = python2.5
+deps = simplejson
+
+[testenv:py25-full]
+basepython = python2.5
+deps =
+     MySQL-python
+     pycurl
+     simplejson
+     twisted==11.0.0
+
+[testenv:py26-full]
+deps =
+     MySQL-python
+     pycurl
+     twisted==11.0.0
+
+[testenv:py27-full]
+basepython = python2.7
+deps =
+     MySQL-python
+     pycurl
+     twisted==11.0.0
diff --git a/maint/vm/ubuntu11.04/Vagrantfile b/maint/vm/ubuntu11.04/Vagrantfile
new file mode 100644
index 0000000..00a1938
--- /dev/null
+++ b/maint/vm/ubuntu11.04/Vagrantfile
@@ -0,0 +1,8 @@
+Vagrant::Config.run do |config|
+    config.vm.box = "ubuntu11.04"
+
+    config.vm.network "172.19.1.4"
+    config.vm.share_folder("tornado", "/tornado", "../../..", :nfs=> true)
+
+    config.vm.provision :shell, :path => "setup.sh"
+end
\ No newline at end of file
diff --git a/maint/vm/ubuntu11.04/setup.sh b/maint/vm/ubuntu11.04/setup.sh
new file mode 100644
index 0000000..d5a30f6
--- /dev/null
+++ b/maint/vm/ubuntu11.04/setup.sh
@@ -0,0 +1,56 @@
+#!/bin/sh
+
+set -e
+
+# Ubuntu 10.10+ do some extra permissions checks for hard links.
+# Vagrant's nfs shared folders come through with funny uids, but
+# attempts to access them still work despite the visible permissions
+# being incorrect.
+sysctl -w kernel.yama.protected_nonaccess_hardlinks=0
+
+apt-get update
+
+# libcurl4-gnutls-dev is the default if you ask for libcurl4-dev, but it
+# has bugs that make our tests deadlock (the relevant tests detect this and
+# disable themselves, but it means that to get full coverage we have to use
+# the openssl version).
+# The oddly-named python-software-properties includes add-apt-repository.
+APT_PACKAGES="
+python-pip
+python-dev
+libmysqlclient-dev
+libcurl4-openssl-dev
+python-software-properties
+"
+
+apt-get -y install $APT_PACKAGES
+
+
+# Ubuntu 11.04 has python 2.7 as default; install more from here.
+# The most important thing is to have both 2.5 and a later version so we
+# test with both tornado.epoll and 2.6+ stdlib's select.epoll.
+add-apt-repository ppa:fkrull/deadsnakes
+apt-get update
+
+DEADSNAKES_PACKAGES="
+python2.5
+python2.5-dev
+python2.6
+python2.6-dev
+python3.2
+python3.2-dev
+"
+apt-get -y install $DEADSNAKES_PACKAGES
+
+
+PIP_PACKAGES="
+virtualenv
+tox
+MySQL-python
+pycurl
+twisted
+"
+
+pip install $PIP_PACKAGES
+
+/tornado/maint/vm/shared-setup.sh
diff --git a/maint/vm/ubuntu11.04/tox.ini b/maint/vm/ubuntu11.04/tox.ini
new file mode 100644
index 0000000..87841ac
--- /dev/null
+++ b/maint/vm/ubuntu11.04/tox.ini
@@ -0,0 +1,32 @@
+[tox]
+envlist = py27-full, py25-full, py32, py25, py26, py26-full, py27
+setupdir=/tornado
+toxworkdir=/home/vagrant/tox-tornado
+
+[testenv]
+commands = python -m tornado.test.runtests {posargs:}
+
+[testenv:py25]
+basepython = python2.5
+deps = simplejson
+
+[testenv:py25-full]
+basepython = python2.5
+deps =
+     MySQL-python
+     pycurl
+     simplejson
+     twisted==11.0.0
+
+[testenv:py26-full]
+deps =
+     MySQL-python
+     pycurl
+     twisted==11.0.0
+
+[testenv:py27-full]
+basepython = python2.7
+deps =
+     MySQL-python
+     pycurl
+     twisted==11.0.0
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index 861a9f5..0000000
--- a/setup.cfg
+++ /dev/null
@@ -1,5 +0,0 @@
-[egg_info]
-tag_build = 
-tag_date = 0
-tag_svn_revision = 0
-
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..88c8c37
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,72 @@
+# Tox (http://codespeak.net/~hpk/tox/) is a tool for running tests
+# in multiple virtualenvs.  This configuration file will run the tornado
+# test suite on all supported python versions.  To use it, "pip install tox"
+# and then run "tox" from this directory.
+#
+# See also tornado/test/run_pyversion_tests.py, which is faster but
+# less thorough.
+#
+# On my macports-based setup, the environment variable
+# ARCHFLAGS='-arch x86_64' must be set when building pycurl, and a
+# symlink from mysql_config to mysql_config5 must exist when building
+# MySQL-python.
+[tox]
+# "-full" variants include optional dependencies, to ensure
+# that things work both in a bare install and with all the extras.
+envlist = py27-full, py27-curl, py25-full, py32, pypy, py25, py26, py26-full, py27
+[testenv]
+commands = python -m tornado.test.runtests {posargs:}
+
+# python will import relative to the current working directory by default,
+# so cd into the tox working directory to avoid picking up the working
+# copy of the files (especially important for 2to3).
+changedir = {toxworkdir}
+# Note that PYTHONPATH must not be set when running tox (and setting it
+# with the following doesn't seem to work, since tox/virtualenv appends to
+# PYTHONPATH)
+#environment = PYTHONPATH=
+
+[testenv:py25]
+basepython = python2.5
+deps = simplejson
+
+[testenv:py25-full]
+basepython = python2.5
+deps =
+     MySQL-python
+     pycurl
+     simplejson
+     twisted>=11.1.0
+
+# py26-full deliberately runs an older version of twisted to ensure
+# we're still compatible with the oldest version we support.
+[testenv:py26-full]
+basepython = python2.6
+deps =
+     MySQL-python
+     pycurl
+     twisted==11.0.0
+
+[testenv:py27-full]
+basepython = python2.7
+deps =
+     MySQL-python
+     pycurl
+     twisted>=11.1.0
+
+[testenv:py27-curl]
+# Same as py27-full, but runs the tests with curl_httpclient by default.
+# Note that httpclient_test is always run with both client implementations;
+# this flag controls which client all the other tests use.
+basepython = python2.7
+deps =
+     MySQL-python
+     pycurl
+     twisted>=11.0.0
+commands = python -m tornado.test.runtests --httpclient=tornado.curl_httpclient.CurlAsyncHTTPClient {posargs:}
+
+# No pypy-full yet: pycurl doesn't build with pypy, and installing
+# twisted under pypy takes a *very* long time.  MySQL-python builds with
+# pypy, but doesn't work.
+
+# No py32-full yet: none of our dependencies currently work on python3.
diff --git a/website/Makefile b/website/Makefile
index 6357830..6032d14 100644
--- a/website/Makefile
+++ b/website/Makefile
@@ -7,6 +7,7 @@
 .PHONY: coverage
 coverage:
 	sphinx-build -b coverage ${SPHINXOPTS} sphinx/build/coverage
+	cat sphinx/build/coverage/python.txt
 
 clean:
 	rm -rf sphinx/build
\ No newline at end of file
diff --git a/website/app.yaml b/website/app.yaml
index 9484a91..92e2fbf 100644
--- a/website/app.yaml
+++ b/website/app.yaml
@@ -1,14 +1,15 @@
 application: python-tornado
 version: 2
-runtime: python
+runtime: python27
+threadsafe: yes
 api_version: 1
 
 handlers:
 - url: /static/tornado-0.1.tar.gz
-  script: website.py
+  script: website.application
 
 - url: /static/tornado-0.2.tar.gz
-  script: website.py
+  script: website.application
 
 - url: /static/
   static_dir: static
@@ -22,10 +23,10 @@
   upload: static/favicon.ico
 
 - url: /documentation/?
-  script: website.py
+  script: website.application
 
 - url: /documentation
   static_dir: sphinx/build/html
 
 - url: /.*
-  script: website.py
+  script: website.application
diff --git a/website/sphinx/conf.py b/website/sphinx/conf.py
index 1b89d28..84f833e 100644
--- a/website/sphinx/conf.py
+++ b/website/sphinx/conf.py
@@ -4,10 +4,6 @@
 sys.path.insert(0, os.path.abspath("../.."))
 import tornado
 
-# For our version of sphinx_coverage.py.  The version in sphinx 1.0.7
-# has too many false positives; this version comes from upstream HG.
-sys.path.append(os.path.abspath("."))
-
 master_doc = "index"
 
 project = "Tornado"
@@ -15,7 +11,7 @@
 
 version = release = tornado.version
 
-extensions = ["sphinx.ext.autodoc", "sphinx_coverage", "sphinx.ext.viewcode"]
+extensions = ["sphinx.ext.autodoc", "sphinx.ext.coverage", "sphinx.ext.viewcode"]
 
 primary_domain = 'py'
 default_role = 'py:obj'
@@ -24,14 +20,27 @@
 autoclass_content = "both"
 
 coverage_skip_undoc_in_source = True
+coverage_ignore_modules = [
+    "tornado.platform.twisted",
+    ]
 # I wish this could go in a per-module file...
 coverage_ignore_classes = [
+    # tornado.gen
+    "Multi",
+    "Runner",
+    "YieldPoint",
+
     # tornado.web
     "ChunkedTransferEncoding",
     "GZipContentEncoding",
     "OutputTransform",
     "TemplateModule",
     "url",
+
+    # tornado.websocket
+    "WebSocketProtocol",
+    "WebSocketProtocol13",
+    "WebSocketProtocol76",
     ]
 
 coverage_ignore_functions = [
diff --git a/website/sphinx/gen.rst b/website/sphinx/gen.rst
new file mode 100644
index 0000000..4281eb3
--- /dev/null
+++ b/website/sphinx/gen.rst
@@ -0,0 +1,28 @@
+``tornado.gen`` --- Simplify asynchronous code
+==============================================
+
+.. automodule:: tornado.gen
+
+   Decorator
+   ---------
+
+   .. autofunction:: engine
+
+   Yield points
+   ------------
+
+   Instances of the following classes may be used in yield expressions
+   in the generator.  
+
+   .. autoclass:: Task
+
+   .. autoclass:: Callback
+
+   .. autoclass:: Wait
+
+   .. autoclass:: WaitAll
+
+   Other classes
+   -------------
+
+   .. autoclass:: Arguments
diff --git a/website/sphinx/integration.rst b/website/sphinx/integration.rst
index bf0f131..1df2fa0 100644
--- a/website/sphinx/integration.rst
+++ b/website/sphinx/integration.rst
@@ -5,5 +5,6 @@
 
    auth
    database
+   twisted
    websocket
    wsgi
diff --git a/website/sphinx/ioloop.rst b/website/sphinx/ioloop.rst
index 1ff1723..3e6e8ed 100644
--- a/website/sphinx/ioloop.rst
+++ b/website/sphinx/ioloop.rst
@@ -13,9 +13,11 @@
 
    .. automethod:: IOLoop.instance
    .. automethod:: IOLoop.initialized
+   .. automethod:: IOLoop.install
    .. automethod:: IOLoop.start
-   .. automethod:: IOLoop.stop
    .. automethod:: IOLoop.running
+   .. automethod:: IOLoop.stop
+   .. automethod:: IOLoop.close
 
    I/O events
    ^^^^^^^^^^
diff --git a/website/sphinx/netutil.rst b/website/sphinx/netutil.rst
new file mode 100644
index 0000000..f7a0e0d
--- /dev/null
+++ b/website/sphinx/netutil.rst
@@ -0,0 +1,5 @@
+``tornado.netutil`` --- Miscellaneous network utilities
+=======================================================
+
+.. automodule:: tornado.netutil
+   :members:
diff --git a/website/sphinx/networking.rst b/website/sphinx/networking.rst
index e77622d..f71de71 100644
--- a/website/sphinx/networking.rst
+++ b/website/sphinx/networking.rst
@@ -6,3 +6,4 @@
    ioloop
    iostream
    httpclient
+   netutil
diff --git a/website/sphinx/overview.rst b/website/sphinx/overview.rst
index c33e499..29c88ee 100644
--- a/website/sphinx/overview.rst
+++ b/website/sphinx/overview.rst
@@ -1,3 +1,5 @@
+.. currentmodule:: tornado.web
+
 Overview
 ========
 
@@ -138,6 +140,9 @@
 4. One of the HTTP methods is called: ``get()``, ``post()``, ``put()``,
    etc. If the URL regular expression contains capturing groups, they
    are passed as arguments to this method.
+5. When the request is finished, ``on_finish()`` is called.  For synchronous
+   handlers this is immediately after ``get()`` (etc) return; for
+   asynchronous handlers it is after the call to ``finish()``.
 
 Here is an example demonstrating the ``initialize()`` method:
 
@@ -156,8 +161,8 @@
 
 Other methods designed for overriding include:
 
--  ``get_error_html(self, status_code, exception=None, **kwargs)`` -
-   returns HTML (as a string) for use on error pages.
+-  ``write_error(self, status_code, exc_info=None, **kwargs)`` -
+   outputs HTML for use on error pages.
 -  ``get_current_user(self)`` - see `User
    Authentication <#user-authentication>`_ below
 -  ``get_user_locale(self)`` - returns ``locale`` object to use for the
@@ -166,6 +171,40 @@
    ``@authenticated`` decorator (default is in ``Application`` settings)
 -  ``get_template_path(self)`` - returns location of template files
    (default is in ``Application`` settings)
+-  ``set_default_headers(self)`` - may be used to set additional headers
+   on the response (such as a custom ``Server`` header)
+
+Error Handling
+~~~~~~~~~~~~~~
+
+There are three ways to return an error from a `RequestHandler`:
+
+1. Manually call `~tornado.web.RequestHandler.set_status` and output the
+   response body normally.
+2. Call `~RequestHandler.send_error`.  This discards
+   any pending unflushed output and calls `~RequestHandler.write_error` to
+   generate an error page.
+3. Raise an exception.  `tornado.web.HTTPError` can be used to generate
+   a specified status code; all other exceptions return a 500 status.
+   The exception handler uses `~RequestHandler.send_error` and
+   `~RequestHandler.write_error` to generate the error page.
+
+The default error page includes a stack trace in debug mode and a one-line
+description of the error (e.g. "500: Internal Server Error") otherwise.
+To produce a custom error page, override `RequestHandler.write_error`.
+This method may produce output normally via methods such as 
+`~RequestHandler.write` and `~RequestHandler.render`.  If the error was
+caused by an exception, an ``exc_info`` triple will be passed as a keyword
+argument (note that this exception is not guaranteed to be the current
+exception in ``sys.exc_info``, so ``write_error`` must use e.g.
+`traceback.format_exception` instead of `traceback.format_exc`).
+
+In Tornado 2.0 and earlier, custom error pages were implemented by overriding
+``RequestHandler.get_error_html``, which returned the error page as a string
+instead of calling the normal output methods (and had slightly different
+semantics for exceptions).  This method is still supported, but it is
+deprecated and applications are encouraged to switch to 
+`RequestHandler.write_error`.
 
 Redirection
 ~~~~~~~~~~~
@@ -317,6 +356,14 @@
 these places the name of an alternative escaping function may be used
 instead of ``None``.
 
+Note that while Tornado's automatic escaping is helpful in avoiding
+XSS vulnerabilities, it is not sufficient in all cases.  Expressions
+that appear in certain locations, such as in Javascript or CSS, may need
+additional escaping.  Additionally, either care must be taken to always
+use double quotes and ``xhtml_escape`` in HTML attributes that may contain
+untrusted content, or a separate escaping function must be used for
+attributes (see e.g. http://wonko.com/post/html-escaping)
+
 Cookies and secure cookies
 ~~~~~~~~~~~~~~~~~~~~~~~~~~
 
@@ -1059,13 +1106,3 @@
 <https://github.com/facebook/tornado/tree/master/demos/appengine>`_ for a
 full-featured AppEngine app built on Tornado.
 
-Caveats and support
-~~~~~~~~~~~~~~~~~~~
-
-Because FriendFeed and other large users of Tornado run `behind
-nginx <#running-tornado-in-production>`_ or Apache proxies, Tornado's
-HTTP server currently does not attempt to handle multi-line headers and
-some types of malformed input.
-
-You can discuss Tornado and report bugs on `the Tornado developer
-mailing list <http://groups.google.com/group/python-tornado>`_.
diff --git a/website/sphinx/process.rst b/website/sphinx/process.rst
new file mode 100644
index 0000000..c9ce63b
--- /dev/null
+++ b/website/sphinx/process.rst
@@ -0,0 +1,5 @@
+``tornado.process`` --- Utilities for multiple processes
+========================================================
+
+.. automodule:: tornado.process
+   :members:
diff --git a/website/sphinx/releases.rst b/website/sphinx/releases.rst
index 69606af..05af64f 100644
--- a/website/sphinx/releases.rst
+++ b/website/sphinx/releases.rst
@@ -4,6 +4,9 @@
 .. toctree::
    :maxdepth: 2
 
+   releases/v2.2.0
+   releases/v2.1.1
+   releases/v2.1.0
    releases/v2.0.0
    releases/v1.2.1
    releases/v1.2.0
diff --git a/website/sphinx/releases/v2.0.0.rst b/website/sphinx/releases/v2.0.0.rst
index f53432b..bc8aa3e 100644
--- a/website/sphinx/releases/v2.0.0.rst
+++ b/website/sphinx/releases/v2.0.0.rst
@@ -17,6 +17,8 @@
       a previous release of Tornado must either disable autoescaping or adapt
       their templates to work with it.  For most applications, the simplest
       way to do this is to pass autoescape=None to the Application constructor.
+      Note that this affects certain built-in methods, e.g. xsrf_form_html
+      and linkify, which must now be called with {% raw %} instead of {}
     * Applications that wish to continue using curl_httpclient instead of
       simple_httpclient may do so by calling
         AsyncHTTPClient.configure("tornado.curl_httpclient.CurlAsyncHTTPClient")
diff --git a/website/sphinx/releases/v2.1.0.rst b/website/sphinx/releases/v2.1.0.rst
new file mode 100644
index 0000000..2e1d190
--- /dev/null
+++ b/website/sphinx/releases/v2.1.0.rst
@@ -0,0 +1,164 @@
+What's new in Tornado 2.1
+=========================
+
+Sep 20, 2011
+------------
+
+Backwards-incompatible changes
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+* Support for secure cookies written by pre-1.0 releases of Tornado has
+  been removed.  The `RequestHandler.get_secure_cookie` method no longer
+  takes an ``include_name`` parameter.
+* The ``debug`` application setting now causes stack traces to be displayed
+  in the browser on uncaught exceptions.  Since this may leak sensitive
+  information, debug mode is not recommended for public-facing servers.
+
+Security fixes
+~~~~~~~~~~~~~~
+
+* Diginotar has been removed from the default CA certificates file used
+  by `SimpleAsyncHTTPClient`.
+
+New modules
+~~~~~~~~~~~
+
+* `tornado.gen`:  A generator-based interface to simplify writing 
+  asynchronous functions.
+* `tornado.netutil`:  Parts of `tornado.httpserver` have been extracted into
+  a new module for use with non-HTTP protocols.
+* `tornado.platform.twisted`:  A bridge between the Tornado IOLoop and the
+  Twisted Reactor, allowing code written for Twisted to be run on Tornado.
+* `tornado.process`:  Multi-process mode has been improved, and can now restart
+  crashed child processes.  A new entry point has been added at 
+  `tornado.process.fork_processes`, although
+  `tornado.httpserver.HTTPServer.start` is still supported.
+
+``tornado.web``
+~~~~~~~~~~~~~~~
+
+* `tornado.web.RequestHandler.write_error` replaces ``get_error_html`` as the
+  preferred way to generate custom error pages (``get_error_html`` is still
+  supported, but deprecated)
+* In `tornado.web.Application`, handlers may be specified by
+  (fully-qualified) name instead of importing and passing the class object
+  itself.
+* It is now possible to use a custom subclass of ``StaticFileHandler``
+  with the ``static_handler_class`` application setting, and this subclass
+  can override the behavior of the ``static_url`` method.
+* `~tornado.web.StaticFileHandler` subclasses can now override 
+  ``get_cache_time`` to customize cache control behavior.
+* `tornado.web.RequestHandler.get_secure_cookie` now has a ``max_age_days``
+  parameter to allow applications to override the default one-month expiration.
+* `~tornado.web.RequestHandler.set_cookie` now accepts a ``max_age`` keyword
+  argument to set the ``max-age`` cookie attribute (note underscore vs dash)
+* `tornado.web.RequestHandler.set_default_headers` may be overridden to set
+  headers in a way that does not get reset during error handling.
+* `RequestHandler.add_header` can now be used to set a header that can
+  appear multiple times in the response.
+* `RequestHandler.flush` can now take a callback for flow control.
+* The ``application/json`` content type can now be gzipped.
+* The cookie-signing functions are now accessible as static functions
+  `tornado.web.create_signed_value` and `tornado.web.decode_signed_value`.
+
+``tornado.httpserver``
+~~~~~~~~~~~~~~~~~~~~~~
+
+* To facilitate some advanced multi-process scenarios, ``HTTPServer``
+  has a new method ``add_sockets``, and socket-opening code is
+  available separately as `tornado.netutil.bind_sockets`.
+* The ``cookies`` property is now available on `tornado.httpserver.HTTPRequest`
+  (it is also available in its old location as a property of
+  `~tornado.web.RequestHandler`)
+* `tornado.httpserver.HTTPServer.bind` now takes a backlog argument with the
+  same meaning as ``socket.listen``.
+* `~tornado.httpserver.HTTPServer` can now be run on a unix socket as well
+  as TCP.
+* Fixed exception at startup when ``socket.AI_ADDRCONFIG`` is not available,
+  as on Windows XP
+
+``IOLoop`` and ``IOStream``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+* `~tornado.iostream.IOStream` performance has been improved, especially for
+  small synchronous requests.
+* New methods `tornado.iostream.IOStream.read_until_close` and 
+  `tornado.iostream.IOStream.read_until_regex`.
+* `IOStream.read_bytes` and `IOStream.read_until_close` now take a
+  ``streaming_callback`` argument to return data as it is received rather
+  than all at once.
+* `IOLoop.add_timeout` now accepts `datetime.timedelta` objects in addition
+  to absolute timestamps.
+* `~tornado.ioloop.PeriodicCallback` now sticks to the specified period
+  instead of creeping later due to accumulated errors.
+* `tornado.ioloop.IOLoop` and `tornado.httpclient.HTTPClient` now have
+  ``close()`` methods that should be used in applications that create
+  and destroy many of these objects.
+* `IOLoop.install` can now be used to use a custom subclass of IOLoop
+  as the singleton without monkey-patching.
+* `~tornado.iostream.IOStream` should now always call the close callback
+  instead of the connect callback on a connection error.
+* The `IOStream` close callback will no longer be called while there
+  are pending read callbacks that can be satisfied with buffered data.
+
+
+``tornado.simple_httpclient``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+* Now supports client SSL certificates with the ``client_key`` and 
+  ``client_cert`` parameters to `tornado.httpclient.HTTPRequest`
+* Now takes a maximum buffer size, to allow reading files larger than 100MB
+* Now works with HTTP 1.0 servers that don't send a Content-Length header
+* The ``allow_nonstandard_methods`` flag on HTTP client requests now
+  permits methods other than ``POST`` and ``PUT`` to contain bodies.
+* Fixed file descriptor leaks and multiple callback invocations in
+  `SimpleAsyncHTTPClient`
+* No longer consumes extra connection resources when following redirects.
+* Now works with buggy web servers that separate headers with ``\n`` instead
+  of ``\r\n\r\n``.
+* Now sets ``response.request_time`` correctly.
+* Connect timeouts now work correctly.
+
+
+Other modules
+~~~~~~~~~~~~~
+
+* `tornado.auth.OpenIDMixin` now uses the correct realm when the
+  callback URI is on a different domain.
+* `tornado.autoreload` has a new command-line interface which can be used
+  to wrap any script.  This replaces the ``--autoreload`` argument to
+  `tornado.testing.main` and is more robust against syntax errors.
+* `tornado.autoreload.watch` can be used to watch files other than
+  the sources of imported modules.
+* `tornado.database.Connection` has new variants of ``execute`` and
+  ``executemany`` that return the number of rows affected instead of
+  the last inserted row id.
+* `tornado.locale.load_translations` now accepts any properly-formatted
+  locale name, not just those in the predefined ``LOCALE_NAMES`` list.
+* `tornado.options.define` now takes a ``group`` parameter to group options
+  in ``--help`` output.
+* Template loaders now take a ``namespace`` constructor argument to add
+  entries to the template namespace.
+* `tornado.websocket` now supports the latest ("hybi-10") version of the
+  protocol (the old version, "hixie-76" is still supported; the correct
+  version is detected automatically).
+* `tornado.websocket` now works on Python 3
+
+
+Bug fixes
+~~~~~~~~~
+
+* Windows support has been improved.  Windows is still not an officially
+  supported platform, but the test suite now passes and
+  `tornado.autoreload` works.
+* Uploading files whose names contain special characters will now work.
+* Cookie values containing special characters are now properly quoted
+  and unquoted.
+* Multi-line headers are now supported.
+* Repeated Content-Length headers (which may be added by certain proxies)
+  are now supported in `HTTPServer`.
+* Unicode string literals now work in template expressions.
+* The template ``{% module %}`` directive now works even if applications
+  use a template variable named ``modules``.
+* Requests with "Expect: 100-continue" now work on python 3
+
diff --git a/website/sphinx/releases/v2.1.1.rst b/website/sphinx/releases/v2.1.1.rst
new file mode 100644
index 0000000..69dbbce
--- /dev/null
+++ b/website/sphinx/releases/v2.1.1.rst
@@ -0,0 +1,29 @@
+What's new in Tornado 2.1.1
+===========================
+
+Oct 4, 2011
+-----------
+
+Bug fixes
+~~~~~~~~~
+
+* Fixed handling of closed connections with the ``epoll`` (i.e. Linux)
+  ``IOLoop``.  Previously, closed connections could be shut down too early,
+  which most often manifested as "Stream is closed" exceptions in
+  ``SimpleAsyncHTTPClient``.
+* Fixed a case in which chunked responses could be closed prematurely,
+  leading to truncated output.
+* ``IOStream.connect`` now reports errors more consistently via logging
+  and the close callback (this affects e.g. connections to localhost
+  on FreeBSD).
+* ``IOStream.read_bytes`` again accepts both ``int`` and ``long`` arguments.
+* ``PeriodicCallback`` no longer runs repeatedly when ``IOLoop`` iterations
+  complete faster than the resolution of ``time.time()`` (mainly a problem
+  on Windows).
+
+Backwards-compatibility note
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+* Listening for ``IOLoop.ERROR`` alone is no longer sufficient for detecting
+  closed connections on an otherwise unused socket.  ``IOLoop.ERROR`` must
+  always be used in combination with ``READ`` or ``WRITE``.
diff --git a/website/sphinx/releases/v2.2.0.rst b/website/sphinx/releases/v2.2.0.rst
new file mode 100644
index 0000000..777eab8
--- /dev/null
+++ b/website/sphinx/releases/v2.2.0.rst
@@ -0,0 +1,130 @@
+What's new in Tornado 2.2
+=========================
+
+Jan 30, 2012
+------------
+
+Highlights
+~~~~~~~~~~
+
+* Updated and expanded WebSocket support.
+* Improved compatibility in the Twisted/Tornado bridge.
+* Template errors now generate better stack traces.
+* Better exception handling in `tornado.gen`.
+
+Security fixes
+~~~~~~~~~~~~~~
+
+* `tornado.simple_httpclient` now disables SSLv2 in all cases.  Previously
+  SSLv2 would be allowed if the Python interpreter was linked against a
+  pre-1.0 version of OpenSSL.
+
+Backwards-incompatible changes
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+* `tornado.process.fork_processes` now raises `SystemExit` if all child
+  processes exit cleanly rather than returning ``None``.  The old behavior
+  was surprising and inconsistent with most of the documented examples
+  of this function (which did not check the return value).
+* On Python 2.6, `tornado.simple_httpclient` only supports SSLv3.  This
+  is because Python 2.6 does not expose a way to support both SSLv3 and TLSv1
+  without also supporting the insecure SSLv2.
+* `tornado.websocket` no longer supports the older "draft 76" version
+  of the websocket protocol by default, although this version can
+  be enabled by overriding `tornado.websocket.WebSocketHandler.allow_draft76`.
+
+``tornado.httpclient``
+~~~~~~~~~~~~~~~~~~~~~~
+
+* `SimpleAsyncHTTPClient` no longer hangs on ``HEAD`` requests,
+  responses with no content, or empty ``POST``/``PUT`` response bodies.
+* `SimpleAsyncHTTPClient` now supports 303 and 307 redirect codes.
+* `tornado.curl_httpclient` now accepts non-integer timeouts.
+* `tornado.curl_httpclient` now supports basic authentication with an
+  empty password.
+
+``tornado.httpserver``
+~~~~~~~~~~~~~~~~~~~~~~
+
+* `HTTPServer` with ``xheaders=True`` will no longer accept
+  ``X-Real-IP`` headers that don't look like valid IP addresses.
+* `HTTPServer` now treats the ``Connection`` request header as
+  case-insensitive.
+
+``tornado.ioloop`` and ``tornado.iostream``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+* `IOStream.write` now works correctly when given an empty string.
+* `IOStream.read_until` (and ``read_until_regex``) now perform better
+  when there is a lot of buffered data, which improves peformance of
+  `SimpleAsyncHTTPClient` when downloading files with lots of
+  chunks.
+* `SSLIOStream` now works correctly when ``ssl_version`` is set to
+  a value other than ``SSLv23``.
+* Idle ``IOLoops`` no longer wake up several times a second.
+* `tornado.ioloop.PeriodicCallback` no longer triggers duplicate callbacks
+  when stopped and started repeatedly.
+
+``tornado.template``
+~~~~~~~~~~~~~~~~~~~~
+
+* Exceptions in template code will now show better stack traces that
+  reference lines from the original template file.
+* ``{#`` and ``#}`` can now be used for comments (and unlike the old
+  ``{% comment %}`` directive, these can wrap other template directives).
+* Template directives may now span multiple lines.
+
+``tornado.web``
+~~~~~~~~~~~~~~~
+
+* Now behaves better when given malformed ``Cookie`` headers
+* `RequestHandler.redirect` now has a ``status`` argument to send
+  status codes other than 301 and 302.
+* New method `RequestHandler.on_finish` may be overridden for post-request
+  processing (as a counterpart to `RequestHandler.prepare`)
+* `StaticFileHandler` now outputs ``Content-Length`` and ``Etag`` headers
+  on ``HEAD`` requests.
+* `StaticFileHandler` now has overridable ``get_version`` and
+  ``parse_url_path`` methods for use in subclasses.
+* `RequestHandler.static_url` now takes an ``include_host`` parameter
+  (in addition to the old support for the `RequestHandler.include_host`
+  attribute).
+
+``tornado.websocket``
+~~~~~~~~~~~~~~~~~~~~~
+
+* Updated to support the latest version of the protocol, as finalized
+  in RFC 6455.
+* Many bugs were fixed in all supported protocol versions.
+* `tornado.websocket` no longer supports the older "draft 76" version
+  of the websocket protocol by default, although this version can
+  be enabled by overriding `tornado.websocket.WebSocketHandler.allow_draft76`.
+* `WebSocketHandler.write_message` now accepts a ``binary`` argument
+  to send binary messages.
+* Subprotocols (i.e. the ``Sec-WebSocket-Protocol`` header) are now supported;
+  see the `WebSocketHandler.select_subprotocol` method for details.
+* `WebSocketHandler.get_websocket_scheme` can be used to select the
+  appropriate url scheme (``ws://`` or ``wss://``) in cases where
+  `HTTPRequest.protocol` is not set correctly.
+
+Other modules
+~~~~~~~~~~~~~
+
+* `tornado.auth.TwitterMixin.authenticate_redirect` now takes a
+  ``callback_uri`` parameter.
+* `tornado.auth.TwitterMixin.twitter_request` now accepts both URLs and
+  partial paths (complete URLs are useful for the search API which follows
+  different patterns).
+* Exception handling in `tornado.gen` has been improved.  It is now possible
+  to catch exceptions thrown by a ``Task``.
+* `tornado.netutil.bind_sockets` now works when ``getaddrinfo`` returns
+  duplicate addresses.
+* `tornado.platform.twisted` compatibility has been significantly improved.
+  Twisted version 11.1.0 is now supported in addition to 11.0.0.
+* `tornado.process.fork_processes` correctly reseeds the `random` module
+  even when `os.urandom` is not implemented.
+* `tornado.testing.main` supports a new flag ``--exception_on_interrupt``,
+  which can be set to false to make ``Ctrl-C`` kill the process more
+  reliably (at the expense of stack traces when it does so).
+* `tornado.version_info` is now a four-tuple so official releases can be
+  distinguished from development branches.
diff --git a/website/sphinx/sphinx_coverage.py b/website/sphinx/sphinx_coverage.py
deleted file mode 100644
index 5ff81d7..0000000
--- a/website/sphinx/sphinx_coverage.py
+++ /dev/null
@@ -1,264 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-    sphinx.ext.coverage
-    ~~~~~~~~~~~~~~~~~~~
-
-    Check Python modules and C API for coverage.  Mostly written by Josip
-    Dzolonga for the Google Highly Open Participation contest.
-
-    :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
-    :license: BSD, see LICENSE for details.
-"""
-
-import re
-import glob
-import inspect
-import cPickle as pickle
-from os import path
-
-from sphinx.builders import Builder
-
-
-# utility
-def write_header(f, text, char='-'):
-    f.write(text + '\n')
-    f.write(char * len(text) + '\n')
-
-def compile_regex_list(name, exps, warnfunc):
-    lst = []
-    for exp in exps:
-        try:
-            lst.append(re.compile(exp))
-        except Exception:
-            warnfunc('invalid regex %r in %s' % (exp, name))
-    return lst
-
-
-class CoverageBuilder(Builder):
-
-    name = 'coverage'
-
-    def init(self):
-        self.c_sourcefiles = []
-        for pattern in self.config.coverage_c_path:
-            pattern = path.join(self.srcdir, pattern)
-            self.c_sourcefiles.extend(glob.glob(pattern))
-
-        self.c_regexes = []
-        for (name, exp) in self.config.coverage_c_regexes.items():
-            try:
-                self.c_regexes.append((name, re.compile(exp)))
-            except Exception:
-                self.warn('invalid regex %r in coverage_c_regexes' % exp)
-
-        self.c_ignorexps = {}
-        for (name, exps) in self.config.coverage_ignore_c_items.iteritems():
-            self.c_ignorexps[name] = compile_regex_list(
-                'coverage_ignore_c_items', exps, self.warn)
-        self.mod_ignorexps = compile_regex_list(
-            'coverage_ignore_modules', self.config.coverage_ignore_modules,
-            self.warn)
-        self.cls_ignorexps = compile_regex_list(
-            'coverage_ignore_classes', self.config.coverage_ignore_classes,
-            self.warn)
-        self.fun_ignorexps = compile_regex_list(
-            'coverage_ignore_functions', self.config.coverage_ignore_functions,
-            self.warn)
-
-    def get_outdated_docs(self):
-        return 'coverage overview'
-
-    def write(self, *ignored):
-        self.py_undoc = {}
-        self.build_py_coverage()
-        self.write_py_coverage()
-
-        self.c_undoc = {}
-        self.build_c_coverage()
-        self.write_c_coverage()
-
-    def build_c_coverage(self):
-        # Fetch all the info from the header files
-        c_objects = self.env.domaindata['c']['objects']
-        for filename in self.c_sourcefiles:
-            undoc = []
-            f = open(filename, 'r')
-            try:
-                for line in f:
-                    for key, regex in self.c_regexes:
-                        match = regex.match(line)
-                        if match:
-                            name = match.groups()[0]
-                            if name not in c_objects:
-                                for exp in self.c_ignorexps.get(key, ()):
-                                    if exp.match(name):
-                                        break
-                                else:
-                                    undoc.append((key, name))
-                            continue
-            finally:
-                f.close()
-            if undoc:
-                self.c_undoc[filename] = undoc
-
-    def write_c_coverage(self):
-        output_file = path.join(self.outdir, 'c.txt')
-        op = open(output_file, 'w')
-        try:
-            if self.config.coverage_write_headline:
-                write_header(op, 'Undocumented C API elements', '=')
-            op.write('\n')
-
-            for filename, undoc in self.c_undoc.iteritems():
-                write_header(op, filename)
-                for typ, name in undoc:
-                    op.write(' * %-50s [%9s]\n' % (name, typ))
-                op.write('\n')
-        finally:
-            op.close()
-
-    def build_py_coverage(self):
-        objects = self.env.domaindata['py']['objects']
-        modules = self.env.domaindata['py']['modules']
-
-        skip_undoc = self.config.coverage_skip_undoc_in_source
-
-        for mod_name in modules:
-            ignore = False
-            for exp in self.mod_ignorexps:
-                if exp.match(mod_name):
-                    ignore = True
-                    break
-            if ignore:
-                continue
-
-            try:
-                mod = __import__(mod_name, fromlist=['foo'])
-            except ImportError, err:
-                self.warn('module %s could not be imported: %s' %
-                          (mod_name, err))
-                self.py_undoc[mod_name] = {'error': err}
-                continue
-
-            funcs = []
-            classes = {}
-
-            for name, obj in inspect.getmembers(mod):
-                # diverse module attributes are ignored:
-                if name[0] == '_':
-                    # begins in an underscore
-                    continue
-                if not hasattr(obj, '__module__'):
-                    # cannot be attributed to a module
-                    continue
-                if obj.__module__ != mod_name:
-                    # is not defined in this module
-                    continue
-
-                full_name = '%s.%s' % (mod_name, name)
-
-                if inspect.isfunction(obj):
-                    if full_name not in objects:
-                        for exp in self.fun_ignorexps:
-                            if exp.match(name):
-                                break
-                        else:
-                            if skip_undoc and not obj.__doc__:
-                                continue
-                            funcs.append(name)
-                elif inspect.isclass(obj):
-                    for exp in self.cls_ignorexps:
-                        if exp.match(name):
-                            break
-                    else:
-                        if full_name not in objects:
-                            if skip_undoc and not obj.__doc__:
-                                continue
-                            # not documented at all
-                            classes[name] = []
-                            continue
-
-                        attrs = []
-
-                        for attr_name in dir(obj):
-                            if attr_name not in obj.__dict__:
-                                continue
-                            attr = getattr(obj, attr_name)
-                            if not (inspect.ismethod(attr) or
-                                    inspect.isfunction(attr)):
-                                continue
-                            if attr_name[0] == '_':
-                                # starts with an underscore, ignore it
-                                continue
-                            if skip_undoc and not attr.__doc__:
-                                # skip methods without docstring if wished
-                                continue
-
-                            full_attr_name = '%s.%s' % (full_name, attr_name)
-                            if full_attr_name not in objects:
-                                attrs.append(attr_name)
-
-                        if attrs:
-                            # some attributes are undocumented
-                            classes[name] = attrs
-
-            self.py_undoc[mod_name] = {'funcs': funcs, 'classes': classes}
-
-    def write_py_coverage(self):
-        output_file = path.join(self.outdir, 'python.txt')
-        op = open(output_file, 'w')
-        failed = []
-        try:
-            if self.config.coverage_write_headline:
-                write_header(op, 'Undocumented Python objects', '=')
-            keys = self.py_undoc.keys()
-            keys.sort()
-            for name in keys:
-                undoc = self.py_undoc[name]
-                if 'error' in undoc:
-                    failed.append((name, undoc['error']))
-                else:
-                    if not undoc['classes'] and not undoc['funcs']:
-                        continue
-
-                    write_header(op, name)
-                    if undoc['funcs']:
-                        op.write('Functions:\n')
-                        op.writelines(' * %s\n' % x for x in undoc['funcs'])
-                        op.write('\n')
-                    if undoc['classes']:
-                        op.write('Classes:\n')
-                        for name, methods in sorted(undoc['classes'].iteritems()):
-                            if not methods:
-                                op.write(' * %s\n' % name)
-                            else:
-                                op.write(' * %s -- missing methods:\n' % name)
-                                op.writelines('   - %s\n' % x for x in methods)
-                        op.write('\n')
-
-            if failed:
-                write_header(op, 'Modules that failed to import')
-                op.writelines(' * %s -- %s\n' % x for x in failed)
-        finally:
-            op.close()
-
-    def finish(self):
-        # dump the coverage data to a pickle file too
-        picklepath = path.join(self.outdir, 'undoc.pickle')
-        dumpfile = open(picklepath, 'wb')
-        try:
-            pickle.dump((self.py_undoc, self.c_undoc), dumpfile)
-        finally:
-            dumpfile.close()
-
-
-def setup(app):
-    app.add_builder(CoverageBuilder)
-    app.add_config_value('coverage_ignore_modules', [], False)
-    app.add_config_value('coverage_ignore_functions', [], False)
-    app.add_config_value('coverage_ignore_classes', [], False)
-    app.add_config_value('coverage_c_path', [], False)
-    app.add_config_value('coverage_c_regexes', {}, False)
-    app.add_config_value('coverage_ignore_c_items', {}, False)
-    app.add_config_value('coverage_write_headline', True, False)
-    app.add_config_value('coverage_skip_undoc_in_source', False, False)
diff --git a/website/sphinx/template.rst b/website/sphinx/template.rst
index bb546d1..40d0be2 100644
--- a/website/sphinx/template.rst
+++ b/website/sphinx/template.rst
@@ -2,4 +2,20 @@
 ===================================================
 
 .. automodule:: tornado.template
-   :members:   
+
+   Class reference
+   ---------------
+
+   .. autoclass:: Template
+      :members:
+
+   .. autoclass:: BaseLoader
+      :members:
+
+   .. autoclass:: Loader
+      :members:
+
+   .. autoclass:: DictLoader
+      :members:
+
+   .. autoexception:: ParseError
diff --git a/website/sphinx/twisted.rst b/website/sphinx/twisted.rst
new file mode 100644
index 0000000..6a4d854
--- /dev/null
+++ b/website/sphinx/twisted.rst
@@ -0,0 +1,45 @@
+``tornado.platform.twisted`` --- Run code written for Twisted on Tornado
+========================================================================
+
+.. module:: tornado.platform.twisted
+
+This module contains a Twisted reactor build on the Tornado IOLoop,
+which lets you run applications and libraries written for Twisted in a
+Tornado application.  To use it, simply call `install` at the
+beginning of the application::
+
+    import tornado.platform.twisted
+    tornado.platform.twisted.install()
+    from twisted.internet import reactor
+
+When the app is ready to start, call `IOLoop.instance().start()`
+instead of `reactor.run()`.  This will allow you to use a mixture of
+Twisted and Tornado code in the same process.
+
+It is also possible to create a non-global reactor by calling
+`tornado.platform.twisted.TornadoReactor(io_loop)`.  However, if
+the `IOLoop` and reactor are to be short-lived (such as those used in
+unit tests), additional cleanup may be required.  Specifically, it is
+recommended to call::
+
+    reactor.fireSystemEvent('shutdown')
+    reactor.disconnectAll()
+
+before closing the `IOLoop`.
+
+This module has been tested with Twisted versions 11.0.0 and 11.1.0.
+
+.. function:: install(io_loop=None)
+
+Install this package as the default Twisted reactor.
+
+.. class:: TornadoReactor(io_loop=None)
+
+Twisted reactor built on the Tornado IOLoop.
+
+Since it is intented to be used in applications where the top-level
+event loop is ``io_loop.start()`` rather than ``reactor.run()``,
+it is implemented a little differently than other Twisted reactors.
+We override `mainLoop` instead of `doIteration` and must implement
+timed call functionality on top of `IOLoop.add_timeout` rather than
+using the implementation in `PosixReactorBase`.
diff --git a/website/sphinx/utilities.rst b/website/sphinx/utilities.rst
index 89c9908..775e46c 100644
--- a/website/sphinx/utilities.rst
+++ b/website/sphinx/utilities.rst
@@ -4,8 +4,10 @@
 .. toctree::
 
    autoreload
+   gen
    httputil
    options
+   process
    stack_context
    testing
    
diff --git a/website/sphinx/web.rst b/website/sphinx/web.rst
index fd1af5b..55d69a2 100644
--- a/website/sphinx/web.rst
+++ b/website/sphinx/web.rst
@@ -12,6 +12,7 @@
    
    .. automethod:: RequestHandler.initialize
    .. automethod:: RequestHandler.prepare
+   .. automethod:: RequestHandler.on_finish
 
    Implement any of the following methods to handle the corresponding
    HTTP method.
@@ -39,6 +40,8 @@
 
    .. automethod:: RequestHandler.set_status
    .. automethod:: RequestHandler.set_header
+   .. automethod:: RequestHandler.add_header
+   .. automethod:: RequestHandler.set_default_headers
    .. automethod:: RequestHandler.write
    .. automethod:: RequestHandler.flush
    .. automethod:: RequestHandler.finish
@@ -46,7 +49,7 @@
    .. automethod:: RequestHandler.render_string
    .. automethod:: RequestHandler.redirect
    .. automethod:: RequestHandler.send_error
-   .. automethod:: RequestHandler.get_error_html
+   .. automethod:: RequestHandler.write_error
    .. automethod:: RequestHandler.clear
 
 
diff --git a/website/sphinx/websocket.rst b/website/sphinx/websocket.rst
index c4bd0ed..be7bbc5 100644
--- a/website/sphinx/websocket.rst
+++ b/website/sphinx/websocket.rst
@@ -2,4 +2,30 @@
 ====================================================================
 
 .. automodule:: tornado.websocket
-   :members:
+
+   .. autoclass:: WebSocketHandler
+
+   Event handlers
+   --------------
+
+   .. automethod:: WebSocketHandler.open
+   .. automethod:: WebSocketHandler.on_message
+   .. automethod:: WebSocketHandler.on_close
+   .. automethod:: WebSocketHandler.select_subprotocol
+
+   Output
+   ------
+
+   .. automethod:: WebSocketHandler.write_message
+   .. automethod:: WebSocketHandler.close
+
+   Configuration
+   -------------
+
+   .. automethod:: WebSocketHandler.allow_draft76
+   .. automethod:: WebSocketHandler.get_websocket_scheme
+
+   Other
+   -----
+
+   .. automethod:: WebSocketHandler.async_callback
diff --git a/website/templates/index.html b/website/templates/index.html
index aad0915..40fb166 100644
--- a/website/templates/index.html
+++ b/website/templates/index.html
@@ -4,6 +4,14 @@
   <p><a href="http://www.tornadoweb.org/">Tornado</a> is an open source version of the scalable, non-blocking web server and tools that power <a href="http://friendfeed.com/">FriendFeed</a>. The FriendFeed application is written using a web framework that looks a bit like <a href="http://webpy.org/">web.py</a> or <a href="http://code.google.com/appengine/docs/python/tools/webapp/">Google's webapp</a>, but with additional tools and optimizations to take advantage of the underlying non-blocking infrastructure.</p>
   <p>The framework is distinct from most mainstream web server frameworks (and certainly most Python frameworks) because it is non-blocking and reasonably fast. Because it is non-blocking and uses <a href="http://www.kernel.org/doc/man-pages/online/pages/man4/epoll.4.html"><code>epoll</code></a> or <code>kqueue</code>, it can handle thousands of simultaneous standing connections, which means it is ideal for real-time web services. We built the web server specifically to handle FriendFeed's real-time features &mdash; every active user of FriendFeed maintains an open connection to the FriendFeed servers. (For more information on scaling servers to support thousands of clients, see The <a href="http://www.kegel.com/c10k.html">C10K problem</a>.)</p>
 
+  <h2>Upgrading from Tornado 1.x</h2>
+
+  <p>Tornado 2.0 introduces several potentially backwards-incompatible changes,
+    including in particular automatic escaping of template output.  Users
+    who are upgrading from Tornado 1.x should see the 
+    <a href="/documentation/releases/v2.0.0.html">release notes</a> for
+    information about backwards compatibility.</p>
+
   <h2>Quick links</h2>
 
   <ul>
@@ -47,7 +55,7 @@
   <ul>
     <li>On Python 2.6 and 2.7, there are no dependencies outside the Python standard library, although <a href="http://pycurl.sourceforge.net/">PycURL</a> (version 7.18.2 or higher required; version 7.21.1 or higher recommended) may be used if desired.</li>
     <li>On Python 2.5, PycURL is required, along with <a href="http://pypi.python.org/pypi/simplejson/">simplejson</a> and the Python development headers (typically obtained by installing a package named something like <code>python-dev</code> from your operating system).</li>
-    <li>On Python 3.2, the <a href="http://pypi.python.org/pypi/distribute">distribute</a> package is required.</li>
+    <li>On Python 3.2, the <a href="http://pypi.python.org/pypi/distribute">distribute</a> package is required.  Note that Python 3 support is relatively new and may have bugs.</li>
   </ul></p>
 
   <p><b>Platforms:</b> Tornado should run on any Unix-like platform, although
diff --git a/website/website.py b/website/website.py
index 8fe0c35..24acddc 100644
--- a/website/website.py
+++ b/website/website.py
@@ -16,7 +16,6 @@
 
 import os
 import os.path
-import time
 import tornado.web
 import tornado.wsgi
 import wsgiref.handlers