mirror of
https://github.com/pyscript/pyscript.git
synced 2025-12-21 11:15:36 -05:00
[Worker support] test for no cors headers (#1374)
* test for no cors headers * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix tests * suggested changes * disable directly * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add error message * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * improve test * improve error message * remove py-config tag from cors test --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
@@ -162,6 +162,21 @@ export class PyScriptApp {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
this.config = loadConfigFromElement(el);
|
this.config = loadConfigFromElement(el);
|
||||||
|
if (this.config.execution_thread === 'worker' && crossOriginIsolated === false) {
|
||||||
|
throw new UserError(
|
||||||
|
ErrorCode.BAD_CONFIG,
|
||||||
|
`When execution_thread is "worker", the site must be cross origin isolated, but crossOriginIsolated is false.
|
||||||
|
To be cross origin isolated, the server must use https and also serve with the following headers: ${JSON.stringify(
|
||||||
|
{
|
||||||
|
'Cross-Origin-Embedder-Policy': 'require-corp',
|
||||||
|
'Cross-Origin-Opener-Policy': 'same-origin',
|
||||||
|
},
|
||||||
|
)}.
|
||||||
|
|
||||||
|
The problem may be that one or both of these are missing.
|
||||||
|
`,
|
||||||
|
);
|
||||||
|
}
|
||||||
logger.info('config loaded:\n' + JSON.stringify(this.config, null, 2));
|
logger.info('config loaded:\n' + JSON.stringify(this.config, null, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -123,13 +123,17 @@ def browser_type_launch_args(request):
|
|||||||
return launch_options
|
return launch_options
|
||||||
|
|
||||||
|
|
||||||
class HTTPServer(SuperHTTPServer):
|
class DevServer(SuperHTTPServer):
|
||||||
"""
|
"""
|
||||||
Class for wrapper to run SimpleHTTPServer on Thread.
|
Class for wrapper to run SimpleHTTPServer on Thread.
|
||||||
Ctrl +Only Thread remains dead when terminated with C.
|
Ctrl +Only Thread remains dead when terminated with C.
|
||||||
Keyboard Interrupt passes.
|
Keyboard Interrupt passes.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def __init__(self, base_url, *args, **kwargs):
|
||||||
|
self.base_url = base_url
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
try:
|
try:
|
||||||
self.serve_forever()
|
self.serve_forever()
|
||||||
@@ -140,15 +144,26 @@ class HTTPServer(SuperHTTPServer):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
def http_server(logger):
|
def dev_server(logger):
|
||||||
class MyHTTPRequestHandler(SimpleHTTPRequestHandler):
|
class MyHTTPRequestHandler(SimpleHTTPRequestHandler):
|
||||||
|
enable_cors_headers = True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def my_headers(cls):
|
||||||
|
if cls.enable_cors_headers:
|
||||||
|
return {
|
||||||
|
"Cross-Origin-Embedder-Policy": "require-corp",
|
||||||
|
"Cross-Origin-Opener-Policy": "same-origin",
|
||||||
|
}
|
||||||
|
return {}
|
||||||
|
|
||||||
def end_headers(self):
|
def end_headers(self):
|
||||||
self.send_my_headers()
|
self.send_my_headers()
|
||||||
SimpleHTTPRequestHandler.end_headers(self)
|
SimpleHTTPRequestHandler.end_headers(self)
|
||||||
|
|
||||||
def send_my_headers(self):
|
def send_my_headers(self):
|
||||||
self.send_header("Cross-Origin-Embedder-Policy", "require-corp")
|
for k, v in self.my_headers().items():
|
||||||
self.send_header("Cross-Origin-Opener-Policy", "same-origin")
|
self.send_header(k, v)
|
||||||
|
|
||||||
def log_message(self, fmt, *args):
|
def log_message(self, fmt, *args):
|
||||||
logger.log("http_server", fmt % args, color="blue")
|
logger.log("http_server", fmt % args, color="blue")
|
||||||
@@ -157,12 +172,12 @@ def http_server(logger):
|
|||||||
base_url = f"http://{host}:{port}"
|
base_url = f"http://{host}:{port}"
|
||||||
|
|
||||||
# serve_Run forever under thread
|
# serve_Run forever under thread
|
||||||
server = HTTPServer((host, port), MyHTTPRequestHandler)
|
server = DevServer(base_url, (host, port), MyHTTPRequestHandler)
|
||||||
|
|
||||||
thread = threading.Thread(None, server.run)
|
thread = threading.Thread(None, server.run)
|
||||||
thread.start()
|
thread.start()
|
||||||
|
|
||||||
yield base_url # Transition to test here
|
yield server # Transition to test here
|
||||||
|
|
||||||
# End thread
|
# End thread
|
||||||
server.shutdown()
|
server.shutdown()
|
||||||
|
|||||||
@@ -158,16 +158,17 @@ class PyScriptTest:
|
|||||||
self.tmpdir.chdir()
|
self.tmpdir.chdir()
|
||||||
self.logger = logger
|
self.logger = logger
|
||||||
self.execution_thread = execution_thread
|
self.execution_thread = execution_thread
|
||||||
|
self.dev_server = None
|
||||||
|
|
||||||
if request.config.option.no_fake_server:
|
if request.config.option.no_fake_server:
|
||||||
# use a real HTTP server. Note that as soon as we request the
|
# use a real HTTP server. Note that as soon as we request the
|
||||||
# fixture, the server automatically starts in its own thread.
|
# fixture, the server automatically starts in its own thread.
|
||||||
self.http_server = request.getfixturevalue("http_server")
|
self.dev_server = request.getfixturevalue("dev_server")
|
||||||
|
self.http_server_addr = self.dev_server.base_url
|
||||||
self.router = None
|
self.router = None
|
||||||
self.is_fake_server = False
|
|
||||||
else:
|
else:
|
||||||
# use the internal playwright routing
|
# use the internal playwright routing
|
||||||
self.http_server = "https://fake_server"
|
self.http_server_addr = "https://fake_server"
|
||||||
self.router = SmartRouter(
|
self.router = SmartRouter(
|
||||||
"fake_server",
|
"fake_server",
|
||||||
cache=request.config.cache,
|
cache=request.config.cache,
|
||||||
@@ -175,7 +176,6 @@ class PyScriptTest:
|
|||||||
usepdb=request.config.option.usepdb,
|
usepdb=request.config.option.usepdb,
|
||||||
)
|
)
|
||||||
self.router.install(page)
|
self.router.install(page)
|
||||||
self.is_fake_server = True
|
|
||||||
#
|
#
|
||||||
self.init_page(page)
|
self.init_page(page)
|
||||||
#
|
#
|
||||||
@@ -211,6 +211,18 @@ class PyScriptTest:
|
|||||||
page.on("console", self._on_console)
|
page.on("console", self._on_console)
|
||||||
page.on("pageerror", self._on_pageerror)
|
page.on("pageerror", self._on_pageerror)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def headers(self):
|
||||||
|
if self.dev_server is None:
|
||||||
|
return self.router.headers
|
||||||
|
return self.dev_server.RequestHandlerClass.my_headers()
|
||||||
|
|
||||||
|
def disable_cors_headers(self):
|
||||||
|
if self.dev_server is None:
|
||||||
|
self.router.enable_cors_headers = False
|
||||||
|
else:
|
||||||
|
self.dev_server.RequestHandlerClass.enable_cors_headers = False
|
||||||
|
|
||||||
def run_js(self, code):
|
def run_js(self, code):
|
||||||
"""
|
"""
|
||||||
allows top level await to be present in the `code` parameter
|
allows top level await to be present in the `code` parameter
|
||||||
@@ -301,7 +313,7 @@ class PyScriptTest:
|
|||||||
def goto(self, path):
|
def goto(self, path):
|
||||||
self.logger.reset()
|
self.logger.reset()
|
||||||
self.logger.log("page.goto", path, color="yellow")
|
self.logger.log("page.goto", path, color="yellow")
|
||||||
url = f"{self.http_server}/{path}"
|
url = f"{self.http_server_addr}/{path}"
|
||||||
self.page.goto(url, timeout=0)
|
self.page.goto(url, timeout=0)
|
||||||
|
|
||||||
def wait_for_console(
|
def wait_for_console(
|
||||||
@@ -433,8 +445,8 @@ class PyScriptTest:
|
|||||||
doc = f"""
|
doc = f"""
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<link rel="stylesheet" href="{self.http_server}/build/pyscript.css" />
|
<link rel="stylesheet" href="{self.http_server_addr}/build/pyscript.css" />
|
||||||
<script defer src="{self.http_server}/build/pyscript.js"></script>
|
<script defer src="{self.http_server_addr}/build/pyscript.js"></script>
|
||||||
{extra_head}
|
{extra_head}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -851,6 +863,16 @@ class SmartRouter:
|
|||||||
self.usepdb = usepdb
|
self.usepdb = usepdb
|
||||||
self.page = None
|
self.page = None
|
||||||
self.requests = [] # (status, kind, url)
|
self.requests = [] # (status, kind, url)
|
||||||
|
self.enable_cors_headers = True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def headers(self):
|
||||||
|
if self.enable_cors_headers:
|
||||||
|
return {
|
||||||
|
"Cross-Origin-Embedder-Policy": "require-corp",
|
||||||
|
"Cross-Origin-Opener-Policy": "same-origin",
|
||||||
|
}
|
||||||
|
return {}
|
||||||
|
|
||||||
def install(self, page):
|
def install(self, page):
|
||||||
"""
|
"""
|
||||||
@@ -903,13 +925,9 @@ class SmartRouter:
|
|||||||
assert url.path[0] == "/"
|
assert url.path[0] == "/"
|
||||||
relative_path = url.path[1:]
|
relative_path = url.path[1:]
|
||||||
if os.path.exists(relative_path):
|
if os.path.exists(relative_path):
|
||||||
headers = {
|
route.fulfill(status=200, headers=self.headers, path=relative_path)
|
||||||
"Cross-Origin-Embedder-Policy": "require-corp",
|
|
||||||
"Cross-Origin-Opener-Policy": "same-origin",
|
|
||||||
}
|
|
||||||
route.fulfill(status=200, headers=headers, path=relative_path)
|
|
||||||
else:
|
else:
|
||||||
route.fulfill(status=404)
|
route.fulfill(status=404, headers=self.headers)
|
||||||
return
|
return
|
||||||
|
|
||||||
# network requests might be cached
|
# network requests might be cached
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ class TestSupport(PyScriptTest):
|
|||||||
f"""
|
f"""
|
||||||
JS errors found: 1
|
JS errors found: 1
|
||||||
Error: this is an error
|
Error: this is an error
|
||||||
at {self.http_server}/mytest.html:.*
|
at {self.http_server_addr}/mytest.html:.*
|
||||||
"""
|
"""
|
||||||
).strip()
|
).strip()
|
||||||
assert re.search(expected, msg)
|
assert re.search(expected, msg)
|
||||||
|
|||||||
@@ -32,6 +32,30 @@ class TestBasic(PyScriptTest):
|
|||||||
expected = f"[pyscript/main] Starting the interpreter in {where}"
|
expected = f"[pyscript/main] Starting the interpreter in {where}"
|
||||||
assert expected in self.console.info.lines
|
assert expected in self.console.info.lines
|
||||||
|
|
||||||
|
def test_no_cors_headers(self):
|
||||||
|
self.disable_cors_headers()
|
||||||
|
self.pyscript_run(
|
||||||
|
"""
|
||||||
|
<!-- we don't really need anything here, we just want to check that
|
||||||
|
pyscript starts -->
|
||||||
|
""",
|
||||||
|
wait_for_pyscript=False,
|
||||||
|
)
|
||||||
|
assert self.headers == {}
|
||||||
|
if self.execution_thread == "worker":
|
||||||
|
expected_alert_banner_msg = (
|
||||||
|
'(PY1000): When execution_thread is "worker", the site must be cross origin '
|
||||||
|
"isolated, but crossOriginIsolated is false. To be cross origin isolated, "
|
||||||
|
"the server must use https and also serve with the following headers: "
|
||||||
|
'{"Cross-Origin-Embedder-Policy":"require-corp",'
|
||||||
|
'"Cross-Origin-Opener-Policy":"same-origin"}. '
|
||||||
|
"The problem may be that one or both of these are missing."
|
||||||
|
)
|
||||||
|
alert_banner = self.page.wait_for_selector(".alert-banner")
|
||||||
|
assert expected_alert_banner_msg in alert_banner.inner_text()
|
||||||
|
else:
|
||||||
|
self.assert_no_banners()
|
||||||
|
|
||||||
def test_print(self):
|
def test_print(self):
|
||||||
self.pyscript_run(
|
self.pyscript_run(
|
||||||
"""
|
"""
|
||||||
|
|||||||
Reference in New Issue
Block a user