]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
Add new exception tornado.web.Finish to quietly end a request.
authorBen Darnell <ben@bendarnell.com>
Thu, 19 Jun 2014 13:28:45 +0000 (09:28 -0400)
committerBen Darnell <ben@bendarnell.com>
Thu, 19 Jun 2014 13:51:53 +0000 (09:51 -0400)
This allows error pages to be generated inline with the main code
instead of in write_error and is friendlier to generating error pages
from library code.

Closes #1064.

docs/web.rst
tornado/test/web_test.py
tornado/web.py

index 889b42995644fc1a6038f34ef6c4760a2af86158..277126d6e80a6344467ae421426272c9d93af49a 100644 (file)
    Everything else
    ---------------
    .. autoexception:: HTTPError
+   .. autoexception:: Finish
    .. autoexception:: MissingArgumentError
    .. autoclass:: UIModule
       :members:
index cbb62b9bba8aa0d1c58400306b254aeed278ad23..b32a032651f0f724424a008b8115c52f6a72bc5b 100644 (file)
@@ -10,7 +10,7 @@ from tornado.template import DictLoader
 from tornado.testing import AsyncHTTPTestCase, ExpectLog, gen_test
 from tornado.test.util import unittest
 from tornado.util import u, bytes_type, ObjectDict, unicode_type
-from tornado.web import RequestHandler, authenticated, Application, asynchronous, url, HTTPError, StaticFileHandler, _create_signature_v1, create_signed_value, decode_signed_value, ErrorHandler, UIModule, MissingArgumentError, stream_request_body
+from tornado.web import RequestHandler, authenticated, Application, asynchronous, url, HTTPError, StaticFileHandler, _create_signature_v1, create_signed_value, decode_signed_value, ErrorHandler, UIModule, MissingArgumentError, stream_request_body, Finish
 
 import binascii
 import contextlib
@@ -2307,3 +2307,20 @@ class XSRFTest(SimpleHandlerTestCase):
                 body=urllib_parse.urlencode(dict(_xsrf=body_token)),
                 headers=self.cookie_headers(cookie_token))
             self.assertEqual(response.code, 200)
+
+
+@wsgi_safe
+class FinishExceptionTest(SimpleHandlerTestCase):
+    class Handler(RequestHandler):
+        def get(self):
+            self.set_status(401)
+            self.set_header('WWW-Authenticate', 'Basic realm="something"')
+            self.write('authentication required')
+            raise Finish()
+
+    def test_finish_exception(self):
+        response = self.fetch('/')
+        self.assertEqual(response.code, 401)
+        self.assertEqual('Basic realm="something"',
+                         response.headers.get('WWW-Authenticate'))
+        self.assertEqual(b'authentication required', response.body)
index 209b7ecd02e5b25433d297d2021627bdfc9d418f..d28531e225a94328b7d53489cda89822c0e59235 100644 (file)
@@ -1384,6 +1384,11 @@ class RequestHandler(object):
             " (" + self.request.remote_ip + ")"
 
     def _handle_request_exception(self, e):
+        if isinstance(e, Finish):
+            # Not an error; just finish the request without logging.
+            if not self._finished:
+                self.finish()
+            return
         self.log_exception(*sys.exc_info())
         if self._finished:
             # Extra errors after the request has been finished should
@@ -1938,6 +1943,9 @@ class HTTPError(Exception):
     `RequestHandler.send_error` since it automatically ends the
     current function.
 
+    To customize the response sent with an `HTTPError`, override
+    `RequestHandler.write_error`.
+
     :arg int status_code: HTTP status code.  Must be listed in
         `httplib.responses <http.client.responses>` unless the ``reason``
         keyword argument is given.
@@ -1966,6 +1974,25 @@ class HTTPError(Exception):
             return message
 
 
+class Finish(Exception):
+    """An exception that ends the request without producing an error response.
+
+    When `Finish` is raised in a `RequestHandler`, the request will end
+    (calling `RequestHandler.finish` if it hasn't already been called),
+    but the outgoing response will not be modified and the error-handling
+    methods (including `RequestHandler.write_error`) will not be called.
+
+    This can be a more convenient way to implement custom error pages
+    than overriding ``write_error`` (especially in library code)::
+
+        if self.current_user is None:
+            self.set_status(401)
+            self.set_header('WWW-Authenticate', 'Basic realm="something"')
+            raise Finish()
+    """
+    pass
+
+
 class MissingArgumentError(HTTPError):
     """Exception raised by `RequestHandler.get_argument`.