Skip to content

Commit d230b11

Browse files
committed
Fix Connection.recv(timeout=0) in the sync implementation.
Fix #1552.
1 parent 2abf77f commit d230b11

File tree

3 files changed

+18
-3
lines changed

3 files changed

+18
-3
lines changed

docs/project/changelog.rst

+4
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ notice.
3535
Bug fixes
3636
.........
3737

38+
* Fixed ``connection.recv(timeout=0)`` in the :mod:`threading` implementation.
39+
If a message is already received, it is returned. Previously,
40+
:exc:`TimeoutError` was raised incorrectly.
41+
3842
* Wrapped errors when reading the opening handshake request or response in
3943
:exc:`~exceptions.InvalidMessage` so that :func:`~asyncio.client.connect`
4044
raises :exc:`~exceptions.InvalidHandshake` or a subclass when the opening

src/websockets/sync/messages.py

+8-3
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,12 @@ def get_next_frame(self, timeout: float | None = None) -> Frame:
7979
raise EOFError("stream of frames ended") from None
8080
else:
8181
try:
82-
frame = self.frames.get(block=True, timeout=timeout)
82+
# Check for a frame that's already received if timeout <= 0.
83+
# SimpleQueue.get() doesn't support negative timeout values.
84+
if timeout is not None and timeout <= 0:
85+
frame = self.frames.get(block=False)
86+
else:
87+
frame = self.frames.get(block=True, timeout=timeout)
8388
except queue.Empty:
8489
raise TimeoutError(f"timed out in {timeout:.1f}s") from None
8590
if frame is None:
@@ -143,7 +148,7 @@ def get(self, timeout: float | None = None, decode: bool | None = None) -> Data:
143148
deadline = Deadline(timeout)
144149

145150
# First frame
146-
frame = self.get_next_frame(deadline.timeout())
151+
frame = self.get_next_frame(deadline.timeout(raise_if_elapsed=False))
147152
with self.mutex:
148153
self.maybe_resume()
149154
assert frame.opcode is OP_TEXT or frame.opcode is OP_BINARY
@@ -154,7 +159,7 @@ def get(self, timeout: float | None = None, decode: bool | None = None) -> Data:
154159
# Following frames, for fragmented messages
155160
while not frame.fin:
156161
try:
157-
frame = self.get_next_frame(deadline.timeout())
162+
frame = self.get_next_frame(deadline.timeout(raise_if_elapsed=False))
158163
except TimeoutError:
159164
# Put frames already received back into the queue
160165
# so that future calls to get() can return them.

tests/sync/test_messages.py

+6
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,12 @@ def test_get_timeout_after_first_frame(self):
198198
message = self.assembler.get()
199199
self.assertEqual(message, "café")
200200

201+
def test_get_if_received(self):
202+
"""get returns a text message if it's already received."""
203+
self.assembler.put(Frame(OP_TEXT, b"caf\xc3\xa9"))
204+
message = self.assembler.get(timeout=0)
205+
self.assertEqual(message, "café")
206+
201207
# Test get_iter
202208

203209
def test_get_iter_text_message_already_received(self):

0 commit comments

Comments
 (0)