Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

threading implementation with process_request raise ConnectionClosedError #1419

Open
Flowrey opened this issue Nov 28, 2023 · 3 comments
Open
Labels

Comments

@Flowrey
Copy link

Flowrey commented Nov 28, 2023

I'm using:

  • Python: 3.12
  • websockets: 12.0

When using the threading implementation like in this code:

import http
from websockets.sync.server import serve, ServerConnection, Request, Response
from websockets.http11 import datastructures

def health_check(websocket: ServerConnection, request: Request):
    if request.path == "/healthz":
        return Response(http.HTTPStatus.OK, "OK", datastructures.Headers([]), b"OK")
    else:
        return None


def echo(websocket):
    for message in websocket:
        websocket.send(message)

def main():
    with serve(echo, "localhost", 8765, process_request=health_check) as server:
        server.serve_forever()

main()

Any call to the /healthz endpoint works but raise a ConnectionCloseError:

connection handler failed
Traceback (most recent call last):
  File "/home/flowrey/Code/poc/venv/lib64/python3.12/site-packages/websockets/sync/server.py", line 499, in conn_handler
    handler(connection)
  File "/home/flowrey/Code/poc/server.py", line 13, in echo
    for message in websocket:
  File "/home/flowrey/Code/poc/venv/lib64/python3.12/site-packages/websockets/sync/connection.py", line 162, in __iter__
    yield self.recv()
          ^^^^^^^^^^^
  File "/home/flowrey/Code/poc/venv/lib64/python3.12/site-packages/websockets/sync/connection.py", line 201, in recv
    raise self.protocol.close_exc from self.recv_events_exc
websockets.exceptions.ConnectionClosedError: no close frame received or sent
@aaugustin aaugustin added the bug label Nov 30, 2023
@MauroAntonino
Copy link

I noticed that it is receiving empty bytes as a data response, and this causes the socket to close.

if data == b"":
break

finally:
# This isn't expected to raise an exception.
self.close_socket()

Another thing is that when the status code is not 101, the state does not end up being OPEN, which then closes the connection as well.

if response.status_code == 101:
assert self.state is CONNECTING
self.state = OPEN
else:
self.send_eof()
self.parser = self.discard()
next(self.parser) # start coroutine

if self.protocol.state is not OPEN:
self.recv_events_thread.join(self.close_timeout)
self.close_socket()
self.recv_events_thread.join()

I have made this PR that solved this error for me, but I don't know if it broke tests or other functinalities
#1440

@aaugustin
Copy link
Member

The change causes tests to hang -- because you removed the code paths that manages the end of a connection.

The issue looks legit but that's not the right fix.

@MauroAntonino
Copy link

I think I figured it out now. The code is handling all connections as WebSocket connections. When it is an HTTP connection and a request is made, it is supposed to close the connection, which differs from a WebSocket, where it is expected to keep the connection alive. I created a checker to verify if it is a WebSocket before handling it.

I have created this new PR with this change:
#1443

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants