I have this docker application running several containers. One of these containers is a Python application that can handle both socket io requests and normal HTTP requests. Django’s ASGI handles HTTP/ASGI requests while python-socketio
handles socket requests.
Since there are 4 other applications like this which must all be server via Nginx, I have to specify namespace URLs for both the socket io and ASGI applications for all the applications.
I am using the following configuration for the application:
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
Entrypoint: main.py
which uses Uvicorn
import uvicorn
if __name__ == "__main__":
uvicorn.run(
"config.asgi:application",
host="0.0.0.0",
reload=True,
log_level="debug",
)
When I make a request to connect using Postman to URL, http://localhost:8089/app/socket.io/?token=token&organisation_id=org_id
, I get the following logs:
app_store_service | DEBUG: = connection is CONNECTING
app_store_service | DEBUG: < GET /socket.io/?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzI0MjA5MzQwLCJpYXQiOjE3MjE2MTczNDAsImp0aSI6ImRlMmQ1ZmUyZTk5ZTQ0ZWY5YTdlMTc2NjhmM2UyNTZmIiwidXNlcl9pZCI6IjA2NjdmZDI4LWZkYWMtNzI1Yi04MDAwLWNjM2U5YWE2OGEzNSJ9.zAENI3Xl0k5efwE83u__svuXDUZajv00462XerXCo2c&organisation_id=06689368-db4a-70dd-8000-c37727d378ad&EIO=4&transport=websocket HTTP/1.1
app_store_service | DEBUG: < upgrade: websocket
app_store_service | DEBUG: < connection: upgrade
app_store_service | DEBUG: < host: localhost
app_store_service | DEBUG: < x-real-ip: 172.18.0.1
app_store_service | DEBUG: < x-forwarded-for: 172.18.0.1
app_store_service | DEBUG: < x-forwarded-proto: http
app_store_service | DEBUG: < sec-websocket-version: 13
app_store_service | DEBUG: < sec-websocket-key: OR4YrxOcqdovU4hNL+xkMw==
app_store_service | DEBUG: < sec-websocket-extensions: permessage-deflate; client_max_window_bits
app_store_service | INFO: ('172.18.0.21', 48800) - "WebSocket /socket.io/?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzI0MjA5MzQwLCJpYXQiOjE3MjE2MTczNDAsImp0aSI6ImRlMmQ1ZmUyZTk5ZTQ0ZWY5YTdlMTc2NjhmM2UyNTZmIiwidXNlcl9pZCI6IjA2NjdmZDI4LWZkYWMtNzI1Yi04MDAwLWNjM2U5YWE2OGEzNSJ9.zAENI3Xl0k5efwE83u__svuXDUZajv00462XerXCo2c&organisation_id=06689368-db4a-70dd-8000-c37727d378ad&EIO=4&transport=websocket" [accepted]
app_store_service | DEBUG: > HTTP/1.1 101 Switching Protocols
app_store_service | DEBUG: > Upgrade: websocket
app_store_service | DEBUG: > Connection: Upgrade
app_store_service | DEBUG: > Sec-WebSocket-Accept: QhVhFHD/iSPmU+0qOzIVQgy0HRg=
app_store_service | DEBUG: > Sec-WebSocket-Extensions: permessage-deflate
app_store_service | DEBUG: > date: Mon, 22 Jul 2024 09:41:41 GMT
app_store_service | DEBUG: > server: uvicorn
app_store_service | INFO: connection open
app_store_service | DEBUG: = connection is OPEN
app_store_service | DEBUG: > TEXT '0{"sid":"POZUXfoHMZRUnheFAAAS","upgrades":[],"p...0,"pingInterval":25000}' [86 bytes]
app_store_service | DEBUG: < TEXT '40/app/socket.io/,' [21 bytes]
app_store_service | DEBUG: > TEXT '44/app/socket.io/,"Unable to connect"' [40 bytes]
app_store_service | DEBUG: < CLOSE 1005 (no status received [internal]) [0 bytes]
app_store_service | DEBUG: = connection is CLOSING
app_store_service | DEBUG: > CLOSE 1005 (no status received [internal]) [0 bytes]
app_store_service | DEBUG: x half-closing TCP connection
app_store_service | DEBUG: = connection is CLOSED
nginx-1 | 172.18.0.1 - - [22/Jul/2024:09:41:41 +0000] "GET /app/socket.io/?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzI0MjA5MzQwLCJpYXQiOjE3MjE2MTczNDAsImp0aSI6ImRlMmQ1ZmUyZTk5ZTQ0ZWY5YTdlMTc2NjhmM2UyNTZmIiwidXNlcl9pZCI6IjA2NjdmZDI4LWZkYWMtNzI1Yi04MDAwLWNjM2U5YWE2OGEzNSJ9.zAENI3Xl0k5efwE83u__svuXDUZajv00462XerXCo2c&organisation_id=06689368-db4a-70dd-8000-c37727d378ad&EIO=4&transport=websocket HTTP/1.1" 101 128 "-" "-" "-"
app_store_service | INFO: connection closed
I can see the request is reaching the application but the connection gets closed before any application logic can be executed.
But if I connect directly to the application port and not through Nginx, it works without any issues.
2
Answers
I had this error and it really annoyed me for a while, not 100% sure if this is the exact same root cause I had but this is what I did incase it is of any use. I can’t remember why this works but its something to do with how docker routes things. For the proxy_pass use the following URL format: http://host.docker.internal:PORT Where PORT is the port number (the port number used within docker) of the application you are running
The Socket.IO URL is not given in the connection URL. I’m not sure how this works with Postman, but from JavaScript you would do something like this:
The path that you pass in the connection URL is interpreted as a namespace. Looking at your logs it seems the Socket.IO server is receiving the connection request, but it is trying to connect you to a namespace called
/app/socket.io
.