Ejemplo n.º 1
0
async def test_send_binary_data_to_client(ws_protocol_cls, http_protocol_cls):
    class App(WebSocketResponse):
        async def websocket_connect(self, message):
            await self.send({"type": "websocket.accept"})
            await self.send({"type": "websocket.send", "bytes": b"123"})

    async def get_data(url):
        async with websockets.connect(url) as websocket:
            return await websocket.recv()

    config = Config(app=App,
                    ws=ws_protocol_cls,
                    http=http_protocol_cls,
                    lifespan="off")
    async with run_server(config):
        data = await get_data("ws://127.0.0.1:8000")
        assert data == b"123"
Ejemplo n.º 2
0
            async def __init__(self, *args, **kwargs):
                super().__init__(*args, **kwargs)

                install_serve_encoders_to_fastapi()

                self._serve_app = frozen_app

                # Use uvicorn's lifespan handling code to properly deal with
                # startup and shutdown event.
                self._serve_asgi_lifespan = LifespanOn(
                    Config(self._serve_app, lifespan="on"))
                # Replace uvicorn logger with our own.
                self._serve_asgi_lifespan.logger = logger
                # LifespanOn's logger logs in INFO level thus becomes spammy
                # Within this block we temporarily uplevel for cleaner logging
                with LoggingContext(self._serve_asgi_lifespan.logger,
                                    level=logging.WARNING):
                    await self._serve_asgi_lifespan.startup()
Ejemplo n.º 3
0
async def test_run_password(
    tls_ca_ssl_context,
    tls_ca_certificate_pem_path,
    tls_ca_certificate_private_key_encrypted_path,
):
    config = Config(
        app=app,
        loop="asyncio",
        limit_max_requests=1,
        ssl_keyfile=tls_ca_certificate_private_key_encrypted_path,
        ssl_certfile=tls_ca_certificate_pem_path,
        ssl_keyfile_password="******",
        ssl_ca_certs=tls_ca_certificate_pem_path,
    )
    async with run_server(config):
        async with httpx.AsyncClient(verify=tls_ca_ssl_context) as client:
            response = await client.get("https://127.0.0.1:8000")
    assert response.status_code == 204
Ejemplo n.º 4
0
    def test_should_parse_dir_from_includes(self) -> None:
        app_dir = self.reload_path / "app"
        app_file = app_dir / "src" / "main.py"
        app_first_dir = self.reload_path / "app_first"
        app_first_file = app_first_dir / "src" / "main.py"

        with as_cwd(self.reload_path):
            config = Config(
                app="tests.test_config:asgi_app",
                reload=True,
                reload_includes=[str(app_dir)],
            )
            reloader = self._setup_reloader(config)

            assert self._reload_tester(reloader, app_file)
            assert not self._reload_tester(reloader, app_first_file)

            reloader.shutdown()
Ejemplo n.º 5
0
async def test_asgi_return_value(protocol_cls):
    """
    The ASGI callable should return 'None'. If it doesn't make sure that
    the connection is closed with an error condition.
    """
    async def app(scope, receive, send):
        await send({"type": "websocket.accept"})
        return 123

    async def connect(url):
        async with websockets.connect(url) as websocket:
            _ = await websocket.recv()

    config = Config(app=app, ws=protocol_cls, lifespan="off")
    async with run_server(config):
        with pytest.raises(websockets.exceptions.ConnectionClosed) as exc_info:
            await connect("ws://127.0.0.1:8000")
        assert exc_info.value.code == 1006
Ejemplo n.º 6
0
def run(app, **kwargs):
    config = Config(app, **kwargs)
    server = Server(config=config)

    if config.reload and not isinstance(app, str):
        logger = logging.getLogger("uvicorn.error")
        logger.warn("auto-reload only works when app is passed as an import string.")

    if config.should_reload:
        sock = config.bind_socket()
        supervisor = StatReload(config)
        supervisor.run(server.run, sockets=[sock])
    elif config.workers > 1:
        sock = config.bind_socket()
        supervisor = Multiprocess(config)
        supervisor.run(server.run, sockets=[sock])
    else:
        server.run()
Ejemplo n.º 7
0
async def test_send_binary_data_to_server(protocol_cls):
    class App(WebSocketResponse):
        async def websocket_connect(self, message):
            await self.send({"type": "websocket.accept"})

        async def websocket_receive(self, message):
            _bytes = message.get("bytes")
            await self.send({"type": "websocket.send", "bytes": _bytes})

    async def send_text(url):
        async with websockets.connect(url) as websocket:
            await websocket.send(b"abc")
            return await websocket.recv()

    config = Config(app=App, ws=protocol_cls, lifespan="off")
    async with run_server(config):
        data = await send_text("ws://127.0.0.1:8000")
        assert data == b"abc"
