]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Fix Issue 6706: return None on connect() in case of EWOULDBLOCK/ECONNABORTED error.
authorGiampaolo Rodolà <g.rodola@gmail.com>
Mon, 1 Nov 2010 15:07:14 +0000 (15:07 +0000)
committerGiampaolo Rodolà <g.rodola@gmail.com>
Mon, 1 Nov 2010 15:07:14 +0000 (15:07 +0000)
Doc/library/asyncore.rst
Lib/asyncore.py
Lib/smtpd.py
Misc/NEWS

index 468e5b57d9d6c8f6503cc750d3cc80cd37066045..58691abd4bce9e1b527901b327cbce837b04d5b7 100644 (file)
@@ -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()
+
index 083920c38933192b6e4a1de0134d9ab1e241d85a..daf664443386d95f8cf84870cb57af7ee6a2366d 100644 (file)
@@ -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:
index 2d17b8ddd13ca89b0c25a595736e67610226dcfb..e0544e4c526899f6f09facb254e7919bcd185a46 100755 (executable)
@@ -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.
 """
 
-\f
 # Overview:
 #
 # This file implements the minimal SMTP protocol as defined in RFC 821.  It
@@ -96,7 +95,6 @@ EMPTYSTRING = ''
 COMMASPACE = ', '
 
 
-\f
 def usage(code, msg=''):
     print >> sys.stderr, __doc__ % globals()
     if msg:
@@ -104,7 +102,6 @@ def usage(code, msg=''):
     sys.exit(code)
 
 
-\f
 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>')
 
 
-\f
 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
 
 
-\f
 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 ------------'
 
 
-\f
 class PureProxy(SMTPServer):
     def process_message(self, peer, mailfrom, rcpttos, data):
         lines = data.split('\n')
@@ -399,7 +382,6 @@ class PureProxy(SMTPServer):
         return refused
 
 
-\f
 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)
 
 
-\f
 class Options:
     setuid = 1
     classname = 'PureProxy'
 
 
-\f
 def parseargs():
     global DEBUGSTREAM
     try:
@@ -541,7 +521,6 @@ def parseargs():
     return options
 
 
-\f
 if __name__ == '__main__':
     options = parseargs()
     # Become nobody
index bfb1ebbb61322778eec61436def7c4a66fc9e9b0..b9e38e2963d57260306bd8b37a3b2ae52c4a38ca 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -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.