From: Ben Darnell Date: Thu, 5 Mar 2015 04:25:59 +0000 (-0500) Subject: Count SSL_ERROR_EOF as a kind of connection reset. X-Git-Tag: v4.2.0b1~81 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c60d6468b1f9f6b4af72882fa8b1aa36e29b27cb;p=thirdparty%2Ftornado.git Count SSL_ERROR_EOF as a kind of connection reset. This reduces log spam and lets us remove an old (and timing-sensitive) test workaround. Closes #1362. --- diff --git a/tornado/iostream.py b/tornado/iostream.py index 8c7675c0d..089d413b7 100644 --- a/tornado/iostream.py +++ b/tornado/iostream.py @@ -438,8 +438,7 @@ class BaseIOStream(object): futures.append(self._connect_future) self._connect_future = None for future in futures: - if (isinstance(self.error, (socket.error, IOError)) and - errno_from_exception(self.error) in _ERRNO_CONNRESET): + if self._is_connreset(self.error): # Treat connection resets as closed connections so # clients only have to catch one kind of exception # to avoid logging. @@ -719,7 +718,7 @@ class BaseIOStream(object): chunk = self.read_from_fd() except (socket.error, IOError, OSError) as e: # ssl.SSLError is a subclass of socket.error - if e.args[0] in _ERRNO_CONNRESET: + 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). @@ -841,7 +840,7 @@ class BaseIOStream(object): self._write_buffer_frozen = True break else: - if e.args[0] not in _ERRNO_CONNRESET: + if not self._is_connreset(e): # Broken pipe errors are usually caused by connection # reset, and its better to not log EPIPE errors to # minimize log spam @@ -919,6 +918,14 @@ class BaseIOStream(object): self._state = self._state | state self.io_loop.update_handler(self.fileno(), self._state) + def _is_connreset(self, exc): + """Return true if exc is ECONNRESET or equivalent. + + May be overridden in subclasses. + """ + return (isinstance(exc, (socket.error, IOError)) and + errno_from_exception(exc) in _ERRNO_CONNRESET) + class IOStream(BaseIOStream): r"""Socket-based `IOStream` implementation. @@ -1175,7 +1182,7 @@ class IOStream(BaseIOStream): # Sometimes setsockopt will fail if the socket is closed # at the wrong time. This can happen with HTTPServer # resetting the value to false between requests. - if e.errno not in (errno.EINVAL, errno.ECONNRESET): + if e.errno != errno.EINVAL and not self._is_connreset(e): raise @@ -1250,8 +1257,7 @@ class SSLIOStream(IOStream): # to cause do_handshake to raise EBADF, so make that error # quiet as well. # https://groups.google.com/forum/?fromgroups#!topic/python-tornado/ApucKJat1_0 - if (err.args[0] in _ERRNO_CONNRESET or - err.args[0] == errno.EBADF): + if self._is_connreset(err) or err.args[0] == errno.EBADF: return self.close(exc_info=True) raise except AttributeError: @@ -1385,6 +1391,11 @@ class SSLIOStream(IOStream): return None return chunk + def _is_connreset(self, e): + if isinstance(e, ssl.SSLError) and e.args[0] == ssl.SSL_ERROR_EOF: + return True + return super(SSLIOStream, self)._is_connreset(e) + class PipeIOStream(BaseIOStream): """Pipe-based `IOStream` implementation. diff --git a/tornado/test/simple_httpclient_test.py b/tornado/test/simple_httpclient_test.py index 2d72992e0..088010586 100644 --- a/tornado/test/simple_httpclient_test.py +++ b/tornado/test/simple_httpclient_test.py @@ -446,10 +446,6 @@ class SimpleHTTPSClientTestCase(SimpleHTTPClientTestMixin, AsyncHTTPSTestCase): required=False): resp = self.fetch( "/hello", ssl_options=dict(cert_reqs=ssl.CERT_REQUIRED)) - # On python 2.6, the server logs an exception a little after - # the client finishes ("unexpected EOF") - self.io_loop.add_timeout(self.io_loop.time() + 0.01, self.stop) - self.wait() self.assertRaises(ssl.SSLError, resp.rethrow) @unittest.skipIf(not hasattr(ssl, 'SSLContext'),