Ejemplo n.º 8
0
def test_log_config_json(
    mocked_logging_config_module: MagicMock,
    logging_config: dict,
    json_logging_config: str,
    mocker: MockerFixture,
) -> None:
    """
    Test that one can load a json config from disk.
    """
    mocked_open = mocker.patch("uvicorn.config.open",
                               mocker.mock_open(read_data=json_logging_config))

    config = Config(app=asgi_app, log_config="log_config.json")
    config.load()

    mocked_open.assert_called_once_with("log_config.json")
    mocked_logging_config_module.dictConfig.assert_called_once_with(
        logging_config)
Ejemplo n.º 9
0
    def test_should_reload_when_directories_have_same_prefix(self) -> None:
        app_dir = self.reload_path / "app"
        app_file = app_dir / "src" / "main.py"
        app_first_dir = self.reload_path / "app_first"
        app_first_file = app_first_dir / "src" / "main.py"

        with as_cwd(self.reload_path):
            config = Config(
                app="tests.test_config:asgi_app",
                reload=True,
                reload_dirs=[str(app_dir), str(app_first_dir)],
            )
            reloader = self._setup_reloader(config)

            assert self._reload_tester(reloader, app_file)
            assert self._reload_tester(reloader, app_first_file)

            reloader.shutdown()
Ejemplo n.º 10
0
async def test_subprotocols(protocol_cls, subprotocol):
    class App(WebSocketResponse):
        async def websocket_connect(self, message):
            await self.send({
                "type": "websocket.accept",
                "subprotocol": subprotocol
            })

    async def get_subprotocol(url):
        async with websockets.connect(url,
                                      subprotocols=["proto1",
                                                    "proto2"]) as websocket:
            return websocket.subprotocol

    config = Config(app=App, ws=protocol_cls, lifespan="off")
    async with run_server(config):
        accepted_subprotocol = await get_subprotocol("ws://127.0.0.1:8000")
        assert accepted_subprotocol == subprotocol
Ejemplo n.º 11
0
def run(app, **kwargs):
    config = Config(app, **kwargs)
    server = Server(config=config)

    if config.reload and not isinstance(app, str):
        config.logger_instance.warn(
            "auto-reload only works when app is passed as an import string.")

    if isinstance(app, str) and (config.debug or config.reload):
        socket = config.bind_socket()
        supervisor = StatReload(config)
        supervisor.run(server.run, sockets=[socket])
    elif config.workers > 1:
        socket = config.bind_socket()
        supervisor = Multiprocess(config)
        supervisor.run(server.run, sockets=[socket])
    else:
        server.run()
Ejemplo n.º 12
0
def test_log_config_default(mocked_logging_config_module, use_colors,
                            expected):
    """
    Test that one can specify the use_colors option when using the default logging
    config.
    """
    config = Config(app=asgi_app, use_colors=use_colors)
    config.load()

    mocked_logging_config_module.dictConfig.assert_called_once_with(
        LOGGING_CONFIG)

    (
        (provided_dict_config, ),
        _,
    ) = mocked_logging_config_module.dictConfig.call_args
    assert provided_dict_config["formatters"]["default"][
        "use_colors"] == expected
Ejemplo n.º 13
0
async def test_supports_permessage_deflate_extension(ws_protocol_cls,
                                                     http_protocol_cls):
    class App(WebSocketResponse):
        async def websocket_connect(self, message):
            await self.send({"type": "websocket.accept"})

    async def open_connection(url):
        extension_factories = [ClientPerMessageDeflateFactory()]
        async with websockets.connect(
                url, extensions=extension_factories) as websocket:
            return [extension.name for extension in websocket.extensions]

    config = Config(app=App,
                    ws=ws_protocol_cls,
                    http=http_protocol_cls,
                    lifespan="off")
    async with run_server(config):
        extension_names = await open_connection("ws://127.0.0.1:8000")
        assert "permessage-deflate" in extension_names
Ejemplo n.º 14
0
def test_log_config_yaml(
    mocked_logging_config_module: MagicMock,
    logging_config: dict,
    yaml_logging_config: str,
    mocker: MockerFixture,
    config_filename: str,
) -> None:
    """
    Test that one can load a yaml config from disk.
    """
    mocked_open = mocker.patch("uvicorn.config.open",
                               mocker.mock_open(read_data=yaml_logging_config))

    config = Config(app=asgi_app, log_config=config_filename)
    config.load()

    mocked_open.assert_called_once_with(config_filename)
    mocked_logging_config_module.dictConfig.assert_called_once_with(
        logging_config)
