Socket IO отклоняет запросы от прокси-сервера Nginx

У меня есть приложение docker, в котором запущено несколько контейнеров. Один из этих контейнеров представляет собой Python-приложение, которое может обрабатывать как сокетные io-запросы, так и обычные HTTP-запросы. Django's ASGI обрабатывает HTTP/ASGI запросы, а python-socketio обрабатывает сокетные запросы.

Поскольку есть еще 4 подобных приложения, которые все должны быть серверами через Nginx, я должен указать URL пространства имен для приложений socket io и ASGI для всех приложений.

Я использую следующую конфигурацию для приложения:

location /app/ {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass http://app:8000/; # docker container name is "app"
}

location /app/socket.io/ {
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass http://app:8000/socket.io/; # docker container name is "app"
        proxy_read_timeout 86400s;
        proxy_send_timeout 86400s;
}

asgi.py

import os

import socketio
from django.core.asgi import get_asgi_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")

django_application = get_asgi_application()

from app.async_socketio.server import sio  # noqa 402

# this allows Socket IO to handle requests by default to a
# default path of /socket.io and forwards any other requests
# to the Django application.
application = socketio.ASGIApp(sio, django_application)

# just so all my socket event handles can load
from app.async_socketio.namespace import *  # noqa 403

Точка входа: main.py, который использует Uvicorn

import uvicorn

if __name__ == "__main__":
    uvicorn.run(
        "config.asgi:application",
        host="0.0.0.0",
        reload=True,
        log_level="debug",
    )

Когда я делаю запрос на подключение с помощью Postman к URL, http://localhost:8089/app/socket.io/?token=token&organisation_id=org_id, я получаю следующие журналы:

Я вижу, что запрос достигает приложения, но соединение закрывается до того, как может быть выполнена какая-либо логика приложения.

Но если я подключаюсь напрямую к порту приложения, а не через Nginx, то все работает без проблем.

У меня была эта ошибка, и она действительно раздражала меня некоторое время, не на 100% уверен, что это точно такая же первопричина, но вот что я сделал, если это может быть полезно. Я не могу вспомнить, почему это работает, но это как-то связано с тем, как docker маршрутизирует вещи. Для proxy_pass используйте следующий формат URL: http://host.docker.internal:PORT Где PORT - это номер порта (номер порта, используемый в docker) запущенного приложения

В URL-адресе соединения не указан URL-адрес Socket.IO. Я не уверен, как это работает в Postman, но из JavaScript можно сделать примерно следующее:

const socket = io("http://localhost:8089", {
  path: "/app/socket.io"
});

Путь, который вы передаете в URL-адресе соединения, интерпретируется как пространство имен. Если посмотреть на ваши журналы, кажется, что сервер Socket.IO получает запрос на подключение, но пытается подключить вас к пространству имен с именем /app/socket.io.

Вернуться на верх