xref: /unit/test/test_asgi_websockets.py (revision 2073:bc6ad31ce286)
11625Smax.romanov@nginx.comimport struct
21625Smax.romanov@nginx.comimport time
31625Smax.romanov@nginx.com
41635Szelenkov@nginx.comimport pytest
52066Szelenkov@nginx.comfrom packaging import version
61625Smax.romanov@nginx.comfrom unit.applications.lang.python import TestApplicationPython
71625Smax.romanov@nginx.comfrom unit.applications.websockets import TestApplicationWebsocket
81730Szelenkov@nginx.comfrom unit.option import option
91625Smax.romanov@nginx.com
101625Smax.romanov@nginx.com
111625Smax.romanov@nginx.comclass TestASGIWebsockets(TestApplicationPython):
121848Szelenkov@nginx.com    prerequisites = {
132066Szelenkov@nginx.com        'modules': {
142066Szelenkov@nginx.com            'python': lambda v: version.parse(v) >= version.parse('3.5')
152066Szelenkov@nginx.com        }
161848Szelenkov@nginx.com    }
171625Smax.romanov@nginx.com    load_module = 'asgi'
181625Smax.romanov@nginx.com
191625Smax.romanov@nginx.com    ws = TestApplicationWebsocket()
201625Smax.romanov@nginx.com
211736Szelenkov@nginx.com    @pytest.fixture(autouse=True)
221736Szelenkov@nginx.com    def setup_method_fixture(self, request, skip_alert):
231625Smax.romanov@nginx.com        assert 'success' in self.conf(
241625Smax.romanov@nginx.com            {'http': {'websocket': {'keepalive_interval': 0}}}, 'settings'
251625Smax.romanov@nginx.com        ), 'clear keepalive_interval'
261625Smax.romanov@nginx.com
271625Smax.romanov@nginx.com        skip_alert(r'socket close\(\d+\) failed')
281625Smax.romanov@nginx.com
291625Smax.romanov@nginx.com    def close_connection(self, sock):
301625Smax.romanov@nginx.com        assert self.recvall(sock, read_timeout=0.1) == b'', 'empty soc'
311625Smax.romanov@nginx.com
321625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_CLOSE, self.ws.serialize_close())
331625Smax.romanov@nginx.com
341625Smax.romanov@nginx.com        self.check_close(sock)
351625Smax.romanov@nginx.com
361819Smax.romanov@nginx.com    def check_close(self, sock, code=1000, no_close=False, frame=None):
371819Smax.romanov@nginx.com        if frame == None:
381819Smax.romanov@nginx.com            frame = self.ws.frame_read(sock)
391625Smax.romanov@nginx.com
401625Smax.romanov@nginx.com        assert frame['fin'] == True, 'close fin'
411625Smax.romanov@nginx.com        assert frame['opcode'] == self.ws.OP_CLOSE, 'close opcode'
421625Smax.romanov@nginx.com        assert frame['code'] == code, 'close code'
431625Smax.romanov@nginx.com
441625Smax.romanov@nginx.com        if not no_close:
451625Smax.romanov@nginx.com            sock.close()
461625Smax.romanov@nginx.com
471625Smax.romanov@nginx.com    def check_frame(self, frame, fin, opcode, payload, decode=True):
481625Smax.romanov@nginx.com        if opcode == self.ws.OP_BINARY or not decode:
491625Smax.romanov@nginx.com            data = frame['data']
501625Smax.romanov@nginx.com        else:
511625Smax.romanov@nginx.com            data = frame['data'].decode('utf-8')
521625Smax.romanov@nginx.com
531625Smax.romanov@nginx.com        assert frame['fin'] == fin, 'fin'
541625Smax.romanov@nginx.com        assert frame['opcode'] == opcode, 'opcode'
551625Smax.romanov@nginx.com        assert data == payload, 'payload'
561625Smax.romanov@nginx.com
571625Smax.romanov@nginx.com    def test_asgi_websockets_handshake(self):
581625Smax.romanov@nginx.com        self.load('websockets/mirror')
591625Smax.romanov@nginx.com
601625Smax.romanov@nginx.com        resp, sock, key = self.ws.upgrade()
611625Smax.romanov@nginx.com        sock.close()
621625Smax.romanov@nginx.com
631625Smax.romanov@nginx.com        assert resp['status'] == 101, 'status'
641625Smax.romanov@nginx.com        assert resp['headers']['Upgrade'] == 'websocket', 'upgrade'
651625Smax.romanov@nginx.com        assert resp['headers']['Connection'] == 'Upgrade', 'connection'
661625Smax.romanov@nginx.com        assert resp['headers']['Sec-WebSocket-Accept'] == self.ws.accept(
671625Smax.romanov@nginx.com            key
681625Smax.romanov@nginx.com        ), 'key'
691625Smax.romanov@nginx.com
701780Smax.romanov@nginx.com        # remove "mirror" application
711780Smax.romanov@nginx.com        self.load('websockets/subprotocol')
721780Smax.romanov@nginx.com
731625Smax.romanov@nginx.com    def test_asgi_websockets_subprotocol(self):
741625Smax.romanov@nginx.com        self.load('websockets/subprotocol')
751625Smax.romanov@nginx.com
761625Smax.romanov@nginx.com        resp, sock, key = self.ws.upgrade()
771625Smax.romanov@nginx.com        sock.close()
781625Smax.romanov@nginx.com
791625Smax.romanov@nginx.com        assert resp['status'] == 101, 'status'
801848Szelenkov@nginx.com        assert (
811848Szelenkov@nginx.com            resp['headers']['x-subprotocols'] == "('chat', 'phone', 'video')"
821848Szelenkov@nginx.com        ), 'subprotocols'
831625Smax.romanov@nginx.com        assert resp['headers']['sec-websocket-protocol'] == 'chat', 'key'
841625Smax.romanov@nginx.com
851625Smax.romanov@nginx.com    def test_asgi_websockets_mirror(self):
861625Smax.romanov@nginx.com        self.load('websockets/mirror')
871625Smax.romanov@nginx.com
881625Smax.romanov@nginx.com        message = 'blah'
891625Smax.romanov@nginx.com
901625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
911625Smax.romanov@nginx.com
921625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_TEXT, message)
931625Smax.romanov@nginx.com        frame = self.ws.frame_read(sock)
941625Smax.romanov@nginx.com
951625Smax.romanov@nginx.com        assert message == frame['data'].decode('utf-8'), 'mirror'
961625Smax.romanov@nginx.com
971625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_TEXT, message)
981625Smax.romanov@nginx.com        frame = self.ws.frame_read(sock)
991625Smax.romanov@nginx.com
1001625Smax.romanov@nginx.com        assert message == frame['data'].decode('utf-8'), 'mirror 2'
1011625Smax.romanov@nginx.com
1021625Smax.romanov@nginx.com        sock.close()
1031625Smax.romanov@nginx.com
1041780Smax.romanov@nginx.com    def test_asgi_websockets_mirror_app_change(self):
1051780Smax.romanov@nginx.com        self.load('websockets/mirror')
1061780Smax.romanov@nginx.com
1071780Smax.romanov@nginx.com        message = 'blah'
1081780Smax.romanov@nginx.com
1091780Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
1101780Smax.romanov@nginx.com
1111780Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_TEXT, message)
1121780Smax.romanov@nginx.com        frame = self.ws.frame_read(sock)
1131780Smax.romanov@nginx.com
1141780Smax.romanov@nginx.com        assert message == frame['data'].decode('utf-8'), 'mirror'
1151780Smax.romanov@nginx.com
1161780Smax.romanov@nginx.com        self.load('websockets/subprotocol')
1171780Smax.romanov@nginx.com
1181780Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_TEXT, message)
1191780Smax.romanov@nginx.com        frame = self.ws.frame_read(sock)
1201780Smax.romanov@nginx.com
1211780Smax.romanov@nginx.com        assert message == frame['data'].decode('utf-8'), 'mirror 2'
1221780Smax.romanov@nginx.com
1231780Smax.romanov@nginx.com        sock.close()
1241780Smax.romanov@nginx.com
1251625Smax.romanov@nginx.com    def test_asgi_websockets_no_mask(self):
1261625Smax.romanov@nginx.com        self.load('websockets/mirror')
1271625Smax.romanov@nginx.com
1281625Smax.romanov@nginx.com        message = 'blah'
1291625Smax.romanov@nginx.com
1301625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
1311625Smax.romanov@nginx.com
1321625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_TEXT, message, mask=False)
1331625Smax.romanov@nginx.com
1341625Smax.romanov@nginx.com        frame = self.ws.frame_read(sock)
1351625Smax.romanov@nginx.com
1361625Smax.romanov@nginx.com        assert frame['opcode'] == self.ws.OP_CLOSE, 'no mask opcode'
1371625Smax.romanov@nginx.com        assert frame['code'] == 1002, 'no mask close code'
1381625Smax.romanov@nginx.com
1391625Smax.romanov@nginx.com        sock.close()
1401625Smax.romanov@nginx.com
1411625Smax.romanov@nginx.com    def test_asgi_websockets_fragmentation(self):
1421625Smax.romanov@nginx.com        self.load('websockets/mirror')
1431625Smax.romanov@nginx.com
1441625Smax.romanov@nginx.com        message = 'blah'
1451625Smax.romanov@nginx.com
1461625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
1471625Smax.romanov@nginx.com
1481625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_TEXT, message, fin=False)
1491625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_CONT, ' ', fin=False)
1501625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_CONT, message)
1511625Smax.romanov@nginx.com
1521625Smax.romanov@nginx.com        frame = self.ws.frame_read(sock)
1531625Smax.romanov@nginx.com
1541625Smax.romanov@nginx.com        assert message + ' ' + message == frame['data'].decode(
1551625Smax.romanov@nginx.com            'utf-8'
1561625Smax.romanov@nginx.com        ), 'mirror framing'
1571625Smax.romanov@nginx.com
1581625Smax.romanov@nginx.com        sock.close()
1591625Smax.romanov@nginx.com
1601629Szelenkov@nginx.com    def test_asgi_websockets_length_long(self):
1611629Szelenkov@nginx.com        self.load('websockets/mirror')
1621629Szelenkov@nginx.com
1631629Szelenkov@nginx.com        _, sock, _ = self.ws.upgrade()
1641629Szelenkov@nginx.com
1651629Szelenkov@nginx.com        self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
1661629Szelenkov@nginx.com        self.ws.frame_write(
167*2073Szelenkov@nginx.com            sock, self.ws.OP_CONT, 'fragment2', length=2**64 - 1
1681629Szelenkov@nginx.com        )
1691629Szelenkov@nginx.com
1701629Szelenkov@nginx.com        self.check_close(sock, 1009)  # 1009 - CLOSE_TOO_LARGE
1711629Szelenkov@nginx.com
1721625Smax.romanov@nginx.com    def test_asgi_websockets_frame_fragmentation_invalid(self):
1731625Smax.romanov@nginx.com        self.load('websockets/mirror')
1741625Smax.romanov@nginx.com
1751625Smax.romanov@nginx.com        message = 'blah'
1761625Smax.romanov@nginx.com
1771625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
1781625Smax.romanov@nginx.com
1791625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_PING, message, fin=False)
1801625Smax.romanov@nginx.com
1811625Smax.romanov@nginx.com        frame = self.ws.frame_read(sock)
1821625Smax.romanov@nginx.com
1831625Smax.romanov@nginx.com        frame.pop('data')
1841625Smax.romanov@nginx.com        assert frame == {
1851625Smax.romanov@nginx.com            'fin': True,
1861625Smax.romanov@nginx.com            'rsv1': False,
1871625Smax.romanov@nginx.com            'rsv2': False,
1881625Smax.romanov@nginx.com            'rsv3': False,
1891625Smax.romanov@nginx.com            'opcode': self.ws.OP_CLOSE,
1901625Smax.romanov@nginx.com            'mask': 0,
1911625Smax.romanov@nginx.com            'code': 1002,
1921625Smax.romanov@nginx.com            'reason': 'Fragmented control frame',
1931625Smax.romanov@nginx.com        }, 'close frame'
1941625Smax.romanov@nginx.com
1951625Smax.romanov@nginx.com        sock.close()
1961625Smax.romanov@nginx.com
1971625Smax.romanov@nginx.com    def test_asgi_websockets_large(self):
1981625Smax.romanov@nginx.com        self.load('websockets/mirror')
1991625Smax.romanov@nginx.com
2001625Smax.romanov@nginx.com        message = '0123456789' * 300
2011625Smax.romanov@nginx.com
2021625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
2031625Smax.romanov@nginx.com
2041625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_TEXT, message)
2051625Smax.romanov@nginx.com
2061625Smax.romanov@nginx.com        frame = self.ws.frame_read(sock)
2071625Smax.romanov@nginx.com        data = frame['data'].decode('utf-8')
2081625Smax.romanov@nginx.com
2091625Smax.romanov@nginx.com        frame = self.ws.frame_read(sock)
2101625Smax.romanov@nginx.com        data += frame['data'].decode('utf-8')
2111625Smax.romanov@nginx.com
2121625Smax.romanov@nginx.com        assert message == data, 'large'
2131625Smax.romanov@nginx.com
2141625Smax.romanov@nginx.com        sock.close()
2151625Smax.romanov@nginx.com
2161625Smax.romanov@nginx.com    def test_asgi_websockets_two_clients(self):
2171625Smax.romanov@nginx.com        self.load('websockets/mirror')
2181625Smax.romanov@nginx.com
2191625Smax.romanov@nginx.com        message1 = 'blah1'
2201625Smax.romanov@nginx.com        message2 = 'blah2'
2211625Smax.romanov@nginx.com
2221625Smax.romanov@nginx.com        _, sock1, _ = self.ws.upgrade()
2231625Smax.romanov@nginx.com        _, sock2, _ = self.ws.upgrade()
2241625Smax.romanov@nginx.com
2251625Smax.romanov@nginx.com        self.ws.frame_write(sock1, self.ws.OP_TEXT, message1)
2261625Smax.romanov@nginx.com        self.ws.frame_write(sock2, self.ws.OP_TEXT, message2)
2271625Smax.romanov@nginx.com
2281625Smax.romanov@nginx.com        frame1 = self.ws.frame_read(sock1)
2291625Smax.romanov@nginx.com        frame2 = self.ws.frame_read(sock2)
2301625Smax.romanov@nginx.com
2311625Smax.romanov@nginx.com        assert message1 == frame1['data'].decode('utf-8'), 'client 1'
2321625Smax.romanov@nginx.com        assert message2 == frame2['data'].decode('utf-8'), 'client 2'
2331625Smax.romanov@nginx.com
2341625Smax.romanov@nginx.com        sock1.close()
2351625Smax.romanov@nginx.com        sock2.close()
2361625Smax.romanov@nginx.com
2371625Smax.romanov@nginx.com    @pytest.mark.skip('not yet')
2381625Smax.romanov@nginx.com    def test_asgi_websockets_handshake_upgrade_absent(
2391848Szelenkov@nginx.com        self,
2401625Smax.romanov@nginx.com    ):  # FAIL https://tools.ietf.org/html/rfc6455#section-4.2.1
2411625Smax.romanov@nginx.com        self.load('websockets/mirror')
2421625Smax.romanov@nginx.com
2431625Smax.romanov@nginx.com        resp = self.get(
2441625Smax.romanov@nginx.com            headers={
2451625Smax.romanov@nginx.com                'Host': 'localhost',
2461625Smax.romanov@nginx.com                'Connection': 'Upgrade',
2471625Smax.romanov@nginx.com                'Sec-WebSocket-Key': self.ws.key(),
2481625Smax.romanov@nginx.com                'Sec-WebSocket-Protocol': 'chat',
2491625Smax.romanov@nginx.com                'Sec-WebSocket-Version': 13,
2501625Smax.romanov@nginx.com            },
2511625Smax.romanov@nginx.com        )
2521625Smax.romanov@nginx.com
2531625Smax.romanov@nginx.com        assert resp['status'] == 400, 'upgrade absent'
2541625Smax.romanov@nginx.com
2551625Smax.romanov@nginx.com    def test_asgi_websockets_handshake_case_insensitive(self):
2561625Smax.romanov@nginx.com        self.load('websockets/mirror')
2571625Smax.romanov@nginx.com
2581625Smax.romanov@nginx.com        resp, sock, _ = self.ws.upgrade(
2591625Smax.romanov@nginx.com            headers={
2601625Smax.romanov@nginx.com                'Host': 'localhost',
2611625Smax.romanov@nginx.com                'Upgrade': 'WEBSOCKET',
2621625Smax.romanov@nginx.com                'Connection': 'UPGRADE',
2631625Smax.romanov@nginx.com                'Sec-WebSocket-Key': self.ws.key(),
2641625Smax.romanov@nginx.com                'Sec-WebSocket-Protocol': 'chat',
2651625Smax.romanov@nginx.com                'Sec-WebSocket-Version': 13,
2661625Smax.romanov@nginx.com            }
2671625Smax.romanov@nginx.com        )
2681625Smax.romanov@nginx.com        sock.close()
2691625Smax.romanov@nginx.com
2701625Smax.romanov@nginx.com        assert resp['status'] == 101, 'status'
2711625Smax.romanov@nginx.com
2721625Smax.romanov@nginx.com    @pytest.mark.skip('not yet')
2731625Smax.romanov@nginx.com    def test_asgi_websockets_handshake_connection_absent(self):  # FAIL
2741625Smax.romanov@nginx.com        self.load('websockets/mirror')
2751625Smax.romanov@nginx.com
2761625Smax.romanov@nginx.com        resp = self.get(
2771625Smax.romanov@nginx.com            headers={
2781625Smax.romanov@nginx.com                'Host': 'localhost',
2791625Smax.romanov@nginx.com                'Upgrade': 'websocket',
2801625Smax.romanov@nginx.com                'Sec-WebSocket-Key': self.ws.key(),
2811625Smax.romanov@nginx.com                'Sec-WebSocket-Protocol': 'chat',
2821625Smax.romanov@nginx.com                'Sec-WebSocket-Version': 13,
2831625Smax.romanov@nginx.com            },
2841625Smax.romanov@nginx.com        )
2851625Smax.romanov@nginx.com
2861625Smax.romanov@nginx.com        assert resp['status'] == 400, 'status'
2871625Smax.romanov@nginx.com
2881625Smax.romanov@nginx.com    def test_asgi_websockets_handshake_version_absent(self):
2891625Smax.romanov@nginx.com        self.load('websockets/mirror')
2901625Smax.romanov@nginx.com
2911625Smax.romanov@nginx.com        resp = self.get(
2921625Smax.romanov@nginx.com            headers={
2931625Smax.romanov@nginx.com                'Host': 'localhost',
2941625Smax.romanov@nginx.com                'Upgrade': 'websocket',
2951625Smax.romanov@nginx.com                'Connection': 'Upgrade',
2961625Smax.romanov@nginx.com                'Sec-WebSocket-Key': self.ws.key(),
2971625Smax.romanov@nginx.com                'Sec-WebSocket-Protocol': 'chat',
2981625Smax.romanov@nginx.com            },
2991625Smax.romanov@nginx.com        )
3001625Smax.romanov@nginx.com
3011625Smax.romanov@nginx.com        assert resp['status'] == 426, 'status'
3021625Smax.romanov@nginx.com
3031625Smax.romanov@nginx.com    @pytest.mark.skip('not yet')
3041625Smax.romanov@nginx.com    def test_asgi_websockets_handshake_key_invalid(self):
3051625Smax.romanov@nginx.com        self.load('websockets/mirror')
3061625Smax.romanov@nginx.com
3071625Smax.romanov@nginx.com        resp = self.get(
3081625Smax.romanov@nginx.com            headers={
3091625Smax.romanov@nginx.com                'Host': 'localhost',
3101625Smax.romanov@nginx.com                'Upgrade': 'websocket',
3111625Smax.romanov@nginx.com                'Connection': 'Upgrade',
3121625Smax.romanov@nginx.com                'Sec-WebSocket-Key': '!',
3131625Smax.romanov@nginx.com                'Sec-WebSocket-Protocol': 'chat',
3141625Smax.romanov@nginx.com                'Sec-WebSocket-Version': 13,
3151625Smax.romanov@nginx.com            },
3161625Smax.romanov@nginx.com        )
3171625Smax.romanov@nginx.com
3181625Smax.romanov@nginx.com        assert resp['status'] == 400, 'key length'
3191625Smax.romanov@nginx.com
3201625Smax.romanov@nginx.com        key = self.ws.key()
3211625Smax.romanov@nginx.com        resp = self.get(
3221625Smax.romanov@nginx.com            headers={
3231625Smax.romanov@nginx.com                'Host': 'localhost',
3241625Smax.romanov@nginx.com                'Upgrade': 'websocket',
3251625Smax.romanov@nginx.com                'Connection': 'Upgrade',
3261625Smax.romanov@nginx.com                'Sec-WebSocket-Key': [key, key],
3271625Smax.romanov@nginx.com                'Sec-WebSocket-Protocol': 'chat',
3281625Smax.romanov@nginx.com                'Sec-WebSocket-Version': 13,
3291625Smax.romanov@nginx.com            },
3301625Smax.romanov@nginx.com        )
3311625Smax.romanov@nginx.com
3321848Szelenkov@nginx.com        assert (
3331848Szelenkov@nginx.com            resp['status'] == 400
3341848Szelenkov@nginx.com        ), 'key double'  # FAIL https://tools.ietf.org/html/rfc6455#section-11.3.1
3351625Smax.romanov@nginx.com
3361625Smax.romanov@nginx.com    def test_asgi_websockets_handshake_method_invalid(self):
3371625Smax.romanov@nginx.com        self.load('websockets/mirror')
3381625Smax.romanov@nginx.com
3391625Smax.romanov@nginx.com        resp = self.post(
3401625Smax.romanov@nginx.com            headers={
3411625Smax.romanov@nginx.com                'Host': 'localhost',
3421625Smax.romanov@nginx.com                'Upgrade': 'websocket',
3431625Smax.romanov@nginx.com                'Connection': 'Upgrade',
3441625Smax.romanov@nginx.com                'Sec-WebSocket-Key': self.ws.key(),
3451625Smax.romanov@nginx.com                'Sec-WebSocket-Protocol': 'chat',
3461625Smax.romanov@nginx.com                'Sec-WebSocket-Version': 13,
3471625Smax.romanov@nginx.com            },
3481625Smax.romanov@nginx.com        )
3491625Smax.romanov@nginx.com
3501625Smax.romanov@nginx.com        assert resp['status'] == 400, 'status'
3511625Smax.romanov@nginx.com
3521625Smax.romanov@nginx.com    def test_asgi_websockets_handshake_http_10(self):
3531625Smax.romanov@nginx.com        self.load('websockets/mirror')
3541625Smax.romanov@nginx.com
3551625Smax.romanov@nginx.com        resp = self.get(
3561625Smax.romanov@nginx.com            headers={
3571625Smax.romanov@nginx.com                'Host': 'localhost',
3581625Smax.romanov@nginx.com                'Upgrade': 'websocket',
3591625Smax.romanov@nginx.com                'Connection': 'Upgrade',
3601625Smax.romanov@nginx.com                'Sec-WebSocket-Key': self.ws.key(),
3611625Smax.romanov@nginx.com                'Sec-WebSocket-Protocol': 'chat',
3621625Smax.romanov@nginx.com                'Sec-WebSocket-Version': 13,
3631625Smax.romanov@nginx.com            },
3641625Smax.romanov@nginx.com            http_10=True,
3651625Smax.romanov@nginx.com        )
3661625Smax.romanov@nginx.com
3671625Smax.romanov@nginx.com        assert resp['status'] == 400, 'status'
3681625Smax.romanov@nginx.com
3691625Smax.romanov@nginx.com    def test_asgi_websockets_handshake_uri_invalid(self):
3701625Smax.romanov@nginx.com        self.load('websockets/mirror')
3711625Smax.romanov@nginx.com
3721625Smax.romanov@nginx.com        resp = self.get(
3731625Smax.romanov@nginx.com            headers={
3741625Smax.romanov@nginx.com                'Host': 'localhost',
3751625Smax.romanov@nginx.com                'Upgrade': 'websocket',
3761625Smax.romanov@nginx.com                'Connection': 'Upgrade',
3771625Smax.romanov@nginx.com                'Sec-WebSocket-Key': self.ws.key(),
3781625Smax.romanov@nginx.com                'Sec-WebSocket-Protocol': 'chat',
3791625Smax.romanov@nginx.com                'Sec-WebSocket-Version': 13,
3801625Smax.romanov@nginx.com            },
3811625Smax.romanov@nginx.com            url='!',
3821625Smax.romanov@nginx.com        )
3831625Smax.romanov@nginx.com
3841625Smax.romanov@nginx.com        assert resp['status'] == 400, 'status'
3851625Smax.romanov@nginx.com
3861625Smax.romanov@nginx.com    def test_asgi_websockets_protocol_absent(self):
3871625Smax.romanov@nginx.com        self.load('websockets/mirror')
3881625Smax.romanov@nginx.com
3891625Smax.romanov@nginx.com        key = self.ws.key()
3901625Smax.romanov@nginx.com        resp, sock, _ = self.ws.upgrade(
3911625Smax.romanov@nginx.com            headers={
3921625Smax.romanov@nginx.com                'Host': 'localhost',
3931625Smax.romanov@nginx.com                'Upgrade': 'websocket',
3941625Smax.romanov@nginx.com                'Connection': 'Upgrade',
3951625Smax.romanov@nginx.com                'Sec-WebSocket-Key': key,
3961625Smax.romanov@nginx.com                'Sec-WebSocket-Version': 13,
3971625Smax.romanov@nginx.com            }
3981625Smax.romanov@nginx.com        )
3991625Smax.romanov@nginx.com        sock.close()
4001625Smax.romanov@nginx.com
4011625Smax.romanov@nginx.com        assert resp['status'] == 101, 'status'
4021625Smax.romanov@nginx.com        assert resp['headers']['Upgrade'] == 'websocket', 'upgrade'
4031625Smax.romanov@nginx.com        assert resp['headers']['Connection'] == 'Upgrade', 'connection'
4041625Smax.romanov@nginx.com        assert resp['headers']['Sec-WebSocket-Accept'] == self.ws.accept(
4051625Smax.romanov@nginx.com            key
4061625Smax.romanov@nginx.com        ), 'key'
4071625Smax.romanov@nginx.com
4081625Smax.romanov@nginx.com    # autobahn-testsuite
4091625Smax.romanov@nginx.com    #
4101625Smax.romanov@nginx.com    # Some following tests fail because of Unit does not support UTF-8
4111625Smax.romanov@nginx.com    # validation for websocket frames.  It should be implemented
4121625Smax.romanov@nginx.com    # by application, if necessary.
4131625Smax.romanov@nginx.com
4141625Smax.romanov@nginx.com    def test_asgi_websockets_1_1_1__1_1_8(self):
4151625Smax.romanov@nginx.com        self.load('websockets/mirror')
4161625Smax.romanov@nginx.com
4171625Smax.romanov@nginx.com        opcode = self.ws.OP_TEXT
4181625Smax.romanov@nginx.com
4191625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
4201625Smax.romanov@nginx.com
4211625Smax.romanov@nginx.com        def check_length(length, chopsize=None):
4221625Smax.romanov@nginx.com            payload = '*' * length
4231625Smax.romanov@nginx.com
4241625Smax.romanov@nginx.com            self.ws.frame_write(sock, opcode, payload, chopsize=chopsize)
4251625Smax.romanov@nginx.com
4261625Smax.romanov@nginx.com            frame = self.ws.frame_read(sock)
4271625Smax.romanov@nginx.com            self.check_frame(frame, True, opcode, payload)
4281625Smax.romanov@nginx.com
4291848Szelenkov@nginx.com        check_length(0)  # 1_1_1
4301848Szelenkov@nginx.com        check_length(125)  # 1_1_2
4311848Szelenkov@nginx.com        check_length(126)  # 1_1_3
4321848Szelenkov@nginx.com        check_length(127)  # 1_1_4
4331848Szelenkov@nginx.com        check_length(128)  # 1_1_5
4341848Szelenkov@nginx.com        check_length(65535)  # 1_1_6
4351848Szelenkov@nginx.com        check_length(65536)  # 1_1_7
4361848Szelenkov@nginx.com        check_length(65536, chopsize=997)  # 1_1_8
4371625Smax.romanov@nginx.com
4381625Smax.romanov@nginx.com        self.close_connection(sock)
4391625Smax.romanov@nginx.com
4401625Smax.romanov@nginx.com    def test_asgi_websockets_1_2_1__1_2_8(self):
4411625Smax.romanov@nginx.com        self.load('websockets/mirror')
4421625Smax.romanov@nginx.com
4431625Smax.romanov@nginx.com        opcode = self.ws.OP_BINARY
4441625Smax.romanov@nginx.com
4451625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
4461625Smax.romanov@nginx.com
4471625Smax.romanov@nginx.com        def check_length(length, chopsize=None):
4481625Smax.romanov@nginx.com            payload = b'\xfe' * length
4491625Smax.romanov@nginx.com
4501625Smax.romanov@nginx.com            self.ws.frame_write(sock, opcode, payload, chopsize=chopsize)
4511625Smax.romanov@nginx.com            frame = self.ws.frame_read(sock)
4521625Smax.romanov@nginx.com
4531625Smax.romanov@nginx.com            self.check_frame(frame, True, opcode, payload)
4541625Smax.romanov@nginx.com
4551848Szelenkov@nginx.com        check_length(0)  # 1_2_1
4561848Szelenkov@nginx.com        check_length(125)  # 1_2_2
4571848Szelenkov@nginx.com        check_length(126)  # 1_2_3
4581848Szelenkov@nginx.com        check_length(127)  # 1_2_4
4591848Szelenkov@nginx.com        check_length(128)  # 1_2_5
4601848Szelenkov@nginx.com        check_length(65535)  # 1_2_6
4611848Szelenkov@nginx.com        check_length(65536)  # 1_2_7
4621848Szelenkov@nginx.com        check_length(65536, chopsize=997)  # 1_2_8
4631625Smax.romanov@nginx.com
4641625Smax.romanov@nginx.com        self.close_connection(sock)
4651625Smax.romanov@nginx.com
4661625Smax.romanov@nginx.com    def test_asgi_websockets_2_1__2_6(self):
4671625Smax.romanov@nginx.com        self.load('websockets/mirror')
4681625Smax.romanov@nginx.com
4691625Smax.romanov@nginx.com        op_ping = self.ws.OP_PING
4701625Smax.romanov@nginx.com        op_pong = self.ws.OP_PONG
4711625Smax.romanov@nginx.com
4721625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
4731625Smax.romanov@nginx.com
4741625Smax.romanov@nginx.com        def check_ping(payload, chopsize=None, decode=True):
4751625Smax.romanov@nginx.com            self.ws.frame_write(sock, op_ping, payload, chopsize=chopsize)
4761625Smax.romanov@nginx.com            frame = self.ws.frame_read(sock)
4771625Smax.romanov@nginx.com
4781625Smax.romanov@nginx.com            self.check_frame(frame, True, op_pong, payload, decode=decode)
4791625Smax.romanov@nginx.com
4801848Szelenkov@nginx.com        check_ping('')  # 2_1
4811848Szelenkov@nginx.com        check_ping('Hello, world!')  # 2_2
4821625Smax.romanov@nginx.com        check_ping(b'\x00\xff\xfe\xfd\xfc\xfb\x00\xff', decode=False)  # 2_3
4831848Szelenkov@nginx.com        check_ping(b'\xfe' * 125, decode=False)  # 2_4
4841848Szelenkov@nginx.com        check_ping(b'\xfe' * 125, chopsize=1, decode=False)  # 2_6
4851625Smax.romanov@nginx.com
4861625Smax.romanov@nginx.com        self.close_connection(sock)
4871625Smax.romanov@nginx.com
4881625Smax.romanov@nginx.com        # 2_5
4891625Smax.romanov@nginx.com
4901625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
4911625Smax.romanov@nginx.com
4921625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_PING, b'\xfe' * 126)
4931625Smax.romanov@nginx.com        self.check_close(sock, 1002)
4941625Smax.romanov@nginx.com
4951625Smax.romanov@nginx.com    def test_asgi_websockets_2_7__2_9(self):
4961625Smax.romanov@nginx.com        self.load('websockets/mirror')
4971625Smax.romanov@nginx.com
4981625Smax.romanov@nginx.com        # 2_7
4991625Smax.romanov@nginx.com
5001625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
5011625Smax.romanov@nginx.com
5021625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_PONG, '')
5031625Smax.romanov@nginx.com        assert self.recvall(sock, read_timeout=0.1) == b'', '2_7'
5041625Smax.romanov@nginx.com
5051625Smax.romanov@nginx.com        # 2_8
5061625Smax.romanov@nginx.com
5071625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_PONG, 'unsolicited pong payload')
5081625Smax.romanov@nginx.com        assert self.recvall(sock, read_timeout=0.1) == b'', '2_8'
5091625Smax.romanov@nginx.com
5101625Smax.romanov@nginx.com        # 2_9
5111625Smax.romanov@nginx.com
5121625Smax.romanov@nginx.com        payload = 'ping payload'
5131625Smax.romanov@nginx.com
5141625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_PONG, 'unsolicited pong payload')
5151625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_PING, payload)
5161625Smax.romanov@nginx.com
5171625Smax.romanov@nginx.com        frame = self.ws.frame_read(sock)
5181625Smax.romanov@nginx.com        self.check_frame(frame, True, self.ws.OP_PONG, payload)
5191625Smax.romanov@nginx.com
5201625Smax.romanov@nginx.com        self.close_connection(sock)
5211625Smax.romanov@nginx.com
5221625Smax.romanov@nginx.com    def test_asgi_websockets_2_10__2_11(self):
5231625Smax.romanov@nginx.com        self.load('websockets/mirror')
5241625Smax.romanov@nginx.com
5251625Smax.romanov@nginx.com        # 2_10
5261625Smax.romanov@nginx.com
5271625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
5281625Smax.romanov@nginx.com
5291625Smax.romanov@nginx.com        for i in range(0, 10):
5301625Smax.romanov@nginx.com            self.ws.frame_write(sock, self.ws.OP_PING, 'payload-%d' % i)
5311625Smax.romanov@nginx.com
5321625Smax.romanov@nginx.com        for i in range(0, 10):
5331625Smax.romanov@nginx.com            frame = self.ws.frame_read(sock)
5341625Smax.romanov@nginx.com            self.check_frame(frame, True, self.ws.OP_PONG, 'payload-%d' % i)
5351625Smax.romanov@nginx.com
5361625Smax.romanov@nginx.com        # 2_11
5371625Smax.romanov@nginx.com
5381625Smax.romanov@nginx.com        for i in range(0, 10):
5391625Smax.romanov@nginx.com            opcode = self.ws.OP_PING
5401625Smax.romanov@nginx.com            self.ws.frame_write(sock, opcode, 'payload-%d' % i, chopsize=1)
5411625Smax.romanov@nginx.com
5421625Smax.romanov@nginx.com        for i in range(0, 10):
5431625Smax.romanov@nginx.com            frame = self.ws.frame_read(sock)
5441625Smax.romanov@nginx.com            self.check_frame(frame, True, self.ws.OP_PONG, 'payload-%d' % i)
5451625Smax.romanov@nginx.com
5461625Smax.romanov@nginx.com        self.close_connection(sock)
5471625Smax.romanov@nginx.com
5481625Smax.romanov@nginx.com    @pytest.mark.skip('not yet')
5491625Smax.romanov@nginx.com    def test_asgi_websockets_3_1__3_7(self):
5501625Smax.romanov@nginx.com        self.load('websockets/mirror')
5511625Smax.romanov@nginx.com
5521625Smax.romanov@nginx.com        payload = 'Hello, world!'
5531625Smax.romanov@nginx.com
5541625Smax.romanov@nginx.com        # 3_1
5551625Smax.romanov@nginx.com
5561625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
5571625Smax.romanov@nginx.com
5581625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_TEXT, payload, rsv1=True)
5591625Smax.romanov@nginx.com        self.check_close(sock, 1002)
5601625Smax.romanov@nginx.com
5611625Smax.romanov@nginx.com        # 3_2
5621625Smax.romanov@nginx.com
5631625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
5641625Smax.romanov@nginx.com
5651625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
5661625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_TEXT, payload, rsv2=True)
5671625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_PING, '')
5681625Smax.romanov@nginx.com
5691625Smax.romanov@nginx.com        frame = self.ws.frame_read(sock)
5701625Smax.romanov@nginx.com        self.check_frame(frame, True, self.ws.OP_TEXT, payload)
5711625Smax.romanov@nginx.com
5721625Smax.romanov@nginx.com        self.check_close(sock, 1002, no_close=True)
5731625Smax.romanov@nginx.com
5741625Smax.romanov@nginx.com        assert self.recvall(sock, read_timeout=0.1) == b'', 'empty 3_2'
5751625Smax.romanov@nginx.com        sock.close()
5761625Smax.romanov@nginx.com
5771625Smax.romanov@nginx.com        # 3_3
5781625Smax.romanov@nginx.com
5791625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
5801625Smax.romanov@nginx.com
5811625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
5821625Smax.romanov@nginx.com
5831625Smax.romanov@nginx.com        frame = self.ws.frame_read(sock)
5841625Smax.romanov@nginx.com        self.check_frame(frame, True, self.ws.OP_TEXT, payload)
5851625Smax.romanov@nginx.com
5861625Smax.romanov@nginx.com        self.ws.frame_write(
5871625Smax.romanov@nginx.com            sock, self.ws.OP_TEXT, payload, rsv1=True, rsv2=True
5881625Smax.romanov@nginx.com        )
5891625Smax.romanov@nginx.com
5901625Smax.romanov@nginx.com        self.check_close(sock, 1002, no_close=True)
5911625Smax.romanov@nginx.com
5921625Smax.romanov@nginx.com        assert self.recvall(sock, read_timeout=0.1) == b'', 'empty 3_3'
5931625Smax.romanov@nginx.com        sock.close()
5941625Smax.romanov@nginx.com
5951625Smax.romanov@nginx.com        # 3_4
5961625Smax.romanov@nginx.com
5971625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
5981625Smax.romanov@nginx.com
5991625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_TEXT, payload, chopsize=1)
6001625Smax.romanov@nginx.com        self.ws.frame_write(
6011625Smax.romanov@nginx.com            sock, self.ws.OP_TEXT, payload, rsv3=True, chopsize=1
6021625Smax.romanov@nginx.com        )
6031625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_PING, '')
6041625Smax.romanov@nginx.com
6051625Smax.romanov@nginx.com        frame = self.ws.frame_read(sock)
6061625Smax.romanov@nginx.com        self.check_frame(frame, True, self.ws.OP_TEXT, payload)
6071625Smax.romanov@nginx.com
6081625Smax.romanov@nginx.com        self.check_close(sock, 1002, no_close=True)
6091625Smax.romanov@nginx.com
6101625Smax.romanov@nginx.com        assert self.recvall(sock, read_timeout=0.1) == b'', 'empty 3_4'
6111625Smax.romanov@nginx.com        sock.close()
6121625Smax.romanov@nginx.com
6131625Smax.romanov@nginx.com        # 3_5
6141625Smax.romanov@nginx.com
6151625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
6161625Smax.romanov@nginx.com
6171625Smax.romanov@nginx.com        self.ws.frame_write(
6181625Smax.romanov@nginx.com            sock,
6191625Smax.romanov@nginx.com            self.ws.OP_BINARY,
6201625Smax.romanov@nginx.com            b'\x00\xff\xfe\xfd\xfc\xfb\x00\xff',
6211625Smax.romanov@nginx.com            rsv1=True,
6221625Smax.romanov@nginx.com            rsv3=True,
6231625Smax.romanov@nginx.com        )
6241625Smax.romanov@nginx.com
6251625Smax.romanov@nginx.com        self.check_close(sock, 1002)
6261625Smax.romanov@nginx.com
6271625Smax.romanov@nginx.com        # 3_6
6281625Smax.romanov@nginx.com
6291625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
6301625Smax.romanov@nginx.com
6311625Smax.romanov@nginx.com        self.ws.frame_write(
6321625Smax.romanov@nginx.com            sock, self.ws.OP_PING, payload, rsv2=True, rsv3=True
6331625Smax.romanov@nginx.com        )
6341625Smax.romanov@nginx.com
6351625Smax.romanov@nginx.com        self.check_close(sock, 1002)
6361625Smax.romanov@nginx.com
6371625Smax.romanov@nginx.com        # 3_7
6381625Smax.romanov@nginx.com
6391625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
6401625Smax.romanov@nginx.com
6411625Smax.romanov@nginx.com        self.ws.frame_write(
6421625Smax.romanov@nginx.com            sock, self.ws.OP_CLOSE, payload, rsv1=True, rsv2=True, rsv3=True
6431625Smax.romanov@nginx.com        )
6441625Smax.romanov@nginx.com
6451625Smax.romanov@nginx.com        self.check_close(sock, 1002)
6461625Smax.romanov@nginx.com
6471625Smax.romanov@nginx.com    def test_asgi_websockets_4_1_1__4_2_5(self):
6481625Smax.romanov@nginx.com        self.load('websockets/mirror')
6491625Smax.romanov@nginx.com
6501625Smax.romanov@nginx.com        payload = 'Hello, world!'
6511625Smax.romanov@nginx.com
6521625Smax.romanov@nginx.com        # 4_1_1
6531625Smax.romanov@nginx.com
6541625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
6551625Smax.romanov@nginx.com
6561625Smax.romanov@nginx.com        self.ws.frame_write(sock, 0x03, '')
6571625Smax.romanov@nginx.com        self.check_close(sock, 1002)
6581625Smax.romanov@nginx.com
6591625Smax.romanov@nginx.com        # 4_1_2
6601625Smax.romanov@nginx.com
6611625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
6621625Smax.romanov@nginx.com
6631625Smax.romanov@nginx.com        self.ws.frame_write(sock, 0x04, 'reserved opcode payload')
6641625Smax.romanov@nginx.com        self.check_close(sock, 1002)
6651625Smax.romanov@nginx.com
6661625Smax.romanov@nginx.com        # 4_1_3
6671625Smax.romanov@nginx.com
6681625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
6691625Smax.romanov@nginx.com
6701625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
6711625Smax.romanov@nginx.com
6721625Smax.romanov@nginx.com        frame = self.ws.frame_read(sock)
6731625Smax.romanov@nginx.com        self.check_frame(frame, True, self.ws.OP_TEXT, payload)
6741625Smax.romanov@nginx.com
6751625Smax.romanov@nginx.com        self.ws.frame_write(sock, 0x05, '')
6761625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_PING, '')
6771625Smax.romanov@nginx.com
6781625Smax.romanov@nginx.com        self.check_close(sock, 1002)
6791625Smax.romanov@nginx.com
6801625Smax.romanov@nginx.com        # 4_1_4
6811625Smax.romanov@nginx.com
6821625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
6831625Smax.romanov@nginx.com
6841625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
6851625Smax.romanov@nginx.com
6861625Smax.romanov@nginx.com        frame = self.ws.frame_read(sock)
6871625Smax.romanov@nginx.com        self.check_frame(frame, True, self.ws.OP_TEXT, payload)
6881625Smax.romanov@nginx.com
6891625Smax.romanov@nginx.com        self.ws.frame_write(sock, 0x06, payload)
6901625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_PING, '')
6911625Smax.romanov@nginx.com
6921625Smax.romanov@nginx.com        self.check_close(sock, 1002)
6931625Smax.romanov@nginx.com
6941625Smax.romanov@nginx.com        # 4_1_5
6951625Smax.romanov@nginx.com
6961625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
6971625Smax.romanov@nginx.com
6981625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_TEXT, payload, chopsize=1)
6991625Smax.romanov@nginx.com
7001625Smax.romanov@nginx.com        frame = self.ws.frame_read(sock)
7011625Smax.romanov@nginx.com        self.check_frame(frame, True, self.ws.OP_TEXT, payload)
7021625Smax.romanov@nginx.com
7031625Smax.romanov@nginx.com        self.ws.frame_write(sock, 0x07, payload, chopsize=1)
7041625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_PING, '')
7051625Smax.romanov@nginx.com
7061625Smax.romanov@nginx.com        self.check_close(sock, 1002)
7071625Smax.romanov@nginx.com
7081625Smax.romanov@nginx.com        # 4_2_1
7091625Smax.romanov@nginx.com
7101625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
7111625Smax.romanov@nginx.com
7121625Smax.romanov@nginx.com        self.ws.frame_write(sock, 0x0B, '')
7131625Smax.romanov@nginx.com        self.check_close(sock, 1002)
7141625Smax.romanov@nginx.com
7151625Smax.romanov@nginx.com        # 4_2_2
7161625Smax.romanov@nginx.com
7171625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
7181625Smax.romanov@nginx.com
7191625Smax.romanov@nginx.com        self.ws.frame_write(sock, 0x0C, 'reserved opcode payload')
7201625Smax.romanov@nginx.com        self.check_close(sock, 1002)
7211625Smax.romanov@nginx.com
7221625Smax.romanov@nginx.com        # 4_2_3
7231625Smax.romanov@nginx.com
7241625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
7251625Smax.romanov@nginx.com
7261625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
7271625Smax.romanov@nginx.com
7281625Smax.romanov@nginx.com        frame = self.ws.frame_read(sock)
7291625Smax.romanov@nginx.com        self.check_frame(frame, True, self.ws.OP_TEXT, payload)
7301625Smax.romanov@nginx.com
7311625Smax.romanov@nginx.com        self.ws.frame_write(sock, 0x0D, '')
7321625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_PING, '')
7331625Smax.romanov@nginx.com
7341625Smax.romanov@nginx.com        self.check_close(sock, 1002)
7351625Smax.romanov@nginx.com
7361625Smax.romanov@nginx.com        # 4_2_4
7371625Smax.romanov@nginx.com
7381625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
7391625Smax.romanov@nginx.com
7401625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
7411625Smax.romanov@nginx.com
7421625Smax.romanov@nginx.com        frame = self.ws.frame_read(sock)
7431625Smax.romanov@nginx.com        self.check_frame(frame, True, self.ws.OP_TEXT, payload)
7441625Smax.romanov@nginx.com
7451625Smax.romanov@nginx.com        self.ws.frame_write(sock, 0x0E, payload)
7461625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_PING, '')
7471625Smax.romanov@nginx.com
7481625Smax.romanov@nginx.com        self.check_close(sock, 1002)
7491625Smax.romanov@nginx.com
7501625Smax.romanov@nginx.com        # 4_2_5
7511625Smax.romanov@nginx.com
7521625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
7531625Smax.romanov@nginx.com
7541625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_TEXT, payload, chopsize=1)
7551625Smax.romanov@nginx.com
7561625Smax.romanov@nginx.com        frame = self.ws.frame_read(sock)
7571625Smax.romanov@nginx.com        self.check_frame(frame, True, self.ws.OP_TEXT, payload)
7581625Smax.romanov@nginx.com
7591625Smax.romanov@nginx.com        self.ws.frame_write(sock, 0x0F, payload, chopsize=1)
7601625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_PING, '')
7611625Smax.romanov@nginx.com
7621625Smax.romanov@nginx.com        self.check_close(sock, 1002)
7631625Smax.romanov@nginx.com
7641625Smax.romanov@nginx.com    def test_asgi_websockets_5_1__5_20(self):
7651625Smax.romanov@nginx.com        self.load('websockets/mirror')
7661625Smax.romanov@nginx.com
7671625Smax.romanov@nginx.com        # 5_1
7681625Smax.romanov@nginx.com
7691625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
7701625Smax.romanov@nginx.com
7711625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_PING, 'fragment1', fin=False)
7721625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=True)
7731625Smax.romanov@nginx.com        self.check_close(sock, 1002)
7741625Smax.romanov@nginx.com
7751625Smax.romanov@nginx.com        # 5_2
7761625Smax.romanov@nginx.com
7771625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
7781625Smax.romanov@nginx.com
7791625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_PONG, 'fragment1', fin=False)
7801625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=True)
7811625Smax.romanov@nginx.com        self.check_close(sock, 1002)
7821625Smax.romanov@nginx.com
7831625Smax.romanov@nginx.com        # 5_3
7841625Smax.romanov@nginx.com
7851625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
7861625Smax.romanov@nginx.com
7871625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
7881625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=True)
7891625Smax.romanov@nginx.com
7901625Smax.romanov@nginx.com        frame = self.ws.frame_read(sock)
7911625Smax.romanov@nginx.com        self.check_frame(frame, True, self.ws.OP_TEXT, 'fragment1fragment2')
7921625Smax.romanov@nginx.com
7931625Smax.romanov@nginx.com        # 5_4
7941625Smax.romanov@nginx.com
7951625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
7961625Smax.romanov@nginx.com        assert self.recvall(sock, read_timeout=0.1) == b'', '5_4'
7971625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=True)
7981625Smax.romanov@nginx.com
7991625Smax.romanov@nginx.com        frame = self.ws.frame_read(sock)
8001625Smax.romanov@nginx.com        self.check_frame(frame, True, self.ws.OP_TEXT, 'fragment1fragment2')
8011625Smax.romanov@nginx.com
8021625Smax.romanov@nginx.com        # 5_5
8031625Smax.romanov@nginx.com
8041625Smax.romanov@nginx.com        self.ws.frame_write(
8051625Smax.romanov@nginx.com            sock, self.ws.OP_TEXT, 'fragment1', fin=False, chopsize=1
8061625Smax.romanov@nginx.com        )
8071625Smax.romanov@nginx.com        self.ws.frame_write(
8081625Smax.romanov@nginx.com            sock, self.ws.OP_CONT, 'fragment2', fin=True, chopsize=1
8091625Smax.romanov@nginx.com        )
8101625Smax.romanov@nginx.com
8111625Smax.romanov@nginx.com        frame = self.ws.frame_read(sock)
8121625Smax.romanov@nginx.com        self.check_frame(frame, True, self.ws.OP_TEXT, 'fragment1fragment2')
8131625Smax.romanov@nginx.com
8141625Smax.romanov@nginx.com        # 5_6
8151625Smax.romanov@nginx.com
8161625Smax.romanov@nginx.com        ping_payload = 'ping payload'
8171625Smax.romanov@nginx.com
8181625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
8191625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_PING, ping_payload)
8201625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=True)
8211625Smax.romanov@nginx.com
8221625Smax.romanov@nginx.com        frame = self.ws.frame_read(sock)
8231625Smax.romanov@nginx.com        self.check_frame(frame, True, self.ws.OP_PONG, ping_payload)
8241625Smax.romanov@nginx.com
8251625Smax.romanov@nginx.com        frame = self.ws.frame_read(sock)
8261625Smax.romanov@nginx.com        self.check_frame(frame, True, self.ws.OP_TEXT, 'fragment1fragment2')
8271625Smax.romanov@nginx.com
8281625Smax.romanov@nginx.com        # 5_7
8291625Smax.romanov@nginx.com
8301625Smax.romanov@nginx.com        ping_payload = 'ping payload'
8311625Smax.romanov@nginx.com
8321625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
8331625Smax.romanov@nginx.com        assert self.recvall(sock, read_timeout=0.1) == b'', '5_7'
8341625Smax.romanov@nginx.com
8351625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_PING, ping_payload)
8361625Smax.romanov@nginx.com
8371625Smax.romanov@nginx.com        frame = self.ws.frame_read(sock)
8381625Smax.romanov@nginx.com        self.check_frame(frame, True, self.ws.OP_PONG, ping_payload)
8391625Smax.romanov@nginx.com
8401625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=True)
8411625Smax.romanov@nginx.com
8421625Smax.romanov@nginx.com        frame = self.ws.frame_read(sock)
8431625Smax.romanov@nginx.com        self.check_frame(frame, True, self.ws.OP_TEXT, 'fragment1fragment2')
8441625Smax.romanov@nginx.com
8451625Smax.romanov@nginx.com        # 5_8
8461625Smax.romanov@nginx.com
8471625Smax.romanov@nginx.com        ping_payload = 'ping payload'
8481625Smax.romanov@nginx.com
8491625Smax.romanov@nginx.com        self.ws.frame_write(
8501625Smax.romanov@nginx.com            sock, self.ws.OP_TEXT, 'fragment1', fin=False, chopsize=1
8511625Smax.romanov@nginx.com        )
8521625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_PING, ping_payload, chopsize=1)
8531625Smax.romanov@nginx.com        self.ws.frame_write(
8541625Smax.romanov@nginx.com            sock, self.ws.OP_CONT, 'fragment2', fin=True, chopsize=1
8551625Smax.romanov@nginx.com        )
8561625Smax.romanov@nginx.com
8571625Smax.romanov@nginx.com        frame = self.ws.frame_read(sock)
8581625Smax.romanov@nginx.com        self.check_frame(frame, True, self.ws.OP_PONG, ping_payload)
8591625Smax.romanov@nginx.com
8601625Smax.romanov@nginx.com        frame = self.ws.frame_read(sock)
8611625Smax.romanov@nginx.com        self.check_frame(frame, True, self.ws.OP_TEXT, 'fragment1fragment2')
8621625Smax.romanov@nginx.com
8631625Smax.romanov@nginx.com        # 5_9
8641625Smax.romanov@nginx.com
8651625Smax.romanov@nginx.com        self.ws.frame_write(
8661625Smax.romanov@nginx.com            sock, self.ws.OP_CONT, 'non-continuation payload', fin=True
8671625Smax.romanov@nginx.com        )
8681625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_TEXT, 'Hello, world!', fin=True)
8691625Smax.romanov@nginx.com        self.check_close(sock, 1002)
8701625Smax.romanov@nginx.com
8711625Smax.romanov@nginx.com        # 5_10
8721625Smax.romanov@nginx.com
8731625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
8741625Smax.romanov@nginx.com
8751625Smax.romanov@nginx.com        self.ws.frame_write(
8761625Smax.romanov@nginx.com            sock, self.ws.OP_CONT, 'non-continuation payload', fin=True
8771625Smax.romanov@nginx.com        )
8781625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_TEXT, 'Hello, world!', fin=True)
8791625Smax.romanov@nginx.com        self.check_close(sock, 1002)
8801625Smax.romanov@nginx.com
8811625Smax.romanov@nginx.com        # 5_11
8821625Smax.romanov@nginx.com
8831625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
8841625Smax.romanov@nginx.com
8851625Smax.romanov@nginx.com        self.ws.frame_write(
8861625Smax.romanov@nginx.com            sock,
8871625Smax.romanov@nginx.com            self.ws.OP_CONT,
8881625Smax.romanov@nginx.com            'non-continuation payload',
8891625Smax.romanov@nginx.com            fin=True,
8901625Smax.romanov@nginx.com            chopsize=1,
8911625Smax.romanov@nginx.com        )
8921625Smax.romanov@nginx.com        self.ws.frame_write(
8931625Smax.romanov@nginx.com            sock, self.ws.OP_TEXT, 'Hello, world!', fin=True, chopsize=1
8941625Smax.romanov@nginx.com        )
8951625Smax.romanov@nginx.com        self.check_close(sock, 1002)
8961625Smax.romanov@nginx.com
8971625Smax.romanov@nginx.com        # 5_12
8981625Smax.romanov@nginx.com
8991625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
9001625Smax.romanov@nginx.com
9011625Smax.romanov@nginx.com        self.ws.frame_write(
9021625Smax.romanov@nginx.com            sock, self.ws.OP_CONT, 'non-continuation payload', fin=False
9031625Smax.romanov@nginx.com        )
9041625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_TEXT, 'Hello, world!', fin=True)
9051625Smax.romanov@nginx.com        self.check_close(sock, 1002)
9061625Smax.romanov@nginx.com
9071625Smax.romanov@nginx.com        # 5_13
9081625Smax.romanov@nginx.com
9091625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
9101625Smax.romanov@nginx.com
9111625Smax.romanov@nginx.com        self.ws.frame_write(
9121625Smax.romanov@nginx.com            sock, self.ws.OP_CONT, 'non-continuation payload', fin=False
9131625Smax.romanov@nginx.com        )
9141625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_TEXT, 'Hello, world!', fin=True)
9151625Smax.romanov@nginx.com        self.check_close(sock, 1002)
9161625Smax.romanov@nginx.com
9171625Smax.romanov@nginx.com        # 5_14
9181625Smax.romanov@nginx.com
9191625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
9201625Smax.romanov@nginx.com
9211625Smax.romanov@nginx.com        self.ws.frame_write(
9221625Smax.romanov@nginx.com            sock,
9231625Smax.romanov@nginx.com            self.ws.OP_CONT,
9241625Smax.romanov@nginx.com            'non-continuation payload',
9251625Smax.romanov@nginx.com            fin=False,
9261625Smax.romanov@nginx.com            chopsize=1,
9271625Smax.romanov@nginx.com        )
9281625Smax.romanov@nginx.com        self.ws.frame_write(
9291625Smax.romanov@nginx.com            sock, self.ws.OP_TEXT, 'Hello, world!', fin=True, chopsize=1
9301625Smax.romanov@nginx.com        )
9311625Smax.romanov@nginx.com        self.check_close(sock, 1002)
9321625Smax.romanov@nginx.com
9331625Smax.romanov@nginx.com        # 5_15
9341625Smax.romanov@nginx.com
9351625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
9361625Smax.romanov@nginx.com
9371625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
9381625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=True)
9391625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment3', fin=False)
9401625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment4', fin=True)
9411819Smax.romanov@nginx.com
9421819Smax.romanov@nginx.com        frame = self.ws.frame_read(sock)
9431819Smax.romanov@nginx.com
9441819Smax.romanov@nginx.com        if frame['opcode'] == self.ws.OP_TEXT:
945*2073Szelenkov@nginx.com            self.check_frame(frame, True, self.ws.OP_TEXT, 'fragment1fragment2')
9461819Smax.romanov@nginx.com            frame = None
9471819Smax.romanov@nginx.com
9481819Smax.romanov@nginx.com        self.check_close(sock, 1002, frame=frame)
9491625Smax.romanov@nginx.com
9501625Smax.romanov@nginx.com        # 5_16
9511625Smax.romanov@nginx.com
9521625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
9531625Smax.romanov@nginx.com
9541625Smax.romanov@nginx.com        for i in range(0, 2):
9551625Smax.romanov@nginx.com            self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment1', fin=False)
9561625Smax.romanov@nginx.com            self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment2', fin=False)
9571625Smax.romanov@nginx.com            self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment3', fin=True)
9581625Smax.romanov@nginx.com        self.check_close(sock, 1002)
9591625Smax.romanov@nginx.com
9601625Smax.romanov@nginx.com        # 5_17
9611625Smax.romanov@nginx.com
9621625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
9631625Smax.romanov@nginx.com
9641625Smax.romanov@nginx.com        for i in range(0, 2):
9651625Smax.romanov@nginx.com            self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment1', fin=True)
9661625Smax.romanov@nginx.com            self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment2', fin=False)
9671625Smax.romanov@nginx.com            self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment3', fin=True)
9681625Smax.romanov@nginx.com        self.check_close(sock, 1002)
9691625Smax.romanov@nginx.com
9701625Smax.romanov@nginx.com        # 5_18
9711625Smax.romanov@nginx.com
9721625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
9731625Smax.romanov@nginx.com
9741625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
9751625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment2')
9761625Smax.romanov@nginx.com        self.check_close(sock, 1002)
9771625Smax.romanov@nginx.com
9781625Smax.romanov@nginx.com        # 5_19
9791625Smax.romanov@nginx.com
9801625Smax.romanov@nginx.com        _, sock, _ = self.ws.upgrade()
9811625Smax.romanov@nginx.com
9821625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
9831625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=False)
9841625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_PING, 'pongme 1!')
9851625Smax.romanov@nginx.com
9861625Smax.romanov@nginx.com        time.sleep(1)
9871625Smax.romanov@nginx.com
9881625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment3', fin=False)
9891625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment4', fin=False)
9901625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_PING, 'pongme 2!')
9911625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment5')
9921625Smax.romanov@nginx.com
9931625Smax.romanov@nginx.com        frame = self.ws.frame_read(sock)
9941625Smax.romanov@nginx.com        self.check_frame(frame, True, self.ws.OP_PONG, 'pongme 1!')
9951625Smax.romanov@nginx.com
9961625Smax.romanov@nginx.com        frame = self.ws.frame_read(sock)
9971625Smax.romanov@nginx.com        self.check_frame(frame, True, self.ws.OP_PONG, 'pongme 2!')
9981625Smax.romanov@nginx.com
9991625Smax.romanov@nginx.com        self.check_frame(
10001625Smax.romanov@nginx.com            self.ws.frame_read(sock),
10011625Smax.romanov@nginx.com            True,
10021625Smax.romanov@nginx.com            self.ws.OP_TEXT,
10031625Smax.romanov@nginx.com            'fragment1fragment2fragment3fragment4fragment5',
10041625Smax.romanov@nginx.com        )
10051625Smax.romanov@nginx.com
10061625Smax.romanov@nginx.com        # 5_20
10071625Smax.romanov@nginx.com
10081625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
10091625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=False)
10101625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_PING, 'pongme 1!')
10111625Smax.romanov@nginx.com
10121625Smax.romanov@nginx.com        frame = self.ws.frame_read(sock)
10131625Smax.romanov@nginx.com        self.check_frame(frame, True, self.ws.OP_PONG, 'pongme 1!')
10141625Smax.romanov@nginx.com
10151625Smax.romanov@nginx.com        time.sleep(1)
10161625Smax.romanov@nginx.com
10171625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment3', fin=False)
10181625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment4', fin=False)
10191625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_PING, 'pongme 2!')
10201625Smax.romanov@nginx.com
10211625Smax.romanov@nginx.com        frame = self.ws.frame_read(sock)
10221625Smax.romanov@nginx.com        self.check_frame(frame, True, self.ws.OP_PONG, 'pongme 2!')
10231625Smax.romanov@nginx.com
10241625Smax.romanov@nginx.com        assert self.recvall(sock, read_timeout=0.1) == b'', '5_20'
10251625Smax.romanov@nginx.com        self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment5')
10261625Smax.romanov@nginx.com
10271625Smax.romanov@nginx.com        self.check_frame(
10281625Smax.romanov@nginx.com