Ejemplo n.º 15
0
def test_reload_dir_subdirectories_are_removed(
    reload_directory_structure: Path, ) -> None:
    app_dir = reload_directory_structure / "app"
    app_sub_dir = app_dir / "sub"
    ext_dir = reload_directory_structure / "ext"
    ext_sub_dir = ext_dir / "sub"

    with as_cwd(reload_directory_structure):
        config = Config(
            app="tests.test_config:asgi_app",
            reload=True,
            reload_dirs=[
                str(app_dir),
                str(app_sub_dir),
                str(ext_sub_dir),
                str(ext_dir),
            ],
        )
        assert frozenset(config.reload_dirs) == frozenset([app_dir, ext_dir])
Ejemplo n.º 16
0
    async def create_servers(self, loop, app):
        gunicorn_cfg = self.cfg
        config = Config(app=app,
                        loop=loop,
                        logger=self.log,
                        ws='websockets',
                        timeout_keep_alive=gunicorn_cfg.keepalive)
        ssl_ctx = self.create_ssl_context(
            self.cfg) if self.cfg.is_ssl else None

        for sock in self.sockets:
            global_state = GlobalState()
            protocol = functools.partial(
                self.protocol_class,
                config=config,
                global_state=global_state,
            )
            server = await loop.create_server(protocol, sock=sock, ssl=ssl_ctx)
            self.servers.append((server, global_state))
Ejemplo n.º 17
0
async def test_headers(ws_protocol_cls, http_protocol_cls):
    class App(WebSocketResponse):
        async def websocket_connect(self, message):
            headers = self.scope.get("headers")
            headers = dict(headers)
            assert headers[b"host"].startswith(b"127.0.0.1")
            await self.send({"type": "websocket.accept"})

    async def open_connection(url):
        async with websockets.connect(url) as websocket:
            return websocket.open

    config = Config(app=App,
                    ws=ws_protocol_cls,
                    http=http_protocol_cls,
                    lifespan="off")
    async with run_server(config):
        is_open = await open_connection("ws://127.0.0.1:8000")
        assert is_open
Ejemplo n.º 18
0
async def test_close_connection(ws_protocol_cls, http_protocol_cls):
    class App(WebSocketResponse):
        async def websocket_connect(self, message):
            await self.send({"type": "websocket.close"})

    async def open_connection(url):
        try:
            await websockets.connect(url)
        except websockets.exceptions.InvalidHandshake:
            return False
        return True  # pragma: no cover

    config = Config(app=App,
                    ws=ws_protocol_cls,
                    http=http_protocol_cls,
                    lifespan="off")
    async with run_server(config):
        is_open = await open_connection("ws://127.0.0.1:8000")
        assert not is_open
