terom@4: from twisted.internet import protocol, reactor terom@4: from twisted.python import log terom@1: from datetime import datetime terom@6: import sys terom@1: terom@1: import buffer terom@1: terom@4: PORT = 34888 terom@4: SERVER_HOST = "127.0.0.1" terom@1: terom@6: from api_secret import secret terom@6: terom@1: class ModuleInfo (object) : terom@1: """ terom@1: Some info about a module terom@1: """ terom@1: terom@1: # module's name terom@1: name = None terom@1: terom@1: # module's version, as a 16-bit integer terom@1: version = None terom@1: terom@1: # list of valid event types (strings) terom@1: event_types = None terom@1: terom@1: def __str__ (self) : terom@1: return "Module %s:%d" % (self.name, self.version) terom@1: terom@1: def __repr__ (self) : terom@1: return "" % (self.name, self.version, ", ".join(self.event_types)) terom@1: terom@1: class Event (object) : terom@1: # the ModuleInfo object terom@1: module = None terom@1: terom@1: # the event type as a string terom@1: type = None terom@1: terom@1: # event message as a string (under 255 bytes in length!) terom@1: msg = None terom@1: terom@1: # timestamp as a datetime.datetime terom@1: when = None terom@1: terom@1: def __init__ (self, module, type, msg) : terom@1: assert type in module.event_types, "Invalid event-type %s for %r" % (type, self.module) terom@1: terom@1: self.module = module terom@1: self.type = type terom@1: self.msg = msg terom@1: terom@1: self.when = datetime.now() terom@1: terom@1: def __str__ (self) : terom@1: return "[%s] %s" % (self.type, self.msg) terom@1: terom@1: def __repr__ (self) : terom@1: return "%s @ %s" % (self.type, self.when) terom@4: terom@4: CLIENT_COMMANDS = [ terom@4: "module_init", terom@4: "module_event", terom@4: ] terom@4: terom@4: SERVER_COMMANDS = [ terom@4: "module_ok", terom@4: ] terom@4: terom@1: class ServerProtocol (buffer.StreamProtocol, protocol.Protocol) : terom@4: RECV_COMMANDS = CLIENT_COMMANDS terom@4: SEND_COMMANDS = SERVER_COMMANDS terom@1: terom@1: VALID_STATES = [ terom@1: "wait_init", terom@1: "wait_event" terom@1: ] terom@1: terom@1: # proto state terom@1: state = None terom@1: terom@1: # module info terom@1: module = None terom@1: terom@1: def _assert (self, condition, msg) : terom@1: if not condition : terom@1: self.transport.loseConnection() terom@1: log.err("assert failed in APIProtocol for %s: %s" % (self.module, msg)) terom@1: terom@1: def connectionMade (self) : terom@1: log.msg("Client connected") terom@1: terom@4: def connectionLost (self, reason) : terom@4: log.msg("Connection lost: %s" % reason) terom@4: terom@4: if self.module : terom@4: self.factory.nexus.unregisterModule(self.module, reason.getErrorMessage()) terom@4: terom@1: def on_module_init (self, i) : terom@1: self._assert(not self.module, "module_init with non-None self.module") terom@1: terom@6: peer_secret = i.readVarLen('B') terom@6: terom@6: self._assert(peer_secret == secret, "Mismatching API secrets!") terom@6: terom@4: m = ModuleInfo() terom@1: terom@1: m.name = i.readVarLen('B') terom@1: m.version = i.readItem('H') terom@1: terom@4: m.event_types = list(buffer.readStringStream(i, 'B')) terom@1: m.addr = self.transport.getPeer() terom@1: terom@4: self.module_name = m.name terom@1: terom@4: log.msg("Got mod_init for %r" % m) terom@4: terom@4: self.factory.nexus.registerModule(m, self) terom@1: terom@4: self.module = m terom@4: terom@4: o = self.startCommand('module_ok') terom@4: terom@4: self.send(o) terom@1: terom@1: def on_module_event (self, i) : terom@1: self._assert(self.module, "module_event with None self.module!") terom@1: terom@1: event_type = i.readEnum(self.module.event_types) terom@1: event_msg = i.readVarLen('B') terom@1: terom@4: e = Event(self.module, event_type, event_msg) terom@1: terom@4: log.msg("Got mod_event of %r" % (e)) terom@1: terom@1: self.factory.nexus.handleEvent(e) terom@1: terom@1: def logPrefix (self) : terom@1: if self.module : terom@1: return str(self.module) terom@1: else : terom@6: return super(ServerProtocol, self).logPrefix() terom@1: terom@4: class ClientProtocol (buffer.StreamProtocol, protocol.Protocol) : terom@4: RECV_COMMANDS = SERVER_COMMANDS terom@4: SEND_COMMANDS = CLIENT_COMMANDS terom@1: terom@4: def connectionMade (self) : terom@4: log.msg("Connected to API server, sending module init message") terom@4: terom@4: o = self.startCommand('module_init') terom@6: o.writeVarLen('B', secret) terom@4: o.writeVarLen('B', self.factory.name) terom@4: o.writeItem("H", self.factory.version) terom@4: buffer.writeStringStream(o, 'B', self.factory.event_types) terom@4: terom@4: self.send(o) terom@4: terom@4: def sendEvent (self, event) : terom@4: o = self.startCommand('module_event') terom@4: o.writeEnum(self.factory.event_types, event.type) terom@4: o.writeVarLen('B', event.msg) terom@4: terom@4: self.send(o) terom@4: terom@4: def on_module_ok (self, i) : terom@4: log.msg("Registration OK") terom@4: terom@4: self.factory.connected(self) terom@4: terom@4: def logPrefix (self) : terom@4: return "module %s:%d client" % (self.factory.name, self.factory.version) terom@4: terom@4: class Module (ModuleInfo, protocol.ClientFactory) : terom@4: protocol = ClientProtocol terom@4: terom@4: def __init__ (self) : terom@4: log.msg("Connecting to %s:%d" % (SERVER_HOST, PORT)) terom@4: reactor.connectTCP(SERVER_HOST, PORT, self) terom@4: terom@4: self.connection = None terom@6: terom@6: def run (self) : terom@6: log.startLogging(sys.stderr) terom@6: terom@6: reactor.run() terom@4: terom@4: def connected (self, connection) : terom@4: log.msg("Connected!") terom@4: self.connection = connection terom@4: terom@4: self.handleConnect() terom@4: terom@4: def disconnect (self) : terom@4: self.connection.transport.loseConnection() terom@4: terom@4: def sendEvent (self, type, msg) : terom@4: self.connection.sendEvent(self.buildEvent(type, msg)) terom@4: terom@4: def buildEvent (self, type, msg) : terom@4: return Event(self, type, msg) terom@4: terom@4: def handleConnect (self) : terom@4: """ terom@4: Do something terom@4: """ terom@4: terom@4: pass terom@4: