invisibleroads / socketIO-client

A socket.io client library for Python
http://pypi.python.org/pypi/socketIO-client
MIT License
447 stars 205 forks source link

Client gets disconnected due to race condition (enable_multithread) #210

Open rabrahamkns opened 4 years ago

rabrahamkns commented 4 years ago

Hello I found a race condition where a websocket server will disconnect a socketIO-client. This is happening because of a race condition between the ping loop thread and the data thread (where data is being sent). Here is how it goes: If one thread starts sending a large frame, and it is broken into chunks, and then the second thread sends its data in the middle of the chunks, it breaks the socket io protocol. The header of each frame contains the length and some meta data, which match to a complete frame. But now the frame is polluted with data from 2 threads. As a result, the server drops the client’s connection since the header data is bad with "transport error" (“reserved fields must be empty”). This is happening python2.7/dist-packages/websocket/_core.py line #276, send_frame method:

` def send_frame(self, frame): """ Send the data frame.

    frame: frame data created  by ABNF.create_frame

    >>> ws = create_connection("ws://echo.websocket.org/")
    >>> frame = ABNF.create_frame("Hello", ABNF.OPCODE_TEXT)
    >>> ws.send_frame(frame)
    >>> cont_frame = ABNF.create_frame("My name is ", ABNF.OPCODE_CONT, 0)
    >>> ws.send_frame(frame)
    >>> cont_frame = ABNF.create_frame("Foo Bar", ABNF.OPCODE_CONT, 1)
    >>> ws.send_frame(frame)

    """
    if self.get_mask_key:
        frame.get_mask_key = self.get_mask_key
    data = frame.format()
    length = len(data)
    trace("send: " + repr(data))

    with self.lock:
        while data:
            l = self._send(data)
            data = data[l:]

    return length

`

in socketIO-client lock is defined as NoLock, so it does nothing. This is happening because enable_multithread in python2.7/dist-packages/websocket/_core.py inside craete_connection() is False by default, and I could not find a way to turn this on from socketIO-client. To fix this I added this python2.7/dist-packages/socketIO_client/transports.py line #139: kw['enable_multithread'] = True

And now the websocket send() method is guarded. My server is websocket for node v4.5.0