Merge pull request #1528 from bdarnell/misc

Misc updates
This commit is contained in:
Ben Darnell 2015-09-27 21:35:04 -04:00
commit e438075d22
8 changed files with 78 additions and 33 deletions

View File

@ -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):

View File

@ -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

View File

@ -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)

View File

@ -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")

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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.