fix implementation of connect_ex so it always returns the error code rather than raising

This commit is contained in:
Tavis Rudd
2010-02-24 19:49:21 -08:00
parent a0ab236669
commit 8072243926

View File

@@ -36,8 +36,8 @@ def socket_connect(descriptor, address):
def socket_accept(descriptor): def socket_accept(descriptor):
""" """
Attempts to accept() on the descriptor, returns a client,address tuple Attempts to accept() on the descriptor, returns a client,address tuple
if it succeeds; returns None if it needs to trampoline, and raises if it succeeds; returns None if it needs to trampoline, and raises
any exceptions. any exceptions.
""" """
try: try:
@@ -46,7 +46,7 @@ def socket_accept(descriptor):
if e[0] == errno.EWOULDBLOCK: if e[0] == errno.EWOULDBLOCK:
return None return None
raise raise
if sys.platform[:3]=="win": if sys.platform[:3]=="win":
# winsock sometimes throws ENOTCONN # winsock sometimes throws ENOTCONN
@@ -62,7 +62,7 @@ else:
def set_nonblocking(fd): def set_nonblocking(fd):
""" """
Sets the descriptor to be nonblocking. Works on many file-like Sets the descriptor to be nonblocking. Works on many file-like
objects as well as sockets. Only sockets can be nonblocking on objects as well as sockets. Only sockets can be nonblocking on
Windows, however. Windows, however.
""" """
try: try:
@@ -96,7 +96,7 @@ try:
from socket import _GLOBAL_DEFAULT_TIMEOUT from socket import _GLOBAL_DEFAULT_TIMEOUT
except ImportError: except ImportError:
_GLOBAL_DEFAULT_TIMEOUT = object() _GLOBAL_DEFAULT_TIMEOUT = object()
class GreenSocket(object): class GreenSocket(object):
""" """
@@ -117,14 +117,14 @@ class GreenSocket(object):
self.timeout = fd.gettimeout() or socket.getdefaulttimeout() self.timeout = fd.gettimeout() or socket.getdefaulttimeout()
except AttributeError: except AttributeError:
self.timeout = socket.getdefaulttimeout() self.timeout = socket.getdefaulttimeout()
set_nonblocking(fd) set_nonblocking(fd)
self.fd = fd self.fd = fd
self.closed = False self.closed = False
# when client calls setblocking(0) or settimeout(0) the socket must # when client calls setblocking(0) or settimeout(0) the socket must
# act non-blocking # act non-blocking
self.act_non_blocking = False self.act_non_blocking = False
@property @property
def _sock(self): def _sock(self):
return self return self
@@ -195,11 +195,11 @@ class GreenSocket(object):
else: else:
end = time.time() + self.gettimeout() end = time.time() + self.gettimeout()
while True: while True:
if socket_connect(fd, address):
return 0
if time.time() >= end:
raise socket.timeout(errno.EAGAIN)
try: try:
if socket_connect(fd, address):
return 0
if time.time() >= end:
raise socket.timeout(errno.EAGAIN)
trampoline(fd, write=True, timeout=end-time.time(), trampoline(fd, write=True, timeout=end-time.time(),
timeout_exc=socket.timeout(errno.EAGAIN)) timeout_exc=socket.timeout(errno.EAGAIN))
except socket.error, ex: except socket.error, ex:
@@ -254,9 +254,9 @@ class GreenSocket(object):
return '' return ''
else: else:
raise raise
trampoline(fd, trampoline(fd,
read=True, read=True,
timeout=self.timeout, timeout=self.timeout,
timeout_exc=socket.timeout("timed out")) timeout_exc=socket.timeout("timed out"))
def recvfrom(self, *args): def recvfrom(self, *args):
@@ -357,11 +357,11 @@ class GreenPipe(object):
self.fd = fd self.fd = fd
self.closed = False self.closed = False
self.recvbuffer = '' self.recvbuffer = ''
def close(self): def close(self):
self.fd.close() self.fd.close()
self.closed = True self.closed = True
def fileno(self): def fileno(self):
return self.fd.fileno() return self.fd.fileno()
@@ -419,7 +419,7 @@ class GreenPipe(object):
def flush(self): def flush(self):
pass pass
def readuntil(self, terminator, size=None): def readuntil(self, terminator, size=None):
buf, self.recvbuffer = self.recvbuffer, '' buf, self.recvbuffer = self.recvbuffer, ''
checked = 0 checked = 0
@@ -449,7 +449,7 @@ class GreenPipe(object):
buf += d buf += d
chunk, self.recvbuffer = buf[:size], buf[size:] chunk, self.recvbuffer = buf[:size], buf[size:]
return chunk return chunk
def readline(self, size=None): def readline(self, size=None):
return self.readuntil(self.newlines, size=size) return self.readuntil(self.newlines, size=size)
@@ -484,24 +484,24 @@ except ImportError:
class SSL(object): class SSL(object):
class WantWriteError(object): class WantWriteError(object):
pass pass
class WantReadError(object): class WantReadError(object):
pass pass
class ZeroReturnError(object): class ZeroReturnError(object):
pass pass
class SysCallError(object): class SysCallError(object):
pass pass
def shutdown_safe(sock): def shutdown_safe(sock):
""" Shuts down the socket. This is a convenience method for """ Shuts down the socket. This is a convenience method for
code that wants to gracefully handle regular sockets, SSL.Connection code that wants to gracefully handle regular sockets, SSL.Connection
sockets from PyOpenSSL and ssl.SSLSocket objects from Python 2.6 sockets from PyOpenSSL and ssl.SSLSocket objects from Python 2.6
interchangeably. Both types of ssl socket require a shutdown() before interchangeably. Both types of ssl socket require a shutdown() before
close, but they have different arity on their shutdown method. close, but they have different arity on their shutdown method.
Regular sockets don't need a shutdown before close, but it doesn't hurt. Regular sockets don't need a shutdown before close, but it doesn't hurt.
""" """
try: try:
@@ -516,11 +516,11 @@ def shutdown_safe(sock):
# this will often be the case in an http server context # this will often be the case in an http server context
if e[0] != errno.ENOTCONN: if e[0] != errno.ENOTCONN:
raise raise
def connect(addr, family=socket.AF_INET, bind=None): def connect(addr, family=socket.AF_INET, bind=None):
"""Convenience function for opening client sockets. """Convenience function for opening client sockets.
:param addr: Address of the server to connect to. For TCP sockets, this is a (host, port) tuple. :param addr: Address of the server to connect to. For TCP sockets, this is a (host, port) tuple.
:param family: Socket family, optional. See :mod:`socket` documentation for available families. :param family: Socket family, optional. See :mod:`socket` documentation for available families.
:param bind: Local address to bind to, optional. :param bind: Local address to bind to, optional.
@@ -531,14 +531,14 @@ def connect(addr, family=socket.AF_INET, bind=None):
sock.bind(bind) sock.bind(bind)
sock.connect(addr) sock.connect(addr)
return sock return sock
def listen(addr, family=socket.AF_INET, backlog=50): def listen(addr, family=socket.AF_INET, backlog=50):
"""Convenience function for opening server sockets. This """Convenience function for opening server sockets. This
socket can be used in an ``accept()`` loop. socket can be used in an ``accept()`` loop.
Sets SO_REUSEADDR on the socket to save on annoyance. Sets SO_REUSEADDR on the socket to save on annoyance.
:param addr: Address to listen on. For TCP sockets, this is a (host, port) tuple. :param addr: Address to listen on. For TCP sockets, this is a (host, port) tuple.
:param family: Socket family, optional. See :mod:`socket` documentation for available families. :param family: Socket family, optional. See :mod:`socket` documentation for available families.
:param backlog: The maximum number of queued connections. Should be at least 1; the maximum value is system-dependent. :param backlog: The maximum number of queued connections. Should be at least 1; the maximum value is system-dependent.
@@ -552,41 +552,40 @@ def listen(addr, family=socket.AF_INET, backlog=50):
def wrap_ssl(sock, keyfile=None, certfile=None, server_side=False, def wrap_ssl(sock, keyfile=None, certfile=None, server_side=False,
cert_reqs=None, ssl_version=None, ca_certs=None, cert_reqs=None, ssl_version=None, ca_certs=None,
do_handshake_on_connect=True, suppress_ragged_eofs=True): do_handshake_on_connect=True, suppress_ragged_eofs=True):
"""Convenience function for converting a regular socket into an SSL """Convenience function for converting a regular socket into an SSL
socket. Has the same interface as :func:`ssl.wrap_socket`, but socket. Has the same interface as :func:`ssl.wrap_socket`, but
works on 2.5 or earlier, using PyOpenSSL. works on 2.5 or earlier, using PyOpenSSL.
The preferred idiom is to call wrap_ssl directly on the creation The preferred idiom is to call wrap_ssl directly on the creation
method, e.g., ``wrap_ssl(connect(addr))`` or method, e.g., ``wrap_ssl(connect(addr))`` or
``wrap_ssl(listen(addr), server_side=True)``. This way there is ``wrap_ssl(listen(addr), server_side=True)``. This way there is
no "naked" socket sitting around to accidentally corrupt the SSL no "naked" socket sitting around to accidentally corrupt the SSL
session. session.
:return Green SSL object. :return Green SSL object.
""" """
pass pass
def serve(sock, handle, concurrency=1000): def serve(sock, handle, concurrency=1000):
"""Runs a server on the supplied socket. Calls the function """Runs a server on the supplied socket. Calls the function
*handle* in a separate greenthread for every incoming request. *handle* in a separate greenthread for every incoming request.
This function blocks the calling greenthread; it won't return until This function blocks the calling greenthread; it won't return until
the server completes. If you desire an immediate return, the server completes. If you desire an immediate return,
spawn a new greenthread for :func:`serve`. spawn a new greenthread for :func:`serve`.
The *handle* function must raise an EndServerException to The *handle* function must raise an EndServerException to
gracefully terminate the server -- that's the only way to get the gracefully terminate the server -- that's the only way to get the
server() function to return. Any other uncaught exceptions raised server() function to return. Any other uncaught exceptions raised
in *handle* are raised as exceptions from :func:`serve`, so be in *handle* are raised as exceptions from :func:`serve`, so be
sure to do a good job catching exceptions that your application sure to do a good job catching exceptions that your application
raises. The return value of *handle* is ignored. raises. The return value of *handle* is ignored.
The value in *concurrency* controls the maximum number of The value in *concurrency* controls the maximum number of
greenthreads that will be open at any time handling requests. When greenthreads that will be open at any time handling requests. When
the server hits the concurrency limit, it stops accepting new the server hits the concurrency limit, it stops accepting new
connections until the existing ones complete. connections until the existing ones complete.
""" """
pass pass