diff options
author | oxpa <iippolitov@gmail.com> | 2024-09-17 14:21:10 +0100 |
---|---|---|
committer | oxpa <iippolitov@gmail.com> | 2024-09-17 14:21:10 +0100 |
commit | 2417826d8bebf921ee1be102ef8ce702f0683d66 (patch) | |
tree | 76d29a1705415ed7368870826dbb2f04942ee794 /test | |
parent | 0e79d961bb1ea68674961da1703ffedb1ddf6e43 (diff) | |
parent | 24ed91f40634372d99f67f0e4e3c2ac0abde81bd (diff) | |
download | unit-2417826d8bebf921ee1be102ef8ce702f0683d66.tar.gz unit-2417826d8bebf921ee1be102ef8ce702f0683d66.tar.bz2 |
Merge tag '1.33.0' into packaging.
Unit 1.33.0 release.
Diffstat (limited to '')
-rw-r--r-- | test/conftest.py | 3 | ||||
-rw-r--r-- | test/python/factory/wsgi.py | 23 | ||||
-rw-r--r-- | test/test_chunked.py | 188 | ||||
-rw-r--r-- | test/test_python_application.py | 40 | ||||
-rw-r--r-- | test/test_python_factory.py | 140 | ||||
-rw-r--r-- | test/test_response_headers.py | 13 | ||||
-rw-r--r-- | test/test_routing.py | 72 | ||||
-rw-r--r-- | test/test_variables.py | 101 | ||||
-rw-r--r-- | test/test_wasm-wasi-component.py | 18 | ||||
-rw-r--r-- | test/unit/applications/lang/java.py | 2 | ||||
-rw-r--r-- | test/unit/applications/lang/wasm_component.py | 63 | ||||
-rw-r--r-- | test/unit/applications/tls.py | 6 | ||||
-rw-r--r-- | test/unit/check/cargo_component.py | 4 | ||||
-rw-r--r-- | test/unit/check/discover_available.py | 4 | ||||
-rw-r--r-- | test/unit/http.py | 2 | ||||
-rw-r--r-- | test/unit/status.py | 13 | ||||
-rw-r--r-- | test/wasm_component/hello_world/Cargo.lock | 34 | ||||
-rw-r--r-- | test/wasm_component/hello_world/Cargo.toml | 18 | ||||
-rw-r--r-- | test/wasm_component/hello_world/src/bindings.rs | 109 | ||||
-rw-r--r-- | test/wasm_component/hello_world/src/lib.rs | 31 | ||||
-rw-r--r-- | test/wasm_component/hello_world/wit/world.wit | 6 |
21 files changed, 872 insertions, 18 deletions
diff --git a/test/conftest.py b/test/conftest.py index 2fe4d8dc..91c59e17 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -429,7 +429,8 @@ def _clear_temp_dir(): temporary_dir = unit_instance['temp_dir'] if is_findmnt and not waitforunmount(temporary_dir, timeout=600): - sys.exit('Could not unmount filesystems in tmpdir ({temporary_dir}).') + Log.print_log() + sys.exit(f'Could not unmount filesystems in tmpdir ({temporary_dir}).') for item in Path(temporary_dir).iterdir(): if item.name not in [ diff --git a/test/python/factory/wsgi.py b/test/python/factory/wsgi.py new file mode 100644 index 00000000..8ad4887b --- /dev/null +++ b/test/python/factory/wsgi.py @@ -0,0 +1,23 @@ +def wsgi_a(env, start_response): + start_response("200", [("Content-Length", "1")]) + return [b"1"] + + +def wsgi_b(env, start_response): + start_response("200", [("Content-Length", "1")]) + return [b"2"] + + +def wsgi_a_factory(): + return wsgi_a + + +def wsgi_b_factory(): + return wsgi_b + + +wsgi_invalid_callable = None + + +def wsgi_factory_returning_invalid_callable(): + return wsgi_invalid_callable diff --git a/test/test_chunked.py b/test/test_chunked.py new file mode 100644 index 00000000..caa26f7e --- /dev/null +++ b/test/test_chunked.py @@ -0,0 +1,188 @@ +import re + +import pytest +from unit.applications.lang.python import ApplicationPython + +prerequisites = {'modules': {'python': 'any'}} + +client = ApplicationPython() + + +@pytest.fixture(autouse=True) +def setup_method_fixture(): + client.load('mirror') + + assert 'success' in client.conf( + {"http": {"chunked_transform": True}}, 'settings' + ) + + +def test_chunked(): + def chunks(chunks=[]): + body = '' + + for c in chunks: + body = f'{body}{len(c):x}\r\n{c}\r\n' + + resp = client.get( + headers={ + 'Host': 'localhost', + 'Connection': 'close', + 'Transfer-Encoding': 'chunked', + }, + body=f'{body}0\r\n\r\n', + ) + + expect_body = ''.join(chunks) + + assert resp['status'] == 200 + assert resp['headers']['Content-Length'] == str(len(expect_body)) + assert resp['body'] == expect_body + + chunks() + chunks(['1']) + chunks(['0123456789']) + chunks(['0123456789' * 128]) + chunks(['0123456789' * 512]) + chunks(['0123456789' * 128, '1', '1', '0123456789' * 128, '1']) + + +def test_chunked_pipeline(): + sock = client.get( + no_recv=True, + headers={ + 'Host': 'localhost', + 'Transfer-Encoding': 'chunked', + }, + body='1\r\n$\r\n0\r\n\r\n', + ) + + resp = client.get( + sock=sock, + headers={ + 'Host': 'localhost', + 'Transfer-Encoding': 'chunked', + 'Connection': 'close', + }, + body='1\r\n%\r\n0\r\n\r\n', + raw_resp=True, + ) + + assert len(re.findall('200 OK', resp)) == 2 + assert len(re.findall('Content-Length: 1', resp)) == 2 + assert len(re.findall('$', resp)) == 1 + assert len(re.findall('%', resp)) == 1 + + +def test_chunked_max_body_size(): + assert 'success' in client.conf( + {'max_body_size': 1024, 'chunked_transform': True}, 'settings/http' + ) + + body = f'{2048:x}\r\n{"x" * 2048}\r\n0\r\n\r\n' + + assert ( + client.get( + headers={ + 'Host': 'localhost', + 'Connection': 'close', + 'Transfer-Encoding': 'chunked', + }, + body=body, + )['status'] + == 413 + ) + + +def test_chunked_after_last(): + resp = client.get( + headers={ + 'Host': 'localhost', + 'Connection': 'close', + 'Transfer-Encoding': 'chunked', + }, + body='1\r\na\r\n0\r\n\r\n1\r\nb\r\n0\r\n\r\n', + ) + + assert resp['status'] == 200 + assert resp['headers']['Content-Length'] == '1' + assert resp['body'] == 'a' + + +def test_chunked_transform(): + assert 'success' in client.conf( + {"http": {"chunked_transform": False}}, 'settings' + ) + + assert ( + client.get( + headers={ + 'Host': 'localhost', + 'Connection': 'close', + 'Transfer-Encoding': 'chunked', + }, + body='0\r\n\r\n', + )['status'] + == 411 + ) + + +def test_chunked_invalid(): + # invalid chunkes + + def check_body(body): + assert ( + client.get( + headers={ + 'Host': 'localhost', + 'Connection': 'close', + 'Transfer-Encoding': 'chunked', + }, + body=body, + )['status'] + == 400 + ) + + check_body('1\r\nblah\r\n0\r\n\r\n') + check_body('1\r\n\r\n1\r\n0\r\n\r\n') + check_body('1\r\n1\r\n\r\n0\r\n\r\n') + + # invalid transfer encoding header + + assert ( + client.get( + headers={ + 'Host': 'localhost', + 'Connection': 'close', + 'Transfer-Encoding': ['chunked', 'chunked'], + }, + body='0\r\n\r\n', + )['status'] + == 400 + ), 'two Transfer-Encoding headers' + + assert ( + client.get( + headers={ + 'Host': 'localhost', + 'Connection': 'close', + 'Transfer-Encoding': 'chunked', + 'Content-Length': '5', + }, + body='0\r\n\r\n', + )['status'] + == 400 + ), 'Transfer-Encoding and Content-Length' + + assert ( + client.get( + http_10=True, + headers={ + 'Host': 'localhost', + 'Connection': 'close', + 'Transfer-Encoding': 'chunked', + }, + body='0\r\n\r\n', + )['status'] + == 400 + ), 'Transfer-Encoding HTTP/1.0' diff --git a/test/test_python_application.py b/test/test_python_application.py index 466a59a2..c4803153 100644 --- a/test/test_python_application.py +++ b/test/test_python_application.py @@ -10,6 +10,7 @@ import pytest from packaging import version from unit.applications.lang.python import ApplicationPython +from unit.option import option prerequisites = {'modules': {'python': 'all'}} @@ -64,6 +65,45 @@ custom-header: BLAH }, 'headers' assert resp['body'] == body, 'body' + # REQUEST_URI unchanged + + path = f'{option.test_dir}/python/variables' + assert 'success' in client.conf( + { + "listeners": {"*:8080": {"pass": "routes"}}, + "routes": [ + { + "action": { + "rewrite": "/foo", + "pass": "applications/variables", + } + } + ], + "applications": { + "variables": { + "type": client.get_application_type(), + "processes": {'spare': 0}, + "path": path, + "working_directory": path, + "module": "wsgi", + } + }, + } + ) + + resp = client.http( + f"""POST /bar HTTP/1.1 +Host: localhost +Content-Length: 1 +Custom-Header: blah +Content-Type: text/html +Connection: close + +a""".encode(), + raw=True, + ) + assert resp['headers']['Request-Uri'] == '/bar', 'REQUEST_URI unchanged' + def test_python_application_query_string(): client.load('query_string') diff --git a/test/test_python_factory.py b/test/test_python_factory.py new file mode 100644 index 00000000..d1752c24 --- /dev/null +++ b/test/test_python_factory.py @@ -0,0 +1,140 @@ +from unit.applications.lang.python import ApplicationPython +from unit.option import option + +prerequisites = {"modules": {"python": "all"}} + +client = ApplicationPython() + + +def test_python_factory_targets(): + python_dir = f"{option.test_dir}/python" + + assert "success" in client.conf( + { + "listeners": { + "*:8080": {"pass": "applications/targets/1"}, + "*:8081": {"pass": "applications/targets/2"}, + "*:8082": {"pass": "applications/targets/factory-1"}, + "*:8083": {"pass": "applications/targets/factory-2"}, + }, + "applications": { + "targets": { + "type": client.get_application_type(), + "working_directory": f"{python_dir}/factory/", + "path": f"{python_dir}/factory/", + "targets": { + "1": { + "module": "wsgi", + "callable": "wsgi_a", + "factory": False, + }, + "2": { + "module": "wsgi", + "callable": "wsgi_b", + "factory": False, + }, + "factory-1": { + "module": "wsgi", + "callable": "wsgi_a_factory", + "factory": True, + }, + "factory-2": { + "module": "wsgi", + "callable": "wsgi_b_factory", + "factory": True, + }, + }, + } + }, + } + ) + + resp = client.get(port=8080) + assert resp["status"] == 200 + assert resp["body"] == "1" + + resp = client.get(port=8081) + assert resp["status"] == 200 + assert resp["body"] == "2" + + resp = client.get(port=8082) + assert resp["status"] == 200 + assert resp["body"] == "1" + + resp = client.get(port=8083) + assert resp["status"] == 200 + assert resp["body"] == "2" + + +def test_python_factory_without_targets(): + python_dir = f"{option.test_dir}/python" + + assert "success" in client.conf( + { + "listeners": { + "*:8080": {"pass": "applications/python-app-factory"}, + "*:8081": {"pass": "applications/python-app"}, + }, + "applications": { + "python-app-factory": { + "type": client.get_application_type(), + "working_directory": f"{python_dir}/factory/", + "path": f"{python_dir}/factory/", + "module": "wsgi", + "callable": "wsgi_a_factory", + "factory": True, + }, + "python-app": { + "type": client.get_application_type(), + "working_directory": f"{python_dir}/factory/", + "path": f"{python_dir}/factory/", + "module": "wsgi", + "callable": "wsgi_b", + "factory": False, + }, + }, + } + ) + + resp = client.get(port=8080) + assert resp["status"] == 200 + assert resp["body"] == "1" + + resp = client.get(port=8081) + assert resp["status"] == 200 + assert resp["body"] == "2" + + +def test_python_factory_invalid_callable_value(skip_alert): + skip_alert( + r"failed to apply new conf", + r"did not return callable object", + r"can not be called to fetch callable", + ) + python_dir = f"{option.test_dir}/python" + + invalid_callable_values = [ + "wsgi_factory_returning_invalid_callable", + "wsgi_invalid_callable", + ] + + for callable_value in invalid_callable_values: + assert "error" in client.conf( + { + "listeners": {"*:8080": {"pass": "applications/targets/1"}}, + "applications": { + "targets": { + "type": client.get_application_type(), + "working_directory": f"{python_dir}/factory/", + "path": f"{python_dir}/factory/", + "targets": { + "1": { + "module": "wsgi", + "callable": callable_value, + "factory": True, + }, + }, + } + }, + } + ) diff --git a/test/test_response_headers.py b/test/test_response_headers.py index e62c1293..ddc22124 100644 --- a/test/test_response_headers.py +++ b/test/test_response_headers.py @@ -163,12 +163,11 @@ def test_response_headers_remove(): def test_response_headers_invalid(skip_alert): - skip_alert(r'failed to apply new conf') - def check_invalid(conf): - assert 'error' in client.conf( - conf, - 'routes/0/action/response_headers', - ) + resp = client.conf(conf, 'routes/0/action/response_headers') + assert 'error' in resp + + return resp - check_invalid({"X-Foo": "$u"}) + resp = check_invalid({"X-Foo": "$u"}) + assert 'detail' in resp and 'Unknown variable' in resp['detail'] diff --git a/test/test_routing.py b/test/test_routing.py index 0b6eced2..170f627e 100644 --- a/test/test_routing.py +++ b/test/test_routing.py @@ -2009,3 +2009,75 @@ def test_routes_match_destination_proxy(): ), 'proxy configure' assert client.get()['status'] == 200, 'proxy' + + +def set_if(condition): + assert 'success' in client.conf(f'"{condition}"', 'routes/0/match/if') + + +def test_routes_match_if(): + + def try_if(condition, status): + set_if(condition) + assert client.get(url=f'/{condition}')['status'] == status + + assert 'success' in client.conf( + { + "listeners": {"*:8080": {"pass": "routes"}}, + "routes": [ + { + "match": {"method": "GET"}, + "action": {"return": 200}, + } + ], + "applications": {}, + } + ), 'routing configure' + + # const + + try_if('', 404) + try_if('0', 404) + try_if('false', 404) + try_if('undefined', 404) + try_if('!', 200) + try_if('!null', 200) + try_if('1', 200) + + # variable + + set_if('$arg_foo') + assert client.get(url='/bar?bar')['status'] == 404 + assert client.get(url='/foo_empty?foo')['status'] == 404 + assert client.get(url='/foo?foo=1')['status'] == 200 + + set_if('!$arg_foo') + assert client.get(url='/bar?bar')['status'] == 200 + assert client.get(url='/foo_empty?foo')['status'] == 200 + assert client.get(url='/foo?foo=1')['status'] == 404 + +def test_routes_match_if_njs(require): + require({'modules': {'njs': 'any'}}) + + assert 'success' in client.conf( + { + "listeners": {"*:8080": {"pass": "routes"}}, + "routes": [ + { + "match": {"method": "GET"}, + "action": {"return": 200}, + } + ], + "applications": {}, + } + ) + + set_if('`${args.foo == \'1\'}`') + assert client.get(url='/foo_1?foo=1')['status'] == 200 + assert client.get(url='/foo_2?foo=2')['status'] == 404 + + set_if('!`${args.foo == \'1\'}`') + assert client.get(url='/foo_1?foo=1')['status'] == 404 + assert client.get(url='/foo_2?foo=2')['status'] == 200 + + assert 'error' in client.conf('$arg_', 'routes/0/match/if') diff --git a/test/test_variables.py b/test/test_variables.py index 9aab8a62..e20a3cd7 100644 --- a/test/test_variables.py +++ b/test/test_variables.py @@ -93,7 +93,9 @@ def test_variables_method(search_in_file, wait_for_record): assert wait_for_record(reg, 'access.log') is not None, 'method POST' -def test_variables_request_uri(search_in_file, wait_for_record): +def test_variables_request_uri( + findall, search_in_file, temp_dir, wait_for_record +): set_format('$request_uri') def check_request_uri(req_uri): @@ -108,6 +110,103 @@ def test_variables_request_uri(search_in_file, wait_for_record): check_request_uri('/4%2A') check_request_uri('/9?q#a') + # $request_uri + proxy + + assert 'success' in client.conf( + { + "listeners": { + "*:8080": {"pass": "routes/a"}, + "[::1]:8081": {"pass": "routes/b"}, + }, + "routes": { + "a": [ + { + "action": { + "proxy": "http://[::1]:8081", + } + } + ], + "b": [ + { + "action": { + "return": 200, + } + } + ], + }, + "access_log": { + "path": f'{temp_dir}/access.log', + "format": "$remote_addr $uri $request_uri", + }, + } + ) + + assert search_in_file(r'::1', 'access.log') is None + + assert client.get(url='/blah%25blah?a=b')['status'] == 200 + + assert ( + wait_for_record(fr'^::1 /blah%blah /blah%25blah\?a=b$', 'access.log') + is not None + ), 'req 8081 (proxy)' + assert ( + search_in_file( + fr'^127\.0\.0\.1 /blah%blah /blah%25blah\?a=b$', 'access.log' + ) + is not None + ), 'req 8080' + + # rewrite set $request_uri before proxy + + assert 'success' in client.conf( + { + "a": [ + { + "action": { + "rewrite": "/foo", + "proxy": "http://[::1]:8081", + } + } + ], + "b": [ + { + "action": { + "rewrite": "/bar", + "return": 200, + } + } + ], + }, + 'routes', + ) + + assert len(findall(r'::1', 'access.log')) == 1 + + assert client.get(url='/blah%2Fblah?a=b')['status'] == 200 + + assert ( + wait_for_record(fr'^::1 /bar /foo\?a=b$', 'access.log') is not None + ), 'req 8081 (proxy) rewrite' + assert ( + search_in_file(fr'^127\.0\.0\.1 /foo /blah%2Fblah\?a=b$', 'access.log') + is not None + ), 'req 8080 rewrite' + + # percent-encoded rewrite + + assert len(findall(r'::1', 'access.log')) == 2 + + assert 'success' in client.conf('"/foo%2Ffoo"', 'routes/a/0/action/rewrite') + assert client.get(url='/blah%2Fblah?a=b')['status'] == 200 + + assert ( + wait_for_record( + fr'^127\.0\.0\.1 /foo/foo /blah%2Fblah\?a=b$', 'access.log' + ) + is not None + ), 'req 8080 percent' + assert len(findall(fr'^::1 /bar /foo/foo\?a=b$', 'access.log')) == 1 + def test_variables_uri(search_in_file, wait_for_record): set_format('$uri') diff --git a/test/test_wasm-wasi-component.py b/test/test_wasm-wasi-component.py new file mode 100644 index 00000000..6d3bc485 --- /dev/null +++ b/test/test_wasm-wasi-component.py @@ -0,0 +1,18 @@ +import pytest +from unit.applications.lang.wasm_component import ApplicationWasmComponent + +prerequisites = { + 'modules': {'wasm-wasi-component': 'any'}, + 'features': {'cargo_component': True}, +} + +client = ApplicationWasmComponent() + + +def test_wasm_component(): + client.load('hello_world') + + req = client.get() + + assert client.get()['status'] == 200 + assert req['body'] == 'Hello' diff --git a/test/unit/applications/lang/java.py b/test/unit/applications/lang/java.py index 351d04ce..2b3194ae 100644 --- a/test/unit/applications/lang/java.py +++ b/test/unit/applications/lang/java.py @@ -53,7 +53,7 @@ class ApplicationJava(ApplicationProto): os.makedirs(classes_path) classpath = ( - f'{option.current_dir}/build/tomcat-servlet-api-9.0.86.jar' + f'{option.current_dir}/build/tomcat-servlet-api-9.0.93.jar' ) ws_jars = glob.glob( diff --git a/test/unit/applications/lang/wasm_component.py b/test/unit/applications/lang/wasm_component.py new file mode 100644 index 00000000..a6c8dd14 --- /dev/null +++ b/test/unit/applications/lang/wasm_component.py @@ -0,0 +1,63 @@ +from pathlib import Path +import shutil +import subprocess +from urllib.parse import quote + +from unit.applications.proto import ApplicationProto +from unit.option import option + + +class ApplicationWasmComponent(ApplicationProto): + @staticmethod + def prepare_env(script): + try: + subprocess.check_output( + ['cargo', 'component', '--help'], + stderr=subprocess.STDOUT, + ) + except (subprocess.CalledProcessError, FileNotFoundError): + return None + + temp_dir = Path(f'{option.temp_dir}/wasm_component/') + + if not temp_dir.exists(): + temp_dir.mkdir() + + app_path = f'{temp_dir}/{script}' + + shutil.copytree(f'{option.test_dir}/wasm_component/{script}', app_path) + + try: + output = subprocess.check_output( + ['cargo', 'component', 'build', '--release'], + cwd=app_path, + stderr=subprocess.STDOUT, + ) + except KeyboardInterrupt: + raise + + except subprocess.CalledProcessError: + return None + + return output + + def load(self, script, **kwargs): + self.prepare_env(script) + + component_path = f'{option.temp_dir}/wasm_component/{script}/target/wasm32-wasip1/release/test_wasi_component.wasm' + + self._load_conf( + { + "listeners": { + "*:8080": {"pass": f"applications/{quote(script, '')}"} + }, + "applications": { + script: { + "type": "wasm-wasi-component", + "processes": {"spare": 0}, + "component": component_path, + } + }, + }, + **kwargs, + ) diff --git a/test/unit/applications/tls.py b/test/unit/applications/tls.py index 75354dd9..b48293be 100644 --- a/test/unit/applications/tls.py +++ b/test/unit/applications/tls.py @@ -85,9 +85,13 @@ subjectAltName = @alt_names default_bits = 2048 encrypt_key = no distinguished_name = req_distinguished_name +x509_extensions = myca_extensions {a_sec if alt_names else ""} -[ req_distinguished_name ]''' +[ req_distinguished_name ] + +[ myca_extensions ] +basicConstraints = critical,CA:TRUE''' ) def load(self, script, name=None): diff --git a/test/unit/check/cargo_component.py b/test/unit/check/cargo_component.py new file mode 100644 index 00000000..1c194bfc --- /dev/null +++ b/test/unit/check/cargo_component.py @@ -0,0 +1,4 @@ +from unit.applications.lang.wasm_component import ApplicationWasmComponent + +def check_cargo_component(): + return ApplicationWasmComponent.prepare_env('hello_world') is not None diff --git a/test/unit/check/discover_available.py b/test/unit/check/discover_available.py index 1383a0c3..99e63604 100644 --- a/test/unit/check/discover_available.py +++ b/test/unit/check/discover_available.py @@ -1,6 +1,7 @@ import subprocess import sys +from unit.check.cargo_component import check_cargo_component from unit.check.chroot import check_chroot from unit.check.go import check_go from unit.check.isolation import check_isolation @@ -28,7 +29,7 @@ def discover_available(unit): # discover modules from log file - for module in Log.findall(r'module: ([a-zA-Z]+) (.*) ".*"$'): + for module in Log.findall(r'module: ([a-zA-Z\-]+) (.*) ".*"$'): versions = option.available['modules'].setdefault(module[0], []) if module[1] not in versions: versions.append(module[1]) @@ -44,6 +45,7 @@ def discover_available(unit): # Discover features using check. Features should be discovered after # modules since some features can require modules. + option.available['features']['cargo_component'] = check_cargo_component() option.available['features']['chroot'] = check_chroot() option.available['features']['isolation'] = check_isolation() option.available['features']['unix_abstract'] = check_unix_abstract() diff --git a/test/unit/http.py b/test/unit/http.py index 9401501b..e449c8b5 100644 --- a/test/unit/http.py +++ b/test/unit/http.py @@ -67,7 +67,7 @@ class HTTP1: headers['Content-Type'] = content_type - if 'Content-Length' not in headers: + if 'Content-Length' not in headers and 'Transfer-Encoding' not in headers: headers['Content-Length'] = len(body) for header, value in headers.items(): diff --git a/test/unit/status.py b/test/unit/status.py index 95096a96..679008d0 100644 --- a/test/unit/status.py +++ b/test/unit/status.py @@ -6,16 +6,16 @@ class Status: control = Control() def _check_zeros(): - assert Status.control.conf_get('/status') == { - 'connections': { + status = Status.control.conf_get('/status') + + assert status['connections'] == { 'accepted': 0, 'active': 0, 'idle': 0, 'closed': 0, - }, - 'requests': {'total': 0}, - 'applications': {}, } + assert status['requests'] == {'total': 0} + assert status['applications'] == {} def init(status=None): Status._status = ( @@ -31,6 +31,9 @@ class Status: if k in d2 } + if isinstance(d1, str) or isinstance(d1, list): + return d1 == d2 + return d1 - d2 return find_diffs(Status.control.conf_get('/status'), Status._status) diff --git a/test/wasm_component/hello_world/Cargo.lock b/test/wasm_component/hello_world/Cargo.lock new file mode 100644 index 00000000..2daeb73d --- /dev/null +++ b/test/wasm_component/hello_world/Cargo.lock @@ -0,0 +1,34 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "test-wasi-component" +version = "0.1.0" +dependencies = [ + "bitflags", + "wasi", + "wit-bindgen-rt", +] + +[[package]] +name = "wasi" +version = "0.13.0+wasi-0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "652cd73449d0b957a2743b70c72d79d34a5fa505696488f4ca90b46f6da94118" +dependencies = [ + "bitflags", + "wit-bindgen-rt", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "026d24a27f6712541fa534f2954bd9e0eb66172f033c2157c0f31d106255c497" diff --git a/test/wasm_component/hello_world/Cargo.toml b/test/wasm_component/hello_world/Cargo.toml new file mode 100644 index 00000000..a87fbeb5 --- /dev/null +++ b/test/wasm_component/hello_world/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "test-wasi-component" +version = "0.1.0" +edition = "2021" + +[dependencies] +bitflags = "2.4.2" +wit-bindgen-rt = "0.21.0" +wasi = "0.13.0" + +[lib] +crate-type = ["cdylib"] + +[package.metadata.component] +package = "component:test-wasi-component" +proxy = true + +[package.metadata.component.dependencies] diff --git a/test/wasm_component/hello_world/src/bindings.rs b/test/wasm_component/hello_world/src/bindings.rs new file mode 100644 index 00000000..a0d74c42 --- /dev/null +++ b/test/wasm_component/hello_world/src/bindings.rs @@ -0,0 +1,109 @@ +// Generated by `wit-bindgen` 0.24.0. DO NOT EDIT! +// Options used: +#[doc(hidden)] +#[allow(non_snake_case)] +pub unsafe fn _export_hello_world_cabi<T: Guest>() -> *mut u8 { + #[cfg(target_arch = "wasm32")] + _rt::run_ctors_once(); + let result0 = T::hello_world(); + let ptr1 = _RET_AREA.0.as_mut_ptr().cast::<u8>(); + let vec2 = (result0.into_bytes()).into_boxed_slice(); + let ptr2 = vec2.as_ptr().cast::<u8>(); + let len2 = vec2.len(); + ::core::mem::forget(vec2); + *ptr1.add(4).cast::<usize>() = len2; + *ptr1.add(0).cast::<*mut u8>() = ptr2.cast_mut(); + ptr1 +} +#[doc(hidden)] +#[allow(non_snake_case)] +pub unsafe fn __post_return_hello_world<T: Guest>(arg0: *mut u8) { + let l0 = *arg0.add(0).cast::<*mut u8>(); + let l1 = *arg0.add(4).cast::<usize>(); + _rt::cabi_dealloc(l0, l1, 1); +} +pub trait Guest { + fn hello_world() -> _rt::String; +} +#[doc(hidden)] + +macro_rules! __export_world_example_cabi{ + ($ty:ident with_types_in $($path_to_types:tt)*) => (const _: () = { + + #[export_name = "hello-world"] + unsafe extern "C" fn export_hello_world() -> *mut u8 { + $($path_to_types)*::_export_hello_world_cabi::<$ty>() + } + #[export_name = "cabi_post_hello-world"] + unsafe extern "C" fn _post_return_hello_world(arg0: *mut u8,) { + $($path_to_types)*::__post_return_hello_world::<$ty>(arg0) + } + };); +} +#[doc(hidden)] +pub(crate) use __export_world_example_cabi; +#[repr(align(4))] +struct _RetArea([::core::mem::MaybeUninit<u8>; 8]); +static mut _RET_AREA: _RetArea = + _RetArea([::core::mem::MaybeUninit::uninit(); 8]); +mod _rt { + + #[cfg(target_arch = "wasm32")] + pub fn run_ctors_once() { + wit_bindgen_rt::run_ctors_once(); + } + pub unsafe fn cabi_dealloc(ptr: *mut u8, size: usize, align: usize) { + if size == 0 { + return; + } + let layout = alloc::Layout::from_size_align_unchecked(size, align); + alloc::dealloc(ptr as *mut u8, layout); + } + pub use alloc_crate::alloc; + pub use alloc_crate::string::String; + extern crate alloc as alloc_crate; +} + +/// Generates `#[no_mangle]` functions to export the specified type as the +/// root implementation of all generated traits. +/// +/// For more information see the documentation of `wit_bindgen::generate!`. +/// +/// ```rust +/// # macro_rules! export{ ($($t:tt)*) => (); } +/// # trait Guest {} +/// struct MyType; +/// +/// impl Guest for MyType { +/// // ... +/// } +/// +/// export!(MyType); +/// ``` +#[allow(unused_macros)] +#[doc(hidden)] + +macro_rules! __export_example_impl { + ($ty:ident) => (self::export!($ty with_types_in self);); + ($ty:ident with_types_in $($path_to_types_root:tt)*) => ( + $($path_to_types_root)*::__export_world_example_cabi!($ty with_types_in $($path_to_types_root)*); + ) +} +#[doc(inline)] +pub(crate) use __export_example_impl as export; + +#[cfg(target_arch = "wasm32")] +#[link_section = "component-type:wit-bindgen:0.24.0:example:encoded world"] +#[doc(hidden)] +pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 194] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07E\x01A\x02\x01A\x02\x01\ +@\0\0s\x04\0\x0bhello-world\x01\0\x04\x01%component:test-wasi-component/example\x04\ +\0\x0b\x0d\x01\0\x07example\x03\0\0\0G\x09producers\x01\x0cprocessed-by\x02\x0dw\ +it-component\x070.202.0\x10wit-bindgen-rust\x060.24.0"; + +#[inline(never)] +#[doc(hidden)] +#[cfg(target_arch = "wasm32")] +pub fn __link_custom_section_describing_imports() { + wit_bindgen_rt::maybe_link_cabi_realloc(); +} diff --git a/test/wasm_component/hello_world/src/lib.rs b/test/wasm_component/hello_world/src/lib.rs new file mode 100644 index 00000000..a1e40ef6 --- /dev/null +++ b/test/wasm_component/hello_world/src/lib.rs @@ -0,0 +1,31 @@ +use wasi::http::types::{ + Fields, IncomingRequest, OutgoingBody, OutgoingResponse, ResponseOutparam, +}; + +wasi::http::proxy::export!(Component); + +struct Component; + +impl wasi::exports::http::incoming_handler::Guest for Component { + fn handle(_request: IncomingRequest, response_out: ResponseOutparam) { + + let hdrs = Fields::new(); + let mesg = String::from("Hello"); + let _try = hdrs.set(&"Content-Type".to_string(), &[b"plain/text".to_vec()]); + let _try = hdrs.set(&"Content-Length".to_string(), &[mesg.len().to_string().as_bytes().to_vec()]); + + let resp = OutgoingResponse::new(hdrs); + + // Add the HTTP Response Status Code + resp.set_status_code(200).unwrap(); + + let body = resp.body().unwrap(); + ResponseOutparam::set(response_out, Ok(resp)); + + let out = body.write().unwrap(); + out.blocking_write_and_flush(mesg.as_bytes()).unwrap(); + drop(out); + + OutgoingBody::finish(body, None).unwrap(); + } +} diff --git a/test/wasm_component/hello_world/wit/world.wit b/test/wasm_component/hello_world/wit/world.wit new file mode 100644 index 00000000..82c810ef --- /dev/null +++ b/test/wasm_component/hello_world/wit/world.wit @@ -0,0 +1,6 @@ +package component:test-wasi-component; + +/// An example world for the component to target. +world example { + export hello-world: func() -> string; +} |