As stated in the document,
After inspecting the request, the connect event handler can return False to reject the connection with the client.
# namespace: /live
class MyNamespace(socketio.AsyncNamespace):
def on_connect(self, sid, env):
return False
I found that when returning False in the connect event handler, and a socket.io ERROR packet was received via polling transport before upgrading to websocket.

In the message 44/live, the first 4 means it's a engine.io MESSAGE packet and the second 4 means it's a socket.io ERROR packet.
As the result above shows that returning False prevents the socket.io connection to be established, but the underlying engine.io connection is still alive; that is; the websocket is still opened and will still perform those engine.io behaviors such as ping, pong, etc.

disconnect()# namespace: /live
class MyNamespace(socketio.AsyncNamespace):
async def on_connect(self, sid, env):
await self.disconnect(sid)
Meanwhile I have another try, I call the disconnect() method instead of returing False.
What I found this time was that before upgrading to websocket, there was a socket.io DISCONNECT packet received via polling.

Also the websocket connection was closed asap.

And finally a engine.io CLOSE packet was received.

Is it a feature or a bug that returning False in the connect event handler does not close the underlying engine.io connection but socket.io connection only?
As a result of the current behavior, even if the server rejects the client, the client is still consuming the bandwidth and other resources of the server to perform engine.io behavior.
Why does returning False behave different from disconnect()?
Is it a feature or a bug that returning False in the connect event handler does not close the underlying engine.io connection but socket.io connection only?
This is an area that I consider not well covered in the Socket.IO specification.
You are connecting through the /live namespace, so returning False in the connect event disconnects just that namespace. There could be other namespaces that are still connected, and those would be using the same engine.io connection, so disconnecting everything does not seem to be appropriate here, in my opinion the client should close the engine.io connection once it receives the ERROR packet.
For the disconnect() call, however, you are not passing a namespace, so you are explicitly asking for all namespaces to be disconnected at once, and for that reason the lower level engine.io connection is also disconnected.
I hope that explains the difference in behavior. I don't think this is a great solution and would be open to revisit this and make this behavior and/or documentation more clear.
This is an area that I consider not well covered in the Socket.IO specification.
Yes, I also found that socket.io documentation has not been comprehensive yet.
You are connecting through the /live namespace, so returning False in the connect event disconnects just that namespace. There could be other namespaces that are still connected, and those would be using the same engine.io connection, so disconnecting everything does not seem to be appropriate here, in my opinion the client should close the engine.io connection once it receives the ERROR packet.
I got it. The engine.io connection may still be used by others.
For the disconnect() call, however, you are not passing a namespace, so you are explicitly asking for all namespaces to be disconnected at once, and for that reason the lower level engine.io connection is also disconnected.
In the document, it says that for class-based namespaces, methods are default to the proper namespace when the namespace argument is not given.
As a convenience to methods defined in a class-based namespace, the namespace instance includes versions of several of the methods in the socketio.Server and socketio.AsyncServer classes that default to the proper namespace when the namespace argument is not given.
So according to the document, doesn't self.disconnect() called from /live also mean to disconnect the client from just that namespace?
If I add another namespace sharing the same connection with /live and have some clients connected to the new namespace, the engine.io connection should keep alive after I called self.disconnect() in /live, do I understand it right?
@momocow you are actually correct, I didn't realize you were calling the disconnect method on the namespace.
I need do a more careful review, but my guess is that what's happening is that your client gets the namespace disconnect and then decides to disconnect all the way.
I should mention that calling disconnect in the connect handler is not really something I ever intended to support, so how that is handled is really undefined. A proper test for how the namespace disconnect behaves is to do it from an event handler after the connection has been accepted.
I need do a more careful review, but my guess is that what's happening is that your client gets the namespace disconnect and then decides to disconnect all the way.
Oh, that makes sense since the CLOSE packet was in the request payload of a HTTP POST, which was the upstream part of the polling transport.
I should mention that calling disconnect in the connect handler is not really something I ever intended to support, so how that is handled is really undefined.
I have been thinking about it after I filed the issue; that is, I was trying to disconnect the client before the connection had actually been established (the connection was actually established after the connect event handler returned).
I have tried something like the following snippet.
# namespace: /live
class MyNamespace(socketio.AsyncNamespace):
async def on_connect(self, sid, env):
asyncio.create_task(self.disconnect(sid))
# the connect event handler returned
If I did not misunderstand anything about asyncio.create_task(), it should execute the coroutine in the next tick, right?
(Sorry I'm more experienced in javascript and NodeJS development, but I find that both language, python and javascript, implement the asynchronized event loop in a very similar way.)
If python-socketio handles the return value of the connect event handler and emits the CONNECT packet in the same tick, the snippet will close the socket.io connection right after the connection is established.
I've tried the snippet and found nothing unexpected; that is, only the socket.io connection was closed.
I think I have got a clearer view of how the connection of both socket.io and engine.io are handled and how they are related to the python-socketio. Actually python-socketio focuses on the socket.io layer, as its name states.
Thanks for giving a nice explanation.
Your example will probably fail. You are making the assumption that nothing else will cause a task switch from the time you return from the connect handler until the time the connection is established, and this is actually wrong, because to establish the connection more packets are exchanged, and that creates an opportunity for other tasks to be scheduled to run.
You may want to pass always_connect=True to your AsyncServer class, and then the connect handler is invoked after the connection is established. Returning False in this case issues a disconnect, similar to what you are trying to do.
Thanks for the suggestion, that's really what I'm looking for!