summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAndrey Zelenkov <zelenkov@nginx.com>2019-08-22 15:33:41 +0300
committerAndrey Zelenkov <zelenkov@nginx.com>2019-08-22 15:33:41 +0300
commit9bbf54e23e185e94054072fff2673f6f5cd203e9 (patch)
tree2ed538061b65259a1bb221f25d637740feb9906e
parent08601bbbf07a462924e4c6894b5fd6e83b7725ac (diff)
downloadunit-9bbf54e23e185e94054072fff2673f6f5cd203e9.tar.gz
unit-9bbf54e23e185e94054072fff2673f6f5cd203e9.tar.bz2
Tests: Node.js websockets.
-rwxr-xr-xtest/node/websockets/mirror/app.js31
-rwxr-xr-xtest/node/websockets/mirror_fragmentation/app.js26
-rw-r--r--test/test_node_websockets.py1568
-rw-r--r--test/unit/applications/websockets.py215
-rw-r--r--test/unit/main.py1
5 files changed, 1841 insertions, 0 deletions
diff --git a/test/node/websockets/mirror/app.js b/test/node/websockets/mirror/app.js
new file mode 100755
index 00000000..23746465
--- /dev/null
+++ b/test/node/websockets/mirror/app.js
@@ -0,0 +1,31 @@
+#!/usr/bin/env node
+
+server = require('unit-http').createServer(function() {});
+webSocketServer = require('unit-http/websocket').server;
+//server = require('http').createServer(function() {});
+//webSocketServer = require('websocket').server;
+
+server.listen(7080, function() {});
+
+var wsServer = new webSocketServer({
+ maxReceivedMessageSize: 0x1000000000,
+ maxReceivedFrameSize: 0x1000000000,
+ fragmentOutgoingMessages: false,
+ fragmentationThreshold: 0x1000000000,
+ httpServer: server,
+});
+
+wsServer.on('request', function(request) {
+ var connection = request.accept(null);
+
+ connection.on('message', function(message) {
+ if (message.type === 'utf8') {
+ connection.send(message.utf8Data);
+ } else if (message.type === 'binary') {
+ connection.send(message.binaryData);
+ }
+
+ });
+
+ connection.on('close', function(r) {});
+});
diff --git a/test/node/websockets/mirror_fragmentation/app.js b/test/node/websockets/mirror_fragmentation/app.js
new file mode 100755
index 00000000..7024252a
--- /dev/null
+++ b/test/node/websockets/mirror_fragmentation/app.js
@@ -0,0 +1,26 @@
+#!/usr/bin/env node
+
+server = require('unit-http').createServer(function() {});
+webSocketServer = require('unit-http/websocket').server;
+//server = require('http').createServer(function() {});
+//webSocketServer = require('websocket').server;
+
+server.listen(7080, function() {});
+
+var wsServer = new webSocketServer({
+ httpServer: server
+});
+
+wsServer.on('request', function(request) {
+ //console.log('request');
+ var connection = request.accept(null);
+
+ connection.on('message', function(message) {
+ //console.log('message');
+ connection.send(message.utf8Data);
+ });
+
+ connection.on('close', function(r) {
+ //console.log('close');
+ });
+});
diff --git a/test/test_node_websockets.py b/test/test_node_websockets.py
new file mode 100644
index 00000000..655b5202
--- /dev/null
+++ b/test/test_node_websockets.py
@@ -0,0 +1,1568 @@
+import time
+import struct
+import unittest
+from unit.applications.lang.node import TestApplicationNode
+from unit.applications.websockets import TestApplicationWebsocket
+
+class TestNodeWebsockets(TestApplicationNode):
+ prerequisites = ['node']
+
+ ws = TestApplicationWebsocket()
+
+ @classmethod
+ def setUpClass(cls):
+ raise unittest.SkipTest('Websockets is not available')
+
+ def close_connection(self, sock):
+ self.assertEqual(self.recvall(sock, read_timeout=1), b'', 'empty sock')
+
+ self.ws.frame_write(sock, self.ws.OP_CLOSE, self.ws.serialize_close())
+
+ self.check_close(sock)
+
+ def check_close(self, sock, code = 1000, no_close = False):
+ frame = self.ws.frame_read(sock)
+
+ self.assertEqual(frame['fin'], True, 'close fin')
+ self.assertEqual(frame['opcode'], self.ws.OP_CLOSE, 'close opcode')
+ self.assertEqual(frame['code'], code, 'close code')
+
+ if not no_close:
+ sock.close()
+
+ def check_frame(self, frame, fin, opcode, payload, decode=True):
+ if opcode == self.ws.OP_BINARY or not decode:
+ data = frame['data']
+ else:
+ data = frame['data'].decode('utf-8')
+
+ self.assertEqual(frame['fin'], fin, 'fin')
+ self.assertEqual(frame['opcode'], opcode, 'opcode')
+ self.assertEqual(data, payload, 'payload')
+
+ def test_node_websockets_handshake(self):
+ self.load('websockets/mirror')
+
+ resp, sock, key = self.ws.upgrade()
+ sock.close()
+
+ self.assertEqual(resp['status'], 101, 'status')
+ self.assertEqual(
+ resp['headers']['Upgrade'], 'websocket', 'upgrade'
+ )
+ self.assertEqual(
+ resp['headers']['Connection'], 'Upgrade', 'connection'
+ )
+ self.assertEqual(
+ resp['headers']['Sec-WebSocket-Accept'], self.ws.accept(key), 'key'
+ )
+
+ def test_node_websockets_mirror(self):
+ self.load('websockets/mirror')
+
+ message = 'blah'
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, message)
+ frame = self.ws.frame_read(sock)
+
+ self.assertEqual(
+ message, frame['data'].decode('utf-8'), 'mirror'
+ )
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, message)
+ frame = self.ws.frame_read(sock)
+
+ self.assertEqual(
+ message, frame['data'].decode('utf-8'), 'mirror 2'
+ )
+
+ sock.close()
+
+ def test_node_websockets_no_mask(self):
+ self.load('websockets/mirror')
+
+ message = 'blah'
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, message, mask=False)
+
+ frame = self.ws.frame_read(sock)
+
+ self.assertEqual(frame['opcode'], self.ws.OP_CLOSE, 'no mask opcode')
+ self.assertEqual(frame['code'], 1002, 'no mask close code')
+
+ sock.close()
+
+ def test_node_websockets_fragmentation(self):
+ self.load('websockets/mirror')
+
+ message = 'blah'
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, message, fin=False)
+ self.ws.frame_write(sock, self.ws.OP_CONT, ' ', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_CONT, message)
+
+ frame = self.ws.frame_read(sock)
+
+ self.assertEqual(
+ message + ' ' + message,
+ frame['data'].decode('utf-8'),
+ 'mirror framing',
+ )
+
+ sock.close()
+
+ def test_node_websockets_frame_fragmentation_invalid(self):
+ self.load('websockets/mirror')
+
+ message = 'blah'
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_PING, message, fin=False)
+
+ frame = self.ws.frame_read(sock)
+
+ frame.pop('data')
+ self.assertDictEqual(
+ frame,
+ {
+ 'fin': True,
+ 'rsv1': False,
+ 'rsv2': False,
+ 'rsv3': False,
+ 'opcode': self.ws.OP_CLOSE,
+ 'mask': 0,
+ 'code': 1002,
+ 'reason': 'Fragmented control frame',
+ },
+ 'close frame',
+ )
+
+ sock.close()
+
+ def test_node_websockets_partial_send(self):
+ self.load('websockets/mirror')
+
+ message = 'blah'
+
+ _, sock, _ = self.ws.upgrade()
+
+ frame = self.ws.frame_to_send(self.ws.OP_TEXT, message)
+ sock.sendall(frame[:1])
+ sock.sendall(frame[1:2])
+ sock.sendall(frame[2:3])
+ sock.sendall(frame[3:])
+
+ frame = self.ws.frame_read(sock)
+
+ self.assertEqual(
+ message,
+ frame['data'].decode('utf-8'),
+ 'partial send',
+ )
+
+ sock.close()
+
+ def test_node_websockets_large(self):
+ self.load('websockets/mirror_fragmentation')
+
+ message = '0123456789' * 3000
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, message)
+
+ frame = self.ws.frame_read(sock)
+ data = frame['data'].decode('utf-8')
+
+ frame = self.ws.frame_read(sock)
+ data += frame['data'].decode('utf-8')
+
+ self.assertEqual(message, data, 'large')
+
+ sock.close()
+
+ def test_node_websockets_frame_invalid_opcode(self):
+ self.load('websockets/mirror')
+
+ message = 'blah'
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, message, fin=False)
+ self.ws.frame_write(sock, self.ws.OP_TEXT, message)
+
+ frame = self.ws.frame_read(sock)
+
+ frame.pop('data')
+ frame.pop('reason')
+ self.assertDictEqual(
+ frame,
+ {
+ 'fin': True,
+ 'rsv1': False,
+ 'rsv2': False,
+ 'rsv3': False,
+ 'opcode': self.ws.OP_CLOSE,
+ 'mask': 0,
+ 'code': 1002,
+ },
+ 'close frame',
+ )
+
+ sock.close()
+
+ def test_node_websockets_frame_invalid_opcode_2(self):
+ self.load('websockets/mirror')
+
+ message = 'blah'
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_CONT, message)
+
+ frame = self.ws.frame_read(sock)
+
+ frame.pop('data')
+ self.assertDictEqual(
+ frame,
+ {
+ 'fin': True,
+ 'rsv1': False,
+ 'rsv2': False,
+ 'rsv3': False,
+ 'opcode': self.ws.OP_CLOSE,
+ 'mask': 0,
+ 'code': 1002,
+ 'reason': 'Unrecognized opcode 0',
+ },
+ 'close frame',
+ )
+
+ sock.close()
+
+ def test_node_websockets_two_clients(self):
+ self.load('websockets/mirror')
+
+ message1 = 'blah1'
+ message2 = 'blah2'
+
+ _, sock1, _ = self.ws.upgrade()
+ _, sock2, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock1, self.ws.OP_TEXT, message1)
+ self.ws.frame_write(sock2, self.ws.OP_TEXT, message2)
+
+ frame1 = self.ws.frame_read(sock1)
+ frame2 = self.ws.frame_read(sock2)
+
+ self.assertEqual(
+ message1, frame1['data'].decode('utf-8'), 'client 1'
+ )
+ self.assertEqual(
+ message2, frame2['data'].decode('utf-8'), 'client 2'
+ )
+
+ sock1.close()
+ sock2.close()
+
+ @unittest.skip('not yet')
+ def test_node_websockets_handshake_upgrade_absent(self): # FAIL https://tools.ietf.org/html/rfc6455#section-4.2.1
+ self.load('websockets/mirror')
+
+ key = self.ws.key()
+ resp = self.get(headers={
+ 'Host': 'localhost',
+ 'Connection': 'Upgrade',
+ 'Sec-WebSocket-Key': key,
+ 'Sec-WebSocket-Protocol': 'chat',
+ 'Sec-WebSocket-Version': 13,
+ }, read_timeout=1)
+
+ self.assertEqual(resp['status'], 400, 'upgrade absent')
+
+ def test_node_websockets_handshake_case_insensitive(self):
+ self.load('websockets/mirror')
+
+ key = self.ws.key()
+ resp = self.get(headers={
+ 'Host': 'localhost',
+ 'Upgrade': 'WEBSOCKET',
+ 'Connection': 'UPGRADE',
+ 'Sec-WebSocket-Key': key,
+ 'Sec-WebSocket-Protocol': 'chat',
+ 'Sec-WebSocket-Version': 13,
+ }, read_timeout=1)
+
+ self.assertEqual(resp['status'], 101, 'status')
+
+ @unittest.skip('not yet')
+ def test_node_websockets_handshake_connection_absent(self): # FAIL
+ self.load('websockets/mirror')
+
+ key = self.ws.key()
+ resp = self.get(headers={
+ 'Host': 'localhost',
+ 'Upgrade': 'websocket',
+ 'Sec-WebSocket-Key': key,
+ 'Sec-WebSocket-Protocol': 'chat',
+ 'Sec-WebSocket-Version': 13,
+ }, read_timeout=1)
+
+ self.assertEqual(resp['status'], 400, 'status')
+
+ def test_node_websockets_handshake_version_absent(self):
+ self.load('websockets/mirror')
+
+ key = self.ws.key()
+ resp = self.get(headers={
+ 'Host': 'localhost',
+ 'Upgrade': 'websocket',
+ 'Connection': 'Upgrade',
+ 'Sec-WebSocket-Key': key,
+ 'Sec-WebSocket-Protocol': 'chat'
+ }, read_timeout=1)
+
+ self.assertEqual(resp['status'], 426, 'status')
+
+ @unittest.skip('not yet')
+ def test_node_websockets_handshake_key_invalid(self):
+ self.load('websockets/mirror')
+
+ resp = self.get(headers={
+ 'Host': 'localhost',
+ 'Upgrade': 'websocket',
+ 'Connection': 'Upgrade',
+ 'Sec-WebSocket-Key': '!',
+ 'Sec-WebSocket-Protocol': 'chat',
+ 'Sec-WebSocket-Version': 13
+ }, read_timeout=1)
+
+ self.assertEqual(resp['status'], 400, 'key length')
+
+ key = self.ws.key()
+ resp = self.get(headers={
+ 'Host': 'localhost',
+ 'Upgrade': 'websocket',
+ 'Connection': 'Upgrade',
+ 'Sec-WebSocket-Key': [key, key],
+ 'Sec-WebSocket-Protocol': 'chat',
+ 'Sec-WebSocket-Version': 13
+ }, read_timeout=1)
+
+ self.assertEqual(resp['status'], 400, 'key double') # FAIL https://tools.ietf.org/html/rfc6455#section-11.3.1
+
+ def test_node_websockets_handshake_method_invalid(self):
+ self.load('websockets/mirror')
+
+ key = self.ws.key()
+ resp = self.post(headers={
+ 'Host': 'localhost',
+ 'Upgrade': 'websocket',
+ 'Connection': 'Upgrade',
+ 'Sec-WebSocket-Key': key,
+ 'Sec-WebSocket-Protocol': 'chat',
+ 'Sec-WebSocket-Version': 13
+ }, read_timeout=1)
+
+ self.assertEqual(resp['status'], 400, 'status')
+
+ def test_node_websockets_handshake_http_10(self):
+ self.load('websockets/mirror')
+
+ key = self.ws.key()
+ resp = self.get(headers={
+ 'Host': 'localhost',
+ 'Upgrade': 'websocket',
+ 'Connection': 'Upgrade',
+ 'Sec-WebSocket-Key': key,
+ 'Sec-WebSocket-Protocol': 'chat',
+ 'Sec-WebSocket-Version': 13
+ }, http_10=True, read_timeout=1)
+
+ self.assertEqual(resp['status'], 400, 'status')
+
+ def test_node_websockets_handshake_uri_invalid(self):
+ self.load('websockets/mirror')
+
+ key = self.ws.key()
+ resp = self.get(headers={
+ 'Host': 'localhost',
+ 'Upgrade': 'websocket',
+ 'Connection': 'Upgrade',
+ 'Sec-WebSocket-Key': key,
+ 'Sec-WebSocket-Protocol': 'chat',
+ 'Sec-WebSocket-Version': 13
+ }, url='!', read_timeout=1)
+
+ self.assertEqual(resp['status'], 400, 'status')
+
+ def test_node_websockets_protocol_absent(self):
+ self.load('websockets/mirror')
+
+ key = self.ws.key()
+ resp = self.get(headers={
+ 'Host': 'localhost',
+ 'Upgrade': 'websocket',
+ 'Connection': 'Upgrade',
+ 'Sec-WebSocket-Key': key,
+ 'Sec-WebSocket-Version': 13
+ }, read_timeout=1)
+
+ self.assertEqual(resp['status'], 101, 'status')
+ self.assertEqual(
+ resp['headers']['Upgrade'], 'websocket', 'upgrade'
+ )
+ self.assertEqual(
+ resp['headers']['Connection'], 'Upgrade', 'connection'
+ )
+ self.assertEqual(
+ resp['headers']['Sec-WebSocket-Accept'], self.ws.accept(key), 'key'
+ )
+
+ # autobahn-testsuite
+
+ # Some following tests fail because of Unit does not support UTF-8
+ # validation for websocket frames. It should be implemented
+ # by application, if necessary.
+
+ def test_node_websockets_1_1_1__1_1_8(self):
+ self.load('websockets/mirror')
+
+ opcode = self.ws.OP_TEXT
+
+ _, sock, _ = self.ws.upgrade()
+
+ def check_length(length, chopsize=None):
+ payload = '*' * length
+
+ self.ws.frame_write(sock, opcode, payload, chopsize=chopsize)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, opcode, payload)
+
+ check_length(0) # 1_1_1
+ check_length(125) # 1_1_2
+ check_length(126) # 1_1_3
+ check_length(127) # 1_1_4
+ check_length(128) # 1_1_5
+ check_length(65535) # 1_1_6
+ check_length(65536) # 1_1_7
+ check_length(65536, chopsize = 997) # 1_1_8
+
+ self.close_connection(sock)
+
+ def test_node_websockets_1_2_1__1_2_8(self):
+ self.load('websockets/mirror')
+
+ opcode = self.ws.OP_BINARY
+
+ _, sock, _ = self.ws.upgrade()
+
+ def check_length(length, chopsize=None):
+ payload = b'\xfe' * length
+
+ self.ws.frame_write(sock, opcode, payload, chopsize=chopsize)
+ frame = self.ws.frame_read(sock)
+
+ self.check_frame(frame, True, opcode, payload)
+
+ check_length(0) # 1_2_1
+ check_length(125) # 1_2_2
+ check_length(126) # 1_2_3
+ check_length(127) # 1_2_4
+ check_length(128) # 1_2_5
+ check_length(65535) # 1_2_6
+ check_length(65536) # 1_2_7
+ check_length(65536, chopsize = 997) # 1_2_8
+
+ self.close_connection(sock)
+
+ def test_node_websockets_2_1__2_6(self):
+ self.load('websockets/mirror')
+
+ op_ping = self.ws.OP_PING
+ op_pong = self.ws.OP_PONG
+
+ _, sock, _ = self.ws.upgrade()
+
+ def check_ping(payload, chopsize=None, decode=True):
+ self.ws.frame_write(sock, op_ping, payload, chopsize=chopsize)
+ frame = self.ws.frame_read(sock)
+
+ self.check_frame(frame, True, op_pong, payload, decode=decode)
+
+ check_ping('') # 2_1
+ check_ping('Hello, world!') # 2_2
+ check_ping(b'\x00\xff\xfe\xfd\xfc\xfb\x00\xff', decode=False) # 2_3
+ check_ping(b'\xfe' * 125, decode=False) # 2_4
+ check_ping(b'\xfe' * 125, chopsize=1, decode=False) # 2_6
+
+ self.close_connection(sock)
+
+ # 2_5
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_PING, b'\xfe' * 126)
+ self.check_close(sock, 1002)
+
+ def test_node_websockets_2_7__2_9(self):
+ self.load('websockets/mirror')
+
+ # 2_7
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_PONG, '')
+ self.assertEqual(self.recvall(sock, read_timeout=1), b'', '2_7')
+
+ # 2_8
+
+ self.ws.frame_write(sock, self.ws.OP_PONG, 'unsolicited pong payload')
+ self.assertEqual(self.recvall(sock, read_timeout=1), b'', '2_8')
+
+ # 2_9
+
+ payload = 'ping payload'
+
+ self.ws.frame_write(sock, self.ws.OP_PONG, 'unsolicited pong payload')
+ self.ws.frame_write(sock, self.ws.OP_PING, payload)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_PONG, payload)
+
+ self.close_connection(sock)
+
+ def test_node_websockets_2_10__2_11(self):
+ self.load('websockets/mirror')
+
+ # 2_10
+
+ _, sock, _ = self.ws.upgrade()
+
+ for i in range(0, 10):
+ self.ws.frame_write(sock, self.ws.OP_PING, 'payload-%d' % i)
+
+ for i in range(0, 10):
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_PONG, 'payload-%d' % i)
+
+ # 2_11
+
+ for i in range(0, 10):
+ opcode = self.ws.OP_PING
+ self.ws.frame_write(sock, opcode, 'payload-%d' % i, chopsize=1)
+
+ for i in range(0, 10):
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_PONG, 'payload-%d' % i)
+
+ self.close_connection(sock)
+
+ @unittest.skip('not yet')
+ def test_node_websockets_3_1__3_7(self):
+ self.load('websockets/mirror')
+
+ payload = 'Hello, world!'
+
+ # 3_1
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, payload, rsv1=True)
+ self.check_close(sock, 1002)
+
+ # 3_2
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
+ self.ws.frame_write(sock, self.ws.OP_TEXT, payload, rsv2=True)
+ self.ws.frame_write(sock, self.ws.OP_PING, '')
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, payload)
+
+ self.check_close(sock, 1002, no_close = True)
+
+ self.assertEqual(self.recvall(sock, read_timeout=1), b'', 'empty 3_2')
+ sock.close()
+
+ # 3_3
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, payload)
+
+ self.ws.frame_write(
+ sock,
+ self.ws.OP_TEXT,
+ payload,
+ rsv1=True,
+ rsv2=True,
+ )
+
+ self.check_close(sock, 1002, no_close = True)
+
+ self.assertEqual(self.recvall(sock, read_timeout=1), b'', 'empty 3_3')
+ sock.close()
+
+ # 3_4
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, payload, chopsize=1)
+ self.ws.frame_write(
+ sock,
+ self.ws.OP_TEXT,
+ payload,
+ rsv3=True,
+ chopsize=1
+ )
+ self.ws.frame_write(sock, self.ws.OP_PING, '')
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, payload)
+
+ self.check_close(sock, 1002, no_close = True)
+
+ self.assertEqual(self.recvall(sock, read_timeout=1), b'', 'empty 3_4')
+ sock.close()
+
+ # 3_5
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(
+ sock,
+ self.ws.OP_BINARY,
+ b'\x00\xff\xfe\xfd\xfc\xfb\x00\xff',
+ rsv1=True,
+ rsv3=True,
+ )
+
+ self.check_close(sock, 1002)
+
+ # 3_6
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(
+ sock,
+ self.ws.OP_PING,
+ payload,
+ rsv2=True,
+ rsv3=True,
+ )
+
+ self.check_close(sock, 1002)
+
+ # 3_7
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(
+ sock,
+ self.ws.OP_CLOSE,
+ payload,
+ rsv1=True,
+ rsv2=True,
+ rsv3=True,
+ )
+
+ self.check_close(sock, 1002)
+
+ def test_node_websockets_4_1_1__4_2_5(self):
+ self.load('websockets/mirror')
+
+ payload = 'Hello, world!'
+
+ # 4_1_1
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, 0x03, '')
+ self.check_close(sock, 1002)
+
+ # 4_1_2
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, 0x04, 'reserved opcode payload')
+ self.check_close(sock, 1002)
+
+ # 4_1_3
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, payload)
+
+ self.ws.frame_write(sock, 0x05, '')
+ self.ws.frame_write(sock, self.ws.OP_PING, '')
+
+ self.check_close(sock, 1002)
+
+ # 4_1_4
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, payload)
+
+ self.ws.frame_write(sock, 0x06, payload)
+ self.ws.frame_write(sock, self.ws.OP_PING, '')
+
+ self.check_close(sock, 1002)
+
+ # 4_1_5
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, payload, chopsize=1)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, payload)
+
+ self.ws.frame_write(sock, 0x07, payload, chopsize=1)
+ self.ws.frame_write(sock, self.ws.OP_PING, '')
+
+ self.check_close(sock, 1002)
+
+ # 4_2_1
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, 0x0B, '')
+ self.check_close(sock, 1002)
+
+ # 4_2_2
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, 0x0C, 'reserved opcode payload')
+ self.check_close(sock, 1002)
+
+ # 4_2_3
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, payload)
+
+ self.ws.frame_write(sock, 0x0D, '')
+ self.ws.frame_write(sock, self.ws.OP_PING, '')
+
+ self.check_close(sock, 1002)
+
+ # 4_2_4
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, payload)
+
+ self.ws.frame_write(sock, 0x0E, payload)
+ self.ws.frame_write(sock, self.ws.OP_PING, '')
+
+ self.check_close(sock, 1002)
+
+ # 4_2_5
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, payload, chopsize=1)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, payload)
+
+ self.ws.frame_write(sock, 0x0F, payload, chopsize=1)
+ self.ws.frame_write(sock, self.ws.OP_PING, '')
+
+ self.check_close(sock, 1002)
+
+ def test_node_websockets_5_1__5_20(self):
+ self.load('websockets/mirror')
+
+ # 5_1
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_PING, 'fragment1', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=True)
+ self.check_close(sock, 1002)
+
+ # 5_2
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_PONG, 'fragment1', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=True)
+ self.check_close(sock, 1002)
+
+ # 5_3
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=True)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, 'fragment1fragment2')
+
+ # 5_4
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
+ self.assertEqual(self.recvall(sock, read_timeout=1), b'', '5_4')
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=True)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, 'fragment1fragment2')
+
+ # 5_5
+
+ self.ws.frame_write(
+ sock,
+ self.ws.OP_TEXT,
+ 'fragment1',
+ fin=False,
+ chopsize=1,
+ )
+ self.ws.frame_write(
+ sock,
+ self.ws.OP_CONT,
+ 'fragment2',
+ fin=True,
+ chopsize=1,
+ )
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, 'fragment1fragment2')
+
+ # 5_6
+
+ ping_payload = 'ping payload'
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_PING, ping_payload)
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=True)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_PONG, ping_payload)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, 'fragment1fragment2')
+
+ # 5_7
+
+ ping_payload = 'ping payload'
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
+ self.assertEqual(self.recvall(sock, read_timeout=1), b'', '5_7')
+
+ self.ws.frame_write(sock, self.ws.OP_PING, ping_payload)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_PONG, ping_payload)
+
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=True)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, 'fragment1fragment2')
+
+ # 5_8
+
+ ping_payload = 'ping payload'
+
+ self.ws.frame_write(
+ sock,
+ self.ws.OP_TEXT,
+ 'fragment1',
+ fin=False,
+ chopsize=1,
+ )
+ self.ws.frame_write(sock, self.ws.OP_PING, ping_payload, chopsize=1)
+ self.ws.frame_write(
+ sock,
+ self.ws.OP_CONT,
+ 'fragment2',
+ fin=True,
+ chopsize=1,
+ )
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_PONG, ping_payload)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, 'fragment1fragment2')
+
+ # 5_9
+
+ self.ws.frame_write(
+ sock,
+ self.ws.OP_CONT,
+ 'non-continuation payload',
+ fin=True,
+ )
+ self.ws.frame_write(sock, self.ws.OP_TEXT, 'Hello, world!', fin=True)
+ self.check_close(sock, 1002)
+
+ # 5_10
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(
+ sock,
+ self.ws.OP_CONT,
+ 'non-continuation payload',
+ fin=True,
+ )
+ self.ws.frame_write(sock, self.ws.OP_TEXT, 'Hello, world!', fin=True)
+ self.check_close(sock, 1002)
+
+ # 5_11
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(
+ sock,
+ self.ws.OP_CONT,
+ 'non-continuation payload',
+ fin=True,
+ chopsize=1,
+ )
+ self.ws.frame_write(
+ sock,
+ self.ws.OP_TEXT,
+ 'Hello, world!',
+ fin=True,
+ chopsize=1,
+ )
+ self.check_close(sock, 1002)
+
+ # 5_12
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(
+ sock,
+ self.ws.OP_CONT,
+ 'non-continuation payload',
+ fin=False,
+ )
+ self.ws.frame_write(sock, self.ws.OP_TEXT, 'Hello, world!', fin=True)
+ self.check_close(sock, 1002)
+
+ # 5_13
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(
+ sock,
+ self.ws.OP_CONT,
+ 'non-continuation payload',
+ fin=False,
+ )
+ self.ws.frame_write(sock, self.ws.OP_TEXT, 'Hello, world!', fin=True)
+ self.check_close(sock, 1002)
+
+ # 5_14
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(
+ sock,
+ self.ws.OP_CONT,
+ 'non-continuation payload',
+ fin=False,
+ chopsize=1,
+ )
+ self.ws.frame_write(
+ sock,
+ self.ws.OP_TEXT,
+ 'Hello, world!',
+ fin=True,
+ chopsize=1,
+ )
+ self.check_close(sock, 1002)
+
+ # 5_15
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=True)
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment3', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment4', fin=True)
+ self.check_close(sock, 1002)
+
+ # 5_16
+
+ _, sock, _ = self.ws.upgrade()
+
+ for i in range(0, 2):
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment1', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment2', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment3', fin=True)
+ self.check_close(sock, 1002)
+
+ # 5_17
+
+ _, sock, _ = self.ws.upgrade()
+
+ for i in range(0, 2):
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment1', fin=True)
+ self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment2', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment3', fin=True)
+ self.check_close(sock, 1002)
+
+ # 5_18
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment2')
+ self.check_close(sock, 1002)
+
+ # 5_19
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_PING, 'pongme 1!')
+
+ time.sleep(1)
+
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment3', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment4', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_PING, 'pongme 2!')
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment5')
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_PONG, 'pongme 1!')
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_PONG, 'pongme 2!')
+
+ self.check_frame(
+ self.ws.frame_read(sock),
+ True,
+ self.ws.OP_TEXT,
+ 'fragment1fragment2fragment3fragment4fragment5',
+ )
+
+ # 5_20
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_PING, 'pongme 1!')
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_PONG, 'pongme 1!')
+
+ time.sleep(1)
+
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment3', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment4', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_PING, 'pongme 2!')
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_PONG, 'pongme 2!')
+
+ self.assertEqual(self.recvall(sock, read_timeout=1), b'', '5_20')
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment5')
+
+ self.check_frame(
+ self.ws.frame_read(sock),
+ True,
+ self.ws.OP_TEXT,
+ 'fragment1fragment2fragment3fragment4fragment5',
+ )
+
+ self.close_connection(sock)
+
+ def test_node_websockets_6_1_1__6_4_4(self):
+ self.load('websockets/mirror')
+
+ # 6_1_1
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, '')
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, '')
+
+ # 6_1_2
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, '', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_CONT, '', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_CONT, '')
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, '')
+
+ # 6_1_3
+
+ payload = 'middle frame payload'
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, '', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_CONT, payload, fin=False)
+ self.ws.frame_write(sock, self.ws.OP_CONT, '')
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, payload)
+
+ # 6_2_1
+
+ payload = 'Hello-µ@ßöäüàá-UTF-8!!'
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, payload)
+
+ # 6_2_2
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, payload[:12], fin=False)
+ self.ws.frame_write(sock, self.ws.OP_CONT, payload[12:])
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, payload)
+
+ # 6_2_3
+
+ self.ws.message(sock, self.ws.OP_TEXT, payload, fragmention_size=1)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, payload)
+
+ # 6_2_4
+
+ payload = '\xce\xba\xe1\xbd\xb9\xcf\x83\xce\xbc\xce\xb5'
+
+ self.ws.message(sock, self.ws.OP_TEXT, payload, fragmention_size=1)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, payload)
+
+ self.close_connection(sock)
+
+ # Unit does not support UTF-8 validation
+
+# # 6_3_1 FAIL
+#
+# payload_1 = '\xce\xba\xe1\xbd\xb9\xcf\x83\xce\xbc\xce\xb5'
+# payload_2 = '\xed\xa0\x80'
+# payload_3 = '\x65\x64\x69\x74\x65\x64'
+#
+# payload = payload_1 + payload_2 + payload_3
+#
+# self.ws.message(sock, self.ws.OP_TEXT, payload)
+# self.check_close(sock, 1007)
+#
+# # 6_3_2 FAIL
+#
+# _, sock, _ = self.ws.upgrade()
+#
+# self.ws.message(sock, self.ws.OP_TEXT, payload, fragmention_size=1)
+# self.check_close(sock, 1007)
+#
+# # 6_4_1 ... 6_4_4 FAIL
+
+ def test_node_websockets_7_1_1__7_5_1(self):
+ self.load('websockets/mirror')
+
+ # 7_1_1
+
+ _, sock, _ = self.ws.upgrade()
+
+ payload = "Hello World!"
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, payload)
+
+ self.close_connection(sock)
+
+ # 7_1_2
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_CLOSE, self.ws.serialize_close())
+ self.ws.frame_write(sock, self.ws.OP_CLOSE, self.ws.serialize_close())
+
+ self.check_close(sock)
+
+ # 7_1_3
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_CLOSE, self.ws.serialize_close())
+ self.check_close(sock, no_close = True)
+
+ self.ws.frame_write(sock, self.ws.OP_PING, '')
+ self.assertEqual(self.recvall(sock, read_timeout=1), b'', 'empty sock')
+
+ sock.close()
+
+ # 7_1_4
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_CLOSE, self.ws.serialize_close())
+ self.check_close(sock, no_close = True)
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
+ self.assertEqual(self.recvall(sock, read_timeout=1), b'', 'empty sock')
+
+ sock.close()
+
+ # 7_1_5
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
+ self.ws.frame_write(sock, self.ws.OP_CLOSE, self.ws.serialize_close())
+ self.check_close(sock, no_close = True)
+
+ self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2')
+ self.assertEqual(self.recvall(sock, read_timeout=1), b'', 'empty sock')
+
+ sock.close()
+
+ # 7_1_6
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_TEXT, 'BAsd7&jh23' * 26 * 2**10)
+ self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
+ self.ws.frame_write(sock, self.ws.OP_CLOSE, self.ws.serialize_close())
+
+ self.recvall(sock, read_timeout=1)
+
+ self.ws.frame_write(sock, self.ws.OP_PING, '')
+ self.assertEqual(self.recvall(sock, read_timeout=1), b'', 'empty sock')
+
+ sock.close()
+
+ # 7_3_1 # FAIL
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_CLOSE, '')
+ self.check_close(sock)
+
+ # 7_3_2
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_CLOSE, 'a')
+ self.check_close(sock, 1002)
+
+ # 7_3_3
+
+ _, sock, _ = self.ws.upgrade()
+
+ self.ws.frame_write(sock, self.ws.OP_CLOSE, self.ws.serialize_close())
+ self.check_close(sock)
+
+ # 7_3_4
+
+ _, sock, _ = self.ws.upgrade()
+
+ payload = self.ws.serialize_close(reason = 'Hello World!')
+
+ self.ws.frame_write(sock, self.ws.OP_CLOSE, payload)
+ self.check_close(sock)
+
+ # 7_3_5
+
+ _, sock, _ = self.ws.upgrade()
+
+ payload = self.ws.serialize_close(reason = '*' * 123)
+
+ self.ws.frame_write(sock, self.ws.OP_CLOSE, payload)
+ self.check_close(sock)
+
+ # 7_3_6
+
+ _, sock, _ = self.ws.upgrade()
+
+ payload = self.ws.serialize_close(reason = '*' * 124)
+
+ self.ws.frame_write(sock, self.ws.OP_CLOSE, payload)
+ self.check_close(sock, 1002)
+
+ # 7_5_1 FAIL Unit does not support UTF-8 validation
+
+# _, sock, _ = self.ws.upgrade()
+#
+# payload = self.ws.serialize_close(reason = '\xce\xba\xe1\xbd\xb9\xcf' \
+# '\x83\xce\xbc\xce\xb5\xed\xa0\x80\x65\x64\x69\x74\x65\x64')
+#
+# self.ws.frame_write(sock, self.ws.OP_CLOSE, payload)
+# self.check_close(sock, 1007)
+
+ def test_node_websockets_7_7_X__7_9_X(self):
+ self.load('websockets/mirror')
+
+ valid_codes = [
+ 1000,
+ 1001,
+ 1002,
+ 1003,
+ 1007,
+ 1008,
+ 1009,
+ 1010,
+ 1011,
+ 3000,
+ 3999,
+ 4000,
+ 4999,
+ ]
+
+ invalid_codes = [0, 999, 1004, 1005, 1006, 1016, 1100, 2000, 2999]
+
+ for code in valid_codes:
+ _, sock, _ = self.ws.upgrade()
+
+ payload = self.ws.serialize_close(code = code)
+
+ self.ws.frame_write(sock, self.ws.OP_CLOSE, payload)
+ self.check_close(sock)
+
+ for code in invalid_codes:
+ _, sock, _ = self.ws.upgrade()
+
+ payload = self.ws.serialize_close(code = code)
+
+ self.ws.frame_write(sock, self.ws.OP_CLOSE, payload)
+ self.check_close(sock, 1002)
+
+ def test_node_websockets_7_13_1__7_13_2(self):
+ self.load('websockets/mirror')
+
+ # 7_13_1
+
+ _, sock, _ = self.ws.upgrade()
+
+ payload = self.ws.serialize_close(code = 5000)
+
+ self.ws.frame_write(sock, self.ws.OP_CLOSE, payload)
+ self.check_close(sock, 1002)
+
+ # 7_13_2
+
+ _, sock, _ = self.ws.upgrade()
+
+ payload = struct.pack('!I', 65536) + ''.encode('utf-8')
+
+ self.ws.frame_write(sock, self.ws.OP_CLOSE, payload)
+ self.check_close(sock, 1002)
+
+ def test_node_websockets_9_1_1__9_6_6(self):
+ if not self.unsafe:
+ self.skipTest("unsafe, long run")
+
+ self.load('websockets/mirror')
+
+ self.assertIn(
+ 'success',
+ self.conf(
+ {
+ 'http': {
+ 'websocket': {
+ 'max_frame_size': 33554432,
+ 'keepalive_interval': 0,
+ }
+ }
+ },
+ 'settings',
+ ),
+ 'increase max_frame_size and keepalive_interval',
+ )
+
+ _, sock, _ = self.ws.upgrade()
+
+ op_text = self.ws.OP_TEXT
+ op_binary = self.ws.OP_BINARY
+
+ def check_payload(opcode, length, chopsize=None):
+ if opcode == self.ws.OP_TEXT:
+ payload = '*' * length
+ else:
+ payload = b'*' * length
+
+ self.ws.frame_write(sock, opcode, payload, chopsize=chopsize)
+ frame = self.ws.frame_read(sock, read_timeout=5)
+ self.check_frame(frame, True, opcode, payload)
+
+ def check_message(opcode, f_size):
+ if opcode == self.ws.OP_TEXT:
+ payload = '*' * 4 * 2**20
+ else:
+ payload = b'*' * 4 * 2**20
+
+ self.ws.message(sock, opcode, payload, fragmention_size=f_size)
+ frame = self.ws.frame_read(sock, read_timeout=5)
+ self.check_frame(frame, True, opcode, payload)
+
+ check_payload(op_text, 64 * 2**10) # 9_1_1
+ check_payload(op_text, 256 * 2**10) # 9_1_2
+ check_payload(op_text, 2**20) # 9_1_3
+ check_payload(op_text, 4 * 2**20) # 9_1_4
+ check_payload(op_text, 8 * 2**20) # 9_1_5
+ check_payload(op_text, 16 * 2**20) # 9_1_6
+
+ check_payload(op_binary, 64 * 2**10) # 9_2_1
+ check_payload(op_binary, 256 * 2**10) # 9_2_2
+ check_payload(op_binary, 2**20) # 9_2_3
+ check_payload(op_binary, 4 * 2**20) # 9_2_4
+ check_payload(op_binary, 8 * 2**20) # 9_2_5
+ check_payload(op_binary, 16 * 2**20) # 9_2_6
+
+ if self.system != 'Darwin' and self.system != 'FreeBSD':
+ check_message(op_text, 64) # 9_3_1
+ check_message(op_text, 256) # 9_3_2
+ check_message(op_text, 2**10) # 9_3_3
+ check_message(op_text, 4 * 2**10) # 9_3_4
+ check_message(op_text, 16 * 2**10) # 9_3_5
+ check_message(op_text, 64 * 2**10) # 9_3_6
+ check_message(op_text, 256 * 2**10) # 9_3_7
+ check_message(op_text, 2**20) # 9_3_8
+ check_message(op_text, 4 * 2**20) # 9_3_9
+
+ check_message(op_binary, 64) # 9_4_1
+ check_message(op_binary, 256) # 9_4_2
+ check_message(op_binary, 2**10) # 9_4_3
+ check_message(op_binary, 4 * 2**10) # 9_4_4
+ check_message(op_binary, 16 * 2**10) # 9_4_5
+ check_message(op_binary, 64 * 2**10) # 9_4_6
+ check_message(op_binary, 256 * 2**10) # 9_4_7
+ check_message(op_binary, 2**20) # 9_4_8
+ check_message(op_binary, 4 * 2**20) # 9_4_9
+
+ check_payload(op_text, 2**20, chopsize=64) # 9_5_1
+ check_payload(op_text, 2**20, chopsize=128) # 9_5_2
+ check_payload(op_text, 2**20, chopsize=256) # 9_5_3
+ check_payload(op_text, 2**20, chopsize=512) # 9_5_4
+ check_payload(op_text, 2**20, chopsize=1024) # 9_5_5
+ check_payload(op_text, 2**20, chopsize=2048) # 9_5_6
+
+ check_payload(op_binary, 2**20, chopsize=64) # 9_6_1
+ check_payload(op_binary, 2**20, chopsize=128) # 9_6_2
+ check_payload(op_binary, 2**20, chopsize=256) # 9_6_3
+ check_payload(op_binary, 2**20, chopsize=512) # 9_6_4
+ check_payload(op_binary, 2**20, chopsize=1024) # 9_6_5
+ check_payload(op_binary, 2**20, chopsize=2048) # 9_6_6
+
+ self.close_connection(sock)
+
+ def test_node_websockets_10_1_1(self):
+ self.load('websockets/mirror')
+
+ _, sock, _ = self.ws.upgrade()
+
+ payload = '*' * 65536
+
+ self.ws.message(sock, self.ws.OP_TEXT, payload, fragmention_size=1300)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, payload)
+
+ self.close_connection(sock)
+
+ # settings
+
+ def test_node_websockets_max_frame_size(self):
+ self.load('websockets/mirror')
+
+ self.assertIn(
+ 'success',
+ self.conf(
+ {'http': {'websocket': {'max_frame_size': 100}}}, 'settings'
+ ),
+ 'configure max_frame_size',
+ )
+
+ _, sock, _ = self.ws.upgrade()
+
+ payload = '*' * 94
+ opcode = self.ws.OP_TEXT
+
+ self.ws.frame_write(sock, opcode, payload) # frame length is 100
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, opcode, payload)
+
+ payload = '*' * 95
+
+ self.ws.frame_write(sock, opcode, payload) # frame length is 101
+ self.check_close(sock, 1009) # 1009 - CLOSE_TOO_LARGE
+
+ def test_node_websockets_read_timeout(self):
+ self.load('websockets/mirror')
+
+ self.assertIn(
+ 'success',
+ self.conf(
+ {'http': {'websocket': {'read_timeout': 1}}}, 'settings'
+ ),
+ 'configure read_timeout',
+ )
+
+ _, sock, _ = self.ws.upgrade()
+
+ frame = self.ws.frame_to_send(self.ws.OP_TEXT, 'blah')
+ sock.sendall(frame[:2])
+
+ time.sleep(2)
+
+ self.check_close(sock, 1001) # 1001 - CLOSE_GOING_AWAY
+
+ def test_node_websockets_keepalive_interval(self):
+ self.load('websockets/mirror')
+
+ self.assertIn(
+ 'success',
+ self.conf(
+ {'http': {'websocket': {'keepalive_interval': 1}}}, 'settings'
+ ),
+ 'configure keepalive_interval',
+ )
+
+ _, sock, _ = self.ws.upgrade()
+
+ frame = self.ws.frame_to_send(self.ws.OP_TEXT, 'blah')
+ sock.sendall(frame[:2])
+
+ time.sleep(2)
+
+ frame = self.ws.frame_read(sock)
+ self.check_frame(frame, True, self.ws.OP_PING, '') # PING frame
+
+ sock.close()
+
+if __name__ == '__main__':
+ TestNodeWebsockets.main()
diff --git a/test/unit/applications/websockets.py b/test/unit/applications/websockets.py
new file mode 100644
index 00000000..7b516239
--- /dev/null
+++ b/test/unit/applications/websockets.py
@@ -0,0 +1,215 @@
+import random
+import base64
+import struct
+import select
+import hashlib
+import itertools
+from unit.applications.proto import TestApplicationProto
+
+GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
+
+
+class TestApplicationWebsocket(TestApplicationProto):
+
+ OP_CONT = 0x00
+ OP_TEXT = 0x01
+ OP_BINARY = 0x02
+ OP_CLOSE = 0x08
+ OP_PING = 0x09
+ OP_PONG = 0x0A
+ CLOSE_CODES = [1000, 1001, 1002, 1003, 1007, 1008, 1009, 1010, 1011]
+
+ def __init__(self, preinit=False):
+ self.preinit = preinit
+
+ def key(self):
+ raw_key = bytes(random.getrandbits(8) for _ in range(16))
+ return base64.b64encode(raw_key).decode()
+
+ def accept(self, key):
+ sha1 = hashlib.sha1((key + GUID).encode()).digest()
+ return base64.b64encode(sha1).decode()
+
+ def upgrade(self):
+ key = self.key()
+
+ if self.preinit:
+ self.get()
+
+ resp, sock = self.get(
+ headers={
+ 'Host': 'localhost',
+ 'Upgrade': 'websocket',
+ 'Connection': 'Upgrade',
+ 'Sec-WebSocket-Key': key,
+ 'Sec-WebSocket-Protocol': 'chat',
+ 'Sec-WebSocket-Version': 13,
+ },
+ read_timeout=1,
+ start=True,
+ )
+
+ return (resp, sock, key)
+
+ def apply_mask(self, data, mask):
+ return bytes(b ^ m for b, m in zip(data, itertools.cycle(mask)))
+
+ def serialize_close(self, code = 1000, reason = ''):
+ return struct.pack('!H', code) + reason.encode('utf-8')
+
+ def frame_read(self, sock, read_timeout=1):
+ def recv_bytes(sock, bytes):
+ data = b''
+ while select.select([sock], [], [], read_timeout)[0]:
+ try:
+ if bytes < 65536:
+ data = sock.recv(bytes)
+ else:
+ data = self.recvall(
+ sock,
+ read_timeout=read_timeout,
+ buff_size=bytes,
+ )
+ break
+ except:
+ break
+
+ return data
+
+ frame = {}
+
+ head1, = struct.unpack('!B', recv_bytes(sock, 1))
+ head2, = struct.unpack('!B', recv_bytes(sock, 1))
+
+ frame['fin'] = bool(head1 & 0b10000000)
+ frame['rsv1'] = bool(head1 & 0b01000000)
+ frame['rsv2'] = bool(head1 & 0b00100000)
+ frame['rsv3'] = bool(head1 & 0b00010000)
+ frame['opcode'] = head1 & 0b00001111
+ frame['mask'] = head2 & 0b10000000
+
+ length = head2 & 0b01111111
+ if length == 126:
+ data = recv_bytes(sock, 2)
+ length, = struct.unpack('!H', data)
+ elif length == 127:
+ data = recv_bytes(sock, 8)
+ length, = struct.unpack('!Q', data)
+
+ if frame['mask']:
+ mask_bits = recv_bytes(sock, 4)
+
+ data = recv_bytes(sock, length)
+ if frame['mask']:
+ data = self.apply_mask(data, mask_bits)
+
+ if frame['opcode'] == self.OP_CLOSE:
+ if length >= 2:
+ code, = struct.unpack('!H', data[:2])
+ reason = data[2:].decode('utf-8')
+ if not (code in self.CLOSE_CODES or 3000 <= code < 5000):
+ self.fail('Invalid status code')
+ frame['code'] = code
+ frame['reason'] = reason
+ elif length == 0:
+ frame['code'] = 1005
+ frame['reason'] = ''
+ else:
+ self.fail('Close frame too short')
+
+ frame['data'] = data
+
+ if frame['mask']:
+ self.fail('Received frame with mask')
+
+ return frame
+
+ def frame_to_send(
+ self,
+ opcode,
+ data,
+ fin=True,
+ length=None,
+ rsv1=False,
+ rsv2=False,
+ rsv3=False,
+ mask=True,
+ ):
+ frame = b''
+
+ if isinstance(data, str):
+ data = data.encode('utf-8')
+
+ head1 = (
+ (0b10000000 if fin else 0)
+ | (0b01000000 if rsv1 else 0)
+ | (0b00100000 if rsv2 else 0)
+ | (0b00010000 if rsv3 else 0)
+ | opcode
+ )
+
+ head2 = 0b10000000 if mask else 0
+
+ data_length = len(data) if length is None else length
+ if data_length < 126:
+ frame += struct.pack('!BB', head1, head2 | data_length)
+ elif data_length < 65536:
+ frame += struct.pack('!BBH', head1, head2 | 126, data_length)
+ else:
+ frame += struct.pack('!BBQ', head1, head2 | 127, data_length)
+
+ if mask:
+ mask_bits = struct.pack('!I', random.getrandbits(32))
+ frame += mask_bits
+
+ if mask:
+ frame += self.apply_mask(data, mask_bits)
+ else:
+ frame += data
+
+ return frame
+
+ def frame_write(self, sock, *args, **kwargs):
+ chopsize = kwargs.pop('chopsize') if 'chopsize' in kwargs else None
+
+ frame = self.frame_to_send(*args, **kwargs)
+
+ if chopsize is None:
+ sock.sendall(frame)
+
+ else:
+ pos = 0
+ frame_len = len(frame)
+ while (pos < frame_len):
+ end = min(pos + chopsize, frame_len)
+ sock.sendall(frame[pos:end])
+ pos = end
+
+ def message(self, sock, type, message, fragmention_size=None, **kwargs):
+ message_len = len(message)
+
+ if fragmention_size is None:
+ fragmention_size = message_len
+
+ if message_len <= fragmention_size:
+ self.frame_write(sock, type, message, **kwargs)
+ return
+
+ pos = 0
+ op_code = type
+ while(pos < message_len):
+ end = min(pos + fragmention_size, message_len)
+ fin = (end == message_len)
+ self.frame_write(sock, op_code, message[pos:end], fin=fin, **kwargs)
+ op_code = self.OP_CONT
+ pos = end
+
+ def message_read(self, sock, read_timeout=1):
+ frame = self.frame_read(sock, read_timeout=read_timeout)
+
+ while(not frame['fin']):
+ temp = self.frame_read(sock, read_timeout=read_timeout)
+ frame['data'] += temp['data']
+ frame['fin'] = temp['fin']
+
+ return frame
diff --git a/test/unit/main.py b/test/unit/main.py
index 1f19343e..6a167a9e 100644
--- a/test/unit/main.py
+++ b/test/unit/main.py
@@ -23,6 +23,7 @@ class TestUnit(unittest.TestCase):
os.path.join(os.path.dirname(__file__), os.pardir, os.pardir)
)
architecture = platform.architecture()[0]
+ system = platform.system()
maxDiff = None
detailed = False