Веб-сайты с Соколом и Дафной за входом K8s

#python #websocket #daphne #falconframework #asgi

Вопрос:

Мое приложение ASGI на базе Falcon выполняется через Daphne и отлично работает, когда оно запускается локально и доступно через локальный хост. Приложение упаковано в контейнер и запущено в кластере K8s за входом. На K8s приложение работает не в корневом каталоге домена, а в папке /sub/. Это нарушает маршрутизацию Falcon. Я не смог найти существующее промежуточное программное обеспечение для этого, поэтому я реализовал одно:

 class StripRootPath:
    async def process_request(self, req, resp):
        root_path = req.root_path
        if root_path:
            req.path = req.path[len(root_path) :]
 

Это прекрасно сработает, если я позвоню Дафне вот так

 daphne --root-path /sub/path ...
 

и до тех пор, пока я использую только «обычные» HTTP-запросы. Работа с веб-сайтами завершается неудачно. Сообщения об ошибках предполагали, что пути не могут быть найдены, поэтому я предположил, что мое удаление корневого пути не сработало. Я понял, что был прав. Существует специальная

 async def process_request_ws(self, req, ws):
 

для подключений к веб-сайтам. Я попытался реализовать этот метод точно так process_request же, но req.root_path он пуст. Я не нашел в этом никакого значения, req которое позволило бы мне удалить префикс.

Теперь я задаюсь вопросом, делаю ли я что-то совершенно неправильно или это ошибка и root_path ее нужно установить!?

Ответ №1:

Похоже, это ошибка в Daphne, область запроса для WebSocket не содержит root_path ключа/значения.

process_request_ws отлично сработало внутреннее перенаправление путем изменения req.path , однако я просто жестко закодировал префикс в своем доказательстве концепции test.py :

 import falcon
import falcon.asgi


class InternalRedirect:
    async def process_request(self, req, resp):
        print(f'req.path={req.path}; req.root_path={req.root_path}')
        if req.path.startswith('/root/'):
            req.path = req.path.split('/root', 1)[1]

    async def process_request_ws(self, req, ws):
        print(f'req.path={req.path}; req.root_path={req.root_path}')
        if req.path.startswith('/root/'):
            req.path = req.path.split('/root', 1)[1]


class HelloResource:
    async def on_get(self, req, resp, name):
        resp.media = {'message': f'Hello, {name}!'}

    async def on_websocket(self, req, ws, name):
        try:
            await ws.accept()
            await ws.send_media({'message': f'Hello, {name}'})

            while True:
                payload = await ws.receive_text()
                print(f'Received: [{payload}])')
        except falcon.WebSocketDisconnected:
            pass


app = falcon.asgi.App(middleware=[InternalRedirect()])
app.add_route('/hello/{name}', HelloResource())
 

Я попытался запустить одно и то же приложение в Uvicorn, и root_path оно присутствует как для обычных GET запросов, так и при обновлении до WebSocket:

 $ uvicorn --root-path /root test:app

...

INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL C to quit)
req.path=/root/hello/robot; req.root_path=/root
INFO:     ('127.0.0.1', 33776) - "WebSocket /root/root/hello/robot" [accepted]
INFO:     connection open
INFO:     connection closed
 

ОТО, даже журнал Uvicorn иллюстрирует (NB двойной /root/root ) путаницу root_path в ASGI; в отличие от WSGI SCRIPT_NAME , неясно, должен ли ASGI path включать его или нет.
Смотрите также:

Несмотря на путаницу, Uvicorn должен решить вашу проблему под рукой. Более того, это один из самых популярных и высокопроизводительных серверов приложений ASGI, поэтому я предлагаю просто попробовать заменить Daphne на Uvicorn.