import socket import select import subprocess import re import logging l = logging.getLogger('puppeteer.connection') #l.setLevel(logging.DEBUG) from .errors import ConnectionFail def rw_alias(f): ''' Makes an alias for read/recv and write/send. ''' print "CLASS:",f.im_class return f class Connection(object): ''' A connection handler for puppeteer. Basically a wrapper around sockets or files. Will handle all sorts of intelligent stuff like timeouts and crap. ''' def __init__(self, host=None, port=None, exe=None, args=(), s=None, fd=None): self.host = host self.port = port self.exe_args = [ exe ] + list(args) self.s = s self.p = fd self.connected = None if s is None and fd is None else True def copy(self): return Connection(host=self.host, port=self.port, exe=self.exe_args[0], args=self.exe_args[1:]) def connect(self): ''' Connect! ''' if self.host is not None and self.port is not None: self.s = socket.create_connection((self.host, self.port)) elif self.exe_args[0] is not None: self.p = subprocess.Popen(self.exe_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, bufsize=0) else: raise Exception("How are we supposed to connect, man?") l.info("Connected!") self.connected = True return self def close(self): if self.s: self.s.close() elif self.p: self.p.close() def send(self, msg): ''' Send the message. ''' l.debug("Sending: %r", msg) try: if self.s is not None: return self.s.sendall(msg) elif self.p is not None: return self.p.stdin.write(msg) except socket.error as e: raise ConnectionFail(str(e)) def recv(self, n, timeout=None, quash=False): ''' Receive up to n bytes. ''' #l.debug("recv(%d, timeout=%s)", n, timeout) slist = [ self.s if self.s is not None else self.p.stdout ] if timeout is not None: (rlist, _, _) = select.select(slist, [], [], timeout) else: (rlist, _, _) = select.select(slist, [], []) l.debug("recv with size %d and timeout %s", n, timeout) #print rlist, wlist, xlist if rlist == []: l.debug("RECV TIMEOUT") if not quash: raise ConnectionFail("very timeout") else: return "" try: readsock = rlist[0] if type(readsock) == socket.socket: r = readsock.recv(n) if type(readsock) == file: r = readsock.read(n) l.debug("read: %r",r) except socket.error: r = "" if len(r) == 0 and n != 0: raise ConnectionFail("Received nothing. Much sad.") #l.debug("Got: %r", r) return r def read(self, n=None, timeout=None, quash=False): ''' Read exactly n bytes, or all that's available if n is None. ''' if n is None: self.read_all(timeout=timeout) result = "" while len(result) < n: tmp = self.recv(n - len(result), timeout=timeout, quash=quash) if tmp == "" and timeout is not None: break result += tmp return result # read until the given string def read_until(self, c=None, regex=None, max_chars=None, timeout=None): ''' Read until the given string, or matching the given regex. ''' if c is None and regex is None: # they cannot both be None raise Exception('You must specify at least one argument') if c is not None and regex is not None: raise Exception('You cannot specify both c and regex.') if c is not None: l.debug("Reading until: %r", c) else: l.debug("Reading until: %s", regex) buf = "" tmp = None while max_chars is None or len(buf) < max_chars: l.debug("... so far: %s", buf) if c is not None and buf.endswith(c): break elif regex is not None and re.search(regex, buf): break tmp = self.recv(1, timeout=timeout) if tmp == "": break buf += tmp l.debug("... read: %r", buf) return buf def read_all(self, timeout=None, stepsize=8192): ''' Read as long as there is more stuff and the timeout is not expired. ''' buff = "" while True: try: s = self.recv(stepsize, timeout=timeout) except ConnectionFail: break if len(s) == 0: break buff += s return buff def shutdown(self, what): if self.s is not None: self.s.shutdown(what) elif self.p is not None: if what == socket.SHUT_WR: self.p.stdin.close() if what == socket.SHUT_RD: self.p.stdout.close()