Pithikos / python-websocket-server

A simple fully working websocket-server in Python with no external dependencies
MIT License
1.13k stars 380 forks source link

Socket is not closed after client left #119

Open vzarutskiy opened 1 year ago

vzarutskiy commented 1 year ago

Python-dbg detected warning during websocket server working:

[INFO 2023.04.12 21:14:58 Thread-2:warnings.py:30][_showwarnmsg_impl]: *:164: ResourceWarning: unclosed <socket.socket [closed] fd=11, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0> if check_str in list(self.check_pattern_callbacks.keys()): Object allocated at (most recent call last): File "/usr/lib/python3.8/threading.py", lineno 890 self._bootstrap_inner() File "/usr/lib/python3.8/threading.py", lineno 932 self.run() File "/home/user/.local/lib/python3.8/site-packages/websocket_server/thread.py", lineno 27 self._target(self._args, self._kwargs) File "/usr/lib/python3.8/socketserver.py", lineno 237 self._handle_request_noblock() File "/usr/lib/python3.8/socketserver.py", lineno 311 request, client_address = self.get_request() File "/usr/lib/python3.8/socketserver.py", lineno 502 return self.socket.accept() File "/usr/lib/python3.8/socket.py", lineno 293 sock = socket(self.family, self.type, self.proto, fileno=fd)

Real socket closing happens in case where self._io_refs = 0 (socket.py):

    def close(self):
        # This function should not reference any globals. See issue #808164.
        self._closed = True
        if self._io_refs <= 0:
            self._real_close()

self._io_refs is used in makefile (socket.py):

def makefile(self, mode="r", buffering=None, *,
             encoding=None, errors=None, newline=None):
    ...
    self._io_refs += 1
    ...
    return text

makefile is used in "setup" from StreamRequestHandler (socketserver.py):

class StreamRequestHandler(BaseRequestHandler):
    ...
    def setup(self):
        ...
        self.rfile = self.connection.makefile('rb', self.rbufsize)
        if self.wbufsize == 0:
            self.wfile = _SocketWriter(self.connection)
        else:
            self.wfile = self.connection.makefile('wb', self.wbufsize)

    def finish(self):
        ...
        self.wfile.close()
        self.rfile.close()

Stream closing, which was opened by makefile happens in "finish" method of StreamRequestHandler, but this method does not execute in WebSocketHandler, only StreamRequestHandler.setup(self):

class WebSocketHandler(StreamRequestHandler):
    ...
    def setup(self):
        StreamRequestHandler.setup(self)
        self.keep_alive = True
        self.handshake_done = False
        self.valid_client = False
    ...
    def finish(self):
        self.server._client_left_(self)

Possible fix:

def finish(self):
    StreamRequestHandler.finish(self)
    self.server._client_left_(self)
adamkennedyasurion commented 4 months ago

I'm having the same issue here. Unfortunately, the "Possible fix" did not help me. I still haven't been able to figure it out.

def finish(self):
    StreamRequestHandler.finish(self)
    self.server._client_left_(self)

The only obvious pattern I have found is that it occurs when a client's browser idles or disconnects in the middle of the server transmitting a message to it. I just don't know how to disconnect/stop that one specific client that idled out.