Exemplo n.º 1
0
    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")
Exemplo n.º 2
0
    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)
Exemplo n.º 3
0
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())
Exemplo n.º 4
0
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