def __init__(self, app: flask.Flask): self.app = app self.sio = flask_socketio.SocketIO(app) self.discord = CustomDiscordOAuth2Session(app) self.metrics = PrometheusMetrics(app) self.fernet_encrypt = Fernet(app.config["FERNET_KEY"]) if app.config["GUEST_KEY"] is not None: self.guest_encrypt = Fernet(app.config["GUEST_KEY"]) if app.config["ENFORCE_ROLE"] is not None: self.enforce_role = EnforceDiscordRole(app.config["ENFORCE_ROLE"]) self.expected_headers = connection_headers() self.expected_headers.pop("X-Randovania-Version")
async def _internal_connect_to_server(self): import aiohttp.client_exceptions if self.sio.connected: return waiting_for_on_connect = asyncio.get_running_loop().create_future() self._waiting_for_on_connect = waiting_for_on_connect try: # sio.connect is raising a NotImplementedError, likely due to Windows and/or qasync? engineio.asyncio_client.async_signal_handler_set = True self.connection_state = ConnectionState.Connecting # self.logger.info(f"connect_to_server: sleeping") # await asyncio.sleep(1) # self.logger.info(f"connect_to_server: sleep over") self.logger.info( f"connecting to {self.configuration['server_address']}") self._connect_error = None self._num_emit_failures = 0 await self.sio.connect( self.configuration["server_address"], socketio_path=self.configuration["socketio_path"], transports=['websocket'], headers=connection_headers(), ) await waiting_for_on_connect self.logger.info(f"connected") except (socketio.exceptions.ConnectionError, aiohttp.client_exceptions.ContentTypeError) as e: self.logger.info(f"failed with {e} - {type(e)}") if self._connect_error is None: if isinstance(e, aiohttp.client_exceptions.ContentTypeError): message = e.message else: message = str(e) await self.on_connect_error(message) error = self._connect_error await self.sio.disconnect() raise UnableToConnect(error)
async def test_connect_to_server(tmpdir): # Setup client = NetworkClient(Path(tmpdir), { "server_address": "http://localhost:5000", "socketio_path": "/path" }) async def connect(*args, **kwargs): client._waiting_for_on_connect.set_result(True) client.sio = MagicMock() client.sio.connect = AsyncMock(side_effect=connect) client.sio.connected = False # Run await client.connect_to_server() # Assert assert client.connection_state == ConnectionState.Connecting client.sio.connect.assert_awaited_once_with("http://localhost:5000", socketio_path="/path", transports=["websocket"], headers=connection_headers())
def create_app(): configuration = randovania.get_configuration() dictConfig({ 'version': 1, 'formatters': { 'default': { 'format': '[%(asctime)s] %(levelname)s in %(module)s: %(message)s', } }, 'handlers': { 'wsgi': { 'class': 'logging.StreamHandler', 'stream': 'ext://flask.logging.wsgi_errors_stream', 'formatter': 'default' } }, 'root': { 'level': 'INFO', 'handlers': ['wsgi'] } }) app = flask.Flask(__name__) app.wsgi_app = werkzeug.middleware.proxy_fix.ProxyFix(app.wsgi_app, x_proto=1, x_prefix=1) app.config['SECRET_KEY'] = configuration["server_config"]["secret_key"] app.config["GUEST_KEY"] = configuration["guest_secret"].encode( "ascii") if "guest_secret" in configuration else None app.config["DISCORD_CLIENT_ID"] = configuration["discord_client_id"] app.config["DISCORD_CLIENT_SECRET"] = configuration["server_config"][ "discord_client_secret"] app.config[ "DISCORD_REDIRECT_URI"] = f'{configuration["server_address"]}/login_callback' app.config["FERNET_KEY"] = configuration["server_config"][ "fernet_key"].encode("ascii") app.config["ENFORCE_ROLE"] = configuration["server_config"].get( "enforce_role") version_checking = ClientVersionCheck( configuration["server_config"]["client_version_checking"]) database.db.init(configuration["server_config"]['database_path']) database.db.connect(reuse_if_open=True) database.db.create_tables(database.all_classes) sio = ServerApp(app) app.sio = sio game_session.setup_app(sio) user_session.setup_app(sio) connected_clients = sio.metrics.info( "connected_clients", "How many clients are connected right now.") connected_clients.set(0) @app.route("/") def index(): return randovania.VERSION server_version = randovania.VERSION expected_headers = connection_headers() expected_headers.pop("X-Randovania-Version") @sio.sio.server.on("connect") def connect(sid, environ): try: if "HTTP_X_RANDOVANIA_VERSION" not in environ: raise ConnectionRefusedError("unknown client version") client_app_version = environ["HTTP_X_RANDOVANIA_VERSION"] check_client_version(version_checking, client_app_version, server_version) check_client_headers(expected_headers, environ) connected_clients.inc() forwarded_for = environ.get('HTTP_X_FORWARDED_FOR') app.logger.info( f"Client {sid} at {environ['REMOTE_ADDR']} ({forwarded_for}) with " f"version {client_app_version} connected.") except ConnectionRefusedError: # Do not wrap if it's already a ConnectionRefusedError raise except Exception as e: logging.exception( f"Unknown exception when testing the client's headers: {e}") raise ConnectionRefusedError( f"Unable to check if request is valid: {e}.\n" f"Please file a bug report.") @sio.sio.server.on("disconnect") def disconnect(sid): connected_clients.dec() app.logger.info(f"Client at {sio.current_client_ip()} disconnected.") session = sio.get_server().get_session(sid) if "user-id" in session: game_session.report_user_disconnected(sio, session["user-id"], app.logger) return app