[3.12] gh-126500: test_ssl: Don't stop ThreadedEchoServer on OSError in ConnectionHandler; rely on __exit__ (GH-126503) (GH-126572)

gh-126500: test_ssl: Don't stop ThreadedEchoServer on OSError in ConnectionHandler; rely on __exit__ (GH-126503)

If `read()` in the ConnectionHandler thread raises `OSError` (except `ConnectionError`),
the ConnectionHandler shuts down the entire ThreadedEchoServer,
preventing further connections.
It also does that for `EPROTOTYPE` in `wrap_conn`.

As far as I can see, this is done to avoid the server thread getting stuck,
forgotten, in its accept loop. However, since 2011 (5b95eb90a7)
the server is used as a context manager, and its `__exit__` does `stop()` and `join()`.
(I'm not sure if we *always* used `with` since that commit, but currently we do.)

Make sure that the context manager *is* used, and remove the `server.stop()`
calls from ConnectionHandler.
(cherry picked from commit c9cda1608e)

Co-authored-by: Petr Viktorin <encukou@gmail.com>
This commit is contained in:
Miss Islington (bot) 2024-11-11 15:26:10 +01:00 committed by GitHub
parent d62f100fe1
commit aee80cd5e7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -2300,7 +2300,6 @@ class ThreadedEchoServer(threading.Thread):
# See also http://erickt.github.io/blog/2014/11/19/adventures-in-debugging-a-potential-osx-kernel-bug/
if e.errno != errno.EPROTOTYPE and sys.platform != "darwin":
self.running = False
self.server.stop()
self.close()
return False
else:
@ -2435,10 +2434,6 @@ class ThreadedEchoServer(threading.Thread):
self.close()
self.running = False
# normally, we'd just stop here, but for the test
# harness, we want to stop the server
self.server.stop()
def __init__(self, certificate=None, ssl_version=None,
certreqs=None, cacerts=None,
chatty=True, connectionchatty=False, starttls_server=False,
@ -2472,21 +2467,33 @@ class ThreadedEchoServer(threading.Thread):
self.conn_errors = []
threading.Thread.__init__(self)
self.daemon = True
self._in_context = False
def __enter__(self):
if self._in_context:
raise ValueError('Re-entering ThreadedEchoServer context')
self._in_context = True
self.start(threading.Event())
self.flag.wait()
return self
def __exit__(self, *args):
assert self._in_context
self._in_context = False
self.stop()
self.join()
def start(self, flag=None):
if not self._in_context:
raise ValueError(
'ThreadedEchoServer must be used as a context manager')
self.flag = flag
threading.Thread.start(self)
def run(self):
if not self._in_context:
raise ValueError(
'ThreadedEchoServer must be used as a context manager')
self.sock.settimeout(1.0)
self.sock.listen(5)
self.active = True