#!/usr/bin/env python3 import cbor, gzip, json, lzma, psycopg2, re, socket, time DEFAULT_CONFIG = { "db_blocks": { "type": "postgre", "info_postgre": { "connect": "host=127.0.0.1 port=5432 user=minetest password=PASSWORD dbname=minetest-world" } }, "server": { "listen_addr": "0.0.0.0", "listen_port": 8060, "write_passwords": [] } } CONFIG_PATH = "config.json" RECBUF = 1024 AVAILABLE_FORMATS = {"json": "text/json", "cbor": "application/cbor"} AVAILABLE_COMPRESSIONS = {"none": None, "gzip": "gzip", "lzma": "lzma"} p_clen = re.compile("\r?\ncontent-length: *(\d+)\r?\n?", re.IGNORECASE) p_stmt = re.compile("^(x|y|z)(<|>|=|<=|>=)-?\d{1,5}$") encode = { "json": lambda x: json.dumps(x).encode(), "cbor": cbor.dumps } compress = { "gzip": gzip.compress, "lzma": lzma.compress } def send_response(client, code, resp, resp_format, resp_compression): content_raw = encode[resp_format](resp) compression_str = "" if AVAILABLE_COMPRESSIONS[resp_compression]: compression_str = "Content-Encoding: " + AVAILABLE_COMPRESSIONS[resp_compression] + "\r\n" content_raw = compress[resp_compression](content_raw) mime = AVAILABLE_FORMATS[resp_format] client.sendall(("HTTP/1.1 "+code+"\r\nContent-type: "+mime+"; charset=UTF-8\r\n"+compression_str+"Access-Control-Allow-Origin: *\r\nContent-length: "+str(len(content_raw))+"\r\n\r\n").encode()+content_raw) client.close() if __name__ == "__main__": # Read config config_file = open(CONFIG_PATH, "r") config = json.load(config_file) config_file.close() # Open DB if config["db_blocks"]["type"] == "postgre": conn = psycopg2.connect(CONFIG_DB_CONNECT) # Start server server_addr = CONFIG_LISTEN if ":" in server_addr[0]: # IPv6 sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) else: # IPv4 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(5) server_addr = (config["server"]["listen_addr"], config["server"]["listen_port"]) sock.bind(server_addr) sock.listen(1) print("Server started at "+str(server_addr)) # Listen while True: try: client, addr = sock.accept() except socket.timeout: continue # Get request paquet = b"" header = b"" content = b"" content_len = 0 resp = {} resp_format = "json" resp_compression = "none" lf = 0 while True: raw = client.recv(RECBUF) if raw: paquet += raw if lf >= 0: for c in raw: if c == 10:# LF lf += 1 elif c != 13:# CR lf = 0 if lf > 1: parts = paquet.split(b"\r\n\r\n") header = parts[0] content = parts[1] try: content_len = int(p_clen.search(header.decode()).group(1)) except (AttributeError, ValueError): content_len = 0 break if lf > 1: break else: break while len(content) < content_len: raw = client.recv(RECBUF) paquet += raw content += raw # Get URL httpreq = paquet.split(b"\n") try: url = httpreq[0].split(b" ")[1].decode().split("/") except IndexError: send_response(client, "400 Bad Request", {"error": "bad_http"}, resp_format, resp_compression) continue while "" in url: url.remove("") urll = len(url) if urll > 32: send_response(client, "400 Bad Request", {"error": "too_many_args"}, resp_format, resp_compression) continue stmts = [] bad = False token = None for val in url: if token: if token == 1: if not val in AVAILABLE_FORMATS: bad = True break resp_format = val token = None continue elif token == 2: if not val in AVAILABLE_COMPRESSIONS: bad = True break resp_compression = val token = None continue bad = True break if val == "fmt": token = 1 continue elif val == "cpr": token = 2 continue if not p_stmt.match(val): bad = True break stmts.append("pos"+val) if bad: send_response(client, "400 Bad Request", {"error": "bad_request"}, resp_format, resp_compression) continue if len(stmts) > 6: send_response(client, "400 Bad Request", {"error": "too_many_statements"}, resp_format, resp_compression) continue if len(stmts) == 0: send_response(client, "400 Bad Request", {"error": "no_statement"}, resp_format, resp_compression) continue req = " AND ".join(stmts) cur = conn.cursor() cur.execute("SELECT * FROM blocks WHERE "+req+" LIMIT 1000000;") resp["blocks"] = [] while True: block = cur.fetchone() if not block: break resp["blocks"].append([block[0], block[1], block[2], block[3].hex() if resp_format == "json" else block[3].tobytes() ]) print(req) # Send response send_response(client, "200 OK", resp, resp_format, resp_compression) time.sleep(.2)