From 1cfdd41f7d108cdeb6eac9ce6037965248d3cff2 Mon Sep 17 00:00:00 2001 From: "R. Tyler Ballance" Date: Fri, 27 Aug 2010 14:00:03 -0700 Subject: [PATCH] Move socket.create_connection to green.socket to avoid blocking getaddrinfo() call Given the order of imports and monkey-patching, even when using greendns the `getaddrinfo()` call inside of _socket_nodns.create_connection would use the unpatched (i.e. non-green) version of getaddrinfo, inadvertantly blocking the process in some situations --- eventlet/green/_socket_nodns.py | 36 +-------------------------- eventlet/green/socket.py | 43 +++++++++++++++++++++++++++++---- eventlet/support/greendns.py | 8 +++--- 3 files changed, 43 insertions(+), 44 deletions(-) diff --git a/eventlet/green/_socket_nodns.py b/eventlet/green/_socket_nodns.py index d650c93..d860c06 100644 --- a/eventlet/green/_socket_nodns.py +++ b/eventlet/green/_socket_nodns.py @@ -13,7 +13,7 @@ from eventlet.greenio import _GLOBAL_DEFAULT_TIMEOUT from eventlet.greenio import _fileobject __all__ = __socket.__all__ -__patched__ = ['fromfd', 'socketpair', 'create_connection', 'ssl', 'socket'] +__patched__ = ['fromfd', 'socketpair', 'ssl', 'socket'] try: __original_fromfd__ = __socket.fromfd def fromfd(*args): @@ -31,40 +31,6 @@ except AttributeError: -def create_connection(address, - timeout=_GLOBAL_DEFAULT_TIMEOUT, - source_address=None): - """Connect to *address* and return the socket object. - - Convenience function. Connect to *address* (a 2-tuple ``(host, - port)``) and return the socket object. Passing the optional - *timeout* parameter will set the timeout on the socket instance - before attempting to connect. If no *timeout* is supplied, the - global default timeout setting returned by :func:`getdefaulttimeout` - is used. - """ - - msg = "getaddrinfo returns an empty list" - host, port = address - for res in getaddrinfo(host, port, 0, SOCK_STREAM): - af, socktype, proto, canonname, sa = res - sock = None - try: - sock = socket(af, socktype, proto) - if timeout is not _GLOBAL_DEFAULT_TIMEOUT: - sock.settimeout(timeout) - if source_address: - sock.bind(source_address) - sock.connect(sa) - return sock - - except error, msg: - if sock is not None: - sock.close() - - raise error, msg - - def _convert_to_sslerror(ex): """ Transliterates SSL.SysCallErrors to socket.sslerrors""" return sslerror((ex.args[0], ex.args[1])) diff --git a/eventlet/green/socket.py b/eventlet/green/socket.py index 42f47ac..759e815 100644 --- a/eventlet/green/socket.py +++ b/eventlet/green/socket.py @@ -4,18 +4,18 @@ from eventlet.hubs import get_hub __import__('eventlet.green._socket_nodns') __socket = sys.modules['eventlet.green._socket_nodns'] globals().update(dict([(var, getattr(__socket, var)) - for var in dir(__socket) + for var in dir(__socket) if not var.startswith('__')])) - + __all__ = __socket.__all__ -__patched__ = __socket.__patched__ + ['gethostbyname', 'getaddrinfo'] +__patched__ = __socket.__patched__ + ['gethostbyname', 'getaddrinfo', 'create_connection',] greendns = None if os.environ.get("EVENTLET_NO_GREENDNS",'').lower() != "yes": try: from eventlet.support import greendns - except ImportError: + except ImportError, ex: pass __original_gethostbyname__ = __socket.gethostbyname @@ -60,6 +60,39 @@ else: if greendns: gethostbyname_ex = greendns.gethostbyname_ex getnameinfo = greendns.getnameinfo - __patched__ + ['gethostbyname_ex', 'getnameinfo'] + __patched__ = __patched__ + ['gethostbyname_ex', 'getnameinfo'] + +def create_connection(address, + timeout=_GLOBAL_DEFAULT_TIMEOUT, + source_address=None): + """Connect to *address* and return the socket object. + + Convenience function. Connect to *address* (a 2-tuple ``(host, + port)``) and return the socket object. Passing the optional + *timeout* parameter will set the timeout on the socket instance + before attempting to connect. If no *timeout* is supplied, the + global default timeout setting returned by :func:`getdefaulttimeout` + is used. + """ + + msg = "getaddrinfo returns an empty list" + host, port = address + for res in getaddrinfo(host, port, 0, SOCK_STREAM): + af, socktype, proto, canonname, sa = res + sock = None + try: + sock = socket(af, socktype, proto) + if timeout is not _GLOBAL_DEFAULT_TIMEOUT: + sock.settimeout(timeout) + if source_address: + sock.bind(source_address) + sock.connect(sa) + return sock + + except error, msg: + if sock is not None: + sock.close() + + raise error, msg diff --git a/eventlet/support/greendns.py b/eventlet/support/greendns.py index 81eed13..8720f7d 100644 --- a/eventlet/support/greendns.py +++ b/eventlet/support/greendns.py @@ -41,14 +41,14 @@ from eventlet.green import time from eventlet.green import select dns = patcher.import_patched('dns', - socket=_socket_nodns, - time=time, + socket=_socket_nodns, + time=time, select=select) for pkg in ('dns.query', 'dns.exception', 'dns.inet', 'dns.message', 'dns.rdatatype','dns.resolver', 'dns.reversename'): setattr(dns, pkg.split('.')[1], patcher.import_patched(pkg, - socket=_socket_nodns, - time=time, + socket=_socket_nodns, + time=time, select=select)) socket = _socket_nodns