6 |
6 |
7 # XXX: absolute import plz |
7 # XXX: absolute import plz |
8 socket = __import__('socket') |
8 socket = __import__('socket') |
9 |
9 |
10 import select |
10 import select |
|
11 import errno |
11 |
12 |
12 import urlparse |
13 import urlparse |
13 |
14 |
14 import logging; log = logging.getLogger('pvl.socket') |
15 import logging; log = logging.getLogger('pvl.socket') |
15 |
16 |
16 URL = { |
17 # order matters! |
17 'tcp': (0, socket.SOCK_STREAM ), # AF_UNSPEC |
18 URL = ( |
18 'udp': (0, socket.SOCK_DGRAM ), # AF_UNSPEC |
19 # scheme family socktype |
19 'unix': (socket.AF_UNIX, None ), # socktype is given |
20 ( 'unix', (socket.AF_UNIX, None ) ), # socktype is given |
20 } |
21 ( 'tcp', (0, socket.SOCK_STREAM ) ), # AF_UNSPEC |
|
22 ( 'udp', (0, socket.SOCK_DGRAM ) ), # AF_UNSPEC |
|
23 ) |
|
24 |
|
25 URL_SCHEMES = dict(URL) |
21 |
26 |
22 def parse (str, port=None, scheme='tcp', unix=socket.SOCK_DGRAM) : |
27 def parse (str, port=None, scheme='tcp', unix=socket.SOCK_DGRAM) : |
23 """ |
28 """ |
24 Parse given string into (AF_*, SOCK_*, host, port). |
29 Parse given string into (AF_*, SOCK_*, host, port). |
25 |
30 |
26 For AF_UNIX, the path is in host, and port is empty, and the socktype is the given unix=... value. |
31 For AF_UNIX, the path is in host, and port is empty, and the socktype is the given unix=... value. |
27 """ |
32 """ |
28 |
33 |
29 family, socktype = URL[scheme] |
34 family, socktype = URL_SCHEMES[scheme] |
30 url = urlparse.urlparse(str) |
35 url = urlparse.urlparse(str) |
31 |
36 |
32 # TODO: UNIX? |
37 # TODO: UNIX? |
33 if url.scheme and url.netloc : |
38 if url.scheme and url.netloc : |
34 # proper url |
39 # proper url |
35 family, socktype = URL[url.scheme] |
40 family, socktype = URL_SCHEMES[url.scheme] |
36 |
41 |
37 return family, socktype, url.hostname, url.port or port |
42 return family, socktype, url.hostname, url.port or port |
38 |
43 |
39 elif url.scheme and url.path : |
44 elif url.scheme and url.path : |
40 # host:port |
45 # host:port |
54 |
59 |
55 family, socktype, host, port = parse(str, *args, **kwargs) |
60 family, socktype, host, port = parse(str, *args, **kwargs) |
56 |
61 |
57 if family == socket.AF_UNIX : |
62 if family == socket.AF_UNIX : |
58 raise ValueError("XXX: AF_UNIX is not yet supported", str) |
63 raise ValueError("XXX: AF_UNIX is not yet supported", str) |
|
64 |
59 else : # AF_UNSPEC |
65 else : # AF_UNSPEC |
60 return connect_inet(host, port, family=family, socktype=socktype) |
66 return connect_inet(host, port, family=family, socktype=socktype) |
61 |
67 |
62 def connect_inet (host=None, port=None, family=socket.AF_UNSPEC, socktype=socket.SOCK_STREAM) : |
68 def connect_inet (host=None, port=None, family=socket.AF_UNSPEC, socktype=socket.SOCK_STREAM) : |
63 """ |
69 """ |
101 return sock |
107 return sock |
102 |
108 |
103 else : |
109 else : |
104 raise Exception("Unable to connect: %s:%s: %s" % (host, port, error)) |
110 raise Exception("Unable to connect: %s:%s: %s" % (host, port, error)) |
105 |
111 |
106 def reverse (self, sockaddr, numeric_host=False, numeric_port=True) : |
112 def reverse (sockaddr, numeric_host=False, numeric_port=True) : |
107 """ |
113 """ |
108 Resolve given sockaddr, returning (host, port). |
114 Resolve given sockaddr, returning (host, port). |
109 """ |
115 """ |
110 |
116 |
111 flags = 0 |
117 flags = 0 |
115 |
121 |
116 if numeric_port : |
122 if numeric_port : |
117 flags |= socket.NI_NUMERICSERV |
123 flags |= socket.NI_NUMERICSERV |
118 |
124 |
119 return socket.getnameinfo(sockaddr, flags) |
125 return socket.getnameinfo(sockaddr, flags) |
|
126 |
|
127 def socket_str (sock) : |
|
128 peer = sock.getpeername() |
|
129 |
|
130 # lookup scheme |
|
131 for scheme, (family, socktype) in URL : |
|
132 if family and family != sock.family : |
|
133 continue |
|
134 elif socktype and socktype != sock.type : |
|
135 continue |
|
136 else : |
|
137 break |
|
138 else : |
|
139 scheme = None |
|
140 |
|
141 host, port = reverse(peer) |
|
142 |
|
143 if scheme : |
|
144 return "{scheme}://{host}:{port}".format(scheme=scheme, host=host, port=port) |
|
145 else : |
|
146 return "{host}:{port}".format(host=host, port=port) |
120 |
147 |
121 def nonblocking (call, *args, **kwargs) : |
148 def nonblocking (call, *args, **kwargs) : |
122 """ |
149 """ |
123 Call the given function, which read/writes on a nonblocking file, and return None if it would have blocked. |
150 Call the given function, which read/writes on a nonblocking file, and return None if it would have blocked. |
124 """ |
151 """ |
213 # split out one line |
241 # split out one line |
214 line, self._buf = self._buf.split('\n', 1) |
242 line, self._buf = self._buf.split('\n', 1) |
215 |
243 |
216 # in case we had \r\n |
244 # in case we had \r\n |
217 line = line.rstrip('\r') |
245 line = line.rstrip('\r') |
|
246 |
|
247 log.debug("%s: %s", self, line) |
218 |
248 |
219 return line |
249 return line |
220 |
250 |
221 def readlines (self) : |
251 def readlines (self) : |
222 """ |
252 """ |