diff options
author | Giampaolo RodolĂ <g.rodola@gmail.com> | 2010-11-01 15:07:14 (GMT) |
---|---|---|
committer | Giampaolo RodolĂ <g.rodola@gmail.com> | 2010-11-01 15:07:14 (GMT) |
commit | 19e9fefc660d623ce7c31fb008cde1157ae12aba (patch) | |
tree | 55124f9a3949ae80b5b361330e29c4843680307c | |
parent | abacccc50c891f32c83dcf9ae38fe310ecf87409 (diff) | |
download | cpython-19e9fefc660d623ce7c31fb008cde1157ae12aba.zip cpython-19e9fefc660d623ce7c31fb008cde1157ae12aba.tar.gz cpython-19e9fefc660d623ce7c31fb008cde1157ae12aba.tar.bz2 |
Fix Issue 6706: return None on connect() in case of EWOULDBLOCK/ECONNABORTED error.
-rw-r--r-- | Doc/library/asyncore.rst | 62 | ||||
-rw-r--r-- | Lib/asyncore.py | 11 | ||||
-rwxr-xr-x | Lib/smtpd.py | 31 | ||||
-rw-r--r-- | Misc/NEWS | 3 |
4 files changed, 70 insertions, 37 deletions
diff --git a/Doc/library/asyncore.rst b/Doc/library/asyncore.rst index 468e5b5..58691ab 100644 --- a/Doc/library/asyncore.rst +++ b/Doc/library/asyncore.rst @@ -211,10 +211,13 @@ any that have been added to the map during asynchronous service) is closed. .. method:: accept() Accept a connection. The socket must be bound to an address and listening - for connections. The return value is a pair ``(conn, address)`` where - *conn* is a *new* socket object usable to send and receive data on the - connection, and *address* is the address bound to the socket on the other - end of the connection. + for connections. The return value can be either ``None`` or a pair + ``(conn, address)`` where *conn* is a *new* socket object usable to send + and receive data on the connection, and *address* is the address bound to + the socket on the other end of the connection. + When ``None`` is returned it means the connection didn't take place, in + which case the server should just ignore this event and keep listening + for further incoming connections. .. method:: close() @@ -224,6 +227,12 @@ any that have been added to the map during asynchronous service) is closed. flushed). Sockets are automatically closed when they are garbage-collected. +.. class:: dispatcher_with_send() + + A :class:`dispatcher` subclass which adds simple buffered output capability, + useful for simple clients. For more sophisticated usage use + :class:`asynchat.async_chat`. + .. class:: file_dispatcher() A file_dispatcher takes a file descriptor or file object along with an @@ -240,7 +249,7 @@ any that have been added to the map during asynchronous service) is closed. socket for use by the :class:`file_dispatcher` class. Availability: UNIX. -.. _asyncore-example: +.. _asyncore-example-1: asyncore Example basic HTTP client ---------------------------------- @@ -250,7 +259,7 @@ implement its socket handling:: import asyncore, socket - class http_client(asyncore.dispatcher): + class HTTPClient(asyncore.dispatcher): def __init__(self, host, path): asyncore.dispatcher.__init__(self) @@ -274,6 +283,45 @@ implement its socket handling:: sent = self.send(self.buffer) self.buffer = self.buffer[sent:] - c = http_client('www.python.org', '/') + client = HTTPClient('www.python.org', '/') asyncore.loop() + +.. _asyncore-example-2: + +asyncore Example basic echo server +---------------------------------- + +Here is abasic echo server that uses the :class:`dispatcher` class to accept +connections and dispatches the incoming connections to a handler:: + + import asyncore + import socket + + class EchoHandler(asyncore.dispatcher_with_send): + + def handle_read(self): + data = self.recv(8192) + self.send(data) + + class EchoServer(asyncore.dispatcher): + + def __init__(self, host, port): + asyncore.dispatcher.__init__(self) + self.create_socket(socket.AF_INET, socket.SOCK_STREAM) + self.set_reuse_addr() + self.bind((host, port)) + self.listen(5) + + def handle_accept(self): + pair = self.accept() + if pair is None: + pass + else: + sock, addr = pair + print 'Incoming connection from %s' % repr(addr) + handler = EchoHandler(sock) + + server = EchoServer('localhost', 8080) + asyncore.loop() + diff --git a/Lib/asyncore.py b/Lib/asyncore.py index 083920c..daf6644 100644 --- a/Lib/asyncore.py +++ b/Lib/asyncore.py @@ -350,12 +350,15 @@ class dispatcher: # XXX can return either an address pair or None try: conn, addr = self.socket.accept() - return conn, addr - except socket.error, why: - if why.args[0] == EWOULDBLOCK: - pass + except TypeError: + return None + except socket.error as why: + if why.args[0] in (EWOULDBLOCK, ECONNABORTED): + return None else: raise + else: + return conn, addr def send(self, data): try: diff --git a/Lib/smtpd.py b/Lib/smtpd.py index 2d17b8d..e0544e4 100755 --- a/Lib/smtpd.py +++ b/Lib/smtpd.py @@ -35,7 +35,6 @@ given then 8025 is used. If remotehost is not given then `localhost' is used, and if remoteport is not given, then 25 is used. """ - # Overview: # # This file implements the minimal SMTP protocol as defined in RFC 821. It @@ -96,7 +95,6 @@ EMPTYSTRING = '' COMMASPACE = ', ' - def usage(code, msg=''): print >> sys.stderr, __doc__ % globals() if msg: @@ -104,7 +102,6 @@ def usage(code, msg=''): sys.exit(code) - class SMTPChannel(asynchat.async_chat): COMMAND = 0 DATA = 1 @@ -276,7 +273,6 @@ class SMTPChannel(asynchat.async_chat): self.push('354 End data with <CR><LF>.<CR><LF>') - class SMTPServer(asyncore.dispatcher): def __init__(self, localaddr, remoteaddr): self._localaddr = localaddr @@ -299,22 +295,11 @@ class SMTPServer(asyncore.dispatcher): localaddr, remoteaddr) def handle_accept(self): - try: - conn, addr = self.accept() - except TypeError: - # sometimes accept() might return None - return - except socket.error, err: - # ECONNABORTED might be thrown - if err[0] != errno.ECONNABORTED: - raise - return - else: - # sometimes addr == None instead of (ip, port) - if addr == None: - return - print >> DEBUGSTREAM, 'Incoming connection from %s' % repr(addr) - channel = SMTPChannel(self, conn, addr) + pair = self.accept() + if pair is not None: + conn, addr = pair + print >> DEBUGSTREAM, 'Incoming connection from %s' % repr(addr) + channel = SMTPChannel(self, conn, addr) # API for "doing something useful with the message" def process_message(self, peer, mailfrom, rcpttos, data): @@ -342,7 +327,6 @@ class SMTPServer(asyncore.dispatcher): raise NotImplementedError - class DebuggingServer(SMTPServer): # Do something with the gathered message def process_message(self, peer, mailfrom, rcpttos, data): @@ -358,7 +342,6 @@ class DebuggingServer(SMTPServer): print '------------ END MESSAGE ------------' - class PureProxy(SMTPServer): def process_message(self, peer, mailfrom, rcpttos, data): lines = data.split('\n') @@ -399,7 +382,6 @@ class PureProxy(SMTPServer): return refused - class MailmanProxy(PureProxy): def process_message(self, peer, mailfrom, rcpttos, data): from cStringIO import StringIO @@ -478,13 +460,11 @@ class MailmanProxy(PureProxy): msg.Enqueue(mlist, torequest=1) - class Options: setuid = 1 classname = 'PureProxy' - def parseargs(): global DEBUGSTREAM try: @@ -541,7 +521,6 @@ def parseargs(): return options - if __name__ == '__main__': options = parseargs() # Become nobody @@ -66,6 +66,9 @@ Core and Builtins Library ------- +- Issue 6706: asyncore accept() method no longer raises EWOULDBLOCK/ECONNABORTED + on incomplete connection attempt but returns None instead. + - Issue #10266: uu.decode didn't close in_file explicitly when it was given as a filename. Patch by Brian Brazil. |