diff --git a/justserv/__init__.py b/justserv/__init__.py new file mode 100644 index 0000000..22b304d --- /dev/null +++ b/justserv/__init__.py @@ -0,0 +1,8 @@ +import justserv.patch +import justserv.server +import justserv.client + +#URL = "https://justserv.l--n.de" +URL = "http://localhost:8080" + + diff --git a/justserv/client.py b/justserv/client.py new file mode 100644 index 0000000..be4be17 --- /dev/null +++ b/justserv/client.py @@ -0,0 +1,22 @@ +import requests +import justserv +import copy + +class OnlineState: + def __init__(self, appname = None): + if appname is None: + appname = "" + self.__url = "{}/{}".format(justserv.URL, appname) + self.__laststate = {} + + def getState(self): + s = eval(requests.get(self.__url).text) + self.__laststate = copy.deepcopy(s) + return s + + def patch(self, newstate): + d = justserv.patch.diff(self.__laststate, newstate) + ns = eval(requests.patch(self.__url, data=str(d)).text) + self.__laststate = ns.copy() + return ns + diff --git a/justserv/patch.py b/justserv/patch.py new file mode 100644 index 0000000..a58a955 --- /dev/null +++ b/justserv/patch.py @@ -0,0 +1,30 @@ + + +def patch(state,p): + for k in p.keys(): + if p[k] is None: + del state[k] + elif k in state and isinstance(state[k], dict) and isinstance(p[k], dict): + patch(state[k], p[k]) + else: + state[k] = p[k] + +def diff(state, newstate): + d = {} + + for k in state.keys(): + if not k in newstate: + d[k] = None + + for k in newstate.keys(): + if k in state: + if isinstance(state[k], dict) and isinstance(newstate[k], dict): + dc = diff(state[k], newstate[k]) + if dc: + d[k] = dc + elif state[k] != newstate[k]: + d[k] = newstate[k] + else: + d[k] = newstate[k] + + return d diff --git a/justserv/server.py b/justserv/server.py new file mode 100644 index 0000000..f828ec8 --- /dev/null +++ b/justserv/server.py @@ -0,0 +1,116 @@ +#!/usr/bin/python3 + +import http.server +import os.path +import sys +import random + +import justserv + +class StateStore: + + def __init__(self, dir = None): + if dir is None: + dir = "./__states__" + + if not os.path.isdir(dir): + os.mkdir(dir) + + self.__dir = dir + self.__cache = {} + + def getState(self, appname): + sp = "{}/{}.state".format(self.__dir, appname) + if not appname in self.__cache: + if os.path.isfile(sp): + with open(sp, "rb") as sf: + st = sf.read().decode("utf-8") + self.__cache[appname] = eval(st) + else: + self.__cache[appname] = {} + + return self.__cache[appname] + + def patchState(self, appname, newstate): + state = self.getState(appname) + justserv.patch.patch(state, newstate) + self.setState(appname, state) + + def setState(self, appname, state): + self.__cache[appname] = state + + sp = "{}/{}.state".format(self.__dir, appname) + st = str(state) + with open(sp, "wb") as sf: + sf.write(st.encode("utf-8")) + + +class JSRequestHandler(http.server.BaseHTTPRequestHandler): + _store = StateStore() + queue = {} + + def getAppname(self): + if self.path == "" or self.path == "/": + p = "__root__" + else: + p = self.path.split("/")[1] + return p + + def do_GET(self): + #queue + while True: + ip = random.randint(100000000, 999999999) + if ip not in self.queue: + break + self.queue[ip] = len(self.queue)+1 + print("start queue for {} is {}".format(ip, len(self.queue))) + while True: + if self.queue[ip] == 0: + del self.queue[ip] + break + if min(self.queue.values()) != 0: + self.queue[ip] -= 1 + print("queue for {} is {}".format(ip, len(self.queue))) + + #queue done + + p = self.getAppname() + + s = self._store.getState(p) + sb = str(s).encode("utf-8") + + self.send_response(200) + self.send_header("content-length", len(sb)) + self.end_headers() + + self.wfile.write(sb) + + def do_PATCH(self): + #queue + while True: + ip = random.randint(100000000, 999999999) + if ip not in self.queue: + break + self.queue[ip] = len(self.queue) + print("start queue for {} is {}".format(ip, len(self.queue))) + while True: + if self.queue[ip] == 0: + del self.queue[ip] + break + if min(self.queue.values()) != 0: + self.queue[ip] -= 1 + print("queue for {} is {}".format(ip, len(self.queue))) + + #queue done + + p = self.getAppname() + + bpatch = self.rfile.read(int(self.headers.get('content-length', 0))) + ps = eval(bpatch.decode("utf-8")) + self._store.patchState(p, ps) + return self.do_GET() + +if __name__ == '__main__': + httpd = http.server.HTTPServer( ('', 8080 ), JSRequestHandler) + httpd.serve_forever() +