class RequestHandle(object): DONE = get_event() OVERFLOW = get_event() MAX_SIZE = 1024 * 5024 def __init__(self, spin): self.request = None xmap(spin, TransferHandle.DONE, self.process) # It will not be spawned if it is a websocket connection. xmap( spin, TmpFile.DONE, lambda spin, fd, data: spawn( spin, RequestHandle.DONE, self.request)) def process(self, spin, request, data): self.request = request contype = request.headers.get('connection', '').lower() uptype = request.headers.get('upgrade', '').lower() if contype == 'upgrade' and uptype == 'websocket': spawn(spin, RequestHandle.DONE, request) else: self.accumulate(spin, data) def accumulate(self, spin, data): size = int(self.request.headers.get('content-length', '0')) NonPersistentConnection(spin) # PersistentConnection(spin) if RequestHandle.MAX_SIZE <= size: spawn(spin, RequestHandle.OVERFLOW, self.request) else: TmpFile(spin, data, size, self.request.fd)
class Fixed(object): """ A handle for application layers that demand fixed chunk transmission of data. def on_found(spin, data): print data Fixed(spin, size=4) xmap(spin, FOUND, on_found) If it arises a chunk of size equal or greater than 4 then FOUND is processed. """ FOUND = get_event() def __init__(self, spin, size=4): spin.add_map(LOAD, self.update) self.arr = bytearray() self.size = size def update(self, spin, data): self.arr.extend(data) for ind in xrange(self.size, len(self.arr) + 1, self.size): chunk = buffer(self.arr, ind - self.size, self.size) spin.drive(Fixed.FOUND, chunk) else: try: del self.arr[:ind] except NameError: pass
class TransferHandle(object): DONE = get_event() def __init__(self, spin): xmap( spin, AccUntil.DONE, lambda spin, request, data: spawn( spin, TransferHandle.DONE, Request(request), data))
class AccUntil(object): DONE = get_event() def __init__(self, spin, data='', delim='\r\n\r\n'): self.delim = delim self.arr = bytearray() self.spin = spin spin.add_map(LOAD, self.update) self.update(spin, data) def update(self, spin, data): self.arr.extend(data) if self.delim in self.arr: self.process(spin) def process(self, spin): spin.del_map(LOAD, self.update) a, b = self.arr.split(self.delim, 1) spin.drive(AccUntil.DONE, str(a), str(b))
class TmpFile(object): DONE = get_event() def __init__(self, spin, data, max_size, fd): self.fd = fd self.max_size = max_size spin.add_map(LOAD, self.update) self.update(spin, data) def update(self, spin, data): pos = self.fd.tell() count = self.max_size - pos self.fd.write(data[:count]) if len(data) < count: return spin.del_map(LOAD, self.update) spin.drive(TmpFile.DONE, self.fd, data[count:])
class Terminator: """ A handle for application layer protocols that use '\r\n' as token. def on_found(spin, data): print data Terminator(spin, delim='\r\n') xmap(spin, FOUND, on_found) If it arises the sequence below then on_found is processed. 'some-data\r\n' """ FOUND = get_event() def __init__(self, spin, delim='\r\n'): self.delim = delim self.arr = bytearray() spin.add_map(LOAD, self.update) def update(self, spin, data): self.arr.extend(data) if not self.delim in data: return data = str(self.arr) lst = data.split(self.delim) del self.arr[:] self.arr.extend(lst.pop(-1)) for ind in lst: spin.drive(Terminator.FOUND, ind)
def get_env(header): """ Shouldn't be called outside this module. """ environ = { 'REQUEST_METHOD': 'POST', 'CONTENT_LENGTH': header.get('content-length', 0), 'CONTENT_TYPE': header.get('content-type', 'text') } return environ OPEN_FILE_ERR = get_event() def drop(spin, filename): """ Shouldn't be called outside this module. """ try: fd = open(filename, 'rb') except IOError as excpt: err = excpt.args[0] spawn(spin, OPEN_FILE_ERR, err) else: spin.dumpfile(fd)
def get_env(header): """ Shouldn't be called outside this module. """ environ = { 'REQUEST_METHOD':'POST', 'CONTENT_LENGTH':header.get('content-length', 0), 'CONTENT_TYPE':header.get('content-type', 'text') } return environ OPEN_FILE_ERR = get_event() def drop(spin, filename): """ Shouldn't be called outside this module. """ try: fd = open(filename, 'rb') except IOError as excpt: err = excpt.args[0] spawn(spin, OPEN_FILE_ERR, err) else: spin.dumpfile(fd) def make(searchpath, folder): """
from untwisted.event import get_event from untwisted.splits import Terminator from re import * GENERAL_STR = '[^ ]+' GENERAL_REG = compile(GENERAL_STR) SESSION_STR = '\*\*\*\* Starting FICS session as (?P<username>.+) \*\*\*\*' SESSION_REG = compile(SESSION_STR) TELL_STR = '(?P<nick>[a-zA-Z]+)(?P<mode>.*) tells you:(?P<msg>.+)' TELL_REG = compile(TELL_STR) SAY_STR = '(?P<nick>[a-zA-Z]+)(?P<mode>.*) says:(?P<msg>.+)' SAY_REG = compile(SAY_STR) SHOUT_STR = '(?P<nick>[a-zA-Z]+)(?P<mode>.*) shouts:(?P<msg>.+)' SHOUT_REG = compile(SHOUT_STR) START_SESSION = get_event() TELL = get_event() SAY = get_event() SHOUT = get_event() def install(spin): spin.add_map(Terminator.FOUND, spliter) def spliter(spin, data): m = findall(GENERAL_REG, data) if m: spawn(spin, *m) m = match(SESSION_REG, data) try: username = m.group('username')