IOStreams now save the exception object that caused them to close.
Closes #494.
This commit is contained in:
parent
e5d5d3241f
commit
330941924b
|
@ -21,6 +21,7 @@ from __future__ import absolute_import, division, with_statement
|
|||
import collections
|
||||
import errno
|
||||
import logging
|
||||
import os
|
||||
import socket
|
||||
import sys
|
||||
import re
|
||||
|
@ -48,6 +49,9 @@ class IOStream(object):
|
|||
and may either be connected before passing it to the IOStream or
|
||||
connected with IOStream.connect.
|
||||
|
||||
When a stream is closed due to an error, the IOStream's `error`
|
||||
attribute contains the exception object.
|
||||
|
||||
A very simple (and broken) HTTP client using this class::
|
||||
|
||||
from tornado import ioloop
|
||||
|
@ -84,6 +88,7 @@ class IOStream(object):
|
|||
self.io_loop = io_loop or ioloop.IOLoop.instance()
|
||||
self.max_buffer_size = max_buffer_size
|
||||
self.read_chunk_size = read_chunk_size
|
||||
self.error = None
|
||||
self._read_buffer = collections.deque()
|
||||
self._write_buffer = collections.deque()
|
||||
self._read_buffer_size = 0
|
||||
|
@ -214,6 +219,8 @@ class IOStream(object):
|
|||
def close(self):
|
||||
"""Close this stream."""
|
||||
if self.socket is not None:
|
||||
if any(sys.exc_info()):
|
||||
self.error = sys.exc_info()[1]
|
||||
if self._read_until_close:
|
||||
callback = self._read_callback
|
||||
self._read_callback = None
|
||||
|
@ -264,6 +271,9 @@ class IOStream(object):
|
|||
if not self.socket:
|
||||
return
|
||||
if events & self.io_loop.ERROR:
|
||||
errno = self.socket.getsockopt(socket.SOL_SOCKET,
|
||||
socket.SO_ERROR)
|
||||
self.error = socket.error(errno, os.strerror(errno))
|
||||
# We may have queued up a user callback in _handle_read or
|
||||
# _handle_write, so don't close the IOStream until those
|
||||
# callbacks have had a chance to run.
|
||||
|
|
|
@ -5,6 +5,7 @@ from tornado.iostream import IOStream
|
|||
from tornado.testing import AsyncHTTPTestCase, LogTrapTestCase, get_unused_port
|
||||
from tornado.util import b
|
||||
from tornado.web import RequestHandler, Application
|
||||
import errno
|
||||
import socket
|
||||
import time
|
||||
|
||||
|
@ -91,6 +92,16 @@ class TestIOStream(AsyncHTTPTestCase, LogTrapTestCase):
|
|||
stream.connect(("localhost", port), connect_callback)
|
||||
self.wait()
|
||||
self.assertFalse(self.connect_called)
|
||||
self.assertTrue(isinstance(stream.error, socket.error), stream.error)
|
||||
self.assertEqual(stream.error.args[0], errno.ECONNREFUSED)
|
||||
|
||||
def test_gaierror(self):
|
||||
# Test that IOStream sets its exc_info on getaddrinfo error
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
|
||||
stream = IOStream(s, io_loop=self.io_loop)
|
||||
stream.set_close_callback(self.stop)
|
||||
stream.connect(('adomainthatdoesntexist.asdf', 54321))
|
||||
self.assertTrue(isinstance(stream.error, socket.gaierror), stream.error)
|
||||
|
||||
def test_connection_closed(self):
|
||||
# When a server sends a response and then closes the connection,
|
||||
|
|
Loading…
Reference in New Issue