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 """ |