add initial support for data scheme

This commit is contained in:
Matthew Welch 2025-02-08 20:27:47 -08:00
parent 015e46134e
commit 5f4826cc73
2 changed files with 78 additions and 21 deletions

View File

@ -8,6 +8,7 @@ SUPPORTED_SCHEMES = [
"http",
"https",
"file",
"data"
]
DEFAULT_FILE = "default.html"
@ -22,6 +23,9 @@ class URL:
self.query: str = ""
self.fragment: str = ""
self.default_port = False
self.media_type = ""
self.media_encoding = ""
self.data = ""
if url_string is not None:
parse_url(url_string, self)
@ -75,10 +79,13 @@ class Request:
self.request_string += "\r\n"
def send_request(self, *args, **kwargs):
if self.url.scheme in ["http", "https"]:
return self.http_request(*args, **kwargs)
elif self.url.scheme == "file":
return self.file_request()
if hasattr(self, f"{self.url.scheme.lower()}_request"):
request = getattr(self, f"{self.url.scheme.lower()}_request")
return request(*args, **kwargs)
raise ValueError(f"scheme '{self.url.scheme}' is not supported.")
def https_request(self, *args, **kwargs):
return self.http_request(*args, **kwargs)
def http_request(self, method: str = "GET", headers: dict = None) -> str:
if headers is not None:
@ -121,6 +128,9 @@ class Request:
with open(self.url.path) as f:
return f.read()
def data_request(self):
return self.url.data
def parse_url(url_string: str, url: URL | None = None) -> tuple[URL, bool]:
has_authority = False
@ -164,26 +174,29 @@ def parse_url(url_string: str, url: URL | None = None) -> tuple[URL, bool]:
if url_string.startswith("#"):
url.fragment = url_string[1:]
if url.scheme == "http":
url.port = 80
url.default_port = True
elif url.scheme == "https":
url.port = 443
url.default_port = False
match url.scheme:
case "http":
url.port = 80
url.default_port = True
case "https":
url.port = 443
url.default_port = False
case "file":
if sys.platform == "win32" and url.path.startswith("/") and ":" in url.path:
url.path = url.path[1:]
if url.path == "" or url.path == "/":
return url, False
case "data":
if url.host != "":
return url, False
url.media_type, url.data = url.path.split(",", 1)
if ";" in url.media_type:
url.media_type, url.media_encoding = url.media_type.split(";", 1)
if url.scheme in ["http", "https"]:
if url.path == "" or url.path is None:
url.path = "/"
if url.scheme == "file":
print(f"{url.scheme=}")
print(f"{url.host=}")
print(f"{url.port=}")
print(f"{url.path=}")
print(f"{url.query=}")
print(f"{url.fragment=}")
if sys.platform == "win32" and url.path.startswith("/") and ":" in url.path:
url.path = url.path[1:]
if url.path == "" or url.path == "/":
return url, False
if ":" in url.host:
url.host, port = url.host.split(":", 1)

View File

@ -147,6 +147,16 @@ from browser import Request
"",
True,
),
(
"data:text,This is some text",
"data",
"",
-1,
"text,This is some text",
"",
"",
True,
),
],
)
def test_url_parsing(url_string, scheme, host, port, path, query, fragment, parse_success):
@ -160,6 +170,40 @@ def test_url_parsing(url_string, scheme, host, port, path, query, fragment, pars
assert success == parse_success
@pytest.mark.parametrize(
"url_string,media_type,media_encoding,data,parse_success",
[
(
"data:text,This is some text",
"text",
"",
"This is some text",
True,
),
(
"data:text/plain,This is some text",
"text/plain",
"",
"This is some text",
True,
),
(
"data:text/plain;base64,This is some text",
"text/plain",
"base64",
"This is some text",
True,
),
],
)
def test_data_url_parsing(url_string, media_type, media_encoding, data, parse_success):
url, success = parse_url(url_string)
assert url.media_type == media_type
assert url.media_encoding == media_encoding
assert url.data == data
assert success == parse_success
@pytest.mark.parametrize(
"http_server,url_string",
[