qmsk/net/transport/socket.py
branchconnect-async
changeset 45 bb49bf8222ed
parent 37 14db3fe42b6c
equal deleted inserted replaced
44:01ac7755b15a 45:bb49bf8222ed
     2     Socket implementation helpers
     2     Socket implementation helpers
     3 """
     3 """
     4 
     4 
     5 from qmsk.net.socket import socket
     5 from qmsk.net.socket import socket
     6 from qmsk.net.socket.constants import *
     6 from qmsk.net.socket.constants import *
       
     7 
       
     8 import errno
     7 
     9 
     8 class SocketError (Exception) :
    10 class SocketError (Exception) :
     9     """
    11     """
    10         Base class of errors raised by the Socket classes.
    12         Base class of errors raised by the Socket classes.
    11     """
    13     """
    60         """
    62         """
    61             Initialize with the given pre-existing socket.
    63             Initialize with the given pre-existing socket.
    62         """
    64         """
    63 
    65 
    64         self.sock = sock
    66         self.sock = sock
    65 
    67     
       
    68     def _set_nonblocking (self, nonblocking) :
       
    69         """
       
    70             Set/unset non-blocking I/O mode.
       
    71         """
       
    72         
       
    73         # XXX: implement elsewhere
       
    74         import fcntl, os
       
    75 
       
    76         flags = 0
       
    77 
       
    78         if nonblocking :
       
    79             flags |= os.O_NONBLOCK
       
    80 
       
    81         fcntl.fcntl(self.sock, fcntl.F_SETFL, flags)
    66 
    82 
    67 class Common (Base) :
    83 class Common (Base) :
    68     """
    84     """
    69         Common operations for Client/Service
    85         Common operations for Client/Service
    70     """
    86     """
   102         except OSError, error : 
   118         except OSError, error : 
   103             raise SocketBindAddrinfoError(ai, error)
   119             raise SocketBindAddrinfoError(ai, error)
   104            
   120            
   105         else :
   121         else :
   106             return sock
   122             return sock
       
   123     
       
   124     @classmethod
       
   125     def _resolve_endpoint (cls, endpoint, socktype = None, protocol = 0, flags = 0) :
       
   126         """
       
   127             Yield an *iterator* of AddrInfo objects by resolving the given endpoint endpoint using parameters suitable
       
   128             for this socket.
       
   129         """
       
   130 
       
   131         if socktype is None :
       
   132             socktype = cls._SOCKTYPE
       
   133         
       
   134         for ai in endpoint.resolve(socktype, protocol, flags) :
       
   135             yield ai
   107 
   136 
   108     @classmethod
   137     @classmethod
   109     def _bind_endpoint (cls, endpoint, socktype = None, protocol=0) :
   138     def _bind_endpoint (cls, endpoint, socktype = None, protocol=0) :
   110         """
   139         """
   111             This will resolve the given endpoint, and attempt to create and bind a suitable socket and return it.
   140             This will resolve the given endpoint, and attempt to create and bind a suitable socket and return it.
   119             XXX: bind to all of the given endpoint's addresses instead of just one...?
   148             XXX: bind to all of the given endpoint's addresses instead of just one...?
   120         """
   149         """
   121         
   150         
   122         errors = []
   151         errors = []
   123         
   152         
   124         if socktype is None :
       
   125             socktype = cls._SOCKTYPE
       
   126         
       
   127         # resolve the endpoint and try socket+bind
   153         # resolve the endpoint and try socket+bind
   128         for ai in endpoint.resolve(socktype, protocol, AI_PASSIVE) :
   154         for ai in cls._resolve_endpoint(endpoint, socktype, protocol, AI_PASSIVE) :
   129             try :
   155             try :
   130                 # try to socket+bind this addrinfo
   156                 # try to socket+bind this addrinfo
   131                 sock = cls._bind_addrinfo(ai)
   157                 sock = cls._bind_addrinfo(ai)
   132             
   158             
   133             except SocketBindAddrinfoError, error :
   159             except SocketBindAddrinfoError, error :
   151             if given. If no endpoint is given, this simply creates a socket with the given settings and does not bind
   177             if given. If no endpoint is given, this simply creates a socket with the given settings and does not bind
   152             it anywhere.
   178             it anywhere.
   153         """
   179         """
   154         
   180         
   155         if endpoint is not None :
   181         if endpoint is not None :
       
   182             assert not family
       
   183 
   156             # create a suitable socket bound to a the given endpoint
   184             # create a suitable socket bound to a the given endpoint
   157             self.sock = self._bind_endpoint(endpoint, socktype, protocol)
   185             self.sock = self._bind_endpoint(endpoint, socktype, protocol)
   158         
   186         
   159         else :
   187         else :
   160             assert family
   188             assert family
   210         return "Unable to connect() to any addresses on endpoint %s:\n%s" % (self.endpoint, "\n".join(
   238         return "Unable to connect() to any addresses on endpoint %s:\n%s" % (self.endpoint, "\n".join(
   211             "\t%s" % (error, ) for error in self.errors
   239             "\t%s" % (error, ) for error in self.errors
   212         ))
   240         ))
   213 
   241 
   214 
   242 
   215 class Client (Common) :
   243 class Connect (Common) :
   216     """
   244     """
   217         Connecting socket
   245         Connecting socket
   218     """
   246     """
   219 
   247 
   220     @classmethod
   248     @classmethod
   221     def _connect_sock_addr (cls, sock, addr) :
   249     def _connect_sock_addr (cls, sock, addr) :
   222         """
   250         """
   223             Attempt to connect the given socket to the given address.
   251             Attempt to connect the given socket to the given address.
   224         """
   252         """
   225 
   253         
   226         sock.connect(addr)
   254         sock.connect(addr)
   227 
   255 
   228     @classmethod
   256     @classmethod
   229     def _connect_sock_addrinfo (cls, sock, ai) :
   257     def _connect_sock_addrinfo (cls, sock, ai, nonblocking=False) :
   230         """
   258         """
   231             Attempt to connect the given socket to the given addrinfo's address.
   259             Attempt to connect the given socket to the given addrinfo's address.
       
   260                 
       
   261                 nonblocking - treat EINPROGRESS as success
   232         """
   262         """
   233 
   263 
   234         try :
   264         try :
   235             cls._connect_sock_addr(sock, ai.addr)
   265             cls._connect_sock_addr(sock, ai.addr)
   236 
   266 
   237         # XXX: except socket.error as e :
   267         # XXX: except socket.error as e :
   238         except OSError, error :
   268         except OSError, error :
   239             raise SocketConnectAddrinfoError(ai, error)
   269             if nonblocking and error.errno == errno.EINPROGRESS :
   240 
   270                 return
   241     @classmethod
   271 
   242     def _connect_addrinfo (cls, ai) :
   272             else :
       
   273                 raise SocketConnectAddrinfoError(ai, error)
       
   274 
       
   275     @classmethod
       
   276     def _connect_addrinfo (cls, ai, nonblocking=False) :
   243         """
   277         """
   244             Attempt to create a socket and connect it based on the given addrinfo, returning the new socket is succesfull.
   278             Attempt to create a socket and connect it based on the given addrinfo, returning the new socket is succesfull.
   245         """
   279         """
   246 
   280 
   247         try :
   281         try :
   250 
   284 
   251         # XXX: except socket.error as e :
   285         # XXX: except socket.error as e :
   252         except OSError, error : 
   286         except OSError, error : 
   253             raise SocketConnectAddrinfoError(ai, error)
   287             raise SocketConnectAddrinfoError(ai, error)
   254 
   288 
   255 
       
   256         # try and connect() it
   289         # try and connect() it
   257         cls._connect_sock_addrinfo(sock, ai)
   290         cls._connect_sock_addrinfo(sock, ai, nonblocking)
   258         
   291         
   259         # return once succesfully
   292         # return once succesfully
   260         return sock
   293         return sock
   261 
   294 
   262     @classmethod
   295     @classmethod
   265             Connect this socket to the given remote endpoint, using the given parameters to resolve the endpoint.
   298             Connect this socket to the given remote endpoint, using the given parameters to resolve the endpoint.
   266         """
   299         """
   267         
   300         
   268         errors = []
   301         errors = []
   269         
   302         
   270         if socktype is None :
       
   271             socktype = cls._SOCKTYPE
       
   272 
       
   273         # resolve the endpoint and try socket+bind
   303         # resolve the endpoint and try socket+bind
   274         for ai in endpoint.resolve(socktype, protocol) :
   304         for ai in cls._resolve_endpoint(endpoint, socktype, protocol) :
   275             try :
   305             try :
   276                 # try to connect the socket to this addrinfo
   306                 # try to connect the socket to this addrinfo
   277                 cls._connect_sock_addrinfo(sock, ai)
   307                 cls._connect_sock_addrinfo(sock, ai)
   278             
   308             
   279             except SocketConnectAddrinfoError, error :
   309             except SocketConnectAddrinfoError, error :
   298             endpoint.
   328             endpoint.
   299         """
   329         """
   300 
   330 
   301         errors = []
   331         errors = []
   302         
   332         
   303         if socktype is None :
       
   304             socktype = cls._SOCKTYPE
       
   305 
       
   306         # resolve the endpoint and try socket+bind
   333         # resolve the endpoint and try socket+bind
   307         for ai in endpoint.resolve(socktype, protocol) :
   334         for ai in cls._resolve_endpoint(endpoint, socktype, protocol) :
   308             try :
   335             try :
   309                 # try to socket+connect this addrinfo
   336                 # try to socket+connect this addrinfo
   310                 sock = cls._connect_addrinfo(ai)
   337                 sock = cls._connect_addrinfo(ai)
   311             
   338             
   312             except SocketConnectAddrinfoError, error :
   339             except SocketConnectAddrinfoError, error :
   335             
   362             
   336             If we do not yet have a socket, then we will attempt to connect to the IPv6 address using an IPv6 socket,
   363             If we do not yet have a socket, then we will attempt to connect to the IPv6 address using an IPv6 socket,
   337             and to the IPv4 address using an IPv4 socket.
   364             and to the IPv4 address using an IPv4 socket.
   338         """
   365         """
   339 
   366 
   340         if self.socket :
   367         if self.sock :
   341             # connect with existing socket
   368             # connect with existing socket
   342             self._connect_sock_endpoint(self.socket, endpoint, socktype, protocol)
   369             self._connect_sock_endpoint(self.sock, endpoint, socktype, protocol)
   343 
   370 
   344         else :
   371         else :
   345             # connect with new socket
   372             # connect with new socket
   346             self._connect_endpoint(endpoint, socktype, protocol)
   373             self.sock = self._connect_endpoint(endpoint, socktype, protocol)
   347 
   374 
   348 class Stream (Base) :
   375 class Stream (Base) :
   349     """
   376     """
   350         Unbuffered byte-stream interface.
   377         Unbuffered byte-stream interface.
   351     """
   378     """