From f98057668f633c255a93d653747949fa73073c06 Mon Sep 17 00:00:00 2001 From: Jim Fulton <jim@zope.com> Date: Tue, 27 Apr 2010 20:23:12 +0000 Subject: [PATCH] On Mac OS X, clients that connected and disconnected quickly could cause a ZEO server to stop accepting connections, due to a failure to catch errors in the initial part of the connection process. The failure to properly handle exceptions while accepting connections is potentially problematic on other platforms. Fixes: https://bugs.launchpad.net/zodb/+bug/135108 --- src/CHANGES.txt | 15 +++++++++++++++ src/ZEO/tests/testZEO.py | 28 ++++++++++++++++++++++++++++ src/ZEO/zrpc/server.py | 23 +++++++++++++++++++++-- 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 0eb4919c..9756e30a 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -2,6 +2,21 @@ Change History ================ +3.9.6 (unreleased) +================== + +Bugs Fixed +---------- + +- On Mac OS X, clients that connected and disconnected quickly could + cause a ZEO server to stop accepting connections, due to a failure + to catch errors in the initial part of the connection process. + + The failure to properly handle exceptions while accepting + connections is potentially problematic on other platforms. + + Fixes: https://bugs.launchpad.net/zodb/+bug/135108 + 3.9.5 (2010-04-23) ================== diff --git a/src/ZEO/tests/testZEO.py b/src/ZEO/tests/testZEO.py index 9daf82d3..11ff0c12 100644 --- a/src/ZEO/tests/testZEO.py +++ b/src/ZEO/tests/testZEO.py @@ -1227,6 +1227,34 @@ def runzeo_without_configfile(): testing exit immediately """ +def quick_close_doesnt_kill_server(): + r""" + + Start a server: + + >>> addr, _ = start_server() + + Now connect and immediately disconnect. This caused the server to + die in the past: + + >>> import socket, struct + >>> for i in range(5): + ... s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + ... s.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, + ... struct.pack('ii', 1, 0)) + ... s.connect(addr) + ... s.close() + + Now we should be able to connect as normal: + + >>> db = ZEO.DB(addr) + >>> db.storage.is_connected() + True + + >>> db.close() + + """ + slow_test_classes = [ BlobAdaptedFileStorageTests, BlobWritableCacheTests, DemoStorageTests, FileStorageTests, MappingStorageTests, diff --git a/src/ZEO/zrpc/server.py b/src/ZEO/zrpc/server.py index 3a29810b..25fc22fb 100644 --- a/src/ZEO/zrpc/server.py +++ b/src/ZEO/zrpc/server.py @@ -17,6 +17,7 @@ import types from ZEO.zrpc.connection import Connection from ZEO.zrpc.log import log +import ZEO.zrpc.log import logging # Export the main asyncore loop @@ -54,5 +55,23 @@ class Dispatcher(asyncore.dispatcher): except socket.error, msg: log("accepted failed: %s" % msg) return - c = self.factory(sock, addr) - log("connect from %s: %s" % (repr(addr), c)) + + # We could short-circuit the attempt below in some edge cases + # and avoid a log message by checking for addr being None. + # Unfortunately, our test for the code below, + # quick_close_doesnt_kill_server, causes addr to be None and + # we'd have to write a test for the non-None case, which is + # *even* harder to provoke. :/ So we'll leave things as they + # are for now. + + # It might be better to check whether the socket has been + # closed, but I don't see a way to do that. :( + + try: + c = self.factory(sock, addr) + except: + if sock.fileno() in asyncore.socket_map: + del asyncore.socket_map[sock.fileno()] + ZEO.zrpc.log.logger.exception("Error in handle_accept") + else: + log("connect from %s: %s" % (repr(addr), c)) -- 2.30.9