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