class SocketBuffer(StateMachine): def __init__(self, sock, max_buffer): StateMachine.__init__(self, 'reading') self._sock = sock self._max_buffer = max_buffer def __repr__(self): return '<SocketBuffer at 0x%x: socket %s max_buffer %i>' % ( id(self), self._sock, self._max_buffer) def setup(self): src = self._src = SocketEventSource(self._sock) src.stop_writing() # We'll start writing when we need to. self.mainloop.add_event_source(src) self._wbuf = StringBuffer() spec = [ # state, source, event_class, new_state, callback ('reading', src, SocketReadable, 'reading', self._fill), ('reading', self, _WriteBufferNotEmpty, 'rw', self._start_writing), ('reading', self, SocketBufferEof, 'idle', None), ('reading', self, _Close, None, self._really_close), ('rw', src, SocketReadable, 'rw', self._fill), ('rw', src, SocketWriteable, 'rw', self._flush), ('rw', self, _WriteBufferIsEmpty, 'reading', self._stop_writing), ('rw', self, SocketBufferEof, 'w', None), ('rw', self, _Close, 'wc', None), ('idle', self, _WriteBufferNotEmpty, 'w', self._start_writing), ('idle', self, _Close, None, self._really_close), ('w', src, SocketWriteable, 'w', self._flush), ('w', self, _WriteBufferIsEmpty, 'idle', self._stop_writing), ('wc', src, SocketWriteable, 'wc', self._flush), ('wc', self, _WriteBufferIsEmpty, None, self._really_close), ] self.add_transitions(spec) def write(self, data): '''Put data into write queue.''' was_empty = len(self._wbuf) == 0 self._wbuf.add(data) if was_empty and len(self._wbuf) > 0: self._start_writing(None, None) self.mainloop.queue_event(self, _WriteBufferNotEmpty()) def close(self): '''Tell state machine to terminate.''' self.mainloop.queue_event(self, _Close()) def _report_error(self, event_source, event): logging.error(str(event)) def _fill(self, event_source, event): try: data = event.sock.read(self._max_buffer) except (IOError, OSError), e: logging.debug( '%s: _fill(): Exception %s from sock.read()', self, e) return [SocketError(event.sock, e)] if data: self.mainloop.queue_event(self, SocketBufferNewData(data)) else: event_source.stop_reading() self.mainloop.queue_event(self, SocketBufferEof())
class JsonMachine(StateMachine): '''A state machine for sending/receiving JSON messages across TCP.''' max_buffer = 16 * 1024 def __init__(self, conn): StateMachine.__init__(self, 'rw') self.conn = conn self.debug_json = False def __repr__(self): return '<JsonMachine at 0x%x: socket %s, max_buffer %s>' % \ (id(self), self.conn, self.max_buffer) def setup(self): sockbuf = self.sockbuf = SocketBuffer(self.conn, self.max_buffer) self.mainloop.add_state_machine(sockbuf) self._eof = False self.receive_buf = StringBuffer() spec = [ # state, source, event_class, new_state, callback ('rw', sockbuf, SocketBufferNewData, 'rw', self._parse), ('rw', sockbuf, SocketBufferEof, 'w', self._send_eof), ('rw', self, _Close2, None, self._really_close), ('w', self, _Close2, None, self._really_close), ] self.add_transitions(spec) def send(self, msg): '''Send a message to the other side.''' if self.debug_json: logging.debug('JsonMachine: Sending message %s' % repr(msg)) s = json.dumps(yaml.safe_dump(msg)) if self.debug_json: logging.debug('JsonMachine: As %s' % repr(s)) self.sockbuf.write('%s\n' % s) def close(self): '''Tell state machine it should shut down. The state machine will vanish once it has flushed any pending writes. ''' self.mainloop.queue_event(self, _Close2()) def _parse(self, event_source, event): data = event.data self.receive_buf.add(data) if self.debug_json: logging.debug('JsonMachine: Received: %s' % repr(data)) while True: line = self.receive_buf.readline() if line is None: break line = line.rstrip() if self.debug_json: logging.debug('JsonMachine: line: %s' % repr(line)) msg = yaml.load(json.loads(line)) self.mainloop.queue_event(self, JsonNewMessage(msg)) def _send_eof(self, event_source, event): self.mainloop.queue_event(self, JsonEof()) def _really_close(self, event_source, event): self.sockbuf.close() self._send_eof(event_source, event)
class SocketBuffer(StateMachine): def __init__(self, sock, max_buffer): StateMachine.__init__(self, 'reading') self._sock = sock self._max_buffer = max_buffer def __repr__(self): return '<SocketBuffer at 0x%x: socket %s max_buffer %i>' % ( id(self), self._sock, self._max_buffer) def setup(self): src = self._src = SocketEventSource(self._sock) src.stop_writing() # We'll start writing when we need to. self.mainloop.add_event_source(src) self._wbuf = StringBuffer() spec = [ # state, source, event_class, new_state, callback ('reading', src, SocketReadable, 'reading', self._fill), ('reading', self, _WriteBufferNotEmpty, 'rw', self._start_writing), ('reading', self, SocketBufferEof, 'idle', None), ('reading', self, _Close, None, self._really_close), ('rw', src, SocketReadable, 'rw', self._fill), ('rw', src, SocketWriteable, 'rw', self._flush), ('rw', self, _WriteBufferIsEmpty, 'reading', self._stop_writing), ('rw', self, SocketBufferEof, 'w', None), ('rw', self, _Close, 'wc', None), ('idle', self, _WriteBufferNotEmpty, 'w', self._start_writing), ('idle', self, _Close, None, self._really_close), ('w', src, SocketWriteable, 'w', self._flush), ('w', self, _WriteBufferIsEmpty, 'idle', self._stop_writing), ('wc', src, SocketWriteable, 'wc', self._flush), ('wc', self, _WriteBufferIsEmpty, None, self._really_close), ] self.add_transitions(spec) def write(self, data): '''Put data into write queue.''' was_empty = len(self._wbuf) == 0 self._wbuf.add(data) if was_empty and len(self._wbuf) > 0: self._start_writing(None, None) self.mainloop.queue_event(self, _WriteBufferNotEmpty()) def close(self): '''Tell state machine to terminate.''' self.mainloop.queue_event(self, _Close()) def _report_error(self, event_source, event): logging.error(str(event)) def _fill(self, event_source, event): try: data = event.sock.read(self._max_buffer) except (IOError, OSError), e: logging.debug('%s: _fill(): Exception %s from sock.read()', self, e) return [SocketError(event.sock, e)] if data: self.mainloop.queue_event(self, SocketBufferNewData(data)) else: event_source.stop_reading() self.mainloop.queue_event(self, SocketBufferEof())