commit
e438075d22
|
@ -603,9 +603,12 @@ class HTTPError(Exception):
|
|||
"""
|
||||
def __init__(self, code, message=None, response=None):
|
||||
self.code = code
|
||||
message = message or httputil.responses.get(code, "Unknown")
|
||||
self.message = message or httputil.responses.get(code, "Unknown")
|
||||
self.response = response
|
||||
Exception.__init__(self, "HTTP %d: %s" % (self.code, message))
|
||||
super(HTTPError, self).__init__(code, message, response)
|
||||
|
||||
def __str__(self):
|
||||
return "HTTP %d: %s" % (self.code, self.message)
|
||||
|
||||
|
||||
class _RequestProxy(object):
|
||||
|
|
|
@ -914,7 +914,7 @@ class PollIOLoop(IOLoop):
|
|||
# with other threads, or waking logic will induce a race.
|
||||
with self._callback_lock:
|
||||
if self._closing:
|
||||
raise RuntimeError("IOLoop is closing")
|
||||
return
|
||||
list_empty = not self._callbacks
|
||||
self._callbacks.append(functools.partial(
|
||||
stack_context.wrap(callback), *args, **kwargs))
|
||||
|
@ -927,7 +927,7 @@ class PollIOLoop(IOLoop):
|
|||
self._waker.wake()
|
||||
else:
|
||||
if self._closing:
|
||||
raise RuntimeError("IOLoop is closing")
|
||||
return
|
||||
# If we're on the IOLoop's thread, we don't need the lock,
|
||||
# since we don't need to wake anyone, just add the
|
||||
# callback. Blindly insert into self._callbacks. This is
|
||||
|
|
|
@ -725,18 +725,22 @@ class BaseIOStream(object):
|
|||
to read (i.e. the read returns EWOULDBLOCK or equivalent). On
|
||||
error closes the socket and raises an exception.
|
||||
"""
|
||||
try:
|
||||
chunk = self.read_from_fd()
|
||||
except (socket.error, IOError, OSError) as e:
|
||||
# ssl.SSLError is a subclass of socket.error
|
||||
if self._is_connreset(e):
|
||||
# Treat ECONNRESET as a connection close rather than
|
||||
# an error to minimize log spam (the exception will
|
||||
# be available on self.error for apps that care).
|
||||
while True:
|
||||
try:
|
||||
chunk = self.read_from_fd()
|
||||
except (socket.error, IOError, OSError) as e:
|
||||
if errno_from_exception(e) == errno.EINTR:
|
||||
continue
|
||||
# ssl.SSLError is a subclass of socket.error
|
||||
if self._is_connreset(e):
|
||||
# Treat ECONNRESET as a connection close rather than
|
||||
# an error to minimize log spam (the exception will
|
||||
# be available on self.error for apps that care).
|
||||
self.close(exc_info=True)
|
||||
return
|
||||
self.close(exc_info=True)
|
||||
return
|
||||
self.close(exc_info=True)
|
||||
raise
|
||||
raise
|
||||
break
|
||||
if chunk is None:
|
||||
return 0
|
||||
self._read_buffer.append(chunk)
|
||||
|
|
|
@ -5,6 +5,7 @@ from __future__ import absolute_import, division, print_function, with_statement
|
|||
import base64
|
||||
import binascii
|
||||
from contextlib import closing
|
||||
import copy
|
||||
import functools
|
||||
import sys
|
||||
import threading
|
||||
|
@ -629,3 +630,15 @@ class HTTPRequestTestCase(unittest.TestCase):
|
|||
request = HTTPRequest('http://example.com', if_modified_since=http_date)
|
||||
self.assertEqual(request.headers,
|
||||
{'If-Modified-Since': format_timestamp(http_date)})
|
||||
|
||||
|
||||
class HTTPErrorTestCase(unittest.TestCase):
|
||||
def test_copy(self):
|
||||
e = HTTPError(403)
|
||||
e2 = copy.copy(e)
|
||||
self.assertIsNot(e, e2)
|
||||
self.assertEqual(e.code, e2.code)
|
||||
|
||||
def test_str(self):
|
||||
e = HTTPError(403)
|
||||
self.assertEqual(str(e), "HTTP 403: Forbidden")
|
||||
|
|
|
@ -15,6 +15,7 @@ from tornado.web import RequestHandler, authenticated, Application, asynchronous
|
|||
|
||||
import binascii
|
||||
import contextlib
|
||||
import copy
|
||||
import datetime
|
||||
import email.utils
|
||||
import itertools
|
||||
|
@ -2544,15 +2545,19 @@ class FinishExceptionTest(SimpleHandlerTestCase):
|
|||
def get(self):
|
||||
self.set_status(401)
|
||||
self.set_header('WWW-Authenticate', 'Basic realm="something"')
|
||||
self.write('authentication required')
|
||||
raise Finish()
|
||||
if self.get_argument('finish_value', ''):
|
||||
raise Finish('authentication required')
|
||||
else:
|
||||
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)
|
||||
for url in ['/', '/?finish_value=1']:
|
||||
response = self.fetch(url)
|
||||
self.assertEqual(response.code, 401)
|
||||
self.assertEqual('Basic realm="something"',
|
||||
response.headers.get('WWW-Authenticate'))
|
||||
self.assertEqual(b'authentication required', response.body)
|
||||
|
||||
|
||||
@wsgi_safe
|
||||
|
@ -2671,3 +2676,12 @@ class RequestSummaryTest(SimpleHandlerTestCase):
|
|||
def test_missing_remote_ip(self):
|
||||
resp = self.fetch("/")
|
||||
self.assertEqual(resp.body, b"GET / (None)")
|
||||
|
||||
|
||||
class HTTPErrorTest(unittest.TestCase):
|
||||
def test_copy(self):
|
||||
e = HTTPError(403, reason="Go away")
|
||||
e2 = copy.copy(e)
|
||||
self.assertIsNot(e, e2)
|
||||
self.assertEqual(e.status_code, e2.status_code)
|
||||
self.assertEqual(e.reason, e2.reason)
|
||||
|
|
|
@ -130,7 +130,7 @@ class WebSocketTest(WebSocketBaseTestCase):
|
|||
@gen_test
|
||||
def test_websocket_gen(self):
|
||||
ws = yield self.ws_connect('/echo')
|
||||
ws.write_message('hello')
|
||||
yield ws.write_message('hello')
|
||||
response = yield ws.read_message()
|
||||
self.assertEqual(response, 'hello')
|
||||
yield self.close(ws)
|
||||
|
|
|
@ -1473,7 +1473,7 @@ class RequestHandler(object):
|
|||
if isinstance(e, Finish):
|
||||
# Not an error; just finish the request without logging.
|
||||
if not self._finished:
|
||||
self.finish()
|
||||
self.finish(*e.args)
|
||||
return
|
||||
try:
|
||||
self.log_exception(*sys.exc_info())
|
||||
|
@ -2063,7 +2063,7 @@ class HTTPError(Exception):
|
|||
determined automatically from ``status_code``, but can be used
|
||||
to use a non-standard numeric code.
|
||||
"""
|
||||
def __init__(self, status_code, log_message=None, *args, **kwargs):
|
||||
def __init__(self, status_code=500, log_message=None, *args, **kwargs):
|
||||
self.status_code = status_code
|
||||
self.log_message = log_message
|
||||
self.args = args
|
||||
|
@ -2084,10 +2084,14 @@ class HTTPError(Exception):
|
|||
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.
|
||||
When `Finish` is raised in a `RequestHandler`, the request will
|
||||
end (calling `RequestHandler.finish` if it hasn't already been
|
||||
called), but the error-handling methods (including
|
||||
`RequestHandler.write_error`) will not be called.
|
||||
|
||||
If `Finish()` was created with no arguments, the pending response
|
||||
will be sent as-is. If `Finish()` was given an argument, that
|
||||
argument will be passed to `RequestHandler.finish()`.
|
||||
|
||||
This can be a more convenient way to implement custom error pages
|
||||
than overriding ``write_error`` (especially in library code)::
|
||||
|
@ -2096,6 +2100,10 @@ class Finish(Exception):
|
|||
self.set_status(401)
|
||||
self.set_header('WWW-Authenticate', 'Basic realm="something"')
|
||||
raise Finish()
|
||||
|
||||
.. versionchanged:: 4.3
|
||||
Arguments passed to ``Finish()`` will be passed on to
|
||||
`RequestHandler.finish`.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
|
|
@ -208,12 +208,15 @@ class WebSocketHandler(tornado.web.RequestHandler):
|
|||
.. versionchanged:: 3.2
|
||||
`WebSocketClosedError` was added (previously a closed connection
|
||||
would raise an `AttributeError`)
|
||||
|
||||
.. versionchanged:: 4.3
|
||||
Returns a `.Future` which can be used for flow control.
|
||||
"""
|
||||
if self.ws_connection is None:
|
||||
raise WebSocketClosedError()
|
||||
if isinstance(message, dict):
|
||||
message = tornado.escape.json_encode(message)
|
||||
self.ws_connection.write_message(message, binary=binary)
|
||||
return self.ws_connection.write_message(message, binary=binary)
|
||||
|
||||
def select_subprotocol(self, subprotocols):
|
||||
"""Invoked when a new WebSocket requests specific subprotocols.
|
||||
|
@ -671,7 +674,7 @@ class WebSocketProtocol13(WebSocketProtocol):
|
|||
frame += data
|
||||
self._wire_bytes_out += len(frame)
|
||||
try:
|
||||
self.stream.write(frame)
|
||||
return self.stream.write(frame)
|
||||
except StreamClosedError:
|
||||
self._abort()
|
||||
|
||||
|
@ -688,7 +691,7 @@ class WebSocketProtocol13(WebSocketProtocol):
|
|||
if self._compressor:
|
||||
message = self._compressor.compress(message)
|
||||
flags |= self.RSV1
|
||||
self._write_frame(True, opcode, message, flags=flags)
|
||||
return self._write_frame(True, opcode, message, flags=flags)
|
||||
|
||||
def write_ping(self, data):
|
||||
"""Send ping frame."""
|
||||
|
@ -970,7 +973,7 @@ class WebSocketClientConnection(simple_httpclient._HTTPConnection):
|
|||
|
||||
def write_message(self, message, binary=False):
|
||||
"""Sends a message to the WebSocket server."""
|
||||
self.protocol.write_message(message, binary)
|
||||
return self.protocol.write_message(message, binary)
|
||||
|
||||
def read_message(self, callback=None):
|
||||
"""Reads a message from the WebSocket server.
|
||||
|
|
Loading…
Reference in New Issue