Ejemplo n.º 19
0
    def __init__(self) -> None:
        self.app = FastAPI(
            title="FastAPI Example Server",
            description=
            "Demonstrate Spring Boot Admin Integration with FastAPI",
            docs_url="/api",
        )

        self.pyctuator = Pyctuator(
            self.app,
            "FastAPI Pyctuator",
            "http://*****:*****@self.app.get("/logfile_test_repeater", tags=["pyctuator"])
        # pylint: disable=unused-variable
        def logfile_test_repeater(repeated_string: str) -> str:
            logging.error(repeated_string)
            return repeated_string

        self.server = CustomServer(
            config=(Config(app=self.app, loop="asyncio")))
        self.thread = threading.Thread(target=self.server.run)

        @self.app.get("/httptrace_test_url")
        # pylint: disable=unused-variable
        def get_httptrace_test_url(request: Request,
                                   sleep_sec: Optional[int]) -> Response:
            # Sleep if requested to sleep - used for asserting httptraces timing
            if sleep_sec:
                logging.info("Sleeping %s seconds before replying", sleep_sec)
                time.sleep(sleep_sec)

            # Echo 'User-Data' header as 'resp-data' - used for asserting headers are captured properly
            headers = {
                "resp-data": str(request.headers.get("User-Data")),
                "response-secret": "my password"
            }
            return Response(headers=headers, content="my content")
Ejemplo n.º 20
0
def test_run_password(tls_ca_certificate_pem_path,
                      tls_ca_certificate_private_key_encrypted_path):
    config = Config(
        app=app,
        loop="asyncio",
        limit_max_requests=1,
        ssl_keyfile=tls_ca_certificate_private_key_encrypted_path,
        ssl_certfile=tls_ca_certificate_pem_path,
        ssl_keyfile_password="******",
    )
    server = Server(config=config)
    thread = threading.Thread(target=server.run)
    thread.start()
    while not server.started:
        time.sleep(0.01)
    with no_ssl_verification():
        response = requests.get("https://127.0.0.1:8000")
    assert response.status_code == 204
    thread.join()
Ejemplo n.º 21
0
async def test_extra_headers(ws_protocol_cls, http_protocol_cls):
    class App(WebSocketResponse):
        async def websocket_connect(self, message):
            await self.send({
                "type": "websocket.accept",
                "headers": [(b"extra", b"header")]
            })

    async def open_connection(url):
        async with websockets.connect(url) as websocket:
            return websocket.response_headers

    config = Config(app=App,
                    ws=ws_protocol_cls,
                    http=http_protocol_cls,
                    lifespan="off")
    async with run_server(config):
        extra_headers = await open_connection("ws://127.0.0.1:8000")
        assert extra_headers.get("extra") == "header"
Ejemplo n.º 22
0
def run(app: typing.Union[ASGIApplication, str], **kwargs: typing.Any) -> None:
    config = Config(app, **kwargs)
    server = Server(config=config)

    if (config.reload or config.workers > 1) and not isinstance(app, str):
        logger = logging.getLogger("uvicorn.error")
        logger.warning(
            "You must pass the application as an import string to enable 'reload' or "
            "'workers'.")
        sys.exit(1)

    if config.should_reload:
        sock = config.bind_socket()
        ChangeReload(config, target=server.run, sockets=[sock]).run()
    elif config.workers > 1:
        sock = config.bind_socket()
        Multiprocess(config, target=server.run, sockets=[sock]).run()
    else:
        server.run()
Ejemplo n.º 23
0
    def test_should_not_reload_when_exclude_pattern_match_file_is_changed(self) -> None:
        python_file = self.reload_path / "app" / "src" / "main.py"
        css_file = self.reload_path / "app" / "css" / "main.css"
        js_file = self.reload_path / "app" / "js" / "main.js"

        with as_cwd(self.reload_path):
            config = Config(
                app="tests.test_config:asgi_app",
                reload=True,
                reload_includes=["*"],
                reload_excludes=["*.js"],
            )
            reloader = self._setup_reloader(config)

            assert self._reload_tester(reloader, python_file)
            assert self._reload_tester(reloader, css_file)
            assert not self._reload_tester(reloader, js_file)

            reloader.shutdown()
Ejemplo n.º 24
0
    def run(self):
        log("[api] Starting service")

        try:
            app = FastAPI(exception_handlers={APIException: http_exception_handler})
            app.camera_manager = self.camera_manager

            protected = Depends(HTTPHeaderAuthentication())
            app.include_router(camera.router, prefix="/api", dependencies=[protected])
            app.include_router(camera.public_router, prefix="/api")
            app.include_router(clips.router, prefix="/api", dependencies=[protected])
            app.include_router(clips.public_router, prefix="/api")
            app.include_router(auth.router, prefix="/api")
            app.include_router(system.router, prefix="/api")
            app.include_router(timelapse.router, prefix="/api", dependencies=[protected])
            app.include_router(timelapse.public_router, prefix="/api")

            app.mount("/", StaticFiles(directory="../web/dist", html=True), name="public")

            app.add_middleware(
                CORSMiddleware,
                allow_origins=["*"],
                allow_credentials=True,
                allow_methods=["*"],
                allow_headers=["*"],
            )

            config = Config(app, host=os.environ["API_SERVER_HOST"], port=int(os.environ["API_SERVER_PORT"]))
            self.server = Server(config=config)

            if config.should_reload:
                sock = config.bind_socket()
                supervisor = ChangeReload(config, target=self.server.run, sockets=[sock])
                supervisor.run()
            elif config.workers > 1:
                sock = config.bind_socket()
                supervisor = Multiprocess(config, target=self.server.run, sockets=[sock])
                supervisor.run()
            else:
                self.server.run()

        except KeyboardInterrupt:
            pass
Ejemplo n.º 25
0
def test_log_config_yaml(
    mocked_logging_config_module,
    logging_config,
    yaml_logging_config,
    mocker,
    config_filename,
):
    """
    Test that one can load a yaml config from disk.
    """
    mocked_open = mocker.patch("uvicorn.config.open",
                               mocker.mock_open(read_data=yaml_logging_config))

    config = Config(app=asgi_app, log_config=config_filename)
    config.load()

    mocked_open.assert_called_once_with(config_filename)
    mocked_logging_config_module.dictConfig.assert_called_once_with(
        logging_config)
Ejemplo n.º 26
0
async def test_client_close(protocol_cls):
    async def app(scope, receive, send):
        while True:
            message = await receive()
            if message["type"] == "websocket.connect":
                await send({"type": "websocket.accept"})
            elif message["type"] == "websocket.receive":
                pass
            elif message["type"] == "websocket.disconnect":
                break

    async def websocket_session(url):
        async with websockets.connect(url) as websocket:
            await websocket.ping()
            await websocket.send("abc")

    config = Config(app=app, ws=protocol_cls, lifespan="off")
    async with run_server(config):
        await websocket_session("ws://127.0.0.1:8000")
Ejemplo n.º 27
0
    def __init__(self, *args, **kwargs):
        super(UvicornWorker, self).__init__(*args, **kwargs)

        logger = logging.getLogger("uvicorn.error")
        logger.handlers = self.log.error_log.handlers
        logger.setLevel(self.log.error_log.level)
        logger.propagate = False

        logger = logging.getLogger("uvicorn.access")
        logger.handlers = self.log.access_log.handlers
        logger.setLevel(self.log.access_log.level)
        logger.propagate = False

        config_kwargs = {
            "app": None,
            "log_config": None,
            "timeout_keep_alive": self.cfg.keepalive,
            "timeout_notify": self.timeout,
            "callback_notify": self.callback_notify,
            "limit_max_requests": self.max_requests,
            "forwarded_allow_ips": self.cfg.forwarded_allow_ips,
            "gunicorn_log": self.log,
        }

        if self.cfg.is_ssl:
            ssl_kwargs = {
                "ssl_keyfile": self.cfg.ssl_options.get("keyfile"),
                "ssl_certfile": self.cfg.ssl_options.get("certfile"),
                "ssl_keyfile_password": self.cfg.ssl_options.get("password"),
                "ssl_version": self.cfg.ssl_options.get("ssl_version"),
                "ssl_cert_reqs": self.cfg.ssl_options.get("cert_reqs"),
                "ssl_ca_certs": self.cfg.ssl_options.get("ca_certs"),
                "ssl_ciphers": self.cfg.ssl_options.get("ciphers"),
            }
            config_kwargs.update(ssl_kwargs)

        if self.cfg.settings["backlog"].value:
            config_kwargs["backlog"] = self.cfg.settings["backlog"].value

        config_kwargs.update(self.CONFIG_KWARGS)

        self.config = Config(**config_kwargs)
Ejemplo n.º 28
0
def test_run_chain(tls_certificate_pem_path):
    class CustomServer(Server):
        def install_signal_handlers(self):
            pass

    config = Config(
        app=app,
        loop="asyncio",
        limit_max_requests=1,
        ssl_certfile=tls_certificate_pem_path,
    )
    server = CustomServer(config=config)
    thread = threading.Thread(target=server.run)
    thread.start()
    while not server.started:
        time.sleep(0.01)
    with no_ssl_verification():
        response = requests.get("https://127.0.0.1:8000")
    assert response.status_code == 204
    thread.join()
Ejemplo n.º 29
0
async def test_path_and_raw_path(ws_protocol_cls, http_protocol_cls):
    class App(WebSocketResponse):
        async def websocket_connect(self, message):
            path = self.scope.get("path")
            raw_path = self.scope.get("raw_path")
            assert path == "/one/two"
            assert raw_path == "/one%2Ftwo"
            await self.send({"type": "websocket.accept"})

    async def open_connection(url):
        async with websockets.connect(url) as websocket:
            return websocket.open

    config = Config(app=App,
                    ws=ws_protocol_cls,
                    http=http_protocol_cls,
                    lifespan="off")
    async with run_server(config):
        is_open = await open_connection("ws://127.0.0.1:8000/one%2Ftwo")
        assert is_open
Ejemplo n.º 30
0
    def mainloop(self):
        logger.info("serving at http://%s:%d" % (self.host, self.port))

        from uvicorn.config import Config
        from uvicorn.server import Server

        # uvloop will trigger a: RuntimeError: There is no current event loop in thread 'fastapi-thread'
        config = Config(app,
                        host=self.host,
                        port=self.port,
                        **self.kwargs,
                        loop='asyncio')
        self.server = Server(config=config)
        self.started.set()
        try:
            self.server.run()
        except:
            logger.exception("Oops, server stopped unexpectedly")
        finally:
            self.stopped.set()