--- a/pvl/socket.py Sun Jan 13 02:11:12 2013 +0200
+++ b/pvl/socket.py Sun Jan 13 02:11:30 2013 +0200
@@ -8,16 +8,21 @@
socket = __import__('socket')
import select
+import errno
import urlparse
import logging; log = logging.getLogger('pvl.socket')
-URL = {
- 'tcp': (0, socket.SOCK_STREAM ), # AF_UNSPEC
- 'udp': (0, socket.SOCK_DGRAM ), # AF_UNSPEC
- 'unix': (socket.AF_UNIX, None ), # socktype is given
-}
+# order matters!
+URL = (
+ # scheme family socktype
+ ( 'unix', (socket.AF_UNIX, None ) ), # socktype is given
+ ( 'tcp', (0, socket.SOCK_STREAM ) ), # AF_UNSPEC
+ ( 'udp', (0, socket.SOCK_DGRAM ) ), # AF_UNSPEC
+)
+
+URL_SCHEMES = dict(URL)
def parse (str, port=None, scheme='tcp', unix=socket.SOCK_DGRAM) :
"""
@@ -26,13 +31,13 @@
For AF_UNIX, the path is in host, and port is empty, and the socktype is the given unix=... value.
"""
- family, socktype = URL[scheme]
+ family, socktype = URL_SCHEMES[scheme]
url = urlparse.urlparse(str)
# TODO: UNIX?
if url.scheme and url.netloc :
# proper url
- family, socktype = URL[url.scheme]
+ family, socktype = URL_SCHEMES[url.scheme]
return family, socktype, url.hostname, url.port or port
@@ -56,6 +61,7 @@
if family == socket.AF_UNIX :
raise ValueError("XXX: AF_UNIX is not yet supported", str)
+
else : # AF_UNSPEC
return connect_inet(host, port, family=family, socktype=socktype)
@@ -103,7 +109,7 @@
else :
raise Exception("Unable to connect: %s:%s: %s" % (host, port, error))
-def reverse (self, sockaddr, numeric_host=False, numeric_port=True) :
+def reverse (sockaddr, numeric_host=False, numeric_port=True) :
"""
Resolve given sockaddr, returning (host, port).
"""
@@ -118,6 +124,27 @@
return socket.getnameinfo(sockaddr, flags)
+def socket_str (sock) :
+ peer = sock.getpeername()
+
+ # lookup scheme
+ for scheme, (family, socktype) in URL :
+ if family and family != sock.family :
+ continue
+ elif socktype and socktype != sock.type :
+ continue
+ else :
+ break
+ else :
+ scheme = None
+
+ host, port = reverse(peer)
+
+ if scheme :
+ return "{scheme}://{host}:{port}".format(scheme=scheme, host=host, port=port)
+ else :
+ return "{host}:{port}".format(host=host, port=port)
+
def nonblocking (call, *args, **kwargs) :
"""
Call the given function, which read/writes on a nonblocking file, and return None if it would have blocked.
@@ -163,13 +190,14 @@
buf = nonblocking(self.sock.recv, block)
+ log.debug("%s: %s", self, buf)
+
# eof?
- if not buf :
+ if buf :
+ return buf
+ else :
raise EOFError()
- # ok
- return buf
-
def peek (self) :
"""
Peek at data in buffer.
@@ -216,6 +244,8 @@
# in case we had \r\n
line = line.rstrip('\r')
+ log.debug("%s: %s", self, line)
+
return line
def readlines (self) :
@@ -238,6 +268,9 @@
__iter__ = readlines
+ def __str__ (self) :
+ return socket_str(self.sock)
+
class WriteStream (object) :
"""
Writable stream, supporting non-blocking/buffered writes.
@@ -311,10 +344,18 @@
Write out line.
"""
- self.write(str(line) + eol)
+ log.debug("%s: %s", self, line)
+
+ self.write(str(line))
+ self.write(eol)
def __call__ (self, *lines) :
for line in lines :
self.writeline(line)
# TODO: flush
+
+ def __str__ (self) :
+ return socket_str(self.sock)
+
+