37
37
import pytest
38
38
import trio
39
39
import trustme
40
+ import wsproto
40
41
from trio .testing import memory_stream_pair
41
42
from wsproto .events import CloseConnection
43
+ from wsproto .utilities import LocalProtocolError
42
44
43
45
try :
44
46
from trio .lowlevel import current_task # pylint: disable=ungrouped-imports
63
65
wrap_server_stream
64
66
)
65
67
68
+ WS_PROTO_VERSION = tuple (map (int , wsproto .__version__ .split ('.' )))
66
69
67
70
HOST = '127.0.0.1'
68
71
RESOURCE = '/resource'
@@ -904,7 +907,7 @@ async def handler(request):
904
907
assert client .closed .code == 1009
905
908
906
909
907
- async def test_close_race (nursery , autojump_clock ):
910
+ async def test_server_close_client_disconnect_race (nursery , autojump_clock ):
908
911
"""server attempts close just as client disconnects (issue #96)"""
909
912
910
913
async def handler (request : WebSocketRequest ):
@@ -925,6 +928,40 @@ async def handler(request: WebSocketRequest):
925
928
await trio .sleep (.1 )
926
929
927
930
931
+ @pytest .mark .xfail (
932
+ reason = 'send_message() API oversight for closing-in-process case' ,
933
+ raises = None if WS_PROTO_VERSION < (1 , 2 , 0 ) else LocalProtocolError ,
934
+ strict = True )
935
+ async def test_remote_close_local_message_race (nursery , autojump_clock ):
936
+ """as remote initiates close, local attempts message (issue #175)
937
+
938
+ This exposes multiple problems in the trio-websocket API and implementation:
939
+ * send_message() silently fails if a close is in progress. This was
940
+ likely an oversight in the API, since send_message() raises `ConnectionClosed`
941
+ only in the already-closed case, yet `ConnectionClosed` is defined to cover
942
+ "in the process of closing".
943
+ * with wsproto >= 1.2.0, LocalProtocolError will be leaked
944
+ """
945
+
946
+ async def handler (request : WebSocketRequest ):
947
+ ws = await request .accept ()
948
+ await ws .get_message ()
949
+ await ws .aclose ()
950
+
951
+ server = await nursery .start (
952
+ partial (serve_websocket , handler , HOST , 0 , ssl_context = None ))
953
+
954
+ client = await connect_websocket (nursery , HOST , server .port ,
955
+ RESOURCE , use_ssl = False )
956
+ client ._for_testing_peer_closed_connection = trio .Event ()
957
+ await client .send_message ('foo' )
958
+ await client ._for_testing_peer_closed_connection .wait ()
959
+ with pytest .raises (ConnectionClosed ):
960
+ await client .send_message ('bar' ) # wsproto < 1.2.0: silently ignored
961
+ # wsproto >= 1.2.0: raises LocalProtocolError
962
+ # desired: raises ConnectionClosed
963
+
964
+
928
965
@fail_after (DEFAULT_TEST_MAX_DURATION )
929
966
async def test_server_tcp_closed_on_close_connection_event (nursery ):
930
967
"""ensure server closes TCP immediately after receiving CloseConnection"""
0 commit comments