feat: X-Redirect pages

This commit is contained in:
2026-04-20 11:36:56 +02:00
parent 5fb10ffb9d
commit 5671d68e21
11 changed files with 334 additions and 73 deletions
+151 -11
View File
@@ -156,6 +156,24 @@ class TestRequestHandler(BaseHandlerTestCase):
handler.data_dir = self.data_dir
return handler
def test_do_head_redirect(self) -> None:
handler = self._get_handler("/path")
with (
self.mock_call(
self.registry.get_from_path,
["path"],
Page("path", redirect="https://example.com"),
),
self.expects_status_only(
handler,
http.HTTPStatus.MOVED_PERMANENTLY,
headers={"Location": "https://example.com"},
),
self.patch("http.server.SimpleHTTPRequestHandler.do_HEAD", count=0),
self.seal_mocks(),
):
handler.do_HEAD()
def test_do_head_proxy(self) -> None:
handler = self._get_handler()
with (
@@ -165,9 +183,28 @@ class TestRequestHandler(BaseHandlerTestCase):
handler.do_HEAD()
def test_do_get_index(self) -> None:
handler = self._get_handler("/")
handler = self._get_handler()
with (
self.expects_basic_body(handler, handler.server_signature()),
self.patch("http.server.SimpleHTTPRequestHandler.do_GET", count=0),
self.seal_mocks(),
):
handler.do_GET()
def test_do_get_redirect(self) -> None:
handler = self._get_handler("/path")
with (
self.mock_call(
self.registry.get_from_path,
["path"],
Page("path", redirect="https://example.com"),
),
self.expects_status_only(
handler,
http.HTTPStatus.MOVED_PERMANENTLY,
headers={"Location": "https://example.com"},
),
self.patch("http.server.SimpleHTTPRequestHandler.do_GET", count=0),
self.seal_mocks(),
):
handler.do_GET()
@@ -175,6 +212,10 @@ class TestRequestHandler(BaseHandlerTestCase):
def test_do_get_proxy_on_other_path(self) -> None:
handler = self._get_handler("/file")
with (
self.mock_call(
self.registry.get_from_path,
["file"],
),
self.patch("http.server.SimpleHTTPRequestHandler.do_GET"),
self.seal_mocks(),
):
@@ -183,6 +224,10 @@ class TestRequestHandler(BaseHandlerTestCase):
def test_do_get_proxy_on_other_host(self) -> None:
handler = self._get_handler("/", {"Host": "other_host"})
with (
self.mock_call(
self.registry.get_from_host,
["other_host"],
),
self.patch("http.server.SimpleHTTPRequestHandler.do_GET"),
self.seal_mocks(),
):
@@ -271,7 +316,7 @@ class TestRequestHandler(BaseHandlerTestCase):
):
handler.do_PUT()
def test_do_put_no_content(self) -> None:
def test_do_put_extract_no_content(self) -> None:
handler = self._get_handler("/path", {"X-Token": "secret"})
with (
self.mock_call(self.token_manager.is_valid, ["secret"], True), # noqa: FBT003
@@ -287,7 +332,7 @@ class TestRequestHandler(BaseHandlerTestCase):
):
handler.do_PUT()
def test_do_put_content_too_large(self) -> None:
def test_do_put_extract_content_too_large(self) -> None:
handler = self._get_handler(
"/path", {"X-Token": "secret", "Content-Length": "999999999"}
)
@@ -307,7 +352,7 @@ class TestRequestHandler(BaseHandlerTestCase):
):
handler.do_PUT()
def test_do_put_tar_error(self) -> None:
def test_do_put_extract_tar_error(self) -> None:
handler = self._get_handler(
"/path", {"X-Token": "secret", "Content-Length": "1"}
)
@@ -328,7 +373,7 @@ class TestRequestHandler(BaseHandlerTestCase):
handler.do_PUT()
self.data_dir.extract_tar_bytes.assert_called_once()
def test_do_put_extract_error(self) -> None:
def test_do_put_extract_other_error(self) -> None:
handler = self._get_handler(
"/path", {"X-Token": "secret", "Content-Length": "1"}
)
@@ -347,7 +392,7 @@ class TestRequestHandler(BaseHandlerTestCase):
handler.do_PUT()
self.data_dir.extract_tar_bytes.assert_called_once()
def test_do_put_ok(self) -> None:
def test_do_put_extract_ok(self) -> None:
handler = self._get_handler(
"/path", {"X-Token": "secret", "Content-Length": "1"}
)
@@ -361,7 +406,7 @@ class TestRequestHandler(BaseHandlerTestCase):
),
self.mock_call_unchecked(self.data_dir.extract_tar_bytes),
self.mock_call(self.registry.add, ["path"]),
self.mock_call(self.token_manager.set_token, ["secret", "path"]),
self.mock_call(self.token_manager.set_token, ["path", "secret"]),
self.expects_status_only(
handler, http.HTTPStatus.CREATED, "Resource /path/ updated"
),
@@ -369,7 +414,7 @@ class TestRequestHandler(BaseHandlerTestCase):
):
handler.do_PUT()
def test_do_put_ok_with_host_fail_init(self) -> None:
def test_do_put_extract_with_host_fail_init(self) -> None:
handler = self._get_handler(
"/path",
{"X-Token": "secret", "Content-Length": "1", "X-Host": "example.com"},
@@ -385,7 +430,7 @@ class TestRequestHandler(BaseHandlerTestCase):
self.mock_call(self.registry.get_from_host, ["example.com"], Page("path")),
self.mock_call_unchecked(self.data_dir.extract_tar_bytes),
self.mock_call(self.registry.add, ["path"]),
self.mock_call(self.token_manager.set_token, ["secret", "path"]),
self.mock_call(self.token_manager.set_token, ["path", "secret"]),
self.mock_call(self.cert_manager.create_or_update, ["example.com"], False), # noqa: FBT003
self.expects_status_only(
handler, http.HTTPStatus.CREATED, "Resource /path/ updated"
@@ -394,7 +439,7 @@ class TestRequestHandler(BaseHandlerTestCase):
):
handler.do_PUT()
def test_do_put_ok_with_host(self) -> None:
def test_do_put_extract_with_host(self) -> None:
handler = self._get_handler(
"/path",
{"X-Token": "secret", "Content-Length": "1", "X-Host": "example.com"},
@@ -410,7 +455,88 @@ class TestRequestHandler(BaseHandlerTestCase):
self.mock_call(self.registry.get_from_host, ["example.com"], Page("path")),
self.mock_call_unchecked(self.data_dir.extract_tar_bytes),
self.mock_call(self.registry.add, ["path"]),
self.mock_call(self.token_manager.set_token, ["secret", "path"]),
self.mock_call(self.token_manager.set_token, ["path", "secret"]),
self.mock_call(self.cert_manager.create_or_update, ["example.com"], True), # noqa: FBT003
self.mock_call(self.registry.set_host, ["path", "example.com"]),
self.expects_status_only(
handler, http.HTTPStatus.CREATED, "Resource /path/ updated"
),
self.seal_mocks(),
):
handler.do_PUT()
def test_do_put_redirect_with_content(self) -> None:
handler = self._get_handler(
"/path",
{
"X-Token": "secret",
"X-Redirect": "https://example.com",
"Content-Length": "1",
},
)
with (
self.mock_call(self.token_manager.is_valid, ["secret"], True), # noqa: FBT003
self.mock_call(
self.token_manager.is_valid_for_path,
["secret", "path"],
True, # noqa: FBT003
),
self.expects_error(
handler,
http.HTTPStatus.BAD_REQUEST,
"No content must be sent with X-Redirect",
),
self.seal_mocks(),
):
handler.do_PUT()
def test_do_put_redirect_ok(self) -> None:
handler = self._get_handler(
"/path",
{
"X-Token": "secret",
"X-Redirect": "https://example.com",
},
)
with (
self.mock_call(self.token_manager.is_valid, ["secret"], True), # noqa: FBT003
self.mock_call(
self.token_manager.is_valid_for_path,
["secret", "path"],
True, # noqa: FBT003
),
self.mock_call(self.data_dir.empty, ["path"]),
self.mock_call(self.registry.add, ["path"]),
self.mock_call(self.token_manager.set_token, ["path", "secret"]),
self.mock_call(self.registry.set_redirect, ["path", "https://example.com"]),
self.expects_status_only(
handler, http.HTTPStatus.CREATED, "Resource /path/ updated"
),
self.seal_mocks(),
):
handler.do_PUT()
def test_do_put_redirect_with_host(self) -> None:
handler = self._get_handler(
"/path",
{
"X-Token": "secret",
"X-Redirect": "https://example.com",
"X-Host": "example.com",
},
)
with (
self.mock_call(self.token_manager.is_valid, ["secret"], True), # noqa: FBT003
self.mock_call(
self.token_manager.is_valid_for_path,
["secret", "path"],
True, # noqa: FBT003
),
self.mock_call(self.registry.get_from_host, ["example.com"], Page("path")),
self.mock_call(self.data_dir.empty, ["path"]),
self.mock_call(self.registry.add, ["path"]),
self.mock_call(self.token_manager.set_token, ["path", "secret"]),
self.mock_call(self.registry.set_redirect, ["path", "https://example.com"]),
self.mock_call(self.cert_manager.create_or_update, ["example.com"], True), # noqa: FBT003
self.mock_call(self.registry.set_host, ["path", "example.com"]),
self.expects_status_only(
@@ -577,6 +703,7 @@ class TestRequestHandler(BaseHandlerTestCase):
def test_translate_path_dotfile(self) -> None:
handler = self._get_handler()
with (
self.mock_call(self.registry.get_from_path, ["path"], Page("path")),
self.patch("http.server.SimpleHTTPRequestHandler.translate_path", count=0),
self.seal_mocks(),
):
@@ -603,6 +730,7 @@ class TestRequestHandler(BaseHandlerTestCase):
def test_translate_path_default_host(self) -> None:
handler = self._get_handler()
with (
self.mock_call(self.registry.get_from_path, ["path"], Page("path")),
self.patch_call(
"http.server.SimpleHTTPRequestHandler.translate_path",
["/path/index.html"],
@@ -614,6 +742,18 @@ class TestRequestHandler(BaseHandlerTestCase):
None,
)
def test_translate_path_default_host_not_found(self) -> None:
handler = self._get_handler()
with (
self.mock_call(self.registry.get_from_path, ["path"]),
self.patch("http.server.SimpleHTTPRequestHandler.translate_path", count=0),
self.seal_mocks(),
):
self.assertEqual(
handler.translate_path("/path/index.html"),
"",
)
class TestUpgradeHandler(BaseHandlerTestCase):
def _get_handler(