xref: /unit/test/test_asgi_websockets.py (revision 2066:242192963d93)
1import struct
2import time
3
4import pytest
5from packaging import version
6from unit.applications.lang.python import TestApplicationPython
7from unit.applications.websockets import TestApplicationWebsocket
8from unit.option import option
9
10
11class TestASGIWebsockets(TestApplicationPython):
12    prerequisites = {
13        'modules': {
14            'python': lambda v: version.parse(v) >= version.parse('3.5')
15        }
16    }
17    load_module = 'asgi'
18
19    ws = TestApplicationWebsocket()
20
21    @pytest.fixture(autouse=True)
22    def setup_method_fixture(self, request, skip_alert):
23        assert 'success' in self.conf(
24            {'http': {'websocket': {'keepalive_interval': 0}}}, 'settings'
25        ), 'clear keepalive_interval'
26
27        skip_alert(r'socket close\(\d+\) failed')
28
29    def close_connection(self, sock):
30        assert self.recvall(sock, read_timeout=0.1) == b'', 'empty soc'
31
32        self.ws.frame_write(sock, self.ws.OP_CLOSE, self.ws.serialize_close())
33
34        self.check_close(sock)
35
36    def check_close(self, sock, code=1000, no_close=False, frame=None):
37        if frame == None:
38            frame = self.ws.frame_read(sock)
39
40        assert frame['fin'] == True, 'close fin'
41        assert frame['opcode'] == self.ws.OP_CLOSE, 'close opcode'
42        assert frame['code'] == code, 'close code'
43
44        if not no_close:
45            sock.close()
46
47    def check_frame(self, frame, fin, opcode, payload, decode=True):
48        if opcode == self.ws.OP_BINARY or not decode:
49            data = frame['data']
50        else:
51            data = frame['data'].decode('utf-8')
52
53        assert frame['fin'] == fin, 'fin'
54        assert frame['opcode'] == opcode, 'opcode'
55        assert data == payload, 'payload'
56
57    def test_asgi_websockets_handshake(self):
58        self.load('websockets/mirror')
59
60        resp, sock, key = self.ws.upgrade()
61        sock.close()
62
63        assert resp['status'] == 101, 'status'
64        assert resp['headers']['Upgrade'] == 'websocket', 'upgrade'
65        assert resp['headers']['Connection'] == 'Upgrade', 'connection'
66        assert resp['headers']['Sec-WebSocket-Accept'] == self.ws.accept(
67            key
68        ), 'key'
69
70        # remove "mirror" application
71        self.load('websockets/subprotocol')
72
73    def test_asgi_websockets_subprotocol(self):
74        self.load('websockets/subprotocol')
75
76        resp, sock, key = self.ws.upgrade()
77        sock.close()
78
79        assert resp['status'] == 101, 'status'
80        assert (
81            resp['headers']['x-subprotocols'] == "('chat', 'phone', 'video')"
82        ), 'subprotocols'
83        assert resp['headers']['sec-websocket-protocol'] == 'chat', 'key'
84
85    def test_asgi_websockets_mirror(self):
86        self.load('websockets/mirror')
87
88        message = 'blah'
89
90        _, sock, _ = self.ws.upgrade()
91
92        self.ws.frame_write(sock, self.ws.OP_TEXT, message)
93        frame = self.ws.frame_read(sock)
94
95        assert message == frame['data'].decode('utf-8'), 'mirror'
96
97        self.ws.frame_write(sock, self.ws.OP_TEXT, message)
98        frame = self.ws.frame_read(sock)
99
100        assert message == frame['data'].decode('utf-8'), 'mirror 2'
101
102        sock.close()
103
104    def test_asgi_websockets_mirror_app_change(self):
105        self.load('websockets/mirror')
106
107        message = 'blah'
108
109        _, sock, _ = self.ws.upgrade()
110
111        self.ws.frame_write(sock, self.ws.OP_TEXT, message)
112        frame = self.ws.frame_read(sock)
113
114        assert message == frame['data'].decode('utf-8'), 'mirror'
115
116        self.load('websockets/subprotocol')
117
118        self.ws.frame_write(sock, self.ws.OP_TEXT, message)
119        frame = self.ws.frame_read(sock)
120
121        assert message == frame['data'].decode('utf-8'), 'mirror 2'
122
123        sock.close()
124
125    def test_asgi_websockets_no_mask(self):
126        self.load('websockets/mirror')
127
128        message = 'blah'
129
130        _, sock, _ = self.ws.upgrade()
131
132        self.ws.frame_write(sock, self.ws.OP_TEXT, message, mask=False)
133
134        frame = self.ws.frame_read(sock)
135
136        assert frame['opcode'] == self.ws.OP_CLOSE, 'no mask opcode'
137        assert frame['code'] == 1002, 'no mask close code'
138
139        sock.close()
140
141    def test_asgi_websockets_fragmentation(self):
142        self.load('websockets/mirror')
143
144        message = 'blah'
145
146        _, sock, _ = self.ws.upgrade()
147
148        self.ws.frame_write(sock, self.ws.OP_TEXT, message, fin=False)
149        self.ws.frame_write(sock, self.ws.OP_CONT, ' ', fin=False)
150        self.ws.frame_write(sock, self.ws.OP_CONT, message)
151
152        frame = self.ws.frame_read(sock)
153
154        assert message + ' ' + message == frame['data'].decode(
155            'utf-8'
156        ), 'mirror framing'
157
158        sock.close()
159
160    def test_asgi_websockets_length_long(self):
161        self.load('websockets/mirror')
162
163        _, sock, _ = self.ws.upgrade()
164
165        self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
166        self.ws.frame_write(
167            sock, self.ws.OP_CONT, 'fragment2', length=2 ** 64 - 1
168        )
169
170        self.check_close(sock, 1009)  # 1009 - CLOSE_TOO_LARGE
171
172    def test_asgi_websockets_frame_fragmentation_invalid(self):
173        self.load('websockets/mirror')
174
175        message = 'blah'
176
177        _, sock, _ = self.ws.upgrade()
178
179        self.ws.frame_write(sock, self.ws.OP_PING, message, fin=False)
180
181        frame = self.ws.frame_read(sock)
182
183        frame.pop('data')
184        assert frame == {
185            'fin': True,
186            'rsv1': False,
187            'rsv2': False,
188            'rsv3': False,
189            'opcode': self.ws.OP_CLOSE,
190            'mask': 0,
191            'code': 1002,
192            'reason': 'Fragmented control frame',
193        }, 'close frame'
194
195        sock.close()
196
197    def test_asgi_websockets_large(self):
198        self.load('websockets/mirror')
199
200        message = '0123456789' * 300
201
202        _, sock, _ = self.ws.upgrade()
203
204        self.ws.frame_write(sock, self.ws.OP_TEXT, message)
205
206        frame = self.ws.frame_read(sock)
207        data = frame['data'].decode('utf-8')
208
209        frame = self.ws.frame_read(sock)
210        data += frame['data'].decode('utf-8')
211
212        assert message == data, 'large'
213
214        sock.close()
215
216    def test_asgi_websockets_two_clients(self):
217        self.load('websockets/mirror')
218
219        message1 = 'blah1'
220        message2 = 'blah2'
221
222        _, sock1, _ = self.ws.upgrade()
223        _, sock2, _ = self.ws.upgrade()
224
225        self.ws.frame_write(sock1, self.ws.OP_TEXT, message1)
226        self.ws.frame_write(sock2, self.ws.OP_TEXT, message2)
227
228        frame1 = self.ws.frame_read(sock1)
229        frame2 = self.ws.frame_read(sock2)
230
231        assert message1 == frame1['data'].decode('utf-8'), 'client 1'
232        assert message2 == frame2['data'].decode('utf-8'), 'client 2'
233
234        sock1.close()
235        sock2.close()
236
237    @pytest.mark.skip('not yet')
238    def test_asgi_websockets_handshake_upgrade_absent(
239        self,
240    ):  # FAIL https://tools.ietf.org/html/rfc6455#section-4.2.1
241        self.load('websockets/mirror')
242
243        resp = self.get(
244            headers={
245                'Host': 'localhost',
246                'Connection': 'Upgrade',
247                'Sec-WebSocket-Key': self.ws.key(),
248                'Sec-WebSocket-Protocol': 'chat',
249                'Sec-WebSocket-Version': 13,
250            },
251        )
252
253        assert resp['status'] == 400, 'upgrade absent'
254
255    def test_asgi_websockets_handshake_case_insensitive(self):
256        self.load('websockets/mirror')
257
258        resp, sock, _ = self.ws.upgrade(
259            headers={
260                'Host': 'localhost',
261                'Upgrade': 'WEBSOCKET',
262                'Connection': 'UPGRADE',
263                'Sec-WebSocket-Key': self.ws.key(),
264                'Sec-WebSocket-Protocol': 'chat',
265                'Sec-WebSocket-Version': 13,
266            }
267        )
268        sock.close()
269
270        assert resp['status'] == 101, 'status'
271
272    @pytest.mark.skip('not yet')
273    def test_asgi_websockets_handshake_connection_absent(self):  # FAIL
274        self.load('websockets/mirror')
275
276        resp = self.get(
277            headers={
278                'Host': 'localhost',
279                'Upgrade': 'websocket',
280                'Sec-WebSocket-Key': self.ws.key(),
281                'Sec-WebSocket-Protocol': 'chat',
282                'Sec-WebSocket-Version': 13,
283            },
284        )
285
286        assert resp['status'] == 400, 'status'
287
288    def test_asgi_websockets_handshake_version_absent(self):
289        self.load('websockets/mirror')
290
291        resp = self.get(
292            headers={
293                'Host': 'localhost',
294                'Upgrade': 'websocket',
295                'Connection': 'Upgrade',
296                'Sec-WebSocket-Key': self.ws.key(),
297                'Sec-WebSocket-Protocol': 'chat',
298            },
299        )
300
301        assert resp['status'] == 426, 'status'
302
303    @pytest.mark.skip('not yet')
304    def test_asgi_websockets_handshake_key_invalid(self):
305        self.load('websockets/mirror')
306
307        resp = self.get(
308            headers={
309                'Host': 'localhost',
310                'Upgrade': 'websocket',
311                'Connection': 'Upgrade',
312                'Sec-WebSocket-Key': '!',
313                'Sec-WebSocket-Protocol': 'chat',
314                'Sec-WebSocket-Version': 13,
315            },
316        )
317
318        assert resp['status'] == 400, 'key length'
319
320        key = self.ws.key()
321        resp = self.get(
322            headers={
323                'Host': 'localhost',
324                'Upgrade': 'websocket',
325                'Connection': 'Upgrade',
326                'Sec-WebSocket-Key': [key, key],
327                'Sec-WebSocket-Protocol': 'chat',
328                'Sec-WebSocket-Version': 13,
329            },
330        )
331
332        assert (
333            resp['status'] == 400
334        ), 'key double'  # FAIL https://tools.ietf.org/html/rfc6455#section-11.3.1
335
336    def test_asgi_websockets_handshake_method_invalid(self):
337        self.load('websockets/mirror')
338
339        resp = self.post(
340            headers={
341                'Host': 'localhost',
342                'Upgrade': 'websocket',
343                'Connection': 'Upgrade',
344                'Sec-WebSocket-Key': self.ws.key(),
345                'Sec-WebSocket-Protocol': 'chat',
346                'Sec-WebSocket-Version': 13,
347            },
348        )
349
350        assert resp['status'] == 400, 'status'
351
352    def test_asgi_websockets_handshake_http_10(self):
353        self.load('websockets/mirror')
354
355        resp = self.get(
356            headers={
357                'Host': 'localhost',
358                'Upgrade': 'websocket',
359                'Connection': 'Upgrade',
360                'Sec-WebSocket-Key': self.ws.key(),
361                'Sec-WebSocket-Protocol': 'chat',
362                'Sec-WebSocket-Version': 13,
363            },
364            http_10=True,
365        )
366
367        assert resp['status'] == 400, 'status'
368
369    def test_asgi_websockets_handshake_uri_invalid(self):
370        self.load('websockets/mirror')
371
372        resp = self.get(
373            headers={
374                'Host': 'localhost',
375                'Upgrade': 'websocket',
376                'Connection': 'Upgrade',
377                'Sec-WebSocket-Key': self.ws.key(),
378                'Sec-WebSocket-Protocol': 'chat',
379                'Sec-WebSocket-Version': 13,
380            },
381            url='!',
382        )
383
384        assert resp['status'] == 400, 'status'
385
386    def test_asgi_websockets_protocol_absent(self):
387        self.load('websockets/mirror')
388
389        key = self.ws.key()
390        resp, sock, _ = self.ws.upgrade(
391            headers={
392                'Host': 'localhost',
393                'Upgrade': 'websocket',
394                'Connection': 'Upgrade',
395                'Sec-WebSocket-Key': key,
396                'Sec-WebSocket-Version': 13,
397            }
398        )
399        sock.close()
400
401        assert resp['status'] == 101, 'status'
402        assert resp['headers']['Upgrade'] == 'websocket', 'upgrade'
403        assert resp['headers']['Connection'] == 'Upgrade', 'connection'
404        assert resp['headers']['Sec-WebSocket-Accept'] == self.ws.accept(
405            key
406        ), 'key'
407
408    # autobahn-testsuite
409    #
410    # Some following tests fail because of Unit does not support UTF-8
411    # validation for websocket frames.  It should be implemented
412    # by application, if necessary.
413
414    def test_asgi_websockets_1_1_1__1_1_8(self):
415        self.load('websockets/mirror')
416
417        opcode = self.ws.OP_TEXT
418
419        _, sock, _ = self.ws.upgrade()
420
421        def check_length(length, chopsize=None):
422            payload = '*' * length
423
424            self.ws.frame_write(sock, opcode, payload, chopsize=chopsize)
425
426            frame = self.ws.frame_read(sock)
427            self.check_frame(frame, True, opcode, payload)
428
429        check_length(0)  # 1_1_1
430        check_length(125)  # 1_1_2
431        check_length(126)  # 1_1_3
432        check_length(127)  # 1_1_4
433        check_length(128)  # 1_1_5
434        check_length(65535)  # 1_1_6
435        check_length(65536)  # 1_1_7
436        check_length(65536, chopsize=997)  # 1_1_8
437
438        self.close_connection(sock)
439
440    def test_asgi_websockets_1_2_1__1_2_8(self):
441        self.load('websockets/mirror')
442
443        opcode = self.ws.OP_BINARY
444
445        _, sock, _ = self.ws.upgrade()
446
447        def check_length(length, chopsize=None):
448            payload = b'\xfe' * length
449
450            self.ws.frame_write(sock, opcode, payload, chopsize=chopsize)
451            frame = self.ws.frame_read(sock)
452
453            self.check_frame(frame, True, opcode, payload)
454
455        check_length(0)  # 1_2_1
456        check_length(125)  # 1_2_2
457        check_length(126)  # 1_2_3
458        check_length(127)  # 1_2_4
459        check_length(128)  # 1_2_5
460        check_length(65535)  # 1_2_6
461        check_length(65536)  # 1_2_7
462        check_length(65536, chopsize=997)  # 1_2_8
463
464        self.close_connection(sock)
465
466    def test_asgi_websockets_2_1__2_6(self):
467        self.load('websockets/mirror')
468
469        op_ping = self.ws.OP_PING
470        op_pong = self.ws.OP_PONG
471
472        _, sock, _ = self.ws.upgrade()
473
474        def check_ping(payload, chopsize=None, decode=True):
475            self.ws.frame_write(sock, op_ping, payload, chopsize=chopsize)
476            frame = self.ws.frame_read(sock)
477
478            self.check_frame(frame, True, op_pong, payload, decode=decode)
479
480        check_ping('')  # 2_1
481        check_ping('Hello, world!')  # 2_2
482        check_ping(b'\x00\xff\xfe\xfd\xfc\xfb\x00\xff', decode=False)  # 2_3
483        check_ping(b'\xfe' * 125, decode=False)  # 2_4
484        check_ping(b'\xfe' * 125, chopsize=1, decode=False)  # 2_6
485
486        self.close_connection(sock)
487
488        # 2_5
489
490        _, sock, _ = self.ws.upgrade()
491
492        self.ws.frame_write(sock, self.ws.OP_PING, b'\xfe' * 126)
493        self.check_close(sock, 1002)
494
495    def test_asgi_websockets_2_7__2_9(self):
496        self.load('websockets/mirror')
497
498        # 2_7
499
500        _, sock, _ = self.ws.upgrade()
501
502        self.ws.frame_write(sock, self.ws.OP_PONG, '')
503        assert self.recvall(sock, read_timeout=0.1) == b'', '2_7'
504
505        # 2_8
506
507        self.ws.frame_write(sock, self.ws.OP_PONG, 'unsolicited pong payload')
508        assert self.recvall(sock, read_timeout=0.1) == b'', '2_8'
509
510        # 2_9
511
512        payload = 'ping payload'
513
514        self.ws.frame_write(sock, self.ws.OP_PONG, 'unsolicited pong payload')
515        self.ws.frame_write(sock, self.ws.OP_PING, payload)
516
517        frame = self.ws.frame_read(sock)
518        self.check_frame(frame, True, self.ws.OP_PONG, payload)
519
520        self.close_connection(sock)
521
522    def test_asgi_websockets_2_10__2_11(self):
523        self.load('websockets/mirror')
524
525        # 2_10
526
527        _, sock, _ = self.ws.upgrade()
528
529        for i in range(0, 10):
530            self.ws.frame_write(sock, self.ws.OP_PING, 'payload-%d' % i)
531
532        for i in range(0, 10):
533            frame = self.ws.frame_read(sock)
534            self.check_frame(frame, True, self.ws.OP_PONG, 'payload-%d' % i)
535
536        # 2_11
537
538        for i in range(0, 10):
539            opcode = self.ws.OP_PING
540            self.ws.frame_write(sock, opcode, 'payload-%d' % i, chopsize=1)
541
542        for i in range(0, 10):
543            frame = self.ws.frame_read(sock)
544            self.check_frame(frame, True, self.ws.OP_PONG, 'payload-%d' % i)
545
546        self.close_connection(sock)
547
548    @pytest.mark.skip('not yet')
549    def test_asgi_websockets_3_1__3_7(self):
550        self.load('websockets/mirror')
551
552        payload = 'Hello, world!'
553
554        # 3_1
555
556        _, sock, _ = self.ws.upgrade()
557
558        self.ws.frame_write(sock, self.ws.OP_TEXT, payload, rsv1=True)
559        self.check_close(sock, 1002)
560
561        # 3_2
562
563        _, sock, _ = self.ws.upgrade()
564
565        self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
566        self.ws.frame_write(sock, self.ws.OP_TEXT, payload, rsv2=True)
567        self.ws.frame_write(sock, self.ws.OP_PING, '')
568
569        frame = self.ws.frame_read(sock)
570        self.check_frame(frame, True, self.ws.OP_TEXT, payload)
571
572        self.check_close(sock, 1002, no_close=True)
573
574        assert self.recvall(sock, read_timeout=0.1) == b'', 'empty 3_2'
575        sock.close()
576
577        # 3_3
578
579        _, sock, _ = self.ws.upgrade()
580
581        self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
582
583        frame = self.ws.frame_read(sock)
584        self.check_frame(frame, True, self.ws.OP_TEXT, payload)
585
586        self.ws.frame_write(
587            sock, self.ws.OP_TEXT, payload, rsv1=True, rsv2=True
588        )
589
590        self.check_close(sock, 1002, no_close=True)
591
592        assert self.recvall(sock, read_timeout=0.1) == b'', 'empty 3_3'
593        sock.close()
594
595        # 3_4
596
597        _, sock, _ = self.ws.upgrade()
598
599        self.ws.frame_write(sock, self.ws.OP_TEXT, payload, chopsize=1)
600        self.ws.frame_write(
601            sock, self.ws.OP_TEXT, payload, rsv3=True, chopsize=1
602        )
603        self.ws.frame_write(sock, self.ws.OP_PING, '')
604
605        frame = self.ws.frame_read(sock)
606        self.check_frame(frame, True, self.ws.OP_TEXT, payload)
607
608        self.check_close(sock, 1002, no_close=True)
609
610        assert self.recvall(sock, read_timeout=0.1) == b'', 'empty 3_4'
611        sock.close()
612
613        # 3_5
614
615        _, sock, _ = self.ws.upgrade()
616
617        self.ws.frame_write(
618            sock,
619            self.ws.OP_BINARY,
620            b'\x00\xff\xfe\xfd\xfc\xfb\x00\xff',
621            rsv1=True,
622            rsv3=True,
623        )
624
625        self.check_close(sock, 1002)
626
627        # 3_6
628
629        _, sock, _ = self.ws.upgrade()
630
631        self.ws.frame_write(
632            sock, self.ws.OP_PING, payload, rsv2=True, rsv3=True
633        )
634
635        self.check_close(sock, 1002)
636
637        # 3_7
638
639        _, sock, _ = self.ws.upgrade()
640
641        self.ws.frame_write(
642            sock, self.ws.OP_CLOSE, payload, rsv1=True, rsv2=True, rsv3=True
643        )
644
645        self.check_close(sock, 1002)
646
647    def test_asgi_websockets_4_1_1__4_2_5(self):
648        self.load('websockets/mirror')
649
650        payload = 'Hello, world!'
651
652        # 4_1_1
653
654        _, sock, _ = self.ws.upgrade()
655
656        self.ws.frame_write(sock, 0x03, '')
657        self.check_close(sock, 1002)
658
659        # 4_1_2
660
661        _, sock, _ = self.ws.upgrade()
662
663        self.ws.frame_write(sock, 0x04, 'reserved opcode payload')
664        self.check_close(sock, 1002)
665
666        # 4_1_3
667
668        _, sock, _ = self.ws.upgrade()
669
670        self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
671
672        frame = self.ws.frame_read(sock)
673        self.check_frame(frame, True, self.ws.OP_TEXT, payload)
674
675        self.ws.frame_write(sock, 0x05, '')
676        self.ws.frame_write(sock, self.ws.OP_PING, '')
677
678        self.check_close(sock, 1002)
679
680        # 4_1_4
681
682        _, sock, _ = self.ws.upgrade()
683
684        self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
685
686        frame = self.ws.frame_read(sock)
687        self.check_frame(frame, True, self.ws.OP_TEXT, payload)
688
689        self.ws.frame_write(sock, 0x06, payload)
690        self.ws.frame_write(sock, self.ws.OP_PING, '')
691
692        self.check_close(sock, 1002)
693
694        # 4_1_5
695
696        _, sock, _ = self.ws.upgrade()
697
698        self.ws.frame_write(sock, self.ws.OP_TEXT, payload, chopsize=1)
699
700        frame = self.ws.frame_read(sock)
701        self.check_frame(frame, True, self.ws.OP_TEXT, payload)
702
703        self.ws.frame_write(sock, 0x07, payload, chopsize=1)
704        self.ws.frame_write(sock, self.ws.OP_PING, '')
705
706        self.check_close(sock, 1002)
707
708        # 4_2_1
709
710        _, sock, _ = self.ws.upgrade()
711
712        self.ws.frame_write(sock, 0x0B, '')
713        self.check_close(sock, 1002)
714
715        # 4_2_2
716
717        _, sock, _ = self.ws.upgrade()
718
719        self.ws.frame_write(sock, 0x0C, 'reserved opcode payload')
720        self.check_close(sock, 1002)
721
722        # 4_2_3
723
724        _, sock, _ = self.ws.upgrade()
725
726        self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
727
728        frame = self.ws.frame_read(sock)
729        self.check_frame(frame, True, self.ws.OP_TEXT, payload)
730
731        self.ws.frame_write(sock, 0x0D, '')
732        self.ws.frame_write(sock, self.ws.OP_PING, '')
733
734        self.check_close(sock, 1002)
735
736        # 4_2_4
737
738        _, sock, _ = self.ws.upgrade()
739
740        self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
741
742        frame = self.ws.frame_read(sock)
743        self.check_frame(frame, True, self.ws.OP_TEXT, payload)
744
745        self.ws.frame_write(sock, 0x0E, payload)
746        self.ws.frame_write(sock, self.ws.OP_PING, '')
747
748        self.check_close(sock, 1002)
749
750        # 4_2_5
751
752        _, sock, _ = self.ws.upgrade()
753
754        self.ws.frame_write(sock, self.ws.OP_TEXT, payload, chopsize=1)
755
756        frame = self.ws.frame_read(sock)
757        self.check_frame(frame, True, self.ws.OP_TEXT, payload)
758
759        self.ws.frame_write(sock, 0x0F, payload, chopsize=1)
760        self.ws.frame_write(sock, self.ws.OP_PING, '')
761
762        self.check_close(sock, 1002)
763
764    def test_asgi_websockets_5_1__5_20(self):
765        self.load('websockets/mirror')
766
767        # 5_1
768
769        _, sock, _ = self.ws.upgrade()
770
771        self.ws.frame_write(sock, self.ws.OP_PING, 'fragment1', fin=False)
772        self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=True)
773        self.check_close(sock, 1002)
774
775        # 5_2
776
777        _, sock, _ = self.ws.upgrade()
778
779        self.ws.frame_write(sock, self.ws.OP_PONG, 'fragment1', fin=False)
780        self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=True)
781        self.check_close(sock, 1002)
782
783        # 5_3
784
785        _, sock, _ = self.ws.upgrade()
786
787        self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
788        self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=True)
789
790        frame = self.ws.frame_read(sock)
791        self.check_frame(frame, True, self.ws.OP_TEXT, 'fragment1fragment2')
792
793        # 5_4
794
795        self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
796        assert self.recvall(sock, read_timeout=0.1) == b'', '5_4'
797        self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=True)
798
799        frame = self.ws.frame_read(sock)
800        self.check_frame(frame, True, self.ws.OP_TEXT, 'fragment1fragment2')
801
802        # 5_5
803
804        self.ws.frame_write(
805            sock, self.ws.OP_TEXT, 'fragment1', fin=False, chopsize=1
806        )
807        self.ws.frame_write(
808            sock, self.ws.OP_CONT, 'fragment2', fin=True, chopsize=1
809        )
810
811        frame = self.ws.frame_read(sock)
812        self.check_frame(frame, True, self.ws.OP_TEXT, 'fragment1fragment2')
813
814        # 5_6
815
816        ping_payload = 'ping payload'
817
818        self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
819        self.ws.frame_write(sock, self.ws.OP_PING, ping_payload)
820        self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=True)
821
822        frame = self.ws.frame_read(sock)
823        self.check_frame(frame, True, self.ws.OP_PONG, ping_payload)
824
825        frame = self.ws.frame_read(sock)
826        self.check_frame(frame, True, self.ws.OP_TEXT, 'fragment1fragment2')
827
828        # 5_7
829
830        ping_payload = 'ping payload'
831
832        self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
833        assert self.recvall(sock, read_timeout=0.1) == b'', '5_7'
834
835        self.ws.frame_write(sock, self.ws.OP_PING, ping_payload)
836
837        frame = self.ws.frame_read(sock)
838        self.check_frame(frame, True, self.ws.OP_PONG, ping_payload)
839
840        self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=True)
841
842        frame = self.ws.frame_read(sock)
843        self.check_frame(frame, True, self.ws.OP_TEXT, 'fragment1fragment2')
844
845        # 5_8
846
847        ping_payload = 'ping payload'
848
849        self.ws.frame_write(
850            sock, self.ws.OP_TEXT, 'fragment1', fin=False, chopsize=1
851        )
852        self.ws.frame_write(sock, self.ws.OP_PING, ping_payload, chopsize=1)
853        self.ws.frame_write(
854            sock, self.ws.OP_CONT, 'fragment2', fin=True, chopsize=1
855        )
856
857        frame = self.ws.frame_read(sock)
858        self.check_frame(frame, True, self.ws.OP_PONG, ping_payload)
859
860        frame = self.ws.frame_read(sock)
861        self.check_frame(frame, True, self.ws.OP_TEXT, 'fragment1fragment2')
862
863        # 5_9
864
865        self.ws.frame_write(
866            sock, self.ws.OP_CONT, 'non-continuation payload', fin=True
867        )
868        self.ws.frame_write(sock, self.ws.OP_TEXT, 'Hello, world!', fin=True)
869        self.check_close(sock, 1002)
870
871        # 5_10
872
873        _, sock, _ = self.ws.upgrade()
874
875        self.ws.frame_write(
876            sock, self.ws.OP_CONT, 'non-continuation payload', fin=True
877        )
878        self.ws.frame_write(sock, self.ws.OP_TEXT, 'Hello, world!', fin=True)
879        self.check_close(sock, 1002)
880
881        # 5_11
882
883        _, sock, _ = self.ws.upgrade()
884
885        self.ws.frame_write(
886            sock,
887            self.ws.OP_CONT,
888            'non-continuation payload',
889            fin=True,
890            chopsize=1,
891        )
892        self.ws.frame_write(
893            sock, self.ws.OP_TEXT, 'Hello, world!', fin=True, chopsize=1
894        )
895        self.check_close(sock, 1002)
896
897        # 5_12
898
899        _, sock, _ = self.ws.upgrade()
900
901        self.ws.frame_write(
902            sock, self.ws.OP_CONT, 'non-continuation payload', fin=False
903        )
904        self.ws.frame_write(sock, self.ws.OP_TEXT, 'Hello, world!', fin=True)
905        self.check_close(sock, 1002)
906
907        # 5_13
908
909        _, sock, _ = self.ws.upgrade()
910
911        self.ws.frame_write(
912            sock, self.ws.OP_CONT, 'non-continuation payload', fin=False
913        )
914        self.ws.frame_write(sock, self.ws.OP_TEXT, 'Hello, world!', fin=True)
915        self.check_close(sock, 1002)
916
917        # 5_14
918
919        _, sock, _ = self.ws.upgrade()
920
921        self.ws.frame_write(
922            sock,
923            self.ws.OP_CONT,
924            'non-continuation payload',
925            fin=False,
926            chopsize=1,
927        )
928        self.ws.frame_write(
929            sock, self.ws.OP_TEXT, 'Hello, world!', fin=True, chopsize=1
930        )
931        self.check_close(sock, 1002)
932
933        # 5_15
934
935        _, sock, _ = self.ws.upgrade()
936
937        self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
938        self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=True)
939        self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment3', fin=False)
940        self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment4', fin=True)
941
942        frame = self.ws.frame_read(sock)
943
944        if frame['opcode'] == self.ws.OP_TEXT:
945            self.check_frame(
946                frame, True, self.ws.OP_TEXT, 'fragment1fragment2'
947            )
948            frame = None
949
950        self.check_close(sock, 1002, frame=frame)
951
952        # 5_16
953
954        _, sock, _ = self.ws.upgrade()
955
956        for i in range(0, 2):
957            self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment1', fin=False)
958            self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment2', fin=False)
959            self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment3', fin=True)
960        self.check_close(sock, 1002)
961
962        # 5_17
963
964        _, sock, _ = self.ws.upgrade()
965
966        for i in range(0, 2):
967            self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment1', fin=True)
968            self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment2', fin=False)
969            self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment3', fin=True)
970        self.check_close(sock, 1002)
971
972        # 5_18
973
974        _, sock, _ = self.ws.upgrade()
975
976        self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
977        self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment2')
978        self.check_close(sock, 1002)
979
980        # 5_19
981
982        _, sock, _ = self.ws.upgrade()
983
984        self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
985        self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=False)
986        self.ws.frame_write(sock, self.ws.OP_PING, 'pongme 1!')
987
988        time.sleep(1)
989
990        self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment3', fin=False)
991        self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment4', fin=False)
992        self.ws.frame_write(sock, self.ws.OP_PING, 'pongme 2!')
993        self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment5')
994
995        frame = self.ws.frame_read(sock)
996        self.check_frame(frame, True, self.ws.OP_PONG, 'pongme 1!')
997
998        frame = self.ws.frame_read(sock)
999        self.check_frame(frame, True, self.ws.OP_PONG, 'pongme 2!')
1000
1001        self.check_frame(
1002            self.ws.frame_read(sock),
1003            True,
1004            self.ws.OP_TEXT,
1005            'fragment1fragment2fragment3fragment4fragment5',
1006        )
1007
1008        # 5_20
1009
1010        self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
1011        self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=False)
1012        self.ws.frame_write(sock, self.ws.OP_PING, 'pongme 1!')
1013
1014        frame = self.ws.frame_read(sock)
1015        self.check_frame(frame, True, self.ws.OP_PONG, 'pongme 1!')
1016
1017        time.sleep(1)
1018
1019        self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment3', fin=False)
1020        self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment4', fin=False)
1021        self.ws.frame_write(sock, self.ws.OP_PING, 'pongme 2!')
1022
1023        frame = self.ws.frame_read(sock)
1024        self.check_frame(frame, True, self.ws.OP_PONG, 'pongme 2!')
1025
1026        assert self.recvall(sock, read_timeout=0.1) == b'', '5_20'
1027        self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment5')
1028
1029        self.check_frame(
1030            self.ws.frame_read(sock),
1031            True,
1032            self.ws.OP_TEXT,
1033            'fragment1fragment2fragment3fragment4fragment5',
1034        )
1035
1036        self.close_connection(sock)
1037
1038    def test_asgi_websockets_6_1_1__6_4_4(self):
1039        self.load('websockets/mirror')
1040
1041        # 6_1_1
1042
1043        _, sock, _ = self.ws.upgrade()
1044
1045        self.ws.frame_write(sock, self.ws.OP_TEXT, '')
1046        frame = self.ws.frame_read(sock)
1047        self.check_frame(frame, True, self.ws.OP_TEXT, '')
1048
1049        # 6_1_2
1050
1051        self.ws.frame_write(sock, self.ws.OP_TEXT, '', fin=False)
1052        self.ws.frame_write(sock, self.ws.OP_CONT, '', fin=False)
1053        self.ws.frame_write(sock, self.ws.OP_CONT, '')
1054
1055        frame = self.ws.frame_read(sock)
1056        self.check_frame(frame, True, self.ws.OP_TEXT, '')
1057
1058        # 6_1_3
1059
1060        payload = 'middle frame payload'
1061
1062        self.ws.frame_write(sock, self.ws.OP_TEXT, '', fin=False)
1063        self.ws.frame_write(sock, self.ws.OP_CONT, payload, fin=False)
1064        self.ws.frame_write(sock, self.ws.OP_CONT, '')
1065
1066        frame = self.ws.frame_read(sock)
1067        self.check_frame(frame, True, self.ws.OP_TEXT, payload)
1068
1069        # 6_2_1
1070
1071        payload = 'Hello-µ@ßöäüàá-UTF-8!!'
1072
1073        self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
1074
1075        frame = self.ws.frame_read(sock)
1076        self.check_frame(frame, True, self.ws.OP_TEXT, payload)
1077
1078        # 6_2_2
1079
1080        self.ws.frame_write(sock, self.ws.OP_TEXT, payload[:12], fin=False)
1081        self.ws.frame_write(sock, self.ws.OP_CONT, payload[12:])
1082
1083        frame = self.ws.frame_read(sock)
1084        self.check_frame(frame, True, self.ws.OP_TEXT, payload)
1085
1086        # 6_2_3
1087
1088        self.ws.message(sock, self.ws.OP_TEXT, payload, fragmention_size=1)
1089
1090        frame = self.ws.frame_read(sock)
1091        self.check_frame(frame, True, self.ws.OP_TEXT, payload)
1092
1093        # 6_2_4
1094
1095        payload = '\xce\xba\xe1\xbd\xb9\xcf\x83\xce\xbc\xce\xb5'
1096
1097        self.ws.message(sock, self.ws.OP_TEXT, payload, fragmention_size=1)
1098
1099        frame = self.ws.frame_read(sock)
1100        self.check_frame(frame, True, self.ws.OP_TEXT, payload)
1101
1102        self.close_connection(sock)
1103
1104    #        Unit does not support UTF-8 validation
1105    #
1106    #        # 6_3_1 FAIL
1107    #
1108    #        payload_1 = '\xce\xba\xe1\xbd\xb9\xcf\x83\xce\xbc\xce\xb5'
1109    #        payload_2 = '\xed\xa0\x80'
1110    #        payload_3 = '\x65\x64\x69\x74\x65\x64'
1111    #
1112    #        payload = payload_1 + payload_2 + payload_3
1113    #
1114    #        self.ws.message(sock, self.ws.OP_TEXT, payload)
1115    #        self.check_close(sock, 1007)
1116    #
1117    #        # 6_3_2 FAIL
1118    #
1119    #        _, sock, _ = self.ws.upgrade()
1120    #
1121    #        self.ws.message(sock, self.ws.OP_TEXT, payload, fragmention_size=1)
1122    #        self.check_close(sock, 1007)
1123    #
1124    #        # 6_4_1 ... 6_4_4 FAIL
1125
1126    def test_asgi_websockets_7_1_1__7_5_1(self):
1127        self.load('websockets/mirror')
1128
1129        # 7_1_1
1130
1131        _, sock, _ = self.ws.upgrade()
1132
1133        payload = "Hello World!"
1134
1135        self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
1136
1137        frame = self.ws.frame_read(sock)
1138        self.check_frame(frame, True, self.ws.OP_TEXT, payload)
1139
1140        self.close_connection(sock)
1141
1142        # 7_1_2
1143
1144        _, sock, _ = self.ws.upgrade()
1145
1146        self.ws.frame_write(sock, self.ws.OP_CLOSE, self.ws.serialize_close())
1147        self.ws.frame_write(sock, self.ws.OP_CLOSE, self.ws.serialize_close())
1148
1149        self.check_close(sock)
1150
1151        # 7_1_3
1152
1153        _, sock, _ = self.ws.upgrade()
1154
1155        self.ws.frame_write(sock, self.ws.OP_CLOSE, self.ws.serialize_close())
1156        self.check_close(sock, no_close=True)
1157
1158        self.ws.frame_write(sock, self.ws.OP_PING, '')
1159        assert self.recvall(sock, read_timeout=0.1) == b'', 'empty soc'
1160
1161        sock.close()
1162
1163        # 7_1_4
1164
1165        _, sock, _ = self.ws.upgrade()
1166
1167        self.ws.frame_write(sock, self.ws.OP_CLOSE, self.ws.serialize_close())
1168        self.check_close(sock, no_close=True)
1169
1170        self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
1171        assert self.recvall(sock, read_timeout=0.1) == b'', 'empty soc'
1172
1173        sock.close()
1174
1175        # 7_1_5
1176
1177        _, sock, _ = self.ws.upgrade()
1178
1179        self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
1180        self.ws.frame_write(sock, self.ws.OP_CLOSE, self.ws.serialize_close())
1181        self.check_close(sock, no_close=True)
1182
1183        self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2')
1184        assert self.recvall(sock, read_timeout=0.1) == b'', 'empty soc'
1185
1186        sock.close()
1187
1188        # 7_1_6
1189
1190        _, sock, _ = self.ws.upgrade()
1191
1192        self.ws.frame_write(sock, self.ws.OP_TEXT, 'BAsd7&jh23' * 26 * 2 ** 10)
1193        self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
1194        self.ws.frame_write(sock, self.ws.OP_CLOSE, self.ws.serialize_close())
1195
1196        self.recvall(sock, read_timeout=1)
1197
1198        self.ws.frame_write(sock, self.ws.OP_PING, '')
1199        assert self.recvall(sock, read_timeout=0.1) == b'', 'empty soc'
1200
1201        sock.close()
1202
1203        # 7_3_1
1204
1205        _, sock, _ = self.ws.upgrade()
1206
1207        self.ws.frame_write(sock, self.ws.OP_CLOSE, '')
1208        self.check_close(sock)
1209
1210        # 7_3_2
1211
1212        _, sock, _ = self.ws.upgrade()
1213
1214        self.ws.frame_write(sock, self.ws.OP_CLOSE, 'a')
1215        self.check_close(sock, 1002)
1216
1217        # 7_3_3
1218
1219        _, sock, _ = self.ws.upgrade()
1220
1221        self.ws.frame_write(sock, self.ws.OP_CLOSE, self.ws.serialize_close())
1222        self.check_close(sock)
1223
1224        # 7_3_4
1225
1226        _, sock, _ = self.ws.upgrade()
1227
1228        payload = self.ws.serialize_close(reason='Hello World!')
1229
1230        self.ws.frame_write(sock, self.ws.OP_CLOSE, payload)
1231        self.check_close(sock)
1232
1233        # 7_3_5
1234
1235        _, sock, _ = self.ws.upgrade()
1236
1237        payload = self.ws.serialize_close(reason='*' * 123)
1238
1239        self.ws.frame_write(sock, self.ws.OP_CLOSE, payload)
1240        self.check_close(sock)
1241
1242        # 7_3_6
1243
1244        _, sock, _ = self.ws.upgrade()
1245
1246        payload = self.ws.serialize_close(reason='*' * 124)
1247
1248        self.ws.frame_write(sock, self.ws.OP_CLOSE, payload)
1249        self.check_close(sock, 1002)
1250
1251    #        # 7_5_1 FAIL Unit does not support UTF-8 validation
1252    #
1253    #        _, sock, _ = self.ws.upgrade()
1254    #
1255    #        payload = self.ws.serialize_close(reason = '\xce\xba\xe1\xbd\xb9\xcf' \
1256    #            '\x83\xce\xbc\xce\xb5\xed\xa0\x80\x65\x64\x69\x74\x65\x64')
1257    #
1258    #        self.ws.frame_write(sock, self.ws.OP_CLOSE, payload)
1259    #        self.check_close(sock, 1007)
1260
1261    def test_asgi_websockets_7_7_X__7_9_X(self):
1262        self.load('websockets/mirror')
1263
1264        valid_codes = [
1265            1000,
1266            1001,
1267            1002,
1268            1003,
1269            1007,
1270            1008,
1271            1009,
1272            1010,
1273            1011,
1274            3000,
1275            3999,
1276            4000,
1277            4999,
1278        ]
1279
1280        invalid_codes = [0, 999, 1004, 1005, 1006, 1016, 1100, 2000, 2999]
1281
1282        for code in valid_codes:
1283            _, sock, _ = self.ws.upgrade()
1284
1285            payload = self.ws.serialize_close(code=code)
1286
1287            self.ws.frame_write(sock, self.ws.OP_CLOSE, payload)
1288            self.check_close(sock)
1289
1290        for code in invalid_codes:
1291            _, sock, _ = self.ws.upgrade()
1292
1293            payload = self.ws.serialize_close(code=code)
1294
1295            self.ws.frame_write(sock, self.ws.OP_CLOSE, payload)
1296            self.check_close(sock, 1002)
1297
1298    def test_asgi_websockets_7_13_1__7_13_2(self):
1299        self.load('websockets/mirror')
1300
1301        # 7_13_1
1302
1303        _, sock, _ = self.ws.upgrade()
1304
1305        payload = self.ws.serialize_close(code=5000)
1306
1307        self.ws.frame_write(sock, self.ws.OP_CLOSE, payload)
1308        self.check_close(sock, 1002)
1309
1310        # 7_13_2
1311
1312        _, sock, _ = self.ws.upgrade()
1313
1314        payload = struct.pack('!I', 65536) + ''.encode('utf-8')
1315
1316        self.ws.frame_write(sock, self.ws.OP_CLOSE, payload)
1317        self.check_close(sock, 1002)
1318
1319    def test_asgi_websockets_9_1_1__9_6_6(self, is_unsafe):
1320        if not is_unsafe:
1321            pytest.skip('unsafe, long run')
1322
1323        self.load('websockets/mirror')
1324
1325        assert 'success' in self.conf(
1326            {
1327                'http': {
1328                    'websocket': {
1329                        'max_frame_size': 33554432,
1330                        'keepalive_interval': 0,
1331                    }
1332                }
1333            },
1334            'settings',
1335        ), 'increase max_frame_size and keepalive_interval'
1336
1337        _, sock, _ = self.ws.upgrade()
1338
1339        op_text = self.ws.OP_TEXT
1340        op_binary = self.ws.OP_BINARY
1341
1342        def check_payload(opcode, length, chopsize=None):
1343            if opcode == self.ws.OP_TEXT:
1344                payload = '*' * length
1345            else:
1346                payload = b'*' * length
1347
1348            self.ws.frame_write(sock, opcode, payload, chopsize=chopsize)
1349            frame = self.ws.frame_read(sock, read_timeout=5)
1350            self.check_frame(frame, True, opcode, payload)
1351
1352        def check_message(opcode, f_size):
1353            if opcode == self.ws.OP_TEXT:
1354                payload = '*' * 4 * 2 ** 20
1355            else:
1356                payload = b'*' * 4 * 2 ** 20
1357
1358            self.ws.message(sock, opcode, payload, fragmention_size=f_size)
1359            frame = self.ws.frame_read(sock, read_timeout=5)
1360            self.check_frame(frame, True, opcode, payload)
1361
1362        check_payload(op_text, 64 * 2 ** 10)  # 9_1_1
1363        check_payload(op_text, 256 * 2 ** 10)  # 9_1_2
1364        check_payload(op_text, 2 ** 20)  # 9_1_3
1365        check_payload(op_text, 4 * 2 ** 20)  # 9_1_4
1366        check_payload(op_text, 8 * 2 ** 20)  # 9_1_5
1367        check_payload(op_text, 16 * 2 ** 20)  # 9_1_6
1368
1369        check_payload(op_binary, 64 * 2 ** 10)  # 9_2_1
1370        check_payload(op_binary, 256 * 2 ** 10)  # 9_2_2
1371        check_payload(op_binary, 2 ** 20)  # 9_2_3
1372        check_payload(op_binary, 4 * 2 ** 20)  # 9_2_4
1373        check_payload(op_binary, 8 * 2 ** 20)  # 9_2_5
1374        check_payload(op_binary, 16 * 2 ** 20)  # 9_2_6
1375
1376        if option.system != 'Darwin' and option.system != 'FreeBSD':
1377            check_message(op_text, 64)  # 9_3_1
1378            check_message(op_text, 256)  # 9_3_2
1379            check_message(op_text, 2 ** 10)  # 9_3_3
1380            check_message(op_text, 4 * 2 ** 10)  # 9_3_4
1381            check_message(op_text, 16 * 2 ** 10)  # 9_3_5
1382            check_message(op_text, 64 * 2 ** 10)  # 9_3_6
1383            check_message(op_text, 256 * 2 ** 10)  # 9_3_7
1384            check_message(op_text, 2 ** 20)  # 9_3_8
1385            check_message(op_text, 4 * 2 ** 20)  # 9_3_9
1386
1387            check_message(op_binary, 64)  # 9_4_1
1388            check_message(op_binary, 256)  # 9_4_2
1389            check_message(op_binary, 2 ** 10)  # 9_4_3
1390            check_message(op_binary, 4 * 2 ** 10)  # 9_4_4
1391            check_message(op_binary, 16 * 2 ** 10)  # 9_4_5
1392            check_message(op_binary, 64 * 2 ** 10)  # 9_4_6
1393            check_message(op_binary, 256 * 2 ** 10)  # 9_4_7
1394            check_message(op_binary, 2 ** 20)  # 9_4_8
1395            check_message(op_binary, 4 * 2 ** 20)  # 9_4_9
1396
1397        check_payload(op_text, 2 ** 20, chopsize=64)  # 9_5_1
1398        check_payload(op_text, 2 ** 20, chopsize=128)  # 9_5_2
1399        check_payload(op_text, 2 ** 20, chopsize=256)  # 9_5_3
1400        check_payload(op_text, 2 ** 20, chopsize=512)  # 9_5_4
1401        check_payload(op_text, 2 ** 20, chopsize=1024)  # 9_5_5
1402        check_payload(op_text, 2 ** 20, chopsize=2048)  # 9_5_6
1403
1404        check_payload(op_binary, 2 ** 20, chopsize=64)  # 9_6_1
1405        check_payload(op_binary, 2 ** 20, chopsize=128)  # 9_6_2
1406        check_payload(op_binary, 2 ** 20, chopsize=256)  # 9_6_3
1407        check_payload(op_binary, 2 ** 20, chopsize=512)  # 9_6_4
1408        check_payload(op_binary, 2 ** 20, chopsize=1024)  # 9_6_5
1409        check_payload(op_binary, 2 ** 20, chopsize=2048)  # 9_6_6
1410
1411        self.close_connection(sock)
1412
1413    def test_asgi_websockets_10_1_1(self):
1414        self.load('websockets/mirror')
1415
1416        _, sock, _ = self.ws.upgrade()
1417
1418        payload = '*' * 65536
1419
1420        self.ws.message(sock, self.ws.OP_TEXT, payload, fragmention_size=1300)
1421
1422        frame = self.ws.frame_read(sock)
1423        self.check_frame(frame, True, self.ws.OP_TEXT, payload)
1424
1425        self.close_connection(sock)
1426
1427    # settings
1428
1429    def test_asgi_websockets_max_frame_size(self):
1430        self.load('websockets/mirror')
1431
1432        assert 'success' in self.conf(
1433            {'http': {'websocket': {'max_frame_size': 100}}}, 'settings'
1434        ), 'configure max_frame_size'
1435
1436        _, sock, _ = self.ws.upgrade()
1437
1438        payload = '*' * 94
1439        opcode = self.ws.OP_TEXT
1440
1441        self.ws.frame_write(sock, opcode, payload)  # frame length is 100
1442
1443        frame = self.ws.frame_read(sock)
1444        self.check_frame(frame, True, opcode, payload)
1445
1446        payload = '*' * 95
1447
1448        self.ws.frame_write(sock, opcode, payload)  # frame length is 101
1449        self.check_close(sock, 1009)  # 1009 - CLOSE_TOO_LARGE
1450
1451    def test_asgi_websockets_read_timeout(self):
1452        self.load('websockets/mirror')
1453
1454        assert 'success' in self.conf(
1455            {'http': {'websocket': {'read_timeout': 5}}}, 'settings'
1456        ), 'configure read_timeout'
1457
1458        _, sock, _ = self.ws.upgrade()
1459
1460        frame = self.ws.frame_to_send(self.ws.OP_TEXT, 'blah')
1461        sock.sendall(frame[:2])
1462
1463        time.sleep(2)
1464
1465        self.check_close(sock, 1001)  # 1001 - CLOSE_GOING_AWAY
1466
1467    def test_asgi_websockets_keepalive_interval(self):
1468        self.load('websockets/mirror')
1469
1470        assert 'success' in self.conf(
1471            {'http': {'websocket': {'keepalive_interval': 5}}}, 'settings'
1472        ), 'configure keepalive_interval'
1473
1474        _, sock, _ = self.ws.upgrade()
1475
1476        frame = self.ws.frame_to_send(self.ws.OP_TEXT, 'blah')
1477        sock.sendall(frame[:2])
1478
1479        time.sleep(2)
1480
1481        frame = self.ws.frame_read(sock)
1482        self.check_frame(frame, True, self.ws.OP_PING, '')  # PING frame
1483
1484        sock.close()
1485
1486    def test_asgi_websockets_client_locks_app(self):
1487        self.load('websockets/mirror')
1488
1489        message = 'blah'
1490
1491        _, sock, _ = self.ws.upgrade()
1492
1493        assert 'success' in self.conf({}), 'remove app'
1494
1495        self.ws.frame_write(sock, self.ws.OP_TEXT, message)
1496
1497        frame = self.ws.frame_read(sock)
1498
1499        assert message == frame['data'].decode('utf-8'), 'client'
1500
1501        sock.close()
1502