From fab49f4f2ce07df7b7800fc5ddc4adeb8df7ed33 Mon Sep 17 00:00:00 2001 From: Zo Bot Date: Sat, 4 Jul 2026 15:40:16 +0000 Subject: [PATCH] iostream: downgrade the SSL handshake-error log to INFO The SSL handshake-error branch in SSLIOStream._do_ssl_handshake fires on a variety of remote conditions that the application cannot control: - a client that fails to validate the server's certificate sends a tls alert, and the server's handshake then raises and logs here - a client that drops the connection before speaking TLS (SSL_ERROR_EOF / SSL_ERROR_ZERO_RETURN) which lands in the SSL_ERROR_SSL branch via SSL_ERROR_SYSCALL - premature closes from port scanners and other uninvited probes None of these are a problem with the server. They were logged at WARNING, which suggests to the operator that something is wrong on their end, but the only thing the operator can do is configure certificate validation more strictly, which does not change the volume of these messages. Drop the level to INFO so the application can opt in to the messages if it cares, and the default operator view is not polluted by clients making bad requests. Three existing tests expected the old WARNING level via ExpectLog; update them to pass level=logging.INFO so the message is still captured (ExpectLog without an explicit level is becoming a DeprecationWarning match in Tornado 7.0 anyway). Fixes #3347. --- tornado/iostream.py | 2 +- tornado/test/iostream_test.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tornado/iostream.py b/tornado/iostream.py index 53e81fff3..198833c2a 100644 --- a/tornado/iostream.py +++ b/tornado/iostream.py @@ -1366,7 +1366,7 @@ def _do_ssl_handshake(self) -> None: peer = self.socket.getpeername() except Exception: peer = "(not connected)" - gen_log.warning( + gen_log.info( "SSL Error on %s %s: %s", self.socket.fileno(), peer, err ) return self.close(exc_info=err) diff --git a/tornado/test/iostream_test.py b/tornado/test/iostream_test.py index ca909f276..040228ea6 100644 --- a/tornado/test/iostream_test.py +++ b/tornado/test/iostream_test.py @@ -1066,7 +1066,7 @@ def test_start_tls_smtp(self): def test_handshake_fail(self): server_future = self.server_start_tls(_server_ssl_options()) # Certificates are verified with the default configuration. - with ExpectLog(gen_log, "SSL Error"): + with ExpectLog(gen_log, "SSL Error", level=logging.INFO): client_future = self.client_start_tls(server_hostname="localhost") with self.assertRaises(ssl.SSLError): yield client_future @@ -1077,7 +1077,7 @@ def test_handshake_fail(self): def test_check_hostname(self): # Test that server_hostname parameter to start_tls is being used. server_future = self.server_start_tls(_server_ssl_options()) - with ExpectLog(gen_log, "SSL Error"): + with ExpectLog(gen_log, "SSL Error", level=logging.INFO): client_future = self.client_start_tls( ssl.create_default_context(), server_hostname="127.0.0.1" ) @@ -1231,7 +1231,7 @@ async def test_no_match(self): with ExpectLog( gen_log, ".*alert bad certificate", - level=logging.WARNING, + level=logging.INFO, required=platform.system() != "Windows", ): with self.assertRaises(ssl.SSLCertVerificationError):