import sys from asyncio import wait_for, TimeoutError from base64 import b64encode from sanic import Sanic from sanic.response import file, redirect, empty from InputController import InputController from MessageParser import MessageParser app = Sanic( "capybara", env_prefix="CAPYBARA_" ) input_controller = InputController() control_message_parser = MessageParser() # auth packet control_message_parser.add_handler("0", { "auth_string": "str" }) app.static("app", "frontend/dist/", resource_type="dir") @app.get("/") async def home(req): return redirect("/app/app.html") @app.get("/app") async def app_route(req): return redirect("/app/app.html") @app.websocket("/gateway") async def gateway(req, ws): # Before the client is able to send any data, we must await an authorization packet try: auth_payload = await wait_for(ws.recv(), timeout=5) except TimeoutError: await ws.close(code=4001, reason="Invalid auth packet") return code, args = control_message_parser.parse(auth_payload) if ( not code or code != "0" or args["auth_string"] != app.ctx.EXPECTED_AUTH_STRING ): # 0 is the code for the initial auth packet await ws.close(code=4001, reason="Invalid auth packet") return await ws.send("1") # send a single `1` to let the client know the server is accepting input packets while True: input_controller.process_message(await ws.recv()) def main(): if not app.config.get("AUTH_PASSWORD"): print( "capybara: FATAL ERROR: Capybara is expecting the `CAPYBARA_AUTH_PASSWORD` environment variable to be set (to a password of your choice).\nYour users will use this password to authenticate to your server.\nEXITING...", file=sys.stderr ) exit(1) return app.ctx.EXPECTED_AUTH_STRING = "%auth%" + str(b64encode(app.config.AUTH_PASSWORD.encode("utf-8")), "utf-8") app.run(host='0.0.0.0', port=4003, access_log=False) if __name__ == "__main__": main()