summaryrefslogtreecommitdiffhomepage
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/go/ns_inspect/app.go79
-rw-r--r--test/java/websockets_mirror/app.java57
-rw-r--r--test/test_access_log.py2
-rw-r--r--test/test_configuration.py2
-rw-r--r--test/test_go_application.py2
-rw-r--r--test/test_go_isolation.py135
-rw-r--r--test/test_http_header.py2
-rw-r--r--test/test_java_application.py2
-rw-r--r--test/test_java_websockets.py1469
-rw-r--r--test/test_node_application.py2
-rw-r--r--test/test_node_websockets.py529
-rw-r--r--test/test_perl_application.py2
-rw-r--r--test/test_php_application.py2
-rw-r--r--test/test_php_basic.py2
-rw-r--r--test/test_python_application.py33
-rw-r--r--test/test_python_basic.py2
-rw-r--r--test/test_python_environment.py2
-rw-r--r--test/test_python_procman.py2
-rw-r--r--test/test_routing.py2
-rw-r--r--test/test_routing_tls.py2
-rw-r--r--test/test_ruby_application.py2
-rw-r--r--test/test_settings.py2
-rw-r--r--test/test_static.py376
-rw-r--r--test/test_tls.py2
-rw-r--r--test/unit/applications/lang/go.py32
-rw-r--r--test/unit/applications/lang/java.py26
-rw-r--r--test/unit/applications/lang/node.py12
-rw-r--r--test/unit/applications/tls.py22
-rw-r--r--test/unit/applications/websockets.py46
-rw-r--r--test/unit/feature/isolation.py87
-rw-r--r--test/unit/http.py12
-rw-r--r--test/unit/main.py178
32 files changed, 2655 insertions, 472 deletions
diff --git a/test/go/ns_inspect/app.go b/test/go/ns_inspect/app.go
new file mode 100644
index 00000000..ebecbb00
--- /dev/null
+++ b/test/go/ns_inspect/app.go
@@ -0,0 +1,79 @@
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "nginx/unit"
+ "os"
+ "strconv"
+)
+
+type (
+ NS struct {
+ USER uint64
+ PID uint64
+ IPC uint64
+ CGROUP uint64
+ UTS uint64
+ MNT uint64
+ NET uint64
+ }
+
+ Output struct {
+ PID int
+ UID int
+ GID int
+ NS NS
+ }
+)
+
+func abortonerr(err error) {
+ if err != nil {
+ panic(err)
+ }
+}
+
+// returns: [nstype]:[4026531835]
+func getns(nstype string) uint64 {
+ str, err := os.Readlink(fmt.Sprintf("/proc/self/ns/%s", nstype))
+ if err != nil {
+ return 0
+ }
+
+ str = str[len(nstype)+2:]
+ str = str[:len(str)-1]
+ val, err := strconv.ParseUint(str, 10, 64)
+ abortonerr(err)
+ return val
+}
+
+func handler(w http.ResponseWriter, r *http.Request) {
+ pid := os.Getpid()
+ out := &Output{
+ PID: pid,
+ UID: os.Getuid(),
+ GID: os.Getgid(),
+ NS: NS{
+ PID: getns("pid"),
+ USER: getns("user"),
+ MNT: getns("mnt"),
+ IPC: getns("ipc"),
+ UTS: getns("uts"),
+ NET: getns("net"),
+ CGROUP: getns("cgroup"),
+ },
+ }
+ data, err := json.Marshal(out)
+ if err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ return
+ }
+
+ w.Write(data)
+}
+
+func main() {
+ http.HandleFunc("/", handler)
+ unit.ListenAndServe(":7080", nil)
+}
diff --git a/test/java/websockets_mirror/app.java b/test/java/websockets_mirror/app.java
new file mode 100644
index 00000000..ada60231
--- /dev/null
+++ b/test/java/websockets_mirror/app.java
@@ -0,0 +1,57 @@
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import javax.websocket.OnMessage;
+import javax.websocket.OnOpen;
+import javax.websocket.PongMessage;
+import javax.websocket.Session;
+import javax.websocket.server.ServerEndpoint;
+
+@ServerEndpoint("/")
+public class app {
+
+ @OnOpen
+ public void onOpen(Session session) {
+ session.setMaxTextMessageBufferSize(8388608);
+ }
+
+ @OnMessage
+ public void echoTextMessage(Session session, String msg) {
+ try {
+ if (session.isOpen()) {
+ session.getBasicRemote().sendText(msg, true);
+ }
+ } catch (IOException e) {
+ try {
+ session.close();
+ } catch (IOException e1) {
+ // Ignore
+ }
+ }
+ }
+
+ @OnMessage
+ public void echoBinaryMessage(Session session, ByteBuffer bb) {
+ try {
+ if (session.isOpen()) {
+ session.getBasicRemote().sendBinary(bb, true);
+ }
+ } catch (IOException e) {
+ try {
+ session.close();
+ } catch (IOException e1) {
+ // Ignore
+ }
+ }
+ }
+
+ /**
+ * Process a received pong. This is a NO-OP.
+ *
+ * @param pm Ignored.
+ */
+ @OnMessage
+ public void echoPongMessage(PongMessage pm) {
+ // NO-OP
+ }
+}
diff --git a/test/test_access_log.py b/test/test_access_log.py
index fbcc131f..8dc87524 100644
--- a/test/test_access_log.py
+++ b/test/test_access_log.py
@@ -7,7 +7,7 @@ from unit.applications.lang.python import TestApplicationPython
class TestAccessLog(TestApplicationPython):
- prerequisites = ['python']
+ prerequisites = {'modules': ['python']}
def load(self, script):
super().load(script)
diff --git a/test/test_configuration.py b/test/test_configuration.py
index 6e59c0a7..69647858 100644
--- a/test/test_configuration.py
+++ b/test/test_configuration.py
@@ -3,7 +3,7 @@ from unit.control import TestControl
class TestConfiguration(TestControl):
- prerequisites = ['python']
+ prerequisites = {'modules': ['python']}
def test_json_empty(self):
self.assertIn('error', self.conf(''), 'empty')
diff --git a/test/test_go_application.py b/test/test_go_application.py
index 488bfdd5..42429be7 100644
--- a/test/test_go_application.py
+++ b/test/test_go_application.py
@@ -2,7 +2,7 @@ from unit.applications.lang.go import TestApplicationGo
class TestGoApplication(TestApplicationGo):
- prerequisites = ['go']
+ prerequisites = {'modules': ['go']}
def test_go_application_variables(self):
self.load('variables')
diff --git a/test/test_go_isolation.py b/test/test_go_isolation.py
new file mode 100644
index 00000000..780c2b03
--- /dev/null
+++ b/test/test_go_isolation.py
@@ -0,0 +1,135 @@
+import os
+import json
+import unittest
+from unit.applications.lang.go import TestApplicationGo
+from unit.feature.isolation import TestFeatureIsolation
+
+
+class TestGoIsolation(TestApplicationGo):
+ prerequisites = {'modules': ['go'], 'features': ['isolation']}
+
+ isolation = TestFeatureIsolation()
+
+ @classmethod
+ def setUpClass(cls, complete_check=True):
+ unit = super().setUpClass(complete_check=False)
+
+ TestFeatureIsolation().check(cls.available, unit.testdir)
+
+ return unit if not complete_check else unit.complete()
+
+ def isolation_key(self, key):
+ return key in self.available['features']['isolation'].keys()
+
+ def conf_isolation(self, isolation):
+ self.assertIn(
+ 'success',
+ self.conf(isolation, 'applications/ns_inspect/isolation'),
+ 'configure isolation',
+ )
+
+ def test_isolation_values(self):
+ self.load('ns_inspect')
+
+ obj = self.isolation.parsejson(self.get()['body'])
+
+ for ns, ns_value in self.available['features']['isolation'].items():
+ if ns.upper() in obj['NS']:
+ self.assertEqual(
+ obj['NS'][ns.upper()], ns_value, '%s match' % ns
+ )
+
+ def test_isolation_user(self):
+ if not self.isolation_key('unprivileged_userns_clone'):
+ print('unprivileged clone is not available')
+ raise unittest.SkipTest()
+
+ self.load('ns_inspect')
+ obj = self.isolation.parsejson(self.get()['body'])
+
+ self.assertTrue(obj['UID'] != 0, 'uid not zero')
+ self.assertTrue(obj['GID'] != 0, 'gid not zero')
+ self.assertEqual(obj['UID'], os.getuid(), 'uid match')
+ self.assertEqual(obj['GID'], os.getgid(), 'gid match')
+
+ self.conf_isolation({"namespaces": {"credential": True}})
+
+ obj = self.isolation.parsejson(self.get()['body'])
+
+ # default uid and gid maps current user to nobody
+ self.assertEqual(obj['UID'], 65534, 'uid nobody')
+ self.assertEqual(obj['GID'], 65534, 'gid nobody')
+
+ self.conf_isolation(
+ {
+ "namespaces": {"credential": True},
+ "uidmap": [
+ {"container": 1000, "host": os.geteuid(), "size": 1}
+ ],
+ "gidmap": [
+ {"container": 1000, "host": os.getegid(), "size": 1}
+ ],
+ }
+ )
+
+ obj = self.isolation.parsejson(self.get()['body'])
+
+ # default uid and gid maps current user to root
+ self.assertEqual(obj['UID'], 1000, 'uid root')
+ self.assertEqual(obj['GID'], 1000, 'gid root')
+
+ def test_isolation_mnt(self):
+ if not self.isolation_key('mnt'):
+ print('mnt namespace is not supported')
+ raise unittest.SkipTest()
+
+ if not self.isolation_key('unprivileged_userns_clone'):
+ print('unprivileged clone is not available')
+ raise unittest.SkipTest()
+
+ self.load('ns_inspect')
+ self.conf_isolation(
+ {"namespaces": {"mount": True, "credential": True}}
+ )
+
+ obj = self.isolation.parsejson(self.get()['body'])
+
+ # all but user and mnt
+ allns = list(self.available['features']['isolation'].keys())
+ allns.remove('user')
+ allns.remove('mnt')
+
+ for ns in allns:
+ if ns.upper() in obj['NS']:
+ self.assertEqual(
+ obj['NS'][ns.upper()],
+ self.available['features']['isolation'][ns],
+ '%s match' % ns,
+ )
+
+ self.assertNotEqual(
+ obj['NS']['MNT'], self.isolation.getns('mnt'), 'mnt set'
+ )
+ self.assertNotEqual(
+ obj['NS']['USER'], self.isolation.getns('user'), 'user set'
+ )
+
+ def test_isolation_pid(self):
+ if not self.isolation_key('pid'):
+ print('pid namespace is not supported')
+ raise unittest.SkipTest()
+
+ if not self.isolation_key('unprivileged_userns_clone'):
+ print('unprivileged clone is not available')
+ raise unittest.SkipTest()
+
+ self.load('ns_inspect')
+ self.conf_isolation({"namespaces": {"pid": True, "credential": True}})
+
+ obj = self.isolation.parsejson(self.get()['body'])
+
+ self.assertEqual(obj['PID'], 1, 'pid of container is 1')
+
+
+if __name__ == '__main__':
+ TestGoIsolation.main()
diff --git a/test/test_http_header.py b/test/test_http_header.py
index 603f6f0f..b773bd68 100644
--- a/test/test_http_header.py
+++ b/test/test_http_header.py
@@ -3,7 +3,7 @@ from unit.applications.lang.python import TestApplicationPython
class TestHTTPHeader(TestApplicationPython):
- prerequisites = ['python']
+ prerequisites = {'modules': ['python']}
def test_http_header_value_leading_sp(self):
self.load('custom_header')
diff --git a/test/test_java_application.py b/test/test_java_application.py
index 526be565..2e937718 100644
--- a/test/test_java_application.py
+++ b/test/test_java_application.py
@@ -4,7 +4,7 @@ from unit.applications.lang.java import TestApplicationJava
class TestJavaApplication(TestApplicationJava):
- prerequisites = ['java']
+ prerequisites = {'modules': ['java']}
def test_java_conf_error(self):
self.skip_alerts.extend(
diff --git a/test/test_java_websockets.py b/test/test_java_websockets.py
new file mode 100644
index 00000000..3f2c0a8a
--- /dev/null
+++ b/test/test_java_websockets.py
@@ -0,0 +1,1469 @@
+import time
+import struct
+import unittest
+from unit.applications.lang.java import TestApplicationJava
+from unit.applications.websockets import TestApplicationWebsocket
+
+
+class TestJavaWebsockets(TestApplicationJava):
+ prerequisites = {'modules': ['java']}
+
+ ws = TestApplicationWebsocket(True)
+
+ def setUp(self):
+ super().setUp()
+
+ self.assertIn(
+ 'success',
+ self.conf(
+ {'http': {'websocket': {'keepalive_interval': 0}}}, 'settings'
+ ),
+ 'clear keepalive_interval',
+ )
+
+ self.skip_alerts.extend(
+ [r'last message send failed', r'socket close\(\d+\) failed']
+ )
+
+ 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_java_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_java_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_java_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_java_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_java_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_java_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_java_websockets_handshake_upgrade_absent(
+ self
+ ): # FAIL https://tools.ietf.org/html/rfc6455#section-4.2.1
+ self.load('websockets_mirror')
+
+ self.get()
+
+ 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_java_websockets_handshake_case_insensitive(self):
+ self.load('websockets_mirror')
+
+ self.get()
+
+ 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_java_websockets_handshake_connection_absent(self): # FAIL
+ self.load('websockets_mirror')
+
+ self.get()
+
+ 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_java_websockets_handshake_version_absent(self):
+ self.load('websockets_mirror')
+
+ self.get()
+
+ 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_java_websockets_handshake_key_invalid(self):
+ self.load('websockets_mirror')
+
+ self.get()
+
+ 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_java_websockets_handshake_method_invalid(self):
+ self.load('websockets_mirror')
+
+ self.get()
+
+ 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_java_websockets_handshake_http_10(self):
+ self.load('websockets_mirror')
+
+ self.get()
+
+ 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_java_websockets_handshake_uri_invalid(self):
+ self.load('websockets_mirror')
+
+ self.get()
+
+ 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_java_websockets_protocol_absent(self):
+ self.load('websockets_mirror')
+
+ self.get()
+
+ 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_java_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.message_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_java_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.message_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_java_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_java_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_java_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_java_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_java_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_java_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_java_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, read_timeout=3)
+ 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, read_timeout=3)
+ 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_java_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_java_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, code=code)
+
+ 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_java_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_java_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)
+ 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_java_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.message_read(sock)
+ self.check_frame(frame, True, self.ws.OP_TEXT, payload)
+
+ self.close_connection(sock)
+
+ # settings
+
+ def test_java_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_java_websockets_read_timeout(self):
+ self.load('websockets_mirror')
+
+ self.assertIn(
+ 'success',
+ self.conf(
+ {'http': {'websocket': {'read_timeout': 5}}}, '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_java_websockets_keepalive_interval(self):
+ self.load('websockets_mirror')
+
+ self.assertIn(
+ 'success',
+ self.conf(
+ {'http': {'websocket': {'keepalive_interval': 5}}}, '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__':
+ TestJavaWebsockets.main()
diff --git a/test/test_node_application.py b/test/test_node_application.py
index 0354c978..a5b4a108 100644
--- a/test/test_node_application.py
+++ b/test/test_node_application.py
@@ -3,7 +3,7 @@ from unit.applications.lang.node import TestApplicationNode
class TestNodeApplication(TestApplicationNode):
- prerequisites = ['node']
+ prerequisites = {'modules': ['node']}
def test_node_application_basic(self):
self.load('basic')
diff --git a/test/test_node_websockets.py b/test/test_node_websockets.py
index 6652d8c5..b24bee75 100644
--- a/test/test_node_websockets.py
+++ b/test/test_node_websockets.py
@@ -4,8 +4,9 @@ import unittest
from unit.applications.lang.node import TestApplicationNode
from unit.applications.websockets import TestApplicationWebsocket
+
class TestNodeWebsockets(TestApplicationNode):
- prerequisites = ['node']
+ prerequisites = {'modules': ['node']}
ws = TestApplicationWebsocket()
@@ -21,10 +22,7 @@ class TestNodeWebsockets(TestApplicationNode):
)
self.skip_alerts.extend(
- [
- r'last message send failed',
- r'socket close\(\d+\) failed',
- ]
+ [r'last message send failed', r'socket close\(\d+\) failed']
)
def close_connection(self, sock):
@@ -34,7 +32,7 @@ class TestNodeWebsockets(TestApplicationNode):
self.check_close(sock)
- def check_close(self, sock, code = 1000, no_close = False):
+ def check_close(self, sock, code=1000, no_close=False):
frame = self.ws.frame_read(sock)
self.assertEqual(frame['fin'], True, 'close fin')
@@ -61,9 +59,7 @@ class TestNodeWebsockets(TestApplicationNode):
sock.close()
self.assertEqual(resp['status'], 101, 'status')
- self.assertEqual(
- resp['headers']['Upgrade'], 'websocket', 'upgrade'
- )
+ self.assertEqual(resp['headers']['Upgrade'], 'websocket', 'upgrade')
self.assertEqual(
resp['headers']['Connection'], 'Upgrade', 'connection'
)
@@ -81,16 +77,12 @@ class TestNodeWebsockets(TestApplicationNode):
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.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'
- )
+ self.assertEqual(message, frame['data'].decode('utf-8'), 'mirror 2')
sock.close()
@@ -160,29 +152,6 @@ class TestNodeWebsockets(TestApplicationNode):
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')
@@ -202,65 +171,6 @@ class TestNodeWebsockets(TestApplicationNode):
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')
@@ -276,28 +186,29 @@ class TestNodeWebsockets(TestApplicationNode):
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'
- )
+ 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
+ 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)
+ 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')
@@ -305,29 +216,35 @@ class TestNodeWebsockets(TestApplicationNode):
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)
+ 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
+ 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)
+ 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')
@@ -335,13 +252,16 @@ class TestNodeWebsockets(TestApplicationNode):
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)
+ 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')
@@ -349,41 +269,52 @@ class TestNodeWebsockets(TestApplicationNode):
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)
+ 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)
+ 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
+ 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)
+ 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')
@@ -391,14 +322,18 @@ class TestNodeWebsockets(TestApplicationNode):
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)
+ 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')
@@ -406,14 +341,18 @@ class TestNodeWebsockets(TestApplicationNode):
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)
+ 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')
@@ -421,18 +360,19 @@ class TestNodeWebsockets(TestApplicationNode):
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)
+ 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']['Upgrade'], 'websocket', 'upgrade')
self.assertEqual(
resp['headers']['Connection'], 'Upgrade', 'connection'
)
@@ -441,12 +381,11 @@ class TestNodeWebsockets(TestApplicationNode):
)
# 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.
- @unittest.skip('not yet')
def test_node_websockets_1_1_1__1_1_8(self):
self.load('websockets/mirror')
@@ -473,7 +412,6 @@ class TestNodeWebsockets(TestApplicationNode):
self.close_connection(sock)
- @unittest.skip('not yet')
def test_node_websockets_1_2_1__1_2_8(self):
self.load('websockets/mirror')
@@ -606,7 +544,7 @@ class TestNodeWebsockets(TestApplicationNode):
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.check_close(sock, 1002, no_close=True)
self.assertEqual(self.recvall(sock, read_timeout=1), b'', 'empty 3_2')
sock.close()
@@ -621,14 +559,10 @@ class TestNodeWebsockets(TestApplicationNode):
self.check_frame(frame, True, self.ws.OP_TEXT, payload)
self.ws.frame_write(
- sock,
- self.ws.OP_TEXT,
- payload,
- rsv1=True,
- rsv2=True,
+ sock, self.ws.OP_TEXT, payload, rsv1=True, rsv2=True
)
- self.check_close(sock, 1002, no_close = True)
+ self.check_close(sock, 1002, no_close=True)
self.assertEqual(self.recvall(sock, read_timeout=1), b'', 'empty 3_3')
sock.close()
@@ -639,18 +573,14 @@ class TestNodeWebsockets(TestApplicationNode):
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
+ 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.check_close(sock, 1002, no_close=True)
self.assertEqual(self.recvall(sock, read_timeout=1), b'', 'empty 3_4')
sock.close()
@@ -674,11 +604,7 @@ class TestNodeWebsockets(TestApplicationNode):
_, sock, _ = self.ws.upgrade()
self.ws.frame_write(
- sock,
- self.ws.OP_PING,
- payload,
- rsv2=True,
- rsv3=True,
+ sock, self.ws.OP_PING, payload, rsv2=True, rsv3=True
)
self.check_close(sock, 1002)
@@ -688,12 +614,7 @@ class TestNodeWebsockets(TestApplicationNode):
_, sock, _ = self.ws.upgrade()
self.ws.frame_write(
- sock,
- self.ws.OP_CLOSE,
- payload,
- rsv1=True,
- rsv2=True,
- rsv3=True,
+ sock, self.ws.OP_CLOSE, payload, rsv1=True, rsv2=True, rsv3=True
)
self.check_close(sock, 1002)
@@ -815,7 +736,6 @@ class TestNodeWebsockets(TestApplicationNode):
self.check_close(sock, 1002)
- @unittest.skip('not yet')
def test_node_websockets_5_1__5_20(self):
self.load('websockets/mirror')
@@ -857,18 +777,10 @@ class TestNodeWebsockets(TestApplicationNode):
# 5_5
self.ws.frame_write(
- sock,
- self.ws.OP_TEXT,
- 'fragment1',
- fin=False,
- chopsize=1,
+ sock, self.ws.OP_TEXT, 'fragment1', fin=False, chopsize=1
)
self.ws.frame_write(
- sock,
- self.ws.OP_CONT,
- 'fragment2',
- fin=True,
- chopsize=1,
+ sock, self.ws.OP_CONT, 'fragment2', fin=True, chopsize=1
)
frame = self.ws.frame_read(sock)
@@ -910,19 +822,11 @@ class TestNodeWebsockets(TestApplicationNode):
ping_payload = 'ping payload'
self.ws.frame_write(
- sock,
- self.ws.OP_TEXT,
- 'fragment1',
- fin=False,
- chopsize=1,
+ 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,
+ sock, self.ws.OP_CONT, 'fragment2', fin=True, chopsize=1
)
frame = self.ws.frame_read(sock)
@@ -934,10 +838,7 @@ class TestNodeWebsockets(TestApplicationNode):
# 5_9
self.ws.frame_write(
- sock,
- self.ws.OP_CONT,
- 'non-continuation payload',
- fin=True,
+ 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)
@@ -947,10 +848,7 @@ class TestNodeWebsockets(TestApplicationNode):
_, sock, _ = self.ws.upgrade()
self.ws.frame_write(
- sock,
- self.ws.OP_CONT,
- 'non-continuation payload',
- fin=True,
+ 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)
@@ -967,11 +865,7 @@ class TestNodeWebsockets(TestApplicationNode):
chopsize=1,
)
self.ws.frame_write(
- sock,
- self.ws.OP_TEXT,
- 'Hello, world!',
- fin=True,
- chopsize=1,
+ sock, self.ws.OP_TEXT, 'Hello, world!', fin=True, chopsize=1
)
self.check_close(sock, 1002)
@@ -980,10 +874,7 @@ class TestNodeWebsockets(TestApplicationNode):
_, sock, _ = self.ws.upgrade()
self.ws.frame_write(
- sock,
- self.ws.OP_CONT,
- 'non-continuation payload',
- fin=False,
+ 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)
@@ -993,10 +884,7 @@ class TestNodeWebsockets(TestApplicationNode):
_, sock, _ = self.ws.upgrade()
self.ws.frame_write(
- sock,
- self.ws.OP_CONT,
- 'non-continuation payload',
- fin=False,
+ 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)
@@ -1013,11 +901,7 @@ class TestNodeWebsockets(TestApplicationNode):
chopsize=1,
)
self.ws.frame_write(
- sock,
- self.ws.OP_TEXT,
- 'Hello, world!',
- fin=True,
- chopsize=1,
+ sock, self.ws.OP_TEXT, 'Hello, world!', fin=True, chopsize=1
)
self.check_close(sock, 1002)
@@ -1183,8 +1067,8 @@ class TestNodeWebsockets(TestApplicationNode):
self.close_connection(sock)
- # Unit does not support UTF-8 validation
-
+# Unit does not support UTF-8 validation
+#
# # 6_3_1 FAIL
#
# payload_1 = '\xce\xba\xe1\xbd\xb9\xcf\x83\xce\xbc\xce\xb5'
@@ -1235,7 +1119,7 @@ class TestNodeWebsockets(TestApplicationNode):
_, 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.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')
@@ -1247,7 +1131,7 @@ class TestNodeWebsockets(TestApplicationNode):
_, 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.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')
@@ -1260,7 +1144,7 @@ class TestNodeWebsockets(TestApplicationNode):
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.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')
@@ -1271,7 +1155,7 @@ class TestNodeWebsockets(TestApplicationNode):
_, 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, '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())
@@ -1307,7 +1191,7 @@ class TestNodeWebsockets(TestApplicationNode):
_, sock, _ = self.ws.upgrade()
- payload = self.ws.serialize_close(reason = 'Hello World!')
+ payload = self.ws.serialize_close(reason='Hello World!')
self.ws.frame_write(sock, self.ws.OP_CLOSE, payload)
self.check_close(sock)
@@ -1316,7 +1200,7 @@ class TestNodeWebsockets(TestApplicationNode):
_, sock, _ = self.ws.upgrade()
- payload = self.ws.serialize_close(reason = '*' * 123)
+ payload = self.ws.serialize_close(reason='*' * 123)
self.ws.frame_write(sock, self.ws.OP_CLOSE, payload)
self.check_close(sock)
@@ -1325,13 +1209,13 @@ class TestNodeWebsockets(TestApplicationNode):
_, sock, _ = self.ws.upgrade()
- payload = self.ws.serialize_close(reason = '*' * 124)
+ 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
-
+# # 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' \
@@ -1364,7 +1248,7 @@ class TestNodeWebsockets(TestApplicationNode):
for code in valid_codes:
_, sock, _ = self.ws.upgrade()
- payload = self.ws.serialize_close(code = code)
+ payload = self.ws.serialize_close(code=code)
self.ws.frame_write(sock, self.ws.OP_CLOSE, payload)
self.check_close(sock)
@@ -1372,7 +1256,7 @@ class TestNodeWebsockets(TestApplicationNode):
for code in invalid_codes:
_, sock, _ = self.ws.upgrade()
- payload = self.ws.serialize_close(code = code)
+ payload = self.ws.serialize_close(code=code)
self.ws.frame_write(sock, self.ws.OP_CLOSE, payload)
self.check_close(sock, 1002)
@@ -1384,7 +1268,7 @@ class TestNodeWebsockets(TestApplicationNode):
_, sock, _ = self.ws.upgrade()
- payload = self.ws.serialize_close(code = 5000)
+ payload = self.ws.serialize_close(code=5000)
self.ws.frame_write(sock, self.ws.OP_CLOSE, payload)
self.check_close(sock, 1002)
@@ -1437,62 +1321,62 @@ class TestNodeWebsockets(TestApplicationNode):
def check_message(opcode, f_size):
if opcode == self.ws.OP_TEXT:
- payload = '*' * 4 * 2**20
+ payload = '*' * 4 * 2 ** 20
else:
- payload = b'*' * 4 * 2**20
+ 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_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
+ 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
+ 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)
@@ -1536,7 +1420,7 @@ class TestNodeWebsockets(TestApplicationNode):
payload = '*' * 95
self.ws.frame_write(sock, opcode, payload) # frame length is 101
- self.check_close(sock, 1009) # 1009 - CLOSE_TOO_LARGE
+ self.check_close(sock, 1009) # 1009 - CLOSE_TOO_LARGE
def test_node_websockets_read_timeout(self):
self.load('websockets/mirror')
@@ -1556,7 +1440,7 @@ class TestNodeWebsockets(TestApplicationNode):
time.sleep(2)
- self.check_close(sock, 1001) # 1001 - CLOSE_GOING_AWAY
+ self.check_close(sock, 1001) # 1001 - CLOSE_GOING_AWAY
def test_node_websockets_keepalive_interval(self):
self.load('websockets/mirror')
@@ -1581,5 +1465,6 @@ class TestNodeWebsockets(TestApplicationNode):
sock.close()
+
if __name__ == '__main__':
TestNodeWebsockets.main()
diff --git a/test/test_perl_application.py b/test/test_perl_application.py
index bc26b000..bf3c65d5 100644
--- a/test/test_perl_application.py
+++ b/test/test_perl_application.py
@@ -3,7 +3,7 @@ from unit.applications.lang.perl import TestApplicationPerl
class TestPerlApplication(TestApplicationPerl):
- prerequisites = ['perl']
+ prerequisites = {'modules': ['perl']}
def test_perl_application(self):
self.load('variables')
diff --git a/test/test_php_application.py b/test/test_php_application.py
index ee2048b5..d614885c 100644
--- a/test/test_php_application.py
+++ b/test/test_php_application.py
@@ -3,7 +3,7 @@ import unittest
from unit.applications.lang.php import TestApplicationPHP
class TestPHPApplication(TestApplicationPHP):
- prerequisites = ['php']
+ prerequisites = {'modules': ['php']}
def before_disable_functions(self):
body = self.get()['body']
diff --git a/test/test_php_basic.py b/test/test_php_basic.py
index 0c84f206..7ecff1b2 100644
--- a/test/test_php_basic.py
+++ b/test/test_php_basic.py
@@ -2,7 +2,7 @@ from unit.control import TestControl
class TestPHPBasic(TestControl):
- prerequisites = ['php']
+ prerequisites = {'modules': ['php']}
conf_app = {
"app": {
diff --git a/test/test_python_application.py b/test/test_python_application.py
index 3484b25e..5b6e2089 100644
--- a/test/test_python_application.py
+++ b/test/test_python_application.py
@@ -4,7 +4,7 @@ from unit.applications.lang.python import TestApplicationPython
class TestPythonApplication(TestApplicationPython):
- prerequisites = ['python']
+ prerequisites = {'modules': ['python']}
def test_python_application_variables(self):
self.load('variables')
@@ -71,6 +71,37 @@ class TestPythonApplication(TestApplicationPython):
'Query-String header',
)
+ def test_python_application_query_string_space(self):
+ self.load('query_string')
+
+ resp = self.get(url='/ ?var1=val1&var2=val2')
+ self.assertEqual(
+ resp['headers']['Query-String'],
+ 'var1=val1&var2=val2',
+ 'Query-String space',
+ )
+
+ resp = self.get(url='/ %20?var1=val1&var2=val2')
+ self.assertEqual(
+ resp['headers']['Query-String'],
+ 'var1=val1&var2=val2',
+ 'Query-String space 2',
+ )
+
+ resp = self.get(url='/ %20 ?var1=val1&var2=val2')
+ self.assertEqual(
+ resp['headers']['Query-String'],
+ 'var1=val1&var2=val2',
+ 'Query-String space 3',
+ )
+
+ resp = self.get(url='/blah %20 blah? var1= val1 & var2=val2')
+ self.assertEqual(
+ resp['headers']['Query-String'],
+ ' var1= val1 & var2=val2',
+ 'Query-String space 4',
+ )
+
def test_python_application_query_string_empty(self):
self.load('query_string')
diff --git a/test/test_python_basic.py b/test/test_python_basic.py
index e63158e5..67a5f548 100644
--- a/test/test_python_basic.py
+++ b/test/test_python_basic.py
@@ -2,7 +2,7 @@ from unit.control import TestControl
class TestPythonBasic(TestControl):
- prerequisites = ['python']
+ prerequisites = {'modules': ['python']}
conf_app = {
"app": {
diff --git a/test/test_python_environment.py b/test/test_python_environment.py
index 744f4947..fe0baa13 100644
--- a/test/test_python_environment.py
+++ b/test/test_python_environment.py
@@ -2,7 +2,7 @@ from unit.applications.lang.python import TestApplicationPython
class TestPythonEnvironment(TestApplicationPython):
- prerequisites = ['python']
+ prerequisites = {'modules': ['python']}
def test_python_environment_name_null(self):
self.load('environment')
diff --git a/test/test_python_procman.py b/test/test_python_procman.py
index b0c70e53..52d8cacb 100644
--- a/test/test_python_procman.py
+++ b/test/test_python_procman.py
@@ -6,7 +6,7 @@ from unit.applications.lang.python import TestApplicationPython
class TestPythonProcman(TestApplicationPython):
- prerequisites = ['python']
+ prerequisites = {'modules': ['python']}
def pids_for_process(self):
time.sleep(0.2)
diff --git a/test/test_routing.py b/test/test_routing.py
index 6073877d..20e3a1c4 100644
--- a/test/test_routing.py
+++ b/test/test_routing.py
@@ -3,7 +3,7 @@ from unit.applications.proto import TestApplicationProto
class TestRouting(TestApplicationProto):
- prerequisites = ['python']
+ prerequisites = {'modules': ['python']}
def setUp(self):
super().setUp()
diff --git a/test/test_routing_tls.py b/test/test_routing_tls.py
index 433a303e..3df2bc82 100644
--- a/test/test_routing_tls.py
+++ b/test/test_routing_tls.py
@@ -2,7 +2,7 @@ from unit.applications.tls import TestApplicationTLS
class TestRoutingTLS(TestApplicationTLS):
- prerequisites = ['python', 'openssl']
+ prerequisites = {'modules': ['python', 'openssl']}
def test_routes_match_scheme(self):
self.certificate()
diff --git a/test/test_ruby_application.py b/test/test_ruby_application.py
index 67db8a8e..6f82ae81 100644
--- a/test/test_ruby_application.py
+++ b/test/test_ruby_application.py
@@ -3,7 +3,7 @@ from unit.applications.lang.ruby import TestApplicationRuby
class TestRubyApplication(TestApplicationRuby):
- prerequisites = ['ruby']
+ prerequisites = {'modules': ['ruby']}
def test_ruby_application(self):
self.load('variables')
diff --git a/test/test_settings.py b/test/test_settings.py
index 98063440..6b849558 100644
--- a/test/test_settings.py
+++ b/test/test_settings.py
@@ -5,7 +5,7 @@ from unit.applications.lang.python import TestApplicationPython
class TestSettings(TestApplicationPython):
- prerequisites = ['python']
+ prerequisites = {'modules': ['python']}
def test_settings_header_read_timeout(self):
self.load('empty')
diff --git a/test/test_static.py b/test/test_static.py
new file mode 100644
index 00000000..573669a3
--- /dev/null
+++ b/test/test_static.py
@@ -0,0 +1,376 @@
+import os
+import unittest
+from unit.applications.proto import TestApplicationProto
+
+
+class TestStatic(TestApplicationProto):
+ prerequisites = {}
+
+ def setUp(self):
+ super().setUp()
+
+ os.makedirs(self.testdir + '/assets/dir')
+ with open(self.testdir + '/assets/index.html', 'w') as index, \
+ open(self.testdir + '/assets/README', 'w') as readme, \
+ open(self.testdir + '/assets/log.log', 'w') as log, \
+ open(self.testdir + '/assets/dir/file', 'w') as file:
+ index.write('0123456789')
+ readme.write('readme')
+ log.write('[debug]')
+ file.write('blah')
+
+ self._load_conf(
+ {
+ "listeners": {"*:7080": {"pass": "routes"}},
+ "routes": [{"action": {"share": self.testdir + "/assets"}}],
+ "settings": {
+ "http": {
+ "static": {
+ "mime_types": {"text/plain": [".log", "README"]}
+ }
+ }
+ },
+ }
+ )
+
+ def test_static_index(self):
+ self.assertEqual(
+ self.get(url='/index.html')['body'], '0123456789', 'index'
+ )
+ self.assertEqual(self.get(url='/')['body'], '0123456789', 'index 2')
+ self.assertEqual(
+ self.get(url='/dir/')['status'], 404, 'index not found'
+ )
+
+ resp = self.get(url='/index.html/')
+ self.assertEqual(resp['status'], 404, 'index not found 2 status')
+ self.assertEqual(
+ resp['headers']['Content-Type'],
+ 'text/html',
+ 'index not found 2 Content-Type',
+ )
+
+ def test_static_large_file(self):
+ file_size = 32 * 1024 * 1024
+ with open(self.testdir + '/assets/large', 'wb') as f:
+ f.seek(file_size - 1)
+ f.write(b'\0')
+
+ self.assertEqual(
+ len(
+ self.get(url='/large', read_buffer_size=1024 * 1024)['body']
+ ),
+ file_size,
+ 'large file',
+ )
+
+ def test_static_etag(self):
+ etag = self.get(url='/')['headers']['ETag']
+ etag_2 = self.get(url='/README')['headers']['ETag']
+
+ self.assertNotEqual(etag, etag_2, 'different ETag')
+ self.assertEqual(
+ etag, self.get(url='/')['headers']['ETag'], 'same ETag'
+ )
+
+ with open(self.testdir + '/assets/index.html', 'w') as f:
+ f.write('blah')
+
+ self.assertNotEqual(
+ etag, self.get(url='/')['headers']['ETag'], 'new ETag'
+ )
+
+ def test_static_redirect(self):
+ resp = self.get(url='/dir')
+ self.assertEqual(resp['status'], 301, 'redirect status')
+ self.assertEqual(
+ resp['headers']['Location'], '/dir/', 'redirect Location'
+ )
+ self.assertNotIn(
+ 'Content-Type', resp['headers'], 'redirect Content-Type'
+ )
+
+ def test_static_space_in_name(self):
+ os.rename(
+ self.testdir + '/assets/dir/file',
+ self.testdir + '/assets/dir/fi le',
+ )
+ self.waitforfiles(self.testdir + '/assets/dir/fi le')
+ self.assertEqual(
+ self.get(url='/dir/fi le')['body'], 'blah', 'file name'
+ )
+
+ os.rename(self.testdir + '/assets/dir', self.testdir + '/assets/di r')
+ self.waitforfiles(self.testdir + '/assets/di r/fi le')
+ self.assertEqual(
+ self.get(url='/di r/fi le')['body'], 'blah', 'dir name'
+ )
+
+ os.rename(
+ self.testdir + '/assets/di r', self.testdir + '/assets/ di r '
+ )
+ self.waitforfiles(self.testdir + '/assets/ di r /fi le')
+ self.assertEqual(
+ self.get(url='/ di r /fi le')['body'], 'blah', 'dir name enclosing'
+ )
+
+ self.assertEqual(
+ self.get(url='/%20di%20r%20/fi le')['body'], 'blah', 'dir encoded'
+ )
+ self.assertEqual(
+ self.get(url='/ di r %2Ffi le')['body'], 'blah', 'slash encoded'
+ )
+ self.assertEqual(
+ self.get(url='/ di r /fi%20le')['body'], 'blah', 'file encoded'
+ )
+ self.assertEqual(
+ self.get(url='/%20di%20r%20%2Ffi%20le')['body'], 'blah', 'encoded'
+ )
+ self.assertEqual(
+ self.get(url='/%20%64%69%20%72%20%2F%66%69%20%6C%65')['body'],
+ 'blah',
+ 'encoded 2',
+ )
+
+ os.rename(
+ self.testdir + '/assets/ di r /fi le',
+ self.testdir + '/assets/ di r / fi le ',
+ )
+ self.waitforfiles(self.testdir + '/assets/ di r / fi le ')
+ self.assertEqual(
+ self.get(url='/%20di%20r%20/%20fi%20le%20')['body'],
+ 'blah',
+ 'file name enclosing',
+ )
+
+ try:
+ print('файл')
+ utf8 = True
+
+ except:
+ utf8 = False
+
+ if utf8:
+ os.rename(
+ self.testdir + '/assets/ di r / fi le ',
+ self.testdir + '/assets/ di r /фа йл',
+ )
+ self.waitforfiles(self.testdir + '/assets/ di r /фа йл')
+ self.assertEqual(
+ self.get(url='/ di r /фа йл')['body'], 'blah', 'file name 2'
+ )
+
+ os.rename(
+ self.testdir + '/assets/ di r ',
+ self.testdir + '/assets/ди ректория',
+ )
+ self.waitforfiles(self.testdir + '/assets/ди ректория/фа йл')
+ self.assertEqual(
+ self.get(url='/ди ректория/фа йл')['body'], 'blah', 'dir name 2'
+ )
+
+ def test_static_head(self):
+ resp = self.head(url='/')
+ self.assertEqual(resp['status'], 200, 'status')
+ self.assertEqual(resp['body'], '', 'empty body')
+
+ def test_static_two_clients(self):
+ _, sock = self.get(url='/', start=True, no_recv=True)
+ _, sock2 = self.get(url='/', start=True, no_recv=True)
+
+ self.assertEqual(sock.recv(1), b'H', 'client 1')
+ self.assertEqual(sock2.recv(1), b'H', 'client 2')
+ self.assertEqual(sock.recv(1), b'T', 'client 1 again')
+ self.assertEqual(sock2.recv(1), b'T', 'client 2 again')
+
+ sock.close()
+ sock2.close()
+
+ def test_static_mime_types(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ {
+ "text/x-code/x-blah/x-blah": "readme",
+ "text/plain": [".html", ".log", "file"],
+ },
+ 'settings/http/static/mime_types',
+ ),
+ 'configure mime_types',
+ )
+
+ self.assertEqual(
+ self.get(url='/README')['headers']['Content-Type'],
+ 'text/x-code/x-blah/x-blah',
+ 'mime_types string case insensitive',
+ )
+ self.assertEqual(
+ self.get(url='/index.html')['headers']['Content-Type'],
+ 'text/plain',
+ 'mime_types html',
+ )
+ self.assertEqual(
+ self.get(url='/')['headers']['Content-Type'],
+ 'text/plain',
+ 'mime_types index default',
+ )
+ self.assertEqual(
+ self.get(url='/dir/file')['headers']['Content-Type'],
+ 'text/plain',
+ 'mime_types file in dir',
+ )
+
+ def test_static_mime_types_partial_match(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ {
+ "text/x-blah": ["ile", "fil", "f", "e", ".file"],
+ },
+ 'settings/http/static/mime_types',
+ ),
+ 'configure mime_types',
+ )
+ self.assertNotIn(
+ 'Content-Type', self.get(url='/dir/file'), 'partial match'
+ )
+
+ def test_static_mime_types_reconfigure(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ {
+ "text/x-code": "readme",
+ "text/plain": [".html", ".log", "file"],
+ },
+ 'settings/http/static/mime_types',
+ ),
+ 'configure mime_types',
+ )
+
+ self.assertEqual(
+ self.conf_get('settings/http/static/mime_types'),
+ {'text/x-code': 'readme', 'text/plain': ['.html', '.log', 'file']},
+ 'mime_types get',
+ )
+ self.assertEqual(
+ self.conf_get('settings/http/static/mime_types/text%2Fx-code'),
+ 'readme',
+ 'mime_types get string',
+ )
+ self.assertEqual(
+ self.conf_get('settings/http/static/mime_types/text%2Fplain'),
+ ['.html', '.log', 'file'],
+ 'mime_types get array',
+ )
+ self.assertEqual(
+ self.conf_get('settings/http/static/mime_types/text%2Fplain/1'),
+ '.log',
+ 'mime_types get array element',
+ )
+
+ self.assertIn(
+ 'success',
+ self.conf_delete('settings/http/static/mime_types/text%2Fplain/2'),
+ 'mime_types remove array element',
+ )
+ self.assertNotIn(
+ 'Content-Type',
+ self.get(url='/dir/file')['headers'],
+ 'mime_types removed',
+ )
+
+ self.assertIn(
+ 'success',
+ self.conf_post(
+ '"file"', 'settings/http/static/mime_types/text%2Fplain'
+ ),
+ 'mime_types add array element',
+ )
+ self.assertEqual(
+ self.get(url='/dir/file')['headers']['Content-Type'],
+ 'text/plain',
+ 'mime_types reverted',
+ )
+
+ self.assertIn(
+ 'success',
+ self.conf(
+ '"file"', 'settings/http/static/mime_types/text%2Fplain'
+ ),
+ 'configure mime_types update',
+ )
+ self.assertEqual(
+ self.get(url='/dir/file')['headers']['Content-Type'],
+ 'text/plain',
+ 'mime_types updated',
+ )
+ self.assertNotIn(
+ 'Content-Type',
+ self.get(url='/log.log')['headers'],
+ 'mime_types updated 2',
+ )
+
+ self.assertIn(
+ 'success',
+ self.conf(
+ '".log"', 'settings/http/static/mime_types/text%2Fblahblahblah'
+ ),
+ 'configure mime_types create',
+ )
+ self.assertEqual(
+ self.get(url='/log.log')['headers']['Content-Type'],
+ 'text/blahblahblah',
+ 'mime_types create',
+ )
+
+ def test_static_mime_types_correct(self):
+ self.assertIn(
+ 'error',
+ self.conf(
+ {"text/x-code": "readme", "text/plain": "readme"},
+ 'settings/http/static/mime_types',
+ ),
+ 'mime_types same extensions',
+ )
+ self.assertIn(
+ 'error',
+ self.conf(
+ {"text/x-code": [".h", ".c"], "text/plain": ".c"},
+ 'settings/http/static/mime_types',
+ ),
+ 'mime_types same extensions array',
+ )
+ self.assertIn(
+ 'error',
+ self.conf(
+ {
+ "text/x-code": [".h", ".c", "readme"],
+ "text/plain": "README",
+ },
+ 'settings/http/static/mime_types',
+ ),
+ 'mime_types same extensions case insensitive',
+ )
+
+ @unittest.skip('not yet')
+ def test_static_mime_types_invalid(self):
+ self.assertIn(
+ 'error',
+ self.http(
+ b"""PUT /config/settings/http/static/mime_types/%0%00% HTTP/1.1\r
+Host: localhost\r
+Connection: close\r
+Content-Length: 6\r
+\r
+\"blah\"""",
+ raw_resp=True,
+ raw=True,
+ sock_type='unix',
+ addr=self.testdir + '/control.unit.sock',
+ ),
+ 'mime_types invalid',
+ )
+
+if __name__ == '__main__':
+ TestStatic.main()
diff --git a/test/test_tls.py b/test/test_tls.py
index 076a2c38..3514bbcb 100644
--- a/test/test_tls.py
+++ b/test/test_tls.py
@@ -6,7 +6,7 @@ from unit.applications.tls import TestApplicationTLS
class TestTLS(TestApplicationTLS):
- prerequisites = ['python', 'openssl']
+ prerequisites = {'modules': ['python', 'openssl']}
def findall(self, pattern):
with open(self.testdir + '/unit.log', 'r', errors='ignore') as f:
diff --git a/test/unit/applications/lang/go.py b/test/unit/applications/lang/go.py
index e4ab8ffa..15ac1cd9 100644
--- a/test/unit/applications/lang/go.py
+++ b/test/unit/applications/lang/go.py
@@ -4,12 +4,22 @@ from unit.applications.proto import TestApplicationProto
class TestApplicationGo(TestApplicationProto):
- def load(self, script, name='app'):
+ @classmethod
+ def setUpClass(cls, complete_check=True):
+ unit = super().setUpClass(complete_check=False)
- if not os.path.isdir(self.testdir + '/go'):
- os.mkdir(self.testdir + '/go')
+ # check go module
+
+ go_app = TestApplicationGo()
+ go_app.testdir = unit.testdir
+ if go_app.prepare_env('empty', 'app').returncode == 0:
+ cls.available['modules']['go'] = []
- go_app_path = self.current_dir + '/go/'
+ return unit if not complete_check else unit.complete()
+
+ def prepare_env(self, script, name):
+ if not os.path.exists(self.testdir + '/go'):
+ os.mkdir(self.testdir + '/go')
env = os.environ.copy()
env['GOPATH'] = self.pardir + '/go'
@@ -19,12 +29,18 @@ class TestApplicationGo(TestApplicationProto):
'build',
'-o',
self.testdir + '/go/' + name,
- go_app_path + script + '/' + name + '.go',
+ self.current_dir + '/go/' + script + '/' + name + '.go',
],
env=env,
)
+
process.communicate()
+ return process
+
+ def load(self, script, name='app'):
+ self.prepare_env(script, name)
+
self._load_conf(
{
"listeners": {"*:7080": {"pass": "applications/" + script}},
@@ -32,8 +48,10 @@ class TestApplicationGo(TestApplicationProto):
script: {
"type": "external",
"processes": {"spare": 0},
- "working_directory": go_app_path + script,
- "executable": self.testdir + '/go/' + name,
+ "working_directory": self.current_dir
+ + "/go/"
+ + script,
+ "executable": self.testdir + "/go/" + name,
}
},
}
diff --git a/test/unit/applications/lang/java.py b/test/unit/applications/lang/java.py
index ec1c95d9..40bf3662 100644
--- a/test/unit/applications/lang/java.py
+++ b/test/unit/applications/lang/java.py
@@ -1,4 +1,5 @@
import os
+import glob
import shutil
from subprocess import Popen
from unit.applications.proto import TestApplicationProto
@@ -6,11 +7,9 @@ from unit.applications.proto import TestApplicationProto
class TestApplicationJava(TestApplicationProto):
def load(self, script, name='app'):
-
app_path = self.testdir + '/java'
web_inf_path = app_path + '/WEB-INF/'
classes_path = web_inf_path + 'classes/'
-
script_path = self.current_dir + '/java/' + script + '/'
if not os.path.isdir(app_path):
@@ -19,39 +18,48 @@ class TestApplicationJava(TestApplicationProto):
src = []
for f in os.listdir(script_path):
+ file_path = script_path + f
+
if f.endswith('.java'):
- src.append(script_path + f)
+ src.append(file_path)
continue
if f.startswith('.') or f == 'Makefile':
continue
- if os.path.isdir(script_path + f):
+ if os.path.isdir(file_path):
if f == 'WEB-INF':
continue
- shutil.copytree(script_path + f, app_path + '/' + f)
+ shutil.copytree(file_path, app_path + '/' + f)
continue
if f == 'web.xml':
if not os.path.isdir(web_inf_path):
os.makedirs(web_inf_path)
- shutil.copy2(script_path + f, web_inf_path)
+ shutil.copy2(file_path, web_inf_path)
else:
- shutil.copy2(script_path + f, app_path)
+ shutil.copy2(file_path, app_path)
if src:
if not os.path.isdir(classes_path):
os.makedirs(classes_path)
- tomcat_jar = self.pardir + '/build/tomcat-servlet-api-9.0.13.jar'
+ classpath = self.pardir + '/build/tomcat-servlet-api-9.0.13.jar'
+
+ ws_jars = glob.glob(
+ self.pardir + '/build/websocket-api-java-*.jar'
+ )
+
+ if not ws_jars:
+ self.fail('websocket api jar not found.')
javac = [
'javac',
'-encoding', 'utf-8',
'-d', classes_path,
- '-classpath', tomcat_jar,
+ '-classpath', classpath + ':' + ws_jars[0],
]
javac.extend(src)
diff --git a/test/unit/applications/lang/node.py b/test/unit/applications/lang/node.py
index 931c6596..3cc72669 100644
--- a/test/unit/applications/lang/node.py
+++ b/test/unit/applications/lang/node.py
@@ -4,8 +4,18 @@ from unit.applications.proto import TestApplicationProto
class TestApplicationNode(TestApplicationProto):
- def load(self, script, name='app.js'):
+ @classmethod
+ def setUpClass(cls, complete_check=True):
+ unit = super().setUpClass(complete_check=False)
+
+ # check node module
+
+ if os.path.exists(unit.pardir + '/node/node_modules'):
+ cls.available['modules']['node'] = []
+ return unit if not complete_check else unit.complete()
+
+ def load(self, script, name='app.js'):
# copy application
shutil.copytree(
diff --git a/test/unit/applications/tls.py b/test/unit/applications/tls.py
index 6e8deefb..1290279d 100644
--- a/test/unit/applications/tls.py
+++ b/test/unit/applications/tls.py
@@ -1,4 +1,5 @@
import os
+import re
import ssl
import subprocess
from unit.applications.proto import TestApplicationProto
@@ -12,6 +13,27 @@ class TestApplicationTLS(TestApplicationProto):
self.context.check_hostname = False
self.context.verify_mode = ssl.CERT_NONE
+ @classmethod
+ def setUpClass(cls, complete_check=True):
+ unit = super().setUpClass(complete_check=False)
+
+ # check tls module
+
+ try:
+ subprocess.check_output(['which', 'openssl'])
+
+ output = subprocess.check_output(
+ [unit.unitd, '--version'], stderr=subprocess.STDOUT
+ )
+
+ if re.search('--openssl', output.decode()):
+ cls.available['modules']['openssl'] = []
+
+ except:
+ pass
+
+ return unit if not complete_check else unit.complete()
+
def certificate(self, name='default', load=True):
self.openssl_conf()
diff --git a/test/unit/applications/websockets.py b/test/unit/applications/websockets.py
index 417e9504..50ff2797 100644
--- a/test/unit/applications/websockets.py
+++ b/test/unit/applications/websockets.py
@@ -54,24 +54,16 @@ class TestApplicationWebsocket(TestApplicationProto):
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 = ''):
+ def serialize_close(self, code=1000, reason=''):
return struct.pack('!H', code) + reason.encode('utf-8')
- def frame_read(self, sock, read_timeout=10):
+ def frame_read(self, sock, read_timeout=30):
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:
+ data += sock.recv(bytes - len(data))
+
+ if len(data) == bytes:
break
return data
@@ -99,7 +91,11 @@ class TestApplicationWebsocket(TestApplicationProto):
if frame['mask']:
mask_bits = recv_bytes(sock, 4)
- data = recv_bytes(sock, length)
+ data = b''
+
+ if length != 0:
+ data = recv_bytes(sock, length)
+
if frame['mask']:
data = self.apply_mask(data, mask_bits)
@@ -175,14 +171,20 @@ class TestApplicationWebsocket(TestApplicationProto):
frame = self.frame_to_send(*args, **kwargs)
if chopsize is None:
- sock.sendall(frame)
+ try:
+ sock.sendall(frame)
+ except BrokenPipeError:
+ pass
else:
pos = 0
frame_len = len(frame)
- while (pos < frame_len):
+ while pos < frame_len:
end = min(pos + chopsize, frame_len)
- sock.sendall(frame[pos:end])
+ try:
+ sock.sendall(frame[pos:end])
+ except BrokenPipeError:
+ end = frame_len
pos = end
def message(self, sock, type, message, fragmention_size=None, **kwargs):
@@ -197,17 +199,19 @@ class TestApplicationWebsocket(TestApplicationProto):
pos = 0
op_code = type
- while(pos < message_len):
+ 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)
+ 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=10):
frame = self.frame_read(sock, read_timeout=read_timeout)
- while(not frame['fin']):
+ while not frame['fin']:
temp = self.frame_read(sock, read_timeout=read_timeout)
frame['data'] += temp['data']
frame['fin'] = temp['fin']
diff --git a/test/unit/feature/isolation.py b/test/unit/feature/isolation.py
new file mode 100644
index 00000000..9b06ab3c
--- /dev/null
+++ b/test/unit/feature/isolation.py
@@ -0,0 +1,87 @@
+import os
+import json
+from unit.applications.proto import TestApplicationProto
+from unit.applications.lang.go import TestApplicationGo
+from unit.applications.lang.java import TestApplicationJava
+from unit.applications.lang.node import TestApplicationNode
+from unit.applications.lang.perl import TestApplicationPerl
+from unit.applications.lang.php import TestApplicationPHP
+from unit.applications.lang.python import TestApplicationPython
+from unit.applications.lang.ruby import TestApplicationRuby
+
+
+class TestFeatureIsolation(TestApplicationProto):
+ allns = ['pid', 'mnt', 'ipc', 'uts', 'cgroup', 'net']
+
+ def check(self, available, testdir):
+ test_conf = {"namespaces": {"credential": True}}
+
+ module = ''
+ app = 'empty'
+ if 'go' in available['modules']:
+ module = TestApplicationGo()
+
+ elif 'java' in available['modules']:
+ module = TestApplicationJava()
+
+ elif 'node' in available['modules']:
+ module = TestApplicationNode()
+ app = 'basic'
+
+ elif 'perl' in available['modules']:
+ module = TestApplicationPerl()
+ app = 'body_empty'
+
+ elif 'php' in available['modules']:
+ module = TestApplicationPHP()
+ app = 'phpinfo'
+
+ elif 'python' in available['modules']:
+ module = TestApplicationPython()
+
+ elif 'ruby' in available['modules']:
+ module = TestApplicationRuby()
+
+ if not module:
+ return
+
+ module.testdir = testdir
+ module.load(app)
+
+ resp = module.conf(test_conf, 'applications/' + app + '/isolation')
+ if 'success' not in resp:
+ return
+
+ userns = self.getns('user')
+ if not userns:
+ return
+
+ available['features']['isolation'] = {'user': userns}
+
+ unp_clone_path = '/proc/sys/kernel/unprivileged_userns_clone'
+ if os.path.exists(unp_clone_path):
+ with open(unp_clone_path, 'r') as f:
+ if str(f.read()).rstrip() == '1':
+ available['features']['isolation'][
+ 'unprivileged_userns_clone'
+ ] = True
+
+ for ns in self.allns:
+ ns_value = self.getns(ns)
+ if ns_value:
+ available['features']['isolation'][ns] = ns_value
+
+ def getns(self, nstype):
+ # read namespace id from symlink file:
+ # it points to: '<nstype>:[<ns id>]'
+ # # eg.: 'pid:[4026531836]'
+ nspath = '/proc/self/ns/' + nstype
+ data = None
+
+ if os.path.exists(nspath):
+ data = int(os.readlink(nspath)[len(nstype) + 2 : -1])
+
+ return data
+
+ def parsejson(self, data):
+ return json.loads(data.split('\n')[1])
diff --git a/test/unit/http.py b/test/unit/http.py
index c0af8a9e..82a6bd6a 100644
--- a/test/unit/http.py
+++ b/test/unit/http.py
@@ -12,6 +12,11 @@ class TestHTTP(TestUnit):
port = 7080 if 'port' not in kwargs else kwargs['port']
url = '/' if 'url' not in kwargs else kwargs['url']
http = 'HTTP/1.0' if 'http_10' in kwargs else 'HTTP/1.1'
+ read_buffer_size = (
+ 4096
+ if 'read_buffer_size' not in kwargs
+ else kwargs['read_buffer_size']
+ )
headers = (
{'Host': 'localhost', 'Connection': 'close'}
@@ -94,7 +99,9 @@ class TestHTTP(TestUnit):
read_timeout = (
30 if 'read_timeout' not in kwargs else kwargs['read_timeout']
)
- resp = self.recvall(sock, read_timeout=read_timeout).decode(enc)
+ resp = self.recvall(
+ sock, read_timeout=read_timeout, buff_size=read_buffer_size
+ ).decode(enc)
if TestUnit.detailed:
print('<<<')
@@ -118,6 +125,9 @@ class TestHTTP(TestUnit):
def get(self, **kwargs):
return self.http('GET', **kwargs)
+ def head(self, **kwargs):
+ return self.http('HEAD', **kwargs)
+
def post(self, **kwargs):
return self.http('POST', **kwargs)
diff --git a/test/unit/main.py b/test/unit/main.py
index 6a167a9e..873f1815 100644
--- a/test/unit/main.py
+++ b/test/unit/main.py
@@ -12,8 +12,6 @@ import subprocess
from multiprocessing import Process
-available_modules = {}
-
class TestUnit(unittest.TestCase):
current_dir = os.path.abspath(
@@ -28,6 +26,7 @@ class TestUnit(unittest.TestCase):
detailed = False
save_log = False
+ unsafe = False
def __init__(self, methodName='runTest'):
super().__init__(methodName)
@@ -41,10 +40,12 @@ class TestUnit(unittest.TestCase):
if not hasattr(self, 'application_type'):
return super().run(result)
+ # rerun test for each available module version
+
type = self.application_type
- for prerequisite in self.prerequisites:
- if prerequisite in available_modules:
- for version in available_modules[prerequisite]:
+ for module in self.prerequisites['modules']:
+ if module in self.available['modules']:
+ for version in self.available['modules'][module]:
self.application_type = type + ' ' + version
super().run(result)
@@ -63,8 +64,83 @@ class TestUnit(unittest.TestCase):
unittest.main()
@classmethod
- def setUpClass(cls):
- TestUnit().check_modules(*cls.prerequisites)
+ def setUpClass(cls, complete_check=True):
+ cls.available = {'modules': {}, 'features': {}}
+ unit = TestUnit()
+
+ unit._run()
+
+ # read unit.log
+
+ for i in range(50):
+ with open(unit.testdir + '/unit.log', 'r') as f:
+ log = f.read()
+ m = re.search('controller started', log)
+
+ if m is None:
+ time.sleep(0.1)
+ else:
+ break
+
+ if m is None:
+ unit.stop()
+ exit("Unit is writing log too long")
+
+ # discover available modules from unit.log
+
+ for module in re.findall(r'module: ([a-zA-Z]+) (.*) ".*"$', log, re.M):
+ if module[0] not in cls.available['modules']:
+ cls.available['modules'][module[0]] = [module[1]]
+ else:
+ cls.available['modules'][module[0]].append(module[1])
+
+ def check(available, prerequisites):
+ missed = []
+
+ # check modules
+
+ if 'modules' in prerequisites:
+ available_modules = list(available['modules'].keys())
+
+ for module in prerequisites['modules']:
+ if module in available_modules:
+ continue
+
+ missed.append(module)
+
+ if missed:
+ print('Unit has no ' + ', '.join(missed) + ' module(s)')
+ raise unittest.SkipTest()
+
+ # check features
+
+ if 'features' in prerequisites:
+ available_features = list(available['features'].keys())
+
+ for feature in prerequisites['features']:
+ if feature in available_features:
+ continue
+
+ missed.append(feature)
+
+ if missed:
+ print(', '.join(missed) + ' feature(s) not supported')
+ raise unittest.SkipTest()
+
+ def destroy():
+ unit.stop()
+ unit._check_alerts(log)
+ shutil.rmtree(unit.testdir)
+
+ def complete():
+ destroy()
+ check(cls.available, cls.prerequisites)
+
+ if complete_check:
+ complete()
+ else:
+ unit.complete = complete
+ return unit
def setUp(self):
self._run()
@@ -105,92 +181,6 @@ class TestUnit(unittest.TestCase):
else:
self._print_path_to_log()
- def check_modules(self, *modules):
- self._run()
-
- for i in range(50):
- with open(self.testdir + '/unit.log', 'r') as f:
- log = f.read()
- m = re.search('controller started', log)
-
- if m is None:
- time.sleep(0.1)
- else:
- break
-
- if m is None:
- self.stop()
- exit("Unit is writing log too long")
-
- # discover all available modules
-
- global available_modules
- available_modules = {}
- for module in re.findall(r'module: ([a-zA-Z]+) (.*) ".*"$', log, re.M):
- if module[0] not in available_modules:
- available_modules[module[0]] = [module[1]]
- else:
- available_modules[module[0]].append(module[1])
-
- missed_module = ''
- for module in modules:
- if module == 'go':
- env = os.environ.copy()
- env['GOPATH'] = self.pardir + '/go'
-
- try:
- process = subprocess.Popen(
- [
- 'go',
- 'build',
- '-o',
- self.testdir + '/go/check_module',
- self.current_dir + '/go/empty/app.go',
- ],
- env=env,
- )
- process.communicate()
-
- m = module if process.returncode == 0 else None
-
- except:
- m = None
-
- elif module == 'node':
- if os.path.isdir(self.pardir + '/node/node_modules'):
- m = module
- else:
- m = None
-
- elif module == 'openssl':
- try:
- subprocess.check_output(['which', 'openssl'])
-
- output = subprocess.check_output(
- [self.unitd, '--version'],
- stderr=subprocess.STDOUT,
- )
-
- m = re.search('--openssl', output.decode())
-
- except:
- m = None
-
- else:
- if module not in available_modules:
- m = None
-
- if m is None:
- missed_module = module
- break
-
- self.stop()
- self._check_alerts(log)
- shutil.rmtree(self.testdir)
-
- if missed_module:
- raise unittest.SkipTest('Unit has no ' + missed_module + ' module')
-
def stop(self):
if self._started:
self._stop()
@@ -350,6 +340,8 @@ class TestUnit(unittest.TestCase):
TestUnit.save_log = args.save_log
TestUnit.unsafe = args.unsafe
+ # set stdout to non-blocking
+
if TestUnit.detailed:
fcntl.fcntl(sys.stdout.fileno(), fcntl.F_SETFL, 0)