I have an app that uses standard WebSockets with FastAPI, and that works well. Instead of using FastAPI's WebSocket capability, I would like to use Socket.io. I have looked at lots of examples, and while it looks like I have copied the examples almost exactly, I still get a bunch of errors in the command prompt:
←[32mINFO←[0m: 127.0.0.1:56005 - "←[1mOPTIONS /socket.io/?EIO=4&transport=polling&t=ODINHQk HTTP/1.1←[0m" ←[31m400 Bad Request←[0m
←[32mINFO←[0m: 127.0.0.1:56005 - "←[1mGET /socket.io/?EIO=4&transport=polling&t=ODINHQk HTTP/1.1←[0m" ←[31m404 Not Found←[0m
←[32mINFO←[0m: 127.0.0.1:56005 - "←[1mOPTIONS /socket.io/?EIO=4&transport=polling&t=ODINHq1 HTTP/1.1←[0m" ←[31m400 Bad Request←[0m
←[32mINFO←[0m: 127.0.0.1:56005 - "←[1mGET /socket.io/?EIO=4&transport=polling&t=ODINHq1 HTTP/1.1←[0m" ←[31m404 Not Found←[0m
←[32mINFO←[0m: 127.0.0.1:56005 - "←[1mOPTIONS /socket.io/?EIO=4&transport=polling&t=ODINIDT HTTP/1.1←[0m" ←[31m400 Bad Request←[0m
←[32mINFO←[0m: 127.0.0.1:56005 - "←[1mGET /socket.io/?EIO=4&transport=polling&t=ODINIDT HTTP/1.1←[0m" ←[31m404 Not Found←[0m
It GET requests are all a bunch of 404 errors. I have been testing and researching for hours, and nothing seems to work. I have tried the fastapi-socketio package, copying the example exactly, but it is still not connecting. It appears to be a server issue (as opposed to a client issue), since I get these errors when using this tester.
When I test using my own client (created using Dart), I get 403 errors:
←[32mINFO←[0m: ('127.0.0.1', 57251) - "WebSocket /socket.io/" 403
←[32mINFO←[0m: connection failed (403 Forbidden)
←[32mINFO←[0m: connection closed
←[32mINFO←[0m: ('127.0.0.1', 57253) - "WebSocket /socket.io/" 403
←[32mINFO←[0m: connection failed (403 Forbidden)
←[32mINFO←[0m: connection closed
What am I missing?
Here is my code:
from fastapi import FastAPI
from fastapi_socketio import SocketManager
from fastapi.middleware.cors import CORSMiddleware
origins = [
'https://www.piesocket.com',
# IMPORTANT: Add other endpoints here
]
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
sio = SocketManager(
app=app,
cors_allowed_origins=origins
)
@sio.on('join')
async def handle_join(sid, *args, **kwargs):
print('Emitting...')
await sio.emit('test', 'Success!')
print('Success!')
@sio.on('connect')
async def test(sid, *args, **kwargs):
print('Emitting...')
await sio.emit('test', 'Success!')
print('Success!')
@sio.on('test')
async def test(sid, *args, **kwargs):
await sio.emit('hey', 'joe')
This appears to be similar to this issue and this issue, but it is in Python instead of JavaScript.
CodePudding user response:
I think I have an idea of why it's not working. You put in your Flask app:
app = Flask(name, static_folder="../front/build", static_url_path="/")
which is saying to have the static path as the root path. In other words, if there's not an explicit route for it in your app, then it'll try to find and serve a file from ../front/build.
This would also explain the 502 - Bad Gateway error in your nginx log.
EDIT: I have tested this theory and it seems to not have affected anything. However, now I see that you just did
sio = SocketIO()
without passing in app, which could be another reason for why it's not working.
CodePudding user response:
After doing a ton of research, I finally found the answer, and I hope to save others some time by answering it here. It turns out, I needed to create a socket app:
socket_app = socketio.ASGIApp(sio)
Then mount that to my main app at the end:
app.mount('/', socket_app)
Here is the full code:
import socketio
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
origins = [
'https://www.piesocket.com',
# IMPORTANT: Add other endpoints here
]
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
sio = socketio.AsyncServer(
async_mode='asgi',
cors_allowed_origins=origins
)
socket_app = socketio.ASGIApp(sio)
@sio.on('disconnect')
def test_disconnect(sid):
print('Client disconnected')
@sio.on('connect')
async def connection(sid, message):
print('Connected!')
await sio.emit('connect', 'Connected!')
@sio.on('test')
async def test(sid, message):
print(f'Received data from {sid}: {message}')
await sio.emit('test', 'It worked!')
app.mount('/', socket_app)
To run the server, in the command line (and probably terminal as well), change the directory to the folder this file is in, then run this command:
uvicorn main:app --reload
Just replace "main" with the name of the Python file without the .py
.
For basic testing, I used this tool. The default URL is http://localhost:8080
, so be sure to replace the 8080
with the port number your running server is using (probably 8000
), then click Connect.
I was able to solve this based on this comment on GitHub, which I found from here.
If anyone has any other suggestions or improvements, I'm definitely open to them!