[Zodb-checkins] CVS: ZEO/ZEO/zrpc - client.py:1.1.2.10

Jeremy Hylton jeremy@zope.com
Mon, 28 Jan 2002 13:47:24 -0500


Update of /cvs-repository/ZEO/ZEO/zrpc
In directory cvs.zope.org:/tmp/cvs-serv10307

Modified Files:
      Tag: Standby-branch
	client.py 
Log Message:
Fix several corner cases in the ConnectThread logic.

Catch and log errors from more socket calls.  It's possible for even
connect_ex() to raise an exception, e.g. "host not found."

Make sure that every path through connect() method does one of the
following: 

    - Returns without doing anything if connect_ex() returns
      EINPROGRESS or similar.

    - Raises a Connected() exception if the zrpc connection is
      established.

    - Closes the socket, removes it from self.sockets, and logs an
      error in all other cases.

Address Tim's XXX comments.


=== ZEO/ZEO/zrpc/client.py 1.1.2.9 => 1.1.2.10 ===
                     log("attempt connection to %s" % repr(addr),
                         level=zLOG.DEBUG)
-                s = socket.socket(domain, socket.SOCK_STREAM)
+                try:
+                    s = socket.socket(domain, socket.SOCK_STREAM)
+                except socket.error, err:
+                    log("Failed to create socket with domain=%s: %s" % (
+                        domain, err), level=zLOG.ERROR)
+                    continue
                 s.setblocking(0)
                 self.sockets[s] = addr
                 # connect() raises Connected iff it succeeds
@@ -262,33 +267,42 @@
         select() loop in the caller and an exception is a principled
         way to do the abort.
 
-        If the socket sonnects and the initial ZEO setup fails or the
-        connect_ex() returns an error, we close the socket and ignore it.
-        XXX If test_connection() fails (I assume that's what's meant by
-        XXX "the initial ZEO setup fails", the code below doesn't do anything
-        XXX with the socket.  So the comment or the code is wrong.
-        When the socket is closed, it is removed from self.sockets.
+        If the socket sonnects and the initial ZEO setup
+        (notifyConnected()) fails or the connect_ex() returns an
+        error, we close the socket, remove it from self.sockets, and
+        proceed with the other sockets.
 
         If connect_ex() returns EINPROGRESS, we need to try again later.
         """
         addr = self.sockets[s]
-        e = s.connect_ex(addr)
-        if e in _CONNECT_IN_PROGRESS:
-            pass
-        elif e in _CONNECT_OK:
-            c = self.test_connection(s, addr)
-            # XXX we should't log that we got connected if c==0, should we?
-            log("connected to %s" % repr(addr), level=zLOG.DEBUG)
-            if c:
-                raise Connected(s)
+        try:
+            e = s.connect_ex(addr)
+        except socket.error, msg:
+            log("failed to connect to %s: %s" % (addr, msg),
+                level=zLOG.ERROR)
         else:
-            if __debug__:
+            if e in _CONNECT_IN_PROGRESS:
+                return
+            elif e in _CONNECT_OK:
+                c = self.test_connection(s, addr)
+                if c:
+                    log("connected to %s" % repr(addr), level=zLOG.DEBUG)
+                    raise Connected(s)
+            else:
                 log("error connecting to %s: %s" % (addr, errno.errorcode[e]),
                     level=zLOG.DEBUG)
-            s.close()
-            del self.sockets[s]
+        # Any execution that doesn't raise Connected() or return
+        # because of CONNECT_IN_PROGRESS is an error.  Make sure the
+        # socket is closed and remove it from the dict of pending
+        # sockets.
+        s.close()
+        del self.sockets[s]
 
     def test_connection(self, s, addr):
+        # Establish a connection at the zrpc level and call the
+        # client's notifyConnected(), giving the zrpc application a
+        # chance to do app-level check of whether the connection is
+        # okay.
         c = ManagedConnection(s, addr, self.client, self.mgr)
         try:
             self.client.notifyConnected(c)
@@ -296,9 +310,8 @@
             log("error connecting to server: %s" % str(addr),
                 level=zLOG.ERROR, error=sys.exc_info())
             c.close()
-            # XXX what's the state of s at this point?  If s got closed,
-            # XXX self.sockets contains a closed socket, and the
-            # XXX next attempt_connects() select() will blow up.
+            # Closing the ZRPC connection will eventually close the
+            # socket, somewhere in asyncore.
             return 0
         self.mgr.connect_done(c)
         return 1