Hi,
I am having trouble with CORS in python-socketio. I have built a Restful API in Sanic and I am consuming the API with Angular 6. I have added socketio functionality with Python-socketio, however, I am receiving the following error in the browser:
Failed to load http://192.168.33.11:7770/socket.io/?EIO=3&transport=polling&t=MPP6E-6: The 'Access-Control-Allow-Origin' header contains multiple values 'http://localhost:4200, http://localhost:4200', but only one is allowed. Origin 'http://localhost:4200' is therefore not allowed access.
It seems to be adding two origins to the Access-Control-Allow-Origin header.
I have tried setting the cors_allowed_origins to a list containing the http://localhost:4200 but that did not work.
I also had to install the Sanic_Cors (https://github.com/ashleysommer/sanic-cors) module in order to allow CORS for the normal API routes. If I remove the CORS(app) from the below configuration the socket connections seem to be working.
My server currently looks like this:
sio = socketio.AsyncServer(async_mode='sanic', cors_credentials=True)
app = Sanic()
app.config['CORS_AUTOMATIC_OPTIONS'] = True
sio.attach(app)
CORS(app)
Is there a way to have these two modules work in harmony, or to fix the origin header in the Socketio module?
I hope you can help me.
I appreciate any help in advance!
Thanks!
I'll have to check this, but it appears that Sanic is reporting the origin sent by the client two times in a row. Not sure if there is some sort of collision with the sanic-cors package or if it is something else, but that seems to be the root cause.
Hi, thank you for the quick response.
I have found a workaround to this issues if anyone else runs into it, I can use the @cross_origin(app) decorator on each of the API routes, and it will fix the CORS issues without reporting to the client twice. Meaning the socket routes will work as intended.
It might be obvious in retrospect but the order of placement of the decorator, however, was important in my case as I have @authorized() decorator that handles tokens and other user related queries. This was why the decorator did not work at first in my case and was giving errors. So my working code looks like this:
@app.route('/some_route', methods=['POST'])
@cross_origin(app)
@authorized()
async def some_route(request, user):
# do some work and send return response
The @cross_origin(app) should run before the @authorized() in order for my authorize function to work.
+1
I have the same problem, too
@RuWander I did not understand your solution.
The question was about python-socketio - multiple value for Access-Control-Allow-Origin
But the answer, was the sanic handler.
Thank you @RuWander , your solution worked perfectly
+1
I have the same problem, too
@RuWander I did not understand your solution.
The question was about python-socketio - multiple value forAccess-Control-Allow-Origin
But the answer, was the sanic handler.
In sanic, the cors was applied to all connections, even if the socketio connections has there own cors settings set. So there was a duplicate values one from the socketio default and one from the sanic_cors.
By using the sanic cors only on the functions not related to socketio, the things get to normal and no more duplicate values for socketio connections.
So @python-programmer you need to separate things in your app as @RuWander did.
I was just looking at this issue (sorry for taking so long!).
Since you guys have CORS set up on the Sanic side, wouldn't it make sense to disable CORS support on the Socket.IO side? You can do that by passing cors_allowed_origins=[] in your SocketIO constructor. This will disable any efforts from the Socket.IO on setting CORS related headers.
Let me know if that works!
@miguelgrinberg I tried by setting cors_allowed_origins=[]. But it couldn't help. The error is
The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'
I have also tried setting cors_credentials. Couldn't help. @RuWander 's workaround is helped to resolve this.
I finally spent some time investigating this issue.
A working Sanic configuration when using the sanic-cors extension is below:
app.config['CORS_AUTOMATIC_OPTIONS'] = True
app.config['CORS_SUPPORTS_CREDENTIALS'] = True
On the Socket.IO side, the server must be initialized to not do anything related to CORS:
eio = engineio.AsyncServer(async_mode='sanic', cors_allowed_origins=[])
With this configuration, CORS is 100% controlled by Sanic. Hope this helps.
The above has been added to the documentation.
@miguelgrinberg I am under the impression that this workaround is broken since the latest release of python-engineio (https://github.com/miguelgrinberg/python-engineio/commit/7548f704a0a3000b7ac8a6c88796c4ae58aa9c37). I haven't had time to investigate deeper yet, but this is the only thing that seemed to have changed in the last days
@salimaboubacar It's not that it broke, python-engineio had to be changed to address security issues with the WebSocket protocol. If you want the old behavior, you can add cors_allowed_origins='*' to your SocketIO() object.
In general yes, unfortunately, doing that reproduces the exact symptoms of this issue (multiple values for Access-Control-Allow-Origin)
Yes, I just tested it here and you are correct, using cors_allowed_origins=[] does not work like it used to. I'll fix it.
@salimaboubacar can I ask you to test the master branch of python-engineio? Let me know if the problem is addressed with this fix or not.
Sure, will do tomorrow :)
In case others struggle to get things working, I've just had what appears to be exactly this issue using Sanic which was finally solved by specifying transports when creating the client socket:
const socket = io('http://localhost:4329', {
transports: ['websocket', 'polling', 'flashsocket']
)};
as discussed here.
BTW this was with Chrome; FF didn't have these issues.
In case others struggle to get things working, I've just had what appears to be exactly this issue using Sanic which was finally solved by specifying
transportswhen creating the client socket:const socket = io('http://localhost:4329', { transports: ['websocket', 'polling', 'flashsocket'] )};as discussed here.
BTW this was with Chrome; FF didn't have these issues.
_(Dropping this here in case this helps others)_
I was experiencing exactly this symptom using fastapi-socketio - multiple access-control-allow-origin header entries in the FastAPI server response. Chrome complains about errors and doesn't upgrade; FireFox does not.
fastapi-socketio hardwires async_mode="asgi" in the AsyncServer constructor.
I applied @dbrnz's transports quick fix shown above, which helped me move past this problem for now. When time permits I'll investigate a proper fix...
Most helpful comment
In case others struggle to get things working, I've just had what appears to be exactly this issue using Sanic which was finally solved by specifying
transportswhen creating the client socket:as discussed here.
BTW this was with Chrome; FF didn't have these issues.