Skip to content

Commit 2384e70

Browse files
committed
Add test case for #96
1 parent 7b09f64 commit 2384e70

File tree

1 file changed

+110
-0
lines changed

1 file changed

+110
-0
lines changed

Diff for: tests/issue_96.py

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
'''
2+
This is a test for issue #96:
3+
https://github.com/HyperionGray/trio-websocket/issues/96
4+
5+
This is not a part of the automated tests, because its prone to a race condition
6+
and I have not been able to trigger it deterministically. Instead, this "test"
7+
connects multiple times, triggering the bug on average once or twice out of
8+
every 10 connections. The bug causes an uncaught exception which causes the
9+
script to crash.
10+
11+
Note that the server handler and the client have `try/except ConnectionClosed`
12+
blocks. The bug in issue #96 is that the exception is raised in a background
13+
task, so it is not caught by the caller's try/except block. Instead, it crashes
14+
the nursery that the background task is running in.
15+
16+
Here's an example run where the test fails:
17+
18+
(venv) $ python tests/issue_96.py
19+
INFO:root:Connection 0
20+
INFO:root:Connection 1
21+
INFO:root:Connection 2
22+
INFO:root:Connection 3
23+
INFO:root:Connection 4
24+
INFO:root:Connection 5
25+
INFO:root:Connection 6
26+
INFO:root:Connection 7
27+
INFO:root:Connection 8
28+
INFO:root:Connection 9
29+
INFO:root:Connection 10
30+
INFO:root:Connection 11
31+
Traceback (most recent call last):
32+
File "tests/issue_96.py", line 58, in <module>
33+
trio.run(main)
34+
File "/home/mhaase/code/trio-websocket/venv/lib/python3.6/site-packages/trio/_core/_run.py", line 1337, in run
35+
raise runner.main_task_outcome.error
36+
File "tests/issue_96.py", line 54, in main
37+
nursery.cancel_scope.cancel()
38+
File "/home/mhaase/code/trio-websocket/venv/lib/python3.6/site-packages/trio/_core/_run.py", line 397, in __aexit__
39+
raise combined_error_from_nursery
40+
File "/home/mhaase/code/trio-websocket/trio_websocket/_impl.py", line 327, in serve_websocket
41+
await server.run(task_status=task_status)
42+
File "/home/mhaase/code/trio-websocket/trio_websocket/_impl.py", line 1097, in run
43+
await trio.sleep_forever()
44+
File "/home/mhaase/code/trio-websocket/venv/lib/python3.6/site-packages/trio/_core/_run.py", line 397, in __aexit__
45+
raise combined_error_from_nursery
46+
File "/home/mhaase/code/trio-websocket/venv/lib/python3.6/site-packages/trio/_highlevel_serve_listeners.py", line 129, in serve_listeners
47+
task_status.started(listeners)
48+
File "/home/mhaase/code/trio-websocket/venv/lib/python3.6/site-packages/trio/_core/_run.py", line 397, in __aexit__
49+
raise combined_error_from_nursery
50+
File "/home/mhaase/code/trio-websocket/venv/lib/python3.6/site-packages/trio/_highlevel_serve_listeners.py", line 27, in _run_handler
51+
await handler(stream)
52+
File "/home/mhaase/code/trio-websocket/trio_websocket/_impl.py", line 1125, in _handle_connection
53+
await connection.aclose()
54+
File "/home/mhaase/code/trio-websocket/venv/lib/python3.6/site-packages/trio/_core/_run.py", line 397, in __aexit__
55+
raise combined_error_from_nursery
56+
File "/home/mhaase/code/trio-websocket/trio_websocket/_impl.py", line 927, in _reader_task
57+
await handler(event)
58+
File "/home/mhaase/code/trio-websocket/trio_websocket/_impl.py", line 811, in _handle_connection_closed_event
59+
await self._write_pending()
60+
File "/home/mhaase/code/trio-websocket/trio_websocket/_impl.py", line 968, in _write_pending
61+
raise ConnectionClosed(self._close_reason) from None
62+
trio_websocket._impl.ConnectionClosed: <CloseReason code=1006 name=ABNORMAL_CLOSURE reason=None>
63+
64+
You may need to modify the sleep duration to reliably trigger the bug on your
65+
system. On a successful test, it will make 1,000 connections without any
66+
uncaught exceptions.
67+
'''
68+
import functools
69+
import logging
70+
from random import random
71+
72+
import trio
73+
from trio_websocket import connect_websocket, ConnectionClosed, serve_websocket
74+
75+
76+
logging.basicConfig(level=logging.INFO)
77+
78+
79+
async def handler(request):
80+
''' Reverse incoming websocket messages and send them back. '''
81+
try:
82+
ws = await request.accept()
83+
await ws.send_message('issue96')
84+
await trio.sleep(random()/1000)
85+
await trio.aclose_forcefully(ws._stream)
86+
except ConnectionClosed:
87+
pass
88+
89+
90+
async def main():
91+
async with trio.open_nursery() as nursery:
92+
serve = functools.partial(serve_websocket, handler, 'localhost', 8000,
93+
ssl_context=None)
94+
await nursery.start(serve)
95+
96+
for n in range(1000):
97+
logging.info('Connection %d', n)
98+
try:
99+
connection = await connect_websocket(nursery, 'localhost', 8000,
100+
'/', use_ssl=False)
101+
await connection.get_message()
102+
await connection.aclose()
103+
except ConnectionClosed:
104+
pass
105+
106+
nursery.cancel_scope.cancel()
107+
108+
109+
if __name__ == '__main__':
110+
trio.run(main)

0 commit comments

Comments
 (0)