]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
Split overview.rst into separate files.
authorBen Darnell <ben@bendarnell.com>
Sat, 28 Jun 2014 22:40:55 +0000 (15:40 -0700)
committerBen Darnell <ben@bendarnell.com>
Sat, 28 Jun 2014 22:40:55 +0000 (15:40 -0700)
docs/documentation.rst
docs/guide.rst
docs/guide/running.rst [new file with mode: 0644]
docs/guide/security.rst [new file with mode: 0644]
docs/guide/structure.rst [new file with mode: 0644]
docs/guide/templates.rst [new file with mode: 0644]
docs/overview.rst [deleted file]

index beac58a32e85bf2cb3478dad5d667a703e60ea16..678e081be47786d1e2e0ddbab5767591100afb61 100644 (file)
@@ -5,7 +5,6 @@ Tornado Documentation
    :titlesonly:
 
    guide
-   overview
    webframework
    networking
    integration
index 2f608748027e71441fd53831f59ca8bc70e84c9c..96aab0257fe531cc5fcb835eccf033ee3ff6bdea 100644 (file)
@@ -6,3 +6,7 @@ User's guide
    guide/intro
    guide/async
    guide/coroutines
+   guide/structure
+   guide/templates
+   guide/security
+   guide/running
diff --git a/docs/guide/running.rst b/docs/guide/running.rst
new file mode 100644 (file)
index 0000000..bd31b34
--- /dev/null
@@ -0,0 +1,263 @@
+Running and deploying
+=====================
+
+Static files and aggressive file caching
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You can serve static files from Tornado by specifying the
+``static_path`` setting in your application:
+
+::
+
+    settings = {
+        "static_path": os.path.join(os.path.dirname(__file__), "static"),
+        "cookie_secret": "__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__",
+        "login_url": "/login",
+        "xsrf_cookies": True,
+    }
+    application = tornado.web.Application([
+        (r"/", MainHandler),
+        (r"/login", LoginHandler),
+        (r"/(apple-touch-icon\.png)", tornado.web.StaticFileHandler,
+         dict(path=settings['static_path'])),
+    ], **settings)
+
+This setting will automatically make all requests that start with
+``/static/`` serve from that static directory, e.g.,
+`http://localhost:8888/static/foo.png <http://localhost:8888/static/foo.png>`_
+will serve the file ``foo.png`` from the specified static directory. We
+also automatically serve ``/robots.txt`` and ``/favicon.ico`` from the
+static directory (even though they don't start with the ``/static/``
+prefix).
+
+In the above settings, we have explicitly configured Tornado to serve
+``apple-touch-icon.png`` “from” the root with the ``StaticFileHandler``,
+though it is physically in the static file directory. (The capturing
+group in that regular expression is necessary to tell
+``StaticFileHandler`` the requested filename; capturing groups are
+passed to handlers as method arguments.) You could do the same thing to
+serve e.g. ``sitemap.xml`` from the site root. Of course, you can also
+avoid faking a root ``apple-touch-icon.png`` by using the appropriate
+``<link />`` tag in your HTML.
+
+To improve performance, it is generally a good idea for browsers to
+cache static resources aggressively so browsers won't send unnecessary
+``If-Modified-Since`` or ``Etag`` requests that might block the
+rendering of the page. Tornado supports this out of the box with *static
+content versioning*.
+
+To use this feature, use the ``static_url()`` method in your templates
+rather than typing the URL of the static file directly in your HTML:
+
+::
+
+    <html>
+       <head>
+          <title>FriendFeed - {{ _("Home") }}</title>
+       </head>
+       <body>
+         <div><img src="{{ static_url("images/logo.png") }}"/></div>
+       </body>
+     </html>
+
+The ``static_url()`` function will translate that relative path to a URI
+that looks like ``/static/images/logo.png?v=aae54``. The ``v`` argument
+is a hash of the content in ``logo.png``, and its presence makes the
+Tornado server send cache headers to the user's browser that will make
+the browser cache the content indefinitely.
+
+Since the ``v`` argument is based on the content of the file, if you
+update a file and restart your server, it will start sending a new ``v``
+value, so the user's browser will automatically fetch the new file. If
+the file's contents don't change, the browser will continue to use a
+locally cached copy without ever checking for updates on the server,
+significantly improving rendering performance.
+
+In production, you probably want to serve static files from a more
+optimized static file server like `nginx <http://nginx.net/>`_. You can
+configure most any web server to support these caching semantics. Here
+is the nginx configuration we use at FriendFeed:
+
+::
+
+    location /static/ {
+        root /var/friendfeed/static;
+        if ($query_string) {
+            expires max;
+        }
+     }
+
+.. _debug-mode:
+
+Debug mode and automatic reloading
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you pass ``debug=True`` to the ``Application`` constructor, the app
+will be run in debug/development mode. In this mode, several features
+intended for convenience while developing will be enabled (each of which
+is also available as an individual flag; if both are specified the
+individual flag takes precedence):
+
+* ``autoreload=True``: The app will watch for changes to its source
+  files and reload itself when anything changes. This reduces the need
+  to manually restart the server during development. However, certain
+  failures (such as syntax errors at import time) can still take the
+  server down in a way that debug mode cannot currently recover from.
+* ``compiled_template_cache=False``: Templates will not be cached.
+* ``static_hash_cache=False``: Static file hashes (used by the
+  ``static_url`` function) will not be cached
+* ``serve_traceback=True``: When an exception in a ``RequestHandler``
+  is not caught, an error page including a stack trace will be
+  generated.
+
+Autoreload mode is not compatible with the multi-process mode of ``HTTPServer``.
+You must not give ``HTTPServer.start`` an argument other than 1 (or
+call `tornado.process.fork_processes`) if you are using autoreload mode.
+
+The automatic reloading feature of debug mode is available as a
+standalone module in ``tornado.autoreload``.  The two can be used in
+combination to provide extra robustness against syntax errors: set
+``autoreload=True`` within the app to detect changes while it is running,
+and start it with ``python -m tornado.autoreload myserver.py`` to catch
+any syntax errors or other errors at startup.
+
+Reloading loses any Python interpreter command-line arguments (e.g. ``-u``)
+because it re-executes Python using ``sys.executable`` and ``sys.argv``.
+Additionally, modifying these variables will cause reloading to behave
+incorrectly.
+
+On some platforms (including Windows and Mac OSX prior to 10.6), the
+process cannot be updated "in-place", so when a code change is
+detected the old server exits and a new one starts.  This has been
+known to confuse some IDEs.
+
+
+Running Tornado in production
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+At FriendFeed, we use `nginx <http://nginx.net/>`_ as a load balancer
+and static file server. We run multiple instances of the Tornado web
+server on multiple frontend machines. We typically run one Tornado
+frontend per core on the machine (sometimes more depending on
+utilization).
+
+When running behind a load balancer like nginx, it is recommended to
+pass ``xheaders=True`` to the ``HTTPServer`` constructor. This will tell
+Tornado to use headers like ``X-Real-IP`` to get the user's IP address
+instead of attributing all traffic to the balancer's IP address.
+
+This is a barebones nginx config file that is structurally similar to
+the one we use at FriendFeed. It assumes nginx and the Tornado servers
+are running on the same machine, and the four Tornado servers are
+running on ports 8000 - 8003:
+
+::
+
+    user nginx;
+    worker_processes 1;
+
+    error_log /var/log/nginx/error.log;
+    pid /var/run/nginx.pid;
+
+    events {
+        worker_connections 1024;
+        use epoll;
+    }
+
+    http {
+        # Enumerate all the Tornado servers here
+        upstream frontends {
+            server 127.0.0.1:8000;
+            server 127.0.0.1:8001;
+            server 127.0.0.1:8002;
+            server 127.0.0.1:8003;
+        }
+
+        include /etc/nginx/mime.types;
+        default_type application/octet-stream;
+
+        access_log /var/log/nginx/access.log;
+
+        keepalive_timeout 65;
+        proxy_read_timeout 200;
+        sendfile on;
+        tcp_nopush on;
+        tcp_nodelay on;
+        gzip on;
+        gzip_min_length 1000;
+        gzip_proxied any;
+        gzip_types text/plain text/html text/css text/xml
+                   application/x-javascript application/xml
+                   application/atom+xml text/javascript;
+
+        # Only retry if there was a communication error, not a timeout
+        # on the Tornado server (to avoid propagating "queries of death"
+        # to all frontends)
+        proxy_next_upstream error;
+
+        server {
+            listen 80;
+
+            # Allow file uploads
+            client_max_body_size 50M;
+
+            location ^~ /static/ {
+                root /var/www;
+                if ($query_string) {
+                    expires max;
+                }
+            }
+            location = /favicon.ico {
+                rewrite (.*) /static/favicon.ico;
+            }
+            location = /robots.txt {
+                rewrite (.*) /static/robots.txt;
+            }
+
+            location / {
+                proxy_pass_header Server;
+                proxy_set_header Host $http_host;
+                proxy_redirect off;
+                proxy_set_header X-Real-IP $remote_addr;
+                proxy_set_header X-Scheme $scheme;
+                proxy_pass http://frontends;
+            }
+        }
+    }
+
+WSGI and Google AppEngine
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Tornado comes with limited support for `WSGI <http://wsgi.org/>`_.
+However, since WSGI does not support non-blocking requests, you cannot
+use any of the asynchronous/non-blocking features of Tornado in your
+application if you choose to use WSGI instead of Tornado's HTTP server.
+Some of the features that are not available in WSGI applications:
+``@tornado.web.asynchronous``, the ``httpclient`` module, and the
+``auth`` module.
+
+You can create a valid WSGI application from your Tornado request
+handlers by using ``WSGIApplication`` in the ``wsgi`` module instead of
+using ``tornado.web.Application``. Here is an example that uses the
+built-in WSGI ``CGIHandler`` to make a valid `Google
+AppEngine <http://code.google.com/appengine/>`_ application:
+
+::
+
+    import tornado.web
+    import tornado.wsgi
+    import wsgiref.handlers
+
+    class MainHandler(tornado.web.RequestHandler):
+        def get(self):
+            self.write("Hello, world")
+
+    if __name__ == "__main__":
+        application = tornado.wsgi.WSGIApplication([
+            (r"/", MainHandler),
+        ])
+        wsgiref.handlers.CGIHandler().run(application)
+
+See the `appengine example application
+<https://github.com/tornadoweb/tornado/tree/stable/demos/appengine>`_ for a
+full-featured AppEngine app built on Tornado.
diff --git a/docs/guide/security.rst b/docs/guide/security.rst
new file mode 100644 (file)
index 0000000..6522f85
--- /dev/null
@@ -0,0 +1,241 @@
+Authentication and security
+===========================
+
+Cookies and secure cookies
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You can set cookies in the user's browser with the ``set_cookie``
+method:
+
+::
+
+    class MainHandler(tornado.web.RequestHandler):
+        def get(self):
+            if not self.get_cookie("mycookie"):
+                self.set_cookie("mycookie", "myvalue")
+                self.write("Your cookie was not set yet!")
+            else:
+                self.write("Your cookie was set!")
+
+Cookies are easily forged by malicious clients. If you need to set
+cookies to, e.g., save the user ID of the currently logged in user, you
+need to sign your cookies to prevent forgery. Tornado supports this out
+of the box with the ``set_secure_cookie`` and ``get_secure_cookie``
+methods. To use these methods, you need to specify a secret key named
+``cookie_secret`` when you create your application. You can pass in
+application settings as keyword arguments to your application:
+
+::
+
+    application = tornado.web.Application([
+        (r"/", MainHandler),
+    ], cookie_secret="__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__")
+
+Signed cookies contain the encoded value of the cookie in addition to a
+timestamp and an `HMAC <http://en.wikipedia.org/wiki/HMAC>`_ signature.
+If the cookie is old or if the signature doesn't match,
+``get_secure_cookie`` will return ``None`` just as if the cookie isn't
+set. The secure version of the example above:
+
+::
+
+    class MainHandler(tornado.web.RequestHandler):
+        def get(self):
+            if not self.get_secure_cookie("mycookie"):
+                self.set_secure_cookie("mycookie", "myvalue")
+                self.write("Your cookie was not set yet!")
+            else:
+                self.write("Your cookie was set!")
+
+User authentication
+~~~~~~~~~~~~~~~~~~~
+
+The currently authenticated user is available in every request handler
+as ``self.current_user``, and in every template as ``current_user``. By
+default, ``current_user`` is ``None``.
+
+To implement user authentication in your application, you need to
+override the ``get_current_user()`` method in your request handlers to
+determine the current user based on, e.g., the value of a cookie. Here
+is an example that lets users log into the application simply by
+specifying a nickname, which is then saved in a cookie:
+
+::
+
+    class BaseHandler(tornado.web.RequestHandler):
+        def get_current_user(self):
+            return self.get_secure_cookie("user")
+
+    class MainHandler(BaseHandler):
+        def get(self):
+            if not self.current_user:
+                self.redirect("/login")
+                return
+            name = tornado.escape.xhtml_escape(self.current_user)
+            self.write("Hello, " + name)
+
+    class LoginHandler(BaseHandler):
+        def get(self):
+            self.write('<html><body><form action="/login" method="post">'
+                       'Name: <input type="text" name="name">'
+                       '<input type="submit" value="Sign in">'
+                       '</form></body></html>')
+
+        def post(self):
+            self.set_secure_cookie("user", self.get_argument("name"))
+            self.redirect("/")
+
+    application = tornado.web.Application([
+        (r"/", MainHandler),
+        (r"/login", LoginHandler),
+    ], cookie_secret="__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__")
+
+You can require that the user be logged in using the `Python
+decorator <http://www.python.org/dev/peps/pep-0318/>`_
+``tornado.web.authenticated``. If a request goes to a method with this
+decorator, and the user is not logged in, they will be redirected to
+``login_url`` (another application setting). The example above could be
+rewritten:
+
+::
+
+    class MainHandler(BaseHandler):
+        @tornado.web.authenticated
+        def get(self):
+            name = tornado.escape.xhtml_escape(self.current_user)
+            self.write("Hello, " + name)
+
+    settings = {
+        "cookie_secret": "__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__",
+        "login_url": "/login",
+    }
+    application = tornado.web.Application([
+        (r"/", MainHandler),
+        (r"/login", LoginHandler),
+    ], **settings)
+
+If you decorate ``post()`` methods with the ``authenticated`` decorator,
+and the user is not logged in, the server will send a ``403`` response.
+
+Tornado comes with built-in support for third-party authentication
+schemes like Google OAuth. See the `tornado.auth`
+for more details. Check out the `Tornado Blog example application <https://github.com/tornadoweb/tornado/tree/stable/demos/blog>`_ for a
+complete example that uses authentication (and stores user data in a
+MySQL database).
+
+.. _xsrf:
+
+Cross-site request forgery protection
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+`Cross-site request
+forgery <http://en.wikipedia.org/wiki/Cross-site_request_forgery>`_, or
+XSRF, is a common problem for personalized web applications. See the
+`Wikipedia
+article <http://en.wikipedia.org/wiki/Cross-site_request_forgery>`_ for
+more information on how XSRF works.
+
+The generally accepted solution to prevent XSRF is to cookie every user
+with an unpredictable value and include that value as an additional
+argument with every form submission on your site. If the cookie and the
+value in the form submission do not match, then the request is likely
+forged.
+
+Tornado comes with built-in XSRF protection. To include it in your site,
+include the application setting ``xsrf_cookies``:
+
+::
+
+    settings = {
+        "cookie_secret": "__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__",
+        "login_url": "/login",
+        "xsrf_cookies": True,
+    }
+    application = tornado.web.Application([
+        (r"/", MainHandler),
+        (r"/login", LoginHandler),
+    ], **settings)
+
+If ``xsrf_cookies`` is set, the Tornado web application will set the
+``_xsrf`` cookie for all users and reject all ``POST``, ``PUT``, and
+``DELETE`` requests that do not contain a correct ``_xsrf`` value. If
+you turn this setting on, you need to instrument all forms that submit
+via ``POST`` to contain this field. You can do this with the special
+function ``xsrf_form_html()``, available in all templates:
+
+::
+
+    <form action="/new_message" method="post">
+      {% module xsrf_form_html() %}
+      <input type="text" name="message"/>
+      <input type="submit" value="Post"/>
+    </form>
+
+If you submit AJAX ``POST`` requests, you will also need to instrument
+your JavaScript to include the ``_xsrf`` value with each request. This
+is the `jQuery <http://jquery.com/>`_ function we use at FriendFeed for
+AJAX ``POST`` requests that automatically adds the ``_xsrf`` value to
+all requests:
+
+::
+
+    function getCookie(name) {
+        var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
+        return r ? r[1] : undefined;
+    }
+
+    jQuery.postJSON = function(url, args, callback) {
+        args._xsrf = getCookie("_xsrf");
+        $.ajax({url: url, data: $.param(args), dataType: "text", type: "POST",
+            success: function(response) {
+            callback(eval("(" + response + ")"));
+        }});
+    };
+
+For ``PUT`` and ``DELETE`` requests (as well as ``POST`` requests that
+do not use form-encoded arguments), the XSRF token may also be passed
+via an HTTP header named ``X-XSRFToken``.  The XSRF cookie is normally
+set when ``xsrf_form_html`` is used, but in a pure-Javascript application
+that does not use any regular forms you may need to access
+``self.xsrf_token`` manually (just reading the property is enough to
+set the cookie as a side effect).
+
+If you need to customize XSRF behavior on a per-handler basis, you can
+override ``RequestHandler.check_xsrf_cookie()``. For example, if you
+have an API whose authentication does not use cookies, you may want to
+disable XSRF protection by making ``check_xsrf_cookie()`` do nothing.
+However, if you support both cookie and non-cookie-based authentication,
+it is important that XSRF protection be used whenever the current
+request is authenticated with a cookie.
+
+Third party authentication
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Tornado's ``auth`` module implements the authentication and
+authorization protocols for a number of the most popular sites on the
+web, including Google/Gmail, Facebook, Twitter, and FriendFeed.
+The module includes methods to log users in via these sites and, where
+applicable, methods to authorize access to the service so you can, e.g.,
+download a user's address book or publish a Twitter message on their
+behalf.
+
+Here is an example handler that uses Google for authentication, saving
+the Google credentials in a cookie for later access:
+
+::
+
+    class GoogleHandler(tornado.web.RequestHandler, tornado.auth.GoogleMixin):
+        @tornado.web.asynchronous
+        def get(self):
+            if self.get_argument("openid.mode", None):
+                self.get_authenticated_user(self._on_auth)
+                return
+            self.authenticate_redirect()
+
+        def _on_auth(self, user):
+            if not user:
+                self.authenticate_redirect()
+                return
+            # Save the user with, e.g., set_secure_cookie()
+
+See the `tornado.auth` module documentation for more details.
diff --git a/docs/guide/structure.rst b/docs/guide/structure.rst
new file mode 100644 (file)
index 0000000..97e788f
--- /dev/null
@@ -0,0 +1,283 @@
+.. currentmodule:: tornado.web
+
+Structure of a Tornado web application
+======================================
+
+Request handlers and request arguments
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A Tornado web application maps URLs or URL patterns to subclasses of
+`tornado.web.RequestHandler`. Those classes define ``get()`` or
+``post()`` methods to handle HTTP ``GET`` or ``POST`` requests to that
+URL.
+
+This code maps the root URL ``/`` to ``MainHandler`` and the URL pattern
+``/story/([0-9]+)`` to ``StoryHandler``. Regular expression groups are
+passed as arguments to the ``RequestHandler`` methods:
+
+::
+
+    class MainHandler(tornado.web.RequestHandler):
+        def get(self):
+            self.write("You requested the main page")
+
+    class StoryHandler(tornado.web.RequestHandler):
+        def get(self, story_id):
+            self.write("You requested the story " + story_id)
+
+    application = tornado.web.Application([
+        (r"/", MainHandler),
+        (r"/story/([0-9]+)", StoryHandler),
+    ])
+
+You can get query string arguments and parse ``POST`` bodies with the
+``get_argument()`` method:
+
+::
+
+    class MyFormHandler(tornado.web.RequestHandler):
+        def get(self):
+            self.write('<html><body><form action="/myform" method="post">'
+                       '<input type="text" name="message">'
+                       '<input type="submit" value="Submit">'
+                       '</form></body></html>')
+
+        def post(self):
+            self.set_header("Content-Type", "text/plain")
+            self.write("You wrote " + self.get_argument("message"))
+
+Uploaded files are available in ``self.request.files``, which maps names
+(the name of the HTML ``<input type="file">`` element) to a list of
+files. Each file is a dictionary of the form
+``{"filename":..., "content_type":..., "body":...}``.
+
+If you want to send an error response to the client, e.g., 403
+Unauthorized, you can just raise a ``tornado.web.HTTPError`` exception:
+
+::
+
+    if not self.user_is_logged_in():
+        raise tornado.web.HTTPError(403)
+
+The request handler can access the object representing the current
+request with ``self.request``. The ``HTTPRequest`` object includes a
+number of useful attributes, including:
+
+-  ``arguments`` - all of the ``GET`` and ``POST`` arguments
+-  ``files`` - all of the uploaded files (via ``multipart/form-data``
+   POST requests)
+-  ``path`` - the request path (everything before the ``?``)
+-  ``headers`` - the request headers
+
+See the class definition for `tornado.httputil.HTTPServerRequest` for a
+complete list of attributes.
+
+Overriding RequestHandler methods
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In addition to ``get()``/``post()``/etc, certain other methods in
+``RequestHandler`` are designed to be overridden by subclasses when
+necessary. On every request, the following sequence of calls takes
+place:
+
+1. A new RequestHandler object is created on each request
+2. ``initialize()`` is called with keyword arguments from the
+   ``Application`` configuration. (the ``initialize`` method is new in
+   Tornado 1.1; in older versions subclasses would override ``__init__``
+   instead). ``initialize`` should typically just save the arguments
+   passed into member variables; it may not produce any output or call
+   methods like ``send_error``.
+3. ``prepare()`` is called. This is most useful in a base class shared
+   by all of your handler subclasses, as ``prepare`` is called no matter
+   which HTTP method is used. ``prepare`` may produce output; if it
+   calls ``finish`` (or ``send_error``, etc), processing stops here.
+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:
+
+::
+
+    class ProfileHandler(RequestHandler):
+        def initialize(self, database):
+            self.database = database
+
+        def get(self, username):
+            ...
+
+    app = Application([
+        (r'/user/(.*)', ProfileHandler, dict(database=database)),
+        ])
+
+Other methods designed for overriding include:
+
+-  ``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
+   current user
+-  ``get_login_url(self)`` - returns login url to be used by the
+   ``@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`).
+
+Redirection
+~~~~~~~~~~~
+
+There are two main ways you can redirect requests in Tornado:
+``self.redirect`` and with the ``RedirectHandler``.
+
+You can use ``self.redirect`` within a ``RequestHandler`` method (like
+``get``) to redirect users elsewhere. There is also an optional
+parameter ``permanent`` which you can use to indicate that the
+redirection is considered permanent.
+
+This triggers a ``301 Moved Permanently`` HTTP status, which is useful
+for e.g. redirecting to a canonical URL for a page in an SEO-friendly
+manner.
+
+The default value of ``permanent`` is ``False``, which is apt for things
+like redirecting users on successful POST requests.
+
+::
+
+    self.redirect('/some-canonical-page', permanent=True)
+
+``RedirectHandler`` is available for your use when you initialize
+``Application``.
+
+For example, notice how we redirect to a longer download URL on this
+website:
+
+::
+
+    application = tornado.wsgi.WSGIApplication([
+        (r"/([a-z]*)", ContentHandler),
+        (r"/static/tornado-0.2.tar.gz", tornado.web.RedirectHandler,
+         dict(url="https://github.com/downloads/facebook/tornado/tornado-0.2.tar.gz")),
+    ], **settings)
+
+The default ``RedirectHandler`` status code is
+``301 Moved Permanently``, but to use ``302 Found`` instead, set
+``permanent`` to ``False``.
+
+::
+
+    application = tornado.wsgi.WSGIApplication([
+        (r"/foo", tornado.web.RedirectHandler, {"url":"/bar", "permanent":False}),
+    ], **settings)
+
+Note that the default value of ``permanent`` is different in
+``self.redirect`` than in ``RedirectHandler``. This should make some
+sense if you consider that ``self.redirect`` is used in your methods and
+is probably invoked by logic involving environment, authentication, or
+form submission, but ``RedirectHandler`` patterns are going to fire 100%
+of the time they match the request URL.
+
+Non-blocking, asynchronous requests
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When a request handler is executed, the request is automatically
+finished. Since Tornado uses a non-blocking I/O style, you can override
+this default behavior if you want a request to remain open after the
+main request handler method returns using the
+``tornado.web.asynchronous`` decorator.
+
+When you use this decorator, it is your responsibility to call
+``self.finish()`` to finish the HTTP request, or the user's browser will
+simply hang:
+
+::
+
+    class MainHandler(tornado.web.RequestHandler):
+        @tornado.web.asynchronous
+        def get(self):
+            self.write("Hello, world")
+            self.finish()
+
+Here is a real example that makes a call to the FriendFeed API using
+Tornado's built-in asynchronous HTTP client:
+
+::
+
+    class MainHandler(tornado.web.RequestHandler):
+        @tornado.web.asynchronous
+        def get(self):
+            http = tornado.httpclient.AsyncHTTPClient()
+            http.fetch("http://friendfeed-api.com/v2/feed/bret",
+                       callback=self.on_response)
+
+        def on_response(self, response):
+            if response.error: raise tornado.web.HTTPError(500)
+            json = tornado.escape.json_decode(response.body)
+            self.write("Fetched " + str(len(json["entries"])) + " entries "
+                       "from the FriendFeed API")
+            self.finish()
+
+When ``get()`` returns, the request has not finished. When the HTTP
+client eventually calls ``on_response()``, the request is still open,
+and the response is finally flushed to the client with the call to
+``self.finish()``.
+
+For a more advanced asynchronous example, take a look at the `chat
+example application
+<https://github.com/tornadoweb/tornado/tree/stable/demos/chat>`_, which
+implements an AJAX chat room using `long polling
+<http://en.wikipedia.org/wiki/Push_technology#Long_polling>`_.  Users
+of long polling may want to override ``on_connection_close()`` to
+clean up after the client closes the connection (but see that method's
+docstring for caveats).
+
+Asynchronous HTTP clients
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Tornado includes two non-blocking HTTP client implementations:
+``SimpleAsyncHTTPClient`` and ``CurlAsyncHTTPClient``. The simple client
+has no external dependencies because it is implemented directly on top
+of Tornado's ``IOLoop``. The Curl client requires that ``libcurl`` and
+``pycurl`` be installed (and a recent version of each is highly
+recommended to avoid bugs in older version's asynchronous interfaces),
+but is more likely to be compatible with sites that exercise little-used
+parts of the HTTP specification.
+
+Each of these clients is available in its own module
+(``tornado.simple_httpclient`` and ``tornado.curl_httpclient``), as well
+as via a configurable alias in ``tornado.httpclient``.
+``SimpleAsyncHTTPClient`` is the default, but to use a different
+implementation call the ``AsyncHTTPClient.configure`` method at startup:
+
+::
+
+    AsyncHTTPClient.configure('tornado.curl_httpclient.CurlAsyncHTTPClient')
diff --git a/docs/guide/templates.rst b/docs/guide/templates.rst
new file mode 100644 (file)
index 0000000..ee9f776
--- /dev/null
@@ -0,0 +1,308 @@
+Templates and UI
+================
+
+
+Templates
+~~~~~~~~~
+
+You can use any template language supported by Python, but Tornado ships
+with its own templating language that is a lot faster and more flexible
+than many of the most popular templating systems out there. See the
+`tornado.template` module documentation for complete documentation.
+
+A Tornado template is just HTML (or any other text-based format) with
+Python control sequences and expressions embedded within the markup:
+
+::
+
+    <html>
+       <head>
+          <title>{{ title }}</title>
+       </head>
+       <body>
+         <ul>
+           {% for item in items %}
+             <li>{{ escape(item) }}</li>
+           {% end %}
+         </ul>
+       </body>
+     </html>
+
+If you saved this template as "template.html" and put it in the same
+directory as your Python file, you could render this template with:
+
+::
+
+    class MainHandler(tornado.web.RequestHandler):
+        def get(self):
+            items = ["Item 1", "Item 2", "Item 3"]
+            self.render("template.html", title="My title", items=items)
+
+Tornado templates support *control statements* and *expressions*.
+Control statements are surrounded by ``{%`` and ``%}``, e.g.,
+``{% if len(items) > 2 %}``. Expressions are surrounded by ``{{`` and
+``}}``, e.g., ``{{ items[0] }}``.
+
+Control statements more or less map exactly to Python statements. We
+support ``if``, ``for``, ``while``, and ``try``, all of which are
+terminated with ``{% end %}``. We also support *template inheritance*
+using the ``extends`` and ``block`` statements, which are described in
+detail in the documentation for the `tornado.template`.
+
+Expressions can be any Python expression, including function calls.
+Template code is executed in a namespace that includes the following
+objects and functions (Note that this list applies to templates rendered
+using ``RequestHandler.render`` and ``render_string``. If you're using
+the ``template`` module directly outside of a ``RequestHandler`` many of
+these entries are not present).
+
+-  ``escape``: alias for ``tornado.escape.xhtml_escape``
+-  ``xhtml_escape``: alias for ``tornado.escape.xhtml_escape``
+-  ``url_escape``: alias for ``tornado.escape.url_escape``
+-  ``json_encode``: alias for ``tornado.escape.json_encode``
+-  ``squeeze``: alias for ``tornado.escape.squeeze``
+-  ``linkify``: alias for ``tornado.escape.linkify``
+-  ``datetime``: the Python ``datetime`` module
+-  ``handler``: the current ``RequestHandler`` object
+-  ``request``: alias for ``handler.request``
+-  ``current_user``: alias for ``handler.current_user``
+-  ``locale``: alias for ``handler.locale``
+-  ``_``: alias for ``handler.locale.translate``
+-  ``static_url``: alias for ``handler.static_url``
+-  ``xsrf_form_html``: alias for ``handler.xsrf_form_html``
+-  ``reverse_url``: alias for ``Application.reverse_url``
+-  All entries from the ``ui_methods`` and ``ui_modules``
+   ``Application`` settings
+-  Any keyword arguments passed to ``render`` or ``render_string``
+
+When you are building a real application, you are going to want to use
+all of the features of Tornado templates, especially template
+inheritance. Read all about those features in the `tornado.template`
+section (some features, including ``UIModules`` are implemented in the
+``web`` module)
+
+Under the hood, Tornado templates are translated directly to Python. The
+expressions you include in your template are copied verbatim into a
+Python function representing your template. We don't try to prevent
+anything in the template language; we created it explicitly to provide
+the flexibility that other, stricter templating systems prevent.
+Consequently, if you write random stuff inside of your template
+expressions, you will get random Python errors when you execute the
+template.
+
+All template output is escaped by default, using the
+``tornado.escape.xhtml_escape`` function. This behavior can be changed
+globally by passing ``autoescape=None`` to the ``Application`` or
+``TemplateLoader`` constructors, for a template file with the
+``{% autoescape None %}`` directive, or for a single expression by
+replacing ``{{ ... }}`` with ``{% raw ...%}``. Additionally, in each of
+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)
+
+Localization
+~~~~~~~~~~~~
+
+The locale of the current user (whether they are logged in or not) is
+always available as ``self.locale`` in the request handler and as
+``locale`` in templates. The name of the locale (e.g., ``en_US``) is
+available as ``locale.name``, and you can translate strings with the
+``locale.translate`` method. Templates also have the global function
+call ``_()`` available for string translation. The translate function
+has two forms:
+
+::
+
+    _("Translate this string")
+
+which translates the string directly based on the current locale, and
+
+::
+
+    _("A person liked this", "%(num)d people liked this",
+      len(people)) % {"num": len(people)}
+
+which translates a string that can be singular or plural based on the
+value of the third argument. In the example above, a translation of the
+first string will be returned if ``len(people)`` is ``1``, or a
+translation of the second string will be returned otherwise.
+
+The most common pattern for translations is to use Python named
+placeholders for variables (the ``%(num)d`` in the example above) since
+placeholders can move around on translation.
+
+Here is a properly localized template:
+
+::
+
+    <html>
+       <head>
+          <title>FriendFeed - {{ _("Sign in") }}</title>
+       </head>
+       <body>
+         <form action="{{ request.path }}" method="post">
+           <div>{{ _("Username") }} <input type="text" name="username"/></div>
+           <div>{{ _("Password") }} <input type="password" name="password"/></div>
+           <div><input type="submit" value="{{ _("Sign in") }}"/></div>
+           {% module xsrf_form_html() %}
+         </form>
+       </body>
+     </html>
+
+By default, we detect the user's locale using the ``Accept-Language``
+header sent by the user's browser. We choose ``en_US`` if we can't find
+an appropriate ``Accept-Language`` value. If you let user's set their
+locale as a preference, you can override this default locale selection
+by overriding ``get_user_locale`` in your request handler:
+
+::
+
+    class BaseHandler(tornado.web.RequestHandler):
+        def get_current_user(self):
+            user_id = self.get_secure_cookie("user")
+            if not user_id: return None
+            return self.backend.get_user_by_id(user_id)
+
+        def get_user_locale(self):
+            if "locale" not in self.current_user.prefs:
+                # Use the Accept-Language header
+                return None
+            return self.current_user.prefs["locale"]
+
+If ``get_user_locale`` returns ``None``, we fall back on the
+``Accept-Language`` header.
+
+You can load all the translations for your application using the
+``tornado.locale.load_translations`` method. It takes in the name of the
+directory which should contain CSV files named after the locales whose
+translations they contain, e.g., ``es_GT.csv`` or ``fr_CA.csv``. The
+method loads all the translations from those CSV files and infers the
+list of supported locales based on the presence of each CSV file. You
+typically call this method once in the ``main()`` method of your server:
+
+::
+
+    def main():
+        tornado.locale.load_translations(
+            os.path.join(os.path.dirname(__file__), "translations"))
+        start_server()
+
+You can get the list of supported locales in your application with
+``tornado.locale.get_supported_locales()``. The user's locale is chosen
+to be the closest match based on the supported locales. For example, if
+the user's locale is ``es_GT``, and the ``es`` locale is supported,
+``self.locale`` will be ``es`` for that request. We fall back on
+``en_US`` if no close match can be found.
+
+See the `tornado.locale`
+documentation for detailed information on the CSV format and other
+localization methods.
+
+.. _ui-modules:
+
+UI modules
+~~~~~~~~~~
+
+Tornado supports *UI modules* to make it easy to support standard,
+reusable UI widgets across your application. UI modules are like special
+functional calls to render components of your page, and they can come
+packaged with their own CSS and JavaScript.
+
+For example, if you are implementing a blog, and you want to have blog
+entries appear on both the blog home page and on each blog entry page,
+you can make an ``Entry`` module to render them on both pages. First,
+create a Python module for your UI modules, e.g., ``uimodules.py``:
+
+::
+
+    class Entry(tornado.web.UIModule):
+        def render(self, entry, show_comments=False):
+            return self.render_string(
+                "module-entry.html", entry=entry, show_comments=show_comments)
+
+Tell Tornado to use ``uimodules.py`` using the ``ui_modules`` setting in
+your application:
+
+::
+
+    class HomeHandler(tornado.web.RequestHandler):
+        def get(self):
+            entries = self.db.query("SELECT * FROM entries ORDER BY date DESC")
+            self.render("home.html", entries=entries)
+
+    class EntryHandler(tornado.web.RequestHandler):
+        def get(self, entry_id):
+            entry = self.db.get("SELECT * FROM entries WHERE id = %s", entry_id)
+            if not entry: raise tornado.web.HTTPError(404)
+            self.render("entry.html", entry=entry)
+
+    settings = {
+        "ui_modules": uimodules,
+    }
+    application = tornado.web.Application([
+        (r"/", HomeHandler),
+        (r"/entry/([0-9]+)", EntryHandler),
+    ], **settings)
+
+Within ``home.html``, you reference the ``Entry`` module rather than
+printing the HTML directly:
+
+::
+
+    {% for entry in entries %}
+      {% module Entry(entry) %}
+    {% end %}
+
+Within ``entry.html``, you reference the ``Entry`` module with the
+``show_comments`` argument to show the expanded form of the entry:
+
+::
+
+    {% module Entry(entry, show_comments=True) %}
+
+Modules can include custom CSS and JavaScript functions by overriding
+the ``embedded_css``, ``embedded_javascript``, ``javascript_files``, or
+``css_files`` methods:
+
+::
+
+    class Entry(tornado.web.UIModule):
+        def embedded_css(self):
+            return ".entry { margin-bottom: 1em; }"
+
+        def render(self, entry, show_comments=False):
+            return self.render_string(
+                "module-entry.html", show_comments=show_comments)
+
+Module CSS and JavaScript will be included once no matter how many times
+a module is used on a page. CSS is always included in the ``<head>`` of
+the page, and JavaScript is always included just before the ``</body>``
+tag at the end of the page.
+
+When additional Python code is not required, a template file itself may
+be used as a module. For example, the preceding example could be
+rewritten to put the following in ``module-entry.html``:
+
+::
+
+    {{ set_resources(embedded_css=".entry { margin-bottom: 1em; }") }}
+    <!-- more template html... -->
+
+This revised template module would be invoked with
+
+::
+
+    {% module Template("module-entry.html", show_comments=True) %}
+
+The ``set_resources`` function is only available in templates invoked
+via ``{% module Template(...) %}``. Unlike the ``{% include ... %}``
+directive, template modules have a distinct namespace from their
+containing template - they can only see the global template namespace
+and their own keyword arguments.
diff --git a/docs/overview.rst b/docs/overview.rst
deleted file mode 100644 (file)
index 77d930a..0000000
+++ /dev/null
@@ -1,1134 +0,0 @@
-.. currentmodule:: tornado.web
-
-Overview
-========
-
-`FriendFeed's <http://friendfeed.com/>`_ web server is a relatively
-simple, non-blocking web server written in Python. The FriendFeed
-application is written using a web framework that looks a bit like
-`web.py <http://webpy.org/>`_ or Google's
-`webapp <http://code.google.com/appengine/docs/python/tools/webapp/>`_,
-but with additional tools and optimizations to take advantage of the
-non-blocking web server and tools.
-
-`Tornado <https://github.com/tornadoweb/tornado>`_ is an open source
-version of this web server and some of the tools we use most often at
-FriendFeed. 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
-`epoll
-<http://www.kernel.org/doc/man-pages/online/pages/man4/epoll.4.html>`_
-or kqueue, it can handle thousands of simultaneous standing
-connections, which means the framework is ideal for real-time web
-services. We built the web server specifically to handle FriendFeed's
-real-time features — 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 C10K problem
-<http://www.kegel.com/c10k.html>`_.)
-
-Here is the canonical "Hello, world" example app:
-
-::
-
-    import tornado.ioloop
-    import tornado.web
-
-    class MainHandler(tornado.web.RequestHandler):
-        def get(self):
-            self.write("Hello, world")
-
-    application = tornado.web.Application([
-        (r"/", MainHandler),
-    ])
-
-    if __name__ == "__main__":
-        application.listen(8888)
-        tornado.ioloop.IOLoop.instance().start()
-
-We attempted to clean up the code base to reduce interdependencies
-between modules, so you should (theoretically) be able to use any of the
-modules independently in your project without using the whole package.
-
-Request handlers and request arguments
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-A Tornado web application maps URLs or URL patterns to subclasses of
-`tornado.web.RequestHandler`. Those classes define ``get()`` or
-``post()`` methods to handle HTTP ``GET`` or ``POST`` requests to that
-URL.
-
-This code maps the root URL ``/`` to ``MainHandler`` and the URL pattern
-``/story/([0-9]+)`` to ``StoryHandler``. Regular expression groups are
-passed as arguments to the ``RequestHandler`` methods:
-
-::
-
-    class MainHandler(tornado.web.RequestHandler):
-        def get(self):
-            self.write("You requested the main page")
-
-    class StoryHandler(tornado.web.RequestHandler):
-        def get(self, story_id):
-            self.write("You requested the story " + story_id)
-
-    application = tornado.web.Application([
-        (r"/", MainHandler),
-        (r"/story/([0-9]+)", StoryHandler),
-    ])
-
-You can get query string arguments and parse ``POST`` bodies with the
-``get_argument()`` method:
-
-::
-
-    class MyFormHandler(tornado.web.RequestHandler):
-        def get(self):
-            self.write('<html><body><form action="/myform" method="post">'
-                       '<input type="text" name="message">'
-                       '<input type="submit" value="Submit">'
-                       '</form></body></html>')
-
-        def post(self):
-            self.set_header("Content-Type", "text/plain")
-            self.write("You wrote " + self.get_argument("message"))
-
-Uploaded files are available in ``self.request.files``, which maps names
-(the name of the HTML ``<input type="file">`` element) to a list of
-files. Each file is a dictionary of the form
-``{"filename":..., "content_type":..., "body":...}``.
-
-If you want to send an error response to the client, e.g., 403
-Unauthorized, you can just raise a ``tornado.web.HTTPError`` exception:
-
-::
-
-    if not self.user_is_logged_in():
-        raise tornado.web.HTTPError(403)
-
-The request handler can access the object representing the current
-request with ``self.request``. The ``HTTPRequest`` object includes a
-number of useful attributes, including:
-
--  ``arguments`` - all of the ``GET`` and ``POST`` arguments
--  ``files`` - all of the uploaded files (via ``multipart/form-data``
-   POST requests)
--  ``path`` - the request path (everything before the ``?``)
--  ``headers`` - the request headers
-
-See the class definition for `tornado.httputil.HTTPServerRequest` for a
-complete list of attributes.
-
-Overriding RequestHandler methods
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-In addition to ``get()``/``post()``/etc, certain other methods in
-``RequestHandler`` are designed to be overridden by subclasses when
-necessary. On every request, the following sequence of calls takes
-place:
-
-1. A new RequestHandler object is created on each request
-2. ``initialize()`` is called with keyword arguments from the
-   ``Application`` configuration. (the ``initialize`` method is new in
-   Tornado 1.1; in older versions subclasses would override ``__init__``
-   instead). ``initialize`` should typically just save the arguments
-   passed into member variables; it may not produce any output or call
-   methods like ``send_error``.
-3. ``prepare()`` is called. This is most useful in a base class shared
-   by all of your handler subclasses, as ``prepare`` is called no matter
-   which HTTP method is used. ``prepare`` may produce output; if it
-   calls ``finish`` (or ``send_error``, etc), processing stops here.
-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:
-
-::
-
-    class ProfileHandler(RequestHandler):
-        def initialize(self, database):
-            self.database = database
-
-        def get(self, username):
-            ...
-
-    app = Application([
-        (r'/user/(.*)', ProfileHandler, dict(database=database)),
-        ])
-
-Other methods designed for overriding include:
-
--  ``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
-   current user
--  ``get_login_url(self)`` - returns login url to be used by the
-   ``@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`).
-
-Redirection
-~~~~~~~~~~~
-
-There are two main ways you can redirect requests in Tornado:
-``self.redirect`` and with the ``RedirectHandler``.
-
-You can use ``self.redirect`` within a ``RequestHandler`` method (like
-``get``) to redirect users elsewhere. There is also an optional
-parameter ``permanent`` which you can use to indicate that the
-redirection is considered permanent.
-
-This triggers a ``301 Moved Permanently`` HTTP status, which is useful
-for e.g. redirecting to a canonical URL for a page in an SEO-friendly
-manner.
-
-The default value of ``permanent`` is ``False``, which is apt for things
-like redirecting users on successful POST requests.
-
-::
-
-    self.redirect('/some-canonical-page', permanent=True)
-
-``RedirectHandler`` is available for your use when you initialize
-``Application``.
-
-For example, notice how we redirect to a longer download URL on this
-website:
-
-::
-
-    application = tornado.wsgi.WSGIApplication([
-        (r"/([a-z]*)", ContentHandler),
-        (r"/static/tornado-0.2.tar.gz", tornado.web.RedirectHandler,
-         dict(url="https://github.com/downloads/facebook/tornado/tornado-0.2.tar.gz")),
-    ], **settings)
-
-The default ``RedirectHandler`` status code is
-``301 Moved Permanently``, but to use ``302 Found`` instead, set
-``permanent`` to ``False``.
-
-::
-
-    application = tornado.wsgi.WSGIApplication([
-        (r"/foo", tornado.web.RedirectHandler, {"url":"/bar", "permanent":False}),
-    ], **settings)
-
-Note that the default value of ``permanent`` is different in
-``self.redirect`` than in ``RedirectHandler``. This should make some
-sense if you consider that ``self.redirect`` is used in your methods and
-is probably invoked by logic involving environment, authentication, or
-form submission, but ``RedirectHandler`` patterns are going to fire 100%
-of the time they match the request URL.
-
-Templates
-~~~~~~~~~
-
-You can use any template language supported by Python, but Tornado ships
-with its own templating language that is a lot faster and more flexible
-than many of the most popular templating systems out there. See the
-`tornado.template` module documentation for complete documentation.
-
-A Tornado template is just HTML (or any other text-based format) with
-Python control sequences and expressions embedded within the markup:
-
-::
-
-    <html>
-       <head>
-          <title>{{ title }}</title>
-       </head>
-       <body>
-         <ul>
-           {% for item in items %}
-             <li>{{ escape(item) }}</li>
-           {% end %}
-         </ul>
-       </body>
-     </html>
-
-If you saved this template as "template.html" and put it in the same
-directory as your Python file, you could render this template with:
-
-::
-
-    class MainHandler(tornado.web.RequestHandler):
-        def get(self):
-            items = ["Item 1", "Item 2", "Item 3"]
-            self.render("template.html", title="My title", items=items)
-
-Tornado templates support *control statements* and *expressions*.
-Control statements are surrounded by ``{%`` and ``%}``, e.g.,
-``{% if len(items) > 2 %}``. Expressions are surrounded by ``{{`` and
-``}}``, e.g., ``{{ items[0] }}``.
-
-Control statements more or less map exactly to Python statements. We
-support ``if``, ``for``, ``while``, and ``try``, all of which are
-terminated with ``{% end %}``. We also support *template inheritance*
-using the ``extends`` and ``block`` statements, which are described in
-detail in the documentation for the `tornado.template`.
-
-Expressions can be any Python expression, including function calls.
-Template code is executed in a namespace that includes the following
-objects and functions (Note that this list applies to templates rendered
-using ``RequestHandler.render`` and ``render_string``. If you're using
-the ``template`` module directly outside of a ``RequestHandler`` many of
-these entries are not present).
-
--  ``escape``: alias for ``tornado.escape.xhtml_escape``
--  ``xhtml_escape``: alias for ``tornado.escape.xhtml_escape``
--  ``url_escape``: alias for ``tornado.escape.url_escape``
--  ``json_encode``: alias for ``tornado.escape.json_encode``
--  ``squeeze``: alias for ``tornado.escape.squeeze``
--  ``linkify``: alias for ``tornado.escape.linkify``
--  ``datetime``: the Python ``datetime`` module
--  ``handler``: the current ``RequestHandler`` object
--  ``request``: alias for ``handler.request``
--  ``current_user``: alias for ``handler.current_user``
--  ``locale``: alias for ``handler.locale``
--  ``_``: alias for ``handler.locale.translate``
--  ``static_url``: alias for ``handler.static_url``
--  ``xsrf_form_html``: alias for ``handler.xsrf_form_html``
--  ``reverse_url``: alias for ``Application.reverse_url``
--  All entries from the ``ui_methods`` and ``ui_modules``
-   ``Application`` settings
--  Any keyword arguments passed to ``render`` or ``render_string``
-
-When you are building a real application, you are going to want to use
-all of the features of Tornado templates, especially template
-inheritance. Read all about those features in the `tornado.template`
-section (some features, including ``UIModules`` are implemented in the
-``web`` module)
-
-Under the hood, Tornado templates are translated directly to Python. The
-expressions you include in your template are copied verbatim into a
-Python function representing your template. We don't try to prevent
-anything in the template language; we created it explicitly to provide
-the flexibility that other, stricter templating systems prevent.
-Consequently, if you write random stuff inside of your template
-expressions, you will get random Python errors when you execute the
-template.
-
-All template output is escaped by default, using the
-``tornado.escape.xhtml_escape`` function. This behavior can be changed
-globally by passing ``autoescape=None`` to the ``Application`` or
-``TemplateLoader`` constructors, for a template file with the
-``{% autoescape None %}`` directive, or for a single expression by
-replacing ``{{ ... }}`` with ``{% raw ...%}``. Additionally, in each of
-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
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-You can set cookies in the user's browser with the ``set_cookie``
-method:
-
-::
-
-    class MainHandler(tornado.web.RequestHandler):
-        def get(self):
-            if not self.get_cookie("mycookie"):
-                self.set_cookie("mycookie", "myvalue")
-                self.write("Your cookie was not set yet!")
-            else:
-                self.write("Your cookie was set!")
-
-Cookies are easily forged by malicious clients. If you need to set
-cookies to, e.g., save the user ID of the currently logged in user, you
-need to sign your cookies to prevent forgery. Tornado supports this out
-of the box with the ``set_secure_cookie`` and ``get_secure_cookie``
-methods. To use these methods, you need to specify a secret key named
-``cookie_secret`` when you create your application. You can pass in
-application settings as keyword arguments to your application:
-
-::
-
-    application = tornado.web.Application([
-        (r"/", MainHandler),
-    ], cookie_secret="__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__")
-
-Signed cookies contain the encoded value of the cookie in addition to a
-timestamp and an `HMAC <http://en.wikipedia.org/wiki/HMAC>`_ signature.
-If the cookie is old or if the signature doesn't match,
-``get_secure_cookie`` will return ``None`` just as if the cookie isn't
-set. The secure version of the example above:
-
-::
-
-    class MainHandler(tornado.web.RequestHandler):
-        def get(self):
-            if not self.get_secure_cookie("mycookie"):
-                self.set_secure_cookie("mycookie", "myvalue")
-                self.write("Your cookie was not set yet!")
-            else:
-                self.write("Your cookie was set!")
-
-User authentication
-~~~~~~~~~~~~~~~~~~~
-
-The currently authenticated user is available in every request handler
-as ``self.current_user``, and in every template as ``current_user``. By
-default, ``current_user`` is ``None``.
-
-To implement user authentication in your application, you need to
-override the ``get_current_user()`` method in your request handlers to
-determine the current user based on, e.g., the value of a cookie. Here
-is an example that lets users log into the application simply by
-specifying a nickname, which is then saved in a cookie:
-
-::
-
-    class BaseHandler(tornado.web.RequestHandler):
-        def get_current_user(self):
-            return self.get_secure_cookie("user")
-
-    class MainHandler(BaseHandler):
-        def get(self):
-            if not self.current_user:
-                self.redirect("/login")
-                return
-            name = tornado.escape.xhtml_escape(self.current_user)
-            self.write("Hello, " + name)
-
-    class LoginHandler(BaseHandler):
-        def get(self):
-            self.write('<html><body><form action="/login" method="post">'
-                       'Name: <input type="text" name="name">'
-                       '<input type="submit" value="Sign in">'
-                       '</form></body></html>')
-
-        def post(self):
-            self.set_secure_cookie("user", self.get_argument("name"))
-            self.redirect("/")
-
-    application = tornado.web.Application([
-        (r"/", MainHandler),
-        (r"/login", LoginHandler),
-    ], cookie_secret="__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__")
-
-You can require that the user be logged in using the `Python
-decorator <http://www.python.org/dev/peps/pep-0318/>`_
-``tornado.web.authenticated``. If a request goes to a method with this
-decorator, and the user is not logged in, they will be redirected to
-``login_url`` (another application setting). The example above could be
-rewritten:
-
-::
-
-    class MainHandler(BaseHandler):
-        @tornado.web.authenticated
-        def get(self):
-            name = tornado.escape.xhtml_escape(self.current_user)
-            self.write("Hello, " + name)
-
-    settings = {
-        "cookie_secret": "__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__",
-        "login_url": "/login",
-    }
-    application = tornado.web.Application([
-        (r"/", MainHandler),
-        (r"/login", LoginHandler),
-    ], **settings)
-
-If you decorate ``post()`` methods with the ``authenticated`` decorator,
-and the user is not logged in, the server will send a ``403`` response.
-
-Tornado comes with built-in support for third-party authentication
-schemes like Google OAuth. See the `tornado.auth`
-for more details. Check out the `Tornado Blog example application <https://github.com/tornadoweb/tornado/tree/stable/demos/blog>`_ for a
-complete example that uses authentication (and stores user data in a
-MySQL database).
-
-.. _xsrf:
-
-Cross-site request forgery protection
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-`Cross-site request
-forgery <http://en.wikipedia.org/wiki/Cross-site_request_forgery>`_, or
-XSRF, is a common problem for personalized web applications. See the
-`Wikipedia
-article <http://en.wikipedia.org/wiki/Cross-site_request_forgery>`_ for
-more information on how XSRF works.
-
-The generally accepted solution to prevent XSRF is to cookie every user
-with an unpredictable value and include that value as an additional
-argument with every form submission on your site. If the cookie and the
-value in the form submission do not match, then the request is likely
-forged.
-
-Tornado comes with built-in XSRF protection. To include it in your site,
-include the application setting ``xsrf_cookies``:
-
-::
-
-    settings = {
-        "cookie_secret": "__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__",
-        "login_url": "/login",
-        "xsrf_cookies": True,
-    }
-    application = tornado.web.Application([
-        (r"/", MainHandler),
-        (r"/login", LoginHandler),
-    ], **settings)
-
-If ``xsrf_cookies`` is set, the Tornado web application will set the
-``_xsrf`` cookie for all users and reject all ``POST``, ``PUT``, and
-``DELETE`` requests that do not contain a correct ``_xsrf`` value. If
-you turn this setting on, you need to instrument all forms that submit
-via ``POST`` to contain this field. You can do this with the special
-function ``xsrf_form_html()``, available in all templates:
-
-::
-
-    <form action="/new_message" method="post">
-      {% module xsrf_form_html() %}
-      <input type="text" name="message"/>
-      <input type="submit" value="Post"/>
-    </form>
-
-If you submit AJAX ``POST`` requests, you will also need to instrument
-your JavaScript to include the ``_xsrf`` value with each request. This
-is the `jQuery <http://jquery.com/>`_ function we use at FriendFeed for
-AJAX ``POST`` requests that automatically adds the ``_xsrf`` value to
-all requests:
-
-::
-
-    function getCookie(name) {
-        var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
-        return r ? r[1] : undefined;
-    }
-
-    jQuery.postJSON = function(url, args, callback) {
-        args._xsrf = getCookie("_xsrf");
-        $.ajax({url: url, data: $.param(args), dataType: "text", type: "POST",
-            success: function(response) {
-            callback(eval("(" + response + ")"));
-        }});
-    };
-
-For ``PUT`` and ``DELETE`` requests (as well as ``POST`` requests that
-do not use form-encoded arguments), the XSRF token may also be passed
-via an HTTP header named ``X-XSRFToken``.  The XSRF cookie is normally
-set when ``xsrf_form_html`` is used, but in a pure-Javascript application
-that does not use any regular forms you may need to access
-``self.xsrf_token`` manually (just reading the property is enough to
-set the cookie as a side effect).
-
-If you need to customize XSRF behavior on a per-handler basis, you can
-override ``RequestHandler.check_xsrf_cookie()``. For example, if you
-have an API whose authentication does not use cookies, you may want to
-disable XSRF protection by making ``check_xsrf_cookie()`` do nothing.
-However, if you support both cookie and non-cookie-based authentication,
-it is important that XSRF protection be used whenever the current
-request is authenticated with a cookie.
-
-Static files and aggressive file caching
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-You can serve static files from Tornado by specifying the
-``static_path`` setting in your application:
-
-::
-
-    settings = {
-        "static_path": os.path.join(os.path.dirname(__file__), "static"),
-        "cookie_secret": "__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__",
-        "login_url": "/login",
-        "xsrf_cookies": True,
-    }
-    application = tornado.web.Application([
-        (r"/", MainHandler),
-        (r"/login", LoginHandler),
-        (r"/(apple-touch-icon\.png)", tornado.web.StaticFileHandler,
-         dict(path=settings['static_path'])),
-    ], **settings)
-
-This setting will automatically make all requests that start with
-``/static/`` serve from that static directory, e.g.,
-`http://localhost:8888/static/foo.png <http://localhost:8888/static/foo.png>`_
-will serve the file ``foo.png`` from the specified static directory. We
-also automatically serve ``/robots.txt`` and ``/favicon.ico`` from the
-static directory (even though they don't start with the ``/static/``
-prefix).
-
-In the above settings, we have explicitly configured Tornado to serve
-``apple-touch-icon.png`` “from” the root with the ``StaticFileHandler``,
-though it is physically in the static file directory. (The capturing
-group in that regular expression is necessary to tell
-``StaticFileHandler`` the requested filename; capturing groups are
-passed to handlers as method arguments.) You could do the same thing to
-serve e.g. ``sitemap.xml`` from the site root. Of course, you can also
-avoid faking a root ``apple-touch-icon.png`` by using the appropriate
-``<link />`` tag in your HTML.
-
-To improve performance, it is generally a good idea for browsers to
-cache static resources aggressively so browsers won't send unnecessary
-``If-Modified-Since`` or ``Etag`` requests that might block the
-rendering of the page. Tornado supports this out of the box with *static
-content versioning*.
-
-To use this feature, use the ``static_url()`` method in your templates
-rather than typing the URL of the static file directly in your HTML:
-
-::
-
-    <html>
-       <head>
-          <title>FriendFeed - {{ _("Home") }}</title>
-       </head>
-       <body>
-         <div><img src="{{ static_url("images/logo.png") }}"/></div>
-       </body>
-     </html>
-
-The ``static_url()`` function will translate that relative path to a URI
-that looks like ``/static/images/logo.png?v=aae54``. The ``v`` argument
-is a hash of the content in ``logo.png``, and its presence makes the
-Tornado server send cache headers to the user's browser that will make
-the browser cache the content indefinitely.
-
-Since the ``v`` argument is based on the content of the file, if you
-update a file and restart your server, it will start sending a new ``v``
-value, so the user's browser will automatically fetch the new file. If
-the file's contents don't change, the browser will continue to use a
-locally cached copy without ever checking for updates on the server,
-significantly improving rendering performance.
-
-In production, you probably want to serve static files from a more
-optimized static file server like `nginx <http://nginx.net/>`_. You can
-configure most any web server to support these caching semantics. Here
-is the nginx configuration we use at FriendFeed:
-
-::
-
-    location /static/ {
-        root /var/friendfeed/static;
-        if ($query_string) {
-            expires max;
-        }
-     }
-
-Localization
-~~~~~~~~~~~~
-
-The locale of the current user (whether they are logged in or not) is
-always available as ``self.locale`` in the request handler and as
-``locale`` in templates. The name of the locale (e.g., ``en_US``) is
-available as ``locale.name``, and you can translate strings with the
-``locale.translate`` method. Templates also have the global function
-call ``_()`` available for string translation. The translate function
-has two forms:
-
-::
-
-    _("Translate this string")
-
-which translates the string directly based on the current locale, and
-
-::
-
-    _("A person liked this", "%(num)d people liked this",
-      len(people)) % {"num": len(people)}
-
-which translates a string that can be singular or plural based on the
-value of the third argument. In the example above, a translation of the
-first string will be returned if ``len(people)`` is ``1``, or a
-translation of the second string will be returned otherwise.
-
-The most common pattern for translations is to use Python named
-placeholders for variables (the ``%(num)d`` in the example above) since
-placeholders can move around on translation.
-
-Here is a properly localized template:
-
-::
-
-    <html>
-       <head>
-          <title>FriendFeed - {{ _("Sign in") }}</title>
-       </head>
-       <body>
-         <form action="{{ request.path }}" method="post">
-           <div>{{ _("Username") }} <input type="text" name="username"/></div>
-           <div>{{ _("Password") }} <input type="password" name="password"/></div>
-           <div><input type="submit" value="{{ _("Sign in") }}"/></div>
-           {% module xsrf_form_html() %}
-         </form>
-       </body>
-     </html>
-
-By default, we detect the user's locale using the ``Accept-Language``
-header sent by the user's browser. We choose ``en_US`` if we can't find
-an appropriate ``Accept-Language`` value. If you let user's set their
-locale as a preference, you can override this default locale selection
-by overriding ``get_user_locale`` in your request handler:
-
-::
-
-    class BaseHandler(tornado.web.RequestHandler):
-        def get_current_user(self):
-            user_id = self.get_secure_cookie("user")
-            if not user_id: return None
-            return self.backend.get_user_by_id(user_id)
-
-        def get_user_locale(self):
-            if "locale" not in self.current_user.prefs:
-                # Use the Accept-Language header
-                return None
-            return self.current_user.prefs["locale"]
-
-If ``get_user_locale`` returns ``None``, we fall back on the
-``Accept-Language`` header.
-
-You can load all the translations for your application using the
-``tornado.locale.load_translations`` method. It takes in the name of the
-directory which should contain CSV files named after the locales whose
-translations they contain, e.g., ``es_GT.csv`` or ``fr_CA.csv``. The
-method loads all the translations from those CSV files and infers the
-list of supported locales based on the presence of each CSV file. You
-typically call this method once in the ``main()`` method of your server:
-
-::
-
-    def main():
-        tornado.locale.load_translations(
-            os.path.join(os.path.dirname(__file__), "translations"))
-        start_server()
-
-You can get the list of supported locales in your application with
-``tornado.locale.get_supported_locales()``. The user's locale is chosen
-to be the closest match based on the supported locales. For example, if
-the user's locale is ``es_GT``, and the ``es`` locale is supported,
-``self.locale`` will be ``es`` for that request. We fall back on
-``en_US`` if no close match can be found.
-
-See the `tornado.locale`
-documentation for detailed information on the CSV format and other
-localization methods.
-
-.. _ui-modules:
-
-UI modules
-~~~~~~~~~~
-
-Tornado supports *UI modules* to make it easy to support standard,
-reusable UI widgets across your application. UI modules are like special
-functional calls to render components of your page, and they can come
-packaged with their own CSS and JavaScript.
-
-For example, if you are implementing a blog, and you want to have blog
-entries appear on both the blog home page and on each blog entry page,
-you can make an ``Entry`` module to render them on both pages. First,
-create a Python module for your UI modules, e.g., ``uimodules.py``:
-
-::
-
-    class Entry(tornado.web.UIModule):
-        def render(self, entry, show_comments=False):
-            return self.render_string(
-                "module-entry.html", entry=entry, show_comments=show_comments)
-
-Tell Tornado to use ``uimodules.py`` using the ``ui_modules`` setting in
-your application:
-
-::
-
-    class HomeHandler(tornado.web.RequestHandler):
-        def get(self):
-            entries = self.db.query("SELECT * FROM entries ORDER BY date DESC")
-            self.render("home.html", entries=entries)
-
-    class EntryHandler(tornado.web.RequestHandler):
-        def get(self, entry_id):
-            entry = self.db.get("SELECT * FROM entries WHERE id = %s", entry_id)
-            if not entry: raise tornado.web.HTTPError(404)
-            self.render("entry.html", entry=entry)
-
-    settings = {
-        "ui_modules": uimodules,
-    }
-    application = tornado.web.Application([
-        (r"/", HomeHandler),
-        (r"/entry/([0-9]+)", EntryHandler),
-    ], **settings)
-
-Within ``home.html``, you reference the ``Entry`` module rather than
-printing the HTML directly:
-
-::
-
-    {% for entry in entries %}
-      {% module Entry(entry) %}
-    {% end %}
-
-Within ``entry.html``, you reference the ``Entry`` module with the
-``show_comments`` argument to show the expanded form of the entry:
-
-::
-
-    {% module Entry(entry, show_comments=True) %}
-
-Modules can include custom CSS and JavaScript functions by overriding
-the ``embedded_css``, ``embedded_javascript``, ``javascript_files``, or
-``css_files`` methods:
-
-::
-
-    class Entry(tornado.web.UIModule):
-        def embedded_css(self):
-            return ".entry { margin-bottom: 1em; }"
-
-        def render(self, entry, show_comments=False):
-            return self.render_string(
-                "module-entry.html", show_comments=show_comments)
-
-Module CSS and JavaScript will be included once no matter how many times
-a module is used on a page. CSS is always included in the ``<head>`` of
-the page, and JavaScript is always included just before the ``</body>``
-tag at the end of the page.
-
-When additional Python code is not required, a template file itself may
-be used as a module. For example, the preceding example could be
-rewritten to put the following in ``module-entry.html``:
-
-::
-
-    {{ set_resources(embedded_css=".entry { margin-bottom: 1em; }") }}
-    <!-- more template html... -->
-
-This revised template module would be invoked with
-
-::
-
-    {% module Template("module-entry.html", show_comments=True) %}
-
-The ``set_resources`` function is only available in templates invoked
-via ``{% module Template(...) %}``. Unlike the ``{% include ... %}``
-directive, template modules have a distinct namespace from their
-containing template - they can only see the global template namespace
-and their own keyword arguments.
-
-Non-blocking, asynchronous requests
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-When a request handler is executed, the request is automatically
-finished. Since Tornado uses a non-blocking I/O style, you can override
-this default behavior if you want a request to remain open after the
-main request handler method returns using the
-``tornado.web.asynchronous`` decorator.
-
-When you use this decorator, it is your responsibility to call
-``self.finish()`` to finish the HTTP request, or the user's browser will
-simply hang:
-
-::
-
-    class MainHandler(tornado.web.RequestHandler):
-        @tornado.web.asynchronous
-        def get(self):
-            self.write("Hello, world")
-            self.finish()
-
-Here is a real example that makes a call to the FriendFeed API using
-Tornado's built-in asynchronous HTTP client:
-
-::
-
-    class MainHandler(tornado.web.RequestHandler):
-        @tornado.web.asynchronous
-        def get(self):
-            http = tornado.httpclient.AsyncHTTPClient()
-            http.fetch("http://friendfeed-api.com/v2/feed/bret",
-                       callback=self.on_response)
-
-        def on_response(self, response):
-            if response.error: raise tornado.web.HTTPError(500)
-            json = tornado.escape.json_decode(response.body)
-            self.write("Fetched " + str(len(json["entries"])) + " entries "
-                       "from the FriendFeed API")
-            self.finish()
-
-When ``get()`` returns, the request has not finished. When the HTTP
-client eventually calls ``on_response()``, the request is still open,
-and the response is finally flushed to the client with the call to
-``self.finish()``.
-
-For a more advanced asynchronous example, take a look at the `chat
-example application
-<https://github.com/tornadoweb/tornado/tree/stable/demos/chat>`_, which
-implements an AJAX chat room using `long polling
-<http://en.wikipedia.org/wiki/Push_technology#Long_polling>`_.  Users
-of long polling may want to override ``on_connection_close()`` to
-clean up after the client closes the connection (but see that method's
-docstring for caveats).
-
-Asynchronous HTTP clients
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Tornado includes two non-blocking HTTP client implementations:
-``SimpleAsyncHTTPClient`` and ``CurlAsyncHTTPClient``. The simple client
-has no external dependencies because it is implemented directly on top
-of Tornado's ``IOLoop``. The Curl client requires that ``libcurl`` and
-``pycurl`` be installed (and a recent version of each is highly
-recommended to avoid bugs in older version's asynchronous interfaces),
-but is more likely to be compatible with sites that exercise little-used
-parts of the HTTP specification.
-
-Each of these clients is available in its own module
-(``tornado.simple_httpclient`` and ``tornado.curl_httpclient``), as well
-as via a configurable alias in ``tornado.httpclient``.
-``SimpleAsyncHTTPClient`` is the default, but to use a different
-implementation call the ``AsyncHTTPClient.configure`` method at startup:
-
-::
-
-    AsyncHTTPClient.configure('tornado.curl_httpclient.CurlAsyncHTTPClient')
-
-Third party authentication
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Tornado's ``auth`` module implements the authentication and
-authorization protocols for a number of the most popular sites on the
-web, including Google/Gmail, Facebook, Twitter, and FriendFeed.
-The module includes methods to log users in via these sites and, where
-applicable, methods to authorize access to the service so you can, e.g.,
-download a user's address book or publish a Twitter message on their
-behalf.
-
-Here is an example handler that uses Google for authentication, saving
-the Google credentials in a cookie for later access:
-
-::
-
-    class GoogleHandler(tornado.web.RequestHandler, tornado.auth.GoogleMixin):
-        @tornado.web.asynchronous
-        def get(self):
-            if self.get_argument("openid.mode", None):
-                self.get_authenticated_user(self._on_auth)
-                return
-            self.authenticate_redirect()
-
-        def _on_auth(self, user):
-            if not user:
-                self.authenticate_redirect()
-                return
-            # Save the user with, e.g., set_secure_cookie()
-
-See the `tornado.auth` module documentation for more details.
-
-.. _debug-mode:
-
-Debug mode and automatic reloading
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-If you pass ``debug=True`` to the ``Application`` constructor, the app
-will be run in debug/development mode. In this mode, several features
-intended for convenience while developing will be enabled (each of which
-is also available as an individual flag; if both are specified the
-individual flag takes precedence):
-
-* ``autoreload=True``: The app will watch for changes to its source
-  files and reload itself when anything changes. This reduces the need
-  to manually restart the server during development. However, certain
-  failures (such as syntax errors at import time) can still take the
-  server down in a way that debug mode cannot currently recover from.
-* ``compiled_template_cache=False``: Templates will not be cached.
-* ``static_hash_cache=False``: Static file hashes (used by the
-  ``static_url`` function) will not be cached
-* ``serve_traceback=True``: When an exception in a ``RequestHandler``
-  is not caught, an error page including a stack trace will be
-  generated.
-
-Autoreload mode is not compatible with the multi-process mode of ``HTTPServer``.
-You must not give ``HTTPServer.start`` an argument other than 1 (or
-call `tornado.process.fork_processes`) if you are using autoreload mode.
-
-The automatic reloading feature of debug mode is available as a
-standalone module in ``tornado.autoreload``.  The two can be used in
-combination to provide extra robustness against syntax errors: set
-``autoreload=True`` within the app to detect changes while it is running,
-and start it with ``python -m tornado.autoreload myserver.py`` to catch
-any syntax errors or other errors at startup.
-
-Reloading loses any Python interpreter command-line arguments (e.g. ``-u``)
-because it re-executes Python using ``sys.executable`` and ``sys.argv``.
-Additionally, modifying these variables will cause reloading to behave
-incorrectly.
-
-On some platforms (including Windows and Mac OSX prior to 10.6), the
-process cannot be updated "in-place", so when a code change is
-detected the old server exits and a new one starts.  This has been
-known to confuse some IDEs.
-
-
-Running Tornado in production
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-At FriendFeed, we use `nginx <http://nginx.net/>`_ as a load balancer
-and static file server. We run multiple instances of the Tornado web
-server on multiple frontend machines. We typically run one Tornado
-frontend per core on the machine (sometimes more depending on
-utilization).
-
-When running behind a load balancer like nginx, it is recommended to
-pass ``xheaders=True`` to the ``HTTPServer`` constructor. This will tell
-Tornado to use headers like ``X-Real-IP`` to get the user's IP address
-instead of attributing all traffic to the balancer's IP address.
-
-This is a barebones nginx config file that is structurally similar to
-the one we use at FriendFeed. It assumes nginx and the Tornado servers
-are running on the same machine, and the four Tornado servers are
-running on ports 8000 - 8003:
-
-::
-
-    user nginx;
-    worker_processes 1;
-
-    error_log /var/log/nginx/error.log;
-    pid /var/run/nginx.pid;
-
-    events {
-        worker_connections 1024;
-        use epoll;
-    }
-
-    http {
-        # Enumerate all the Tornado servers here
-        upstream frontends {
-            server 127.0.0.1:8000;
-            server 127.0.0.1:8001;
-            server 127.0.0.1:8002;
-            server 127.0.0.1:8003;
-        }
-
-        include /etc/nginx/mime.types;
-        default_type application/octet-stream;
-
-        access_log /var/log/nginx/access.log;
-
-        keepalive_timeout 65;
-        proxy_read_timeout 200;
-        sendfile on;
-        tcp_nopush on;
-        tcp_nodelay on;
-        gzip on;
-        gzip_min_length 1000;
-        gzip_proxied any;
-        gzip_types text/plain text/html text/css text/xml
-                   application/x-javascript application/xml
-                   application/atom+xml text/javascript;
-
-        # Only retry if there was a communication error, not a timeout
-        # on the Tornado server (to avoid propagating "queries of death"
-        # to all frontends)
-        proxy_next_upstream error;
-
-        server {
-            listen 80;
-
-            # Allow file uploads
-            client_max_body_size 50M;
-
-            location ^~ /static/ {
-                root /var/www;
-                if ($query_string) {
-                    expires max;
-                }
-            }
-            location = /favicon.ico {
-                rewrite (.*) /static/favicon.ico;
-            }
-            location = /robots.txt {
-                rewrite (.*) /static/robots.txt;
-            }
-
-            location / {
-                proxy_pass_header Server;
-                proxy_set_header Host $http_host;
-                proxy_redirect off;
-                proxy_set_header X-Real-IP $remote_addr;
-                proxy_set_header X-Scheme $scheme;
-                proxy_pass http://frontends;
-            }
-        }
-    }
-
-WSGI and Google AppEngine
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Tornado comes with limited support for `WSGI <http://wsgi.org/>`_.
-However, since WSGI does not support non-blocking requests, you cannot
-use any of the asynchronous/non-blocking features of Tornado in your
-application if you choose to use WSGI instead of Tornado's HTTP server.
-Some of the features that are not available in WSGI applications:
-``@tornado.web.asynchronous``, the ``httpclient`` module, and the
-``auth`` module.
-
-You can create a valid WSGI application from your Tornado request
-handlers by using ``WSGIApplication`` in the ``wsgi`` module instead of
-using ``tornado.web.Application``. Here is an example that uses the
-built-in WSGI ``CGIHandler`` to make a valid `Google
-AppEngine <http://code.google.com/appengine/>`_ application:
-
-::
-
-    import tornado.web
-    import tornado.wsgi
-    import wsgiref.handlers
-
-    class MainHandler(tornado.web.RequestHandler):
-        def get(self):
-            self.write("Hello, world")
-
-    if __name__ == "__main__":
-        application = tornado.wsgi.WSGIApplication([
-            (r"/", MainHandler),
-        ])
-        wsgiref.handlers.CGIHandler().run(application)
-
-See the `appengine example application
-<https://github.com/tornadoweb/tornado/tree/stable/demos/appengine>`_ for a
-full-featured AppEngine app built on Tornado.