134 lines
4.0 KiB
Python
134 lines
4.0 KiB
Python
import asyncore
|
|
import socket
|
|
import errno
|
|
import re
|
|
import circularlog
|
|
import logging
|
|
|
|
|
|
class LineHandler(asyncore.dispatcher_with_send):
|
|
|
|
def __init__(self, sock):
|
|
self.buffer = b""
|
|
asyncore.dispatcher_with_send.__init__(self, sock)
|
|
self.crlf = 0
|
|
|
|
def handle_read(self):
|
|
data = self.recv(8192)
|
|
if data:
|
|
parts = data.split(b"\n")
|
|
if len(parts) == 1:
|
|
self.buffer += data
|
|
else:
|
|
self.handle_line((self.buffer + parts[0]).decode('ascii'))
|
|
for part in parts[1:-1]:
|
|
if part[-1] == b"\r":
|
|
self.crlf = True
|
|
part = part[:-1]
|
|
else:
|
|
self.crlf = False
|
|
self.handle_line(part.decode('ascii'))
|
|
self.buffer = parts[-1]
|
|
|
|
def send_line(self, line):
|
|
self.send(line.encode('ascii') + (b"\r\n" if self.crlf else b"\n"))
|
|
|
|
def handle_line(self, line):
|
|
'''
|
|
test: simple echo handler
|
|
'''
|
|
self.send_line("> " + line)
|
|
|
|
|
|
class LineServer(asyncore.dispatcher):
|
|
|
|
def __init__(self, host, port, lineHandlerClass):
|
|
asyncore.dispatcher.__init__(self)
|
|
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
self.set_reuse_addr()
|
|
self.bind((host, port))
|
|
self.listen(5)
|
|
self.lineHandlerClass = lineHandlerClass
|
|
|
|
def handle_accept(self):
|
|
pair = self.accept()
|
|
if pair is not None:
|
|
sock, addr = pair
|
|
print("Incoming connection from %s" % repr(addr))
|
|
handler = self.lineHandlerClass(sock)
|
|
|
|
def loop(self):
|
|
asyncore.loop()
|
|
|
|
|
|
class Disconnected(Exception):
|
|
pass
|
|
|
|
|
|
class LineClient(object):
|
|
|
|
def __init__(self, host_port, announcement=None, filter_ascii=False, ridername="r"):
|
|
self.host_port = host_port
|
|
self.filter_ascii = filter_ascii
|
|
self.announcement = announcement
|
|
self.circular = circularlog.Rider(ridername)
|
|
self.connected = False
|
|
|
|
def connect(self):
|
|
logging.info("connect to %s %s", "%s:%d" % self.host_port, getattr(self, 'name', '?'))
|
|
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
self.socket.connect(self.host_port)
|
|
self.connected = True
|
|
self.buffer = [b""]
|
|
if self.announcement:
|
|
self.send_line('\n'.join(self.announcement))
|
|
|
|
def get_line(self):
|
|
if not self.connected:
|
|
logging.info("connect for get_line")
|
|
self.connect()
|
|
while len(self.buffer) <= 1:
|
|
self.socket.setblocking(0)
|
|
try:
|
|
data = self.socket.recv(1024)
|
|
except socket.error as e:
|
|
err = e.args[0]
|
|
if err == errno.EAGAIN or err == errno.EWOULDBLOCK:
|
|
return None
|
|
raise e
|
|
if data == "":
|
|
print(self.buffer, '<')
|
|
self.close()
|
|
raise Disconnected("disconnected")
|
|
self.socket.setblocking(1)
|
|
data = data.split(b'\n')
|
|
self.buffer[0] += data[0]
|
|
for p in data[1:]:
|
|
self.buffer.append(p)
|
|
line = self.buffer.pop(0).decode('ascii')
|
|
if len(line) > 0 and line[-1] == '\r':
|
|
line = line[0:-1]
|
|
self.circular.put("<", line)
|
|
# print '<', line
|
|
if self.filter_ascii:
|
|
# replace non ascii characters
|
|
line = re.sub(r'[^\x00-\x7E]+','?', line)
|
|
return line
|
|
|
|
def send_line(self, line):
|
|
if not self.connected:
|
|
logging.info("connect for cmd: %s", line)
|
|
self.connect()
|
|
# print '>', line
|
|
self.circular.put(">", line)
|
|
self.socket.sendall(line.encode('ascii') + b'\n')
|
|
|
|
def close(self):
|
|
self.socket.close()
|
|
self.connected = False
|
|
|
|
|
|
if __name__ == "__main__":
|
|
server = LineServer("localhost", 9999, LineHandler)
|
|
server.loop()
|