--- a/qmsk/net/transport/tcp.py Mon Aug 31 22:17:54 2009 +0300
+++ b/qmsk/net/transport/tcp.py Mon Aug 31 22:19:16 2009 +0300
@@ -81,43 +81,182 @@
self.sock.close()
+class Connector (socket.Connect) :
+ """
+ The connect() state machine.
+ """
-class Client (socket.Client, client.Client) :
+ def __init__ (self, bind_endpoint=None, family=None) :
+ # bind()?
+ if bind_endpoint or family :
+ # construct a socket as defined
+ self._init_endpoint(bind_endpoint, family=family)
+
+ def start (self, endpoint) :
+ """
+ Start connecting to the given endpoint
+ """
+
+ # connect()-time errors
+ self._errors = []
+
+ # resolve the list of addresses to try and connect to
+ self._ais = self._resolve_endpoint(endpoint)
+
+ def cleanup (self) :
+ """
+ Destroy any used state
+ """
+
+ del self._errors
+ del self._ais
+
+ def build_error (self) :
+ """
+ Build and return an error object for this connect operation, and cleanup
+ """
+
+ error = socket.SocketConnectEndpointError(self._connect_endpoint, self._connect_errors)
+
+ self.cleanup()
+
+ return error
+
+ def operate (self, nonblocking=True) :
+ """
+ Try and connect to each of our AddrInfo's in turn, collecting any errors, until we either run out of
+ addresses to try, or we manage to connect().
+
+ nonblocking - perform non-blocking connect()'s, so put the socket into non-blocking mode and treat
+ EINPROGRESS as a succesful connect().
+
+ Returns True if we managed to connect, otherwise False.
+ """
+
+ # get next addrinfo to try
+ for ai in self._connect_ais :
+ try :
+ if self.sock :
+ # try and connect the existing socket
+ self._connect_sock_addrinfo(self.sock, ai, nonblocking)
+
+ else :
+ # create a new socket and connect it
+ self.sock = self._connect_addrinfo(ai, nonblocking)
+
+ except SocketConnectAddrinfoError, error :
+ # log it
+ self._errors.append(error)
+
+ # try the next one
+ continue
+
+ else :
+ # yay!
+ return True
+
+ else :
+ # unable to connect anywhere, nothing left to try
+ return False
+
+ def next (self) :
+ """
+ Operate asynchronously,
+ """
+
+ if not self.operate() :
+ # faail
+ pass
+
+
+ def connect (self, endpoint) :
+ """
+ Operate synchronously, either raising an error, or returning a socket.
+ """
+
+ # init
+ self.start(endpoint)
+
+ if not self.operate() :
+ # nay :(
+ raise self.build_error()
+
+ else :
+ # yay :)
+ return self.sock
+
+class Client (client.Client) :
"""
An implementation of Client for TCP sockets.
"""
_SOCKTYPE = socket.SOCK_STREAM
- def __init__ (self, connect_endpoint, bind_endpoint=None) :
+ def __init__ (self, connect_endpoint, bind_endpoint=None, family=None) :
"""
Construct a client, connecting to the given remote endpoint.
connect_endpoint - remote Endpoint to connect() to.
bind_endpoint - (optional) local Endpoint to bind() to before connecting.
-
+ family - (optional) family to create sockaddr for
"""
# store
- self.connect_endpoint = connect_endpoint
- self.bind_endpoint = bind_endpoint
+ self._connect_endpoint = connect_endpoint
+
+
+
+
+
+
+ def _connect_next (self) :
+ """
+ Driver for the async connect process
+ """
+
+ if self._connect(async=True) :
+ # yay :)
+ self._connect_deinit()
+
+ self.on_connect()
+
+ else :
+ # nay :(
+ self.on_error(self._connect_error())
def connect (self, cls=Connection) :
"""
- Perform the connect() operation, returning a tcp.Connection.
+ Perform a synchronous connect() operation.
"""
- if self.bind_endpoint :
- # construct a suitable local socket, bound to a specific endpoint
- sock = self._bind_endpoint(self.bind_endpoint)
+ connector = Connector(self.bind_endpoint, self.family)
- # connect it to the remote endpoint
- self._connect_sock_endpoint(sock, self.connect_endpoint)
+ sock = connector.connect(self.connect_endpoint)
- else :
- # let _init_connect_endpoint pick a socket to use
- sock = self._connect_endpoint(self.connect_endpoint)
-
- # construct
return cls(sock)
+ def connect_async (self, reactor=None) :
+ """
+ Perform an asynchronous connect() operation, returning a Connector object.
+ """
+
+ connector = Connector(self.bind_endpoint, self.family)
+
+ connector.start(self.connect_endpoint)
+
+ return connector
+
+ def on_connect (self) :
+ """
+ Succesfully connected.
+ """
+
+ pass
+
+ def on_error (self, error) :
+ """
+ Connection failed.
+ """
+
+ pass
+