from time import monotonic as now import asyncio from .versions import VERSION class Session: def __init__(self, uid, connection, on_timeout): self.uid = uid self.connection = connection self.timeout = now() + 60 #TODO move timeout to env self.watcher_task = asyncio.create_task(self.watch(on_timeout)) self.state = "new" self.err_count = 0 self.timeout_attempt = 0 self.upf = hash(now()) self.pv = "v1" #default protocol version self.static_meta = { # "name" : "unnamed", # "type" : "unknown", # "in" : set(), # "out" : set(), } self.dynamic_meta = { # "active_in" : set() # "active_out" : set() } self.last_data = {} async def feed (self, package): ''' package here is hust a dict with dialect we meet here to solve the dialect ''' com = package.get(VERSION[self.pv]["COMMAND"]) data = package.get(VERSION[self.pv]["PAYLOAD"]) if com is None : return {"type" : "disconnect"} #BAN if self.state == "new" and com == VERSION[self.pv]["CONNECT"]: ... #TODO #process new connection? auth? get and fill some data self.static_meta["name"] = data.get("name", "unnamed") self.static_meta["type"] = data.get("type", "unknown") self.static_meta["in"] = set(data.get("in", [])) self.dynamic_meta["active_in"] = set() self.static_meta["out"] = set(data.get("out", [])) self.dynamic_meta["active_out"] = set() self.static_meta["com"] = set(data.get("com", [])) self.static_meta["auth"] = data.get("token") self.state = "token" self.send({VERSION[self.pv]["COMMAND"] : VERSION[self.pv]["TOKEN_ASK"], VERSION[self.pv]["PAYLOAD"] : self.uid, VERSION[self.pv]["UPF"] : self.upf}) return if self.state == "token" and com == VERSION[self.pv]["TOKEN_ANS"]: self.state = "ready" self.send({VERSION[self.pv]["COMMAND"] : VERSION[self.pv]["HANDSHAKE_ASK"]}) old_token = data.get(VERSION[self.pv]["TOKEN"]) old_upf = data.get(VERSION[self.pv]["UPF"]) if old_token and old_upf: return {"type" : "reconnect", "uid" : old_token, "upf" : old_upf} return if self.state == "ready" and com == VERSION[self.pv]["HANDSHAKE_ANS"]: if not data.get(VERSION[self.pv]["STATUS"]): if self.timeout_attempt == 5 : return {"type" : "disconnect"} self.timeout_attempt += 1 await asyncio.sleep(self.timeout_attempt) self.send({VERSION[self.pv]["COMMAND"] : VERSION[self.pv]["HANDSHAKE_ASK"]}) return {"type" : "await handshake", "state" : data.get(VERSION[self.pv]["STATE"])} self.state = "air" return {"type" : "connect", "uid" : self.uid} if self.state == "air": data = package[VERSION[self.pv]["PAYLOAD"]] if com == VERSION[self.pv]["HEARTBEAT_ASK"]: #TODO update deadline self.timeout = now() + 5 #TODO move to env self.send({VERSION[self.pv]["COMMAND"] : VERSION[self.pv]["HEARTBEAT_ANS"]}) return self.update(com, data) return {"type" : "data", "data" : com} self.err_count += 1 if self.err_count == 5 : return {"type" : "disconnect"} #BAN async def watch(self, on_timeout): while self.state not in {"stop", "migrated"}: await asyncio.sleep(1) if self.state in {"stop", "migrated"}: return if now() >= self.timeout: await on_timeout( self.uid, {"type": "disconnect"}, ) return def update(self, command, data): self.last_data[command] = data def send(self, package): #TODO #decide if we want dialect pack here or not if self.state in {"stop", "migrated"} : return asyncio.create_task( self.connection.send(package) ) async def stop(self): self.state = "stop" await self.connection.close()