I have server like:
main.py
import socketio
from fastapi import FastAPI
app = FastAPI()
sio = socketio.AsyncServer(async_mode="asgi", cors_allowed_origins="*")
sio_app = socketio.ASGIApp(socketio_server=sio, socketio_path="socket.io")
app.mount("/ws", sio_app)
@sio.on("connect")
async def handle_connect(sid, *args, **kwargs):
await sio.emit("msg", "Test msg from FastAPI")
Launch server with uvicorn main:app --reload --host 0.0.0.0
And I try to connect to it with postman with ws://127.0.0.1:8000/ws/socket.io/?EIO=4&transport=websocket
or curl -v "http://127.0.0.1:8000/ws/socket.io/?EIO=4"
On fastapi version 0.108 and lower - I receive 127.0.0.1:58484 - "GET /socket.io/?EIO=4 HTTP/1.1" 200 OK with both
But if I try to use fastapii ^0.109:
Exception in ASGI application
Traceback (most recent call last):
File "/Users/eugene/Library/Caches/pypoetry/virtualenvs/websocket-test-j4LebyL2-py3.12/lib/python3.12/site-packages/uvicorn/protocols/websockets/websockets_impl.py", line 242, in run_asgi
result = await self.app(self.scope, self.asgi_receive, self.asgi_send) # type: ignore[func-returns-value]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/eugene/Library/Caches/pypoetry/virtualenvs/websocket-test-j4LebyL2-py3.12/lib/python3.12/site-packages/uvicorn/middleware/proxy_headers.py", line 60, in __call__
return await self.app(scope, receive, send)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/eugene/Library/Caches/pypoetry/virtualenvs/websocket-test-j4LebyL2-py3.12/lib/python3.12/site-packages/fastapi/applications.py", line 1054, in __call__
await super().__call__(scope, receive, send)
File "/Users/eugene/Library/Caches/pypoetry/virtualenvs/websocket-test-j4LebyL2-py3.12/lib/python3.12/site-packages/starlette/applications.py", line 123, in __call__
await self.middleware_stack(scope, receive, send)
File "/Users/eugene/Library/Caches/pypoetry/virtualenvs/websocket-test-j4LebyL2-py3.12/lib/python3.12/site-packages/starlette/middleware/errors.py", line 151, in __call__
await self.app(scope, receive, send)
File "/Users/eugene/Library/Caches/pypoetry/virtualenvs/websocket-test-j4LebyL2-py3.12/lib/python3.12/site-packages/starlette/middleware/exceptions.py", line 62, in __call__
await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
File "/Users/eugene/Library/Caches/pypoetry/virtualenvs/websocket-test-j4LebyL2-py3.12/lib/python3.12/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
raise exc
File "/Users/eugene/Library/Caches/pypoetry/virtualenvs/websocket-test-j4LebyL2-py3.12/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
await app(scope, receive, sender)
File "/Users/eugene/Library/Caches/pypoetry/virtualenvs/websocket-test-j4LebyL2-py3.12/lib/python3.12/site-packages/starlette/routing.py", line 762, in __call__
await self.middleware_stack(scope, receive, send)
File "/Users/eugene/Library/Caches/pypoetry/virtualenvs/websocket-test-j4LebyL2-py3.12/lib/python3.12/site-packages/starlette/routing.py", line 782, in app
await route.handle(scope, receive, send)
File "/Users/eugene/Library/Caches/pypoetry/virtualenvs/websocket-test-j4LebyL2-py3.12/lib/python3.12/site-packages/starlette/routing.py", line 485, in handle
await self.app(scope, receive, send)
File "/Users/eugene/Library/Caches/pypoetry/virtualenvs/websocket-test-j4LebyL2-py3.12/lib/python3.12/site-packages/engineio/async_drivers/asgi.py", line 77, in __call__
await self.not_found(receive, send)
File "/Users/eugene/Library/Caches/pypoetry/virtualenvs/websocket-test-j4LebyL2-py3.12/lib/python3.12/site-packages/engineio/async_drivers/asgi.py", line 125, in not_found
await send({'type': 'http.response.start',
File "/Users/eugene/Library/Caches/pypoetry/virtualenvs/websocket-test-j4LebyL2-py3.12/lib/python3.12/site-packages/starlette/_exception_handler.py", line 50, in sender
await send(message)
File "/Users/eugene/Library/Caches/pypoetry/virtualenvs/websocket-test-j4LebyL2-py3.12/lib/python3.12/site-packages/uvicorn/protocols/websockets/websockets_impl.py", line 319, in asgi_send
raise RuntimeError(msg % message_type)
RuntimeError: Expected ASGI message 'websocket.accept', 'websocket.close', or 'websocket.http.response.start' but got 'http.response.start'.
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.socket.io/4.5.4/socket.io.min.js"></script>
</head>
<body>
<script>
const socket = io("http://127.0.0.1:8000", { path: "/ws/socket.io" , transports: ["websocket"]});
socket.on("connect", () => {
console.log("Connected to WebSocket server!");
});
socket.on("disconnect", () => {
console.log("Disconnected from WebSocket server");
});
</script>
</body>
</html>
On fastapi 0.108 I receive hello message from Fastapi enter image description here On 0.109+ - server error like with Postman
As far as I can see there something wrong with routes, but I can't get what exactly. Any ideas?
I want to find a problem and understand - should I downgrade fastapi to 0.108 to work with websockets or I can fix it somehow.
You are using FastAPI's mount mechanism to combine the two applications into one. I'm not very familiar with it, but one thing you can try is to set the socketio_path
to /ws/socket.io
, which is in fact what you are using.
Alternatively, you can use the ASGIApp
class itself to create the combined app instead of mount, which is a cleaner solution that keeps the two apps separate. That would be:
fastapi_app = FastAPI()
sio = socketio.AsyncServer(async_mode="asgi", cors_allowed_origins="*")
app = socketio.ASGIApp(socketio_server=sio, other_asgi_app=fastapi_app, socketio_path="/ws/socket.io")