AK: Serialize URL hosts with 'concept-host-serializer'

In order to follow spec text to achieve this, we need to change the
underlying representation of a host in AK::URL to deserialized format.
Before this, we were parsing the host and then immediately serializing
it again.

Making that change resulted in a whole bunch of fallout.

After this change, callers can access the serialized data through
this concept-host-serializer. The functional end result of this
change is that IPv6 hosts are now correctly serialized to be
surrounded with '[' and ']'.
This commit is contained in:
Shannon Booth 2023-07-27 21:40:41 +12:00 committed by Andreas Kling
parent 768f070b86
commit 8751be09f9
Notes: sideshowbarker 2024-07-18 02:13:10 +09:00
36 changed files with 175 additions and 143 deletions

View file

@ -101,12 +101,18 @@ void URL::set_password(DeprecatedString password, ApplyPercentEncoding apply_per
m_valid = compute_validity();
}
void URL::set_host(DeprecatedString host)
void URL::set_host(Host host)
{
m_host = move(host);
m_valid = compute_validity();
}
// https://url.spec.whatwg.org/#concept-host-serializer
ErrorOr<String> URL::serialized_host() const
{
return URLParser::serialize_host(m_host);
}
void URL::set_port(Optional<u16> port)
{
if (port == default_port_for_scheme(m_scheme)) {
@ -157,7 +163,7 @@ bool URL::cannot_have_a_username_or_password_or_port() const
{
// A URL cannot have a username/password/port if its host is null or the empty string, or its scheme is "file".
// FIXME: The spec does not mention anything to do with 'cannot be a base URL'.
return m_host.is_null() || m_host.is_empty() || m_cannot_be_a_base_url || m_scheme == "file"sv;
return m_host.has<Empty>() || m_host == String {} || m_cannot_be_a_base_url || m_scheme == "file"sv;
}
// FIXME: This is by no means complete.
@ -192,7 +198,7 @@ bool URL::compute_validity() const
}
// NOTE: A file URL's host should be the empty string for localhost, not null.
if (m_scheme == "file" && m_host.is_null())
if (m_scheme == "file" && m_host.has<Empty>())
return false;
return true;
@ -227,7 +233,7 @@ URL URL::create_with_file_scheme(DeprecatedString const& path, DeprecatedString
url.set_scheme("file");
// NOTE: If the hostname is localhost (or null, which implies localhost), it should be set to the empty string.
// This is because a file URL always needs a non-null hostname.
url.set_host(hostname.is_null() || hostname == "localhost" ? DeprecatedString::empty() : hostname);
url.set_host(hostname.is_null() || hostname == "localhost" ? String {} : String::from_deprecated_string(hostname).release_value_but_fixme_should_propagate_errors());
url.set_paths(lexical_path.parts());
if (path.ends_with('/'))
url.append_slash();
@ -243,7 +249,8 @@ URL URL::create_with_help_scheme(DeprecatedString const& path, DeprecatedString
url.set_scheme("help");
// NOTE: If the hostname is localhost (or null, which implies localhost), it should be set to the empty string.
// This is because a file URL always needs a non-null hostname.
url.set_host(hostname.is_null() || hostname == "localhost" ? DeprecatedString::empty() : hostname);
url.set_host(hostname.is_null() || hostname == "localhost" ? String {} : String::from_deprecated_string(hostname).release_value_but_fixme_should_propagate_errors());
url.set_paths(lexical_path.parts());
if (path.ends_with('/'))
url.append_slash();
@ -309,7 +316,7 @@ DeprecatedString URL::serialize(ExcludeFragment exclude_fragment) const
output.append(':');
// 2. If urls host is non-null:
if (!m_host.is_null()) {
if (!m_host.has<Empty>()) {
// 1. Append "//" to output.
output.append("//"sv);
@ -329,7 +336,7 @@ DeprecatedString URL::serialize(ExcludeFragment exclude_fragment) const
}
// 3. Append urls host, serialized, to output.
output.append(m_host);
output.append(serialized_host().release_value_but_fixme_should_propagate_errors());
// 4. If urls port is non-null, append U+003A (:) followed by urls port, serialized, to output.
if (m_port.has_value())
@ -342,7 +349,7 @@ DeprecatedString URL::serialize(ExcludeFragment exclude_fragment) const
if (cannot_be_a_base_url()) {
output.append(m_paths[0]);
} else {
if (m_host.is_null() && m_paths.size() > 1 && m_paths[0].is_empty())
if (m_host.has<Empty>() && m_paths.size() > 1 && m_paths[0].is_empty())
output.append("/."sv);
for (auto& segment : m_paths) {
output.append('/');
@ -379,9 +386,9 @@ DeprecatedString URL::serialize_for_display() const
builder.append(m_scheme);
builder.append(':');
if (!m_host.is_null()) {
if (!m_host.has<Empty>()) {
builder.append("//"sv);
builder.append(m_host);
builder.append(serialized_host().release_value_but_fixme_should_propagate_errors());
if (m_port.has_value())
builder.appendff(":{}", *m_port);
}
@ -389,7 +396,7 @@ DeprecatedString URL::serialize_for_display() const
if (cannot_be_a_base_url()) {
builder.append(m_paths[0]);
} else {
if (m_host.is_null() && m_paths.size() > 1 && m_paths[0].is_empty())
if (m_host.has<Empty>() && m_paths.size() > 1 && m_paths[0].is_empty())
builder.append("/."sv);
for (auto& segment : m_paths) {
builder.append('/');
@ -437,7 +444,7 @@ DeprecatedString URL::serialize_origin() const
StringBuilder builder;
builder.append(m_scheme);
builder.append("://"sv);
builder.append(m_host);
builder.append(serialized_host().release_value_but_fixme_should_propagate_errors());
if (m_port.has_value())
builder.appendff(":{}", *m_port);
return builder.to_deprecated_string();

View file

@ -79,7 +79,8 @@ public:
DeprecatedString const& scheme() const { return m_scheme; }
DeprecatedString username(ApplyPercentDecoding = ApplyPercentDecoding::Yes) const;
DeprecatedString password(ApplyPercentDecoding = ApplyPercentDecoding::Yes) const;
DeprecatedString const& host() const { return m_host; }
Host const& host() const { return m_host; }
ErrorOr<String> serialized_host() const;
DeprecatedString basename(ApplyPercentDecoding = ApplyPercentDecoding::Yes) const;
DeprecatedString query(ApplyPercentDecoding = ApplyPercentDecoding::No) const;
DeprecatedString fragment(ApplyPercentDecoding = ApplyPercentDecoding::Yes) const;
@ -101,7 +102,7 @@ public:
void set_scheme(DeprecatedString);
void set_username(DeprecatedString, ApplyPercentEncoding = ApplyPercentEncoding::Yes);
void set_password(DeprecatedString, ApplyPercentEncoding = ApplyPercentEncoding::Yes);
void set_host(DeprecatedString);
void set_host(Host);
void set_port(Optional<u16>);
void set_paths(Vector<DeprecatedString>, ApplyPercentEncoding = ApplyPercentEncoding::Yes);
void set_query(DeprecatedString, ApplyPercentEncoding = ApplyPercentEncoding::Yes);
@ -178,7 +179,7 @@ private:
DeprecatedString m_password;
// A URLs host is null or a host. It is initially null.
DeprecatedString m_host;
Host m_host;
// A URLs port is either null or a 16-bit unsigned integer that identifies a networking port. It is initially null.
Optional<u16> m_port;

View file

@ -32,7 +32,7 @@ static void report_validation_error(SourceLocation const& location = SourceLocat
dbgln_if(URL_PARSER_DEBUG, "URLParser::basic_parse: Validation error! {}", location);
}
static Optional<DeprecatedString> parse_opaque_host(StringView input)
static Optional<URL::Host> parse_opaque_host(StringView input)
{
auto forbidden_host_characters_excluding_percent = "\0\t\n\r #/:<>?@[\\]^|"sv;
for (auto character : forbidden_host_characters_excluding_percent) {
@ -43,7 +43,7 @@ static Optional<DeprecatedString> parse_opaque_host(StringView input)
}
// FIXME: If input contains a code point that is not a URL code point and not U+0025 (%), validation error.
// FIXME: If input contains a U+0025 (%) and the two code points following it are not ASCII hex digits, validation error.
return URL::percent_encode(input, URL::PercentEncodeSet::C0Control);
return String::from_deprecated_string(URL::percent_encode(input, URL::PercentEncodeSet::C0Control)).release_value_but_fixme_should_propagate_errors();
}
struct ParsedIPv4Number {
@ -549,7 +549,7 @@ static bool ends_in_a_number_checker(StringView input)
// https://url.spec.whatwg.org/#concept-host-parser
// NOTE: This is a very bare-bones implementation.
static Optional<DeprecatedString> parse_host(StringView input, bool is_not_special = false)
static Optional<URL::Host> parse_host(StringView input, bool is_not_special = false)
{
// 1. If input starts with U+005B ([), then:
if (input.starts_with('[')) {
@ -563,10 +563,7 @@ static Optional<DeprecatedString> parse_host(StringView input, bool is_not_speci
auto address = parse_ipv6_address(input.substring_view(1, input.length() - 2));
if (!address.has_value())
return {};
StringBuilder output;
serialize_ipv6_address(*address, output);
return output.to_deprecated_string();
return address.release_value();
}
// 2. If isNotSpecial is true, then return the result of opaque-host parsing input.
@ -581,12 +578,16 @@ static Optional<DeprecatedString> parse_host(StringView input, bool is_not_speci
// FIXME: 5. Let asciiDomain be the result of running domain to ASCII on domain.
// FIXME: 6. If asciiDomain is failure, then return failure.
auto& ascii_domain = domain;
auto ascii_domain_or_error = String::from_deprecated_string(domain);
if (ascii_domain_or_error.is_error())
return {};
auto ascii_domain = ascii_domain_or_error.release_value();
// 7. If asciiDomain contains a forbidden domain code point, domain-invalid-code-point validation error, return failure.
auto forbidden_host_characters = "\0\t\n\r #%/:<>?@[\\]^|"sv;
for (auto character : forbidden_host_characters) {
if (ascii_domain.view().contains(character)) {
if (ascii_domain.bytes_as_string_view().contains(character)) {
report_validation_error();
return {};
}
@ -598,11 +599,7 @@ static Optional<DeprecatedString> parse_host(StringView input, bool is_not_speci
if (!ipv4_host.has_value())
return {};
auto result = serialize_ipv4_address(*ipv4_host);
if (result.is_error())
return {};
return result.release_value().to_deprecated_string();
return ipv4_host.release_value();
}
// 9. Return asciiDomain.
@ -880,7 +877,7 @@ URL URLParser::basic_parse(StringView raw_input, Optional<URL> const& base_url,
return *url;
// 4. If urls scheme is "file" and its host is an empty host, then return.
if (url->scheme() == "file"sv && url->host().is_empty())
if (url->scheme() == "file"sv && url->host() == String {})
return *url;
}
@ -1319,7 +1316,7 @@ URL URLParser::basic_parse(StringView raw_input, Optional<URL> const& base_url,
url->m_scheme = "file";
// 2. Set urls host to the empty string.
url->m_host = "";
url->m_host = String {};
// 3. If c is U+002F (/) or U+005C (\), then:
if (code_point == '/' || code_point == '\\') {
@ -1422,7 +1419,7 @@ URL URLParser::basic_parse(StringView raw_input, Optional<URL> const& base_url,
// 2. Otherwise, if buffer is the empty string, then:
else if (buffer.is_empty()) {
// 1. Set urls host to the empty string.
url->m_host = "";
url->m_host = String {};
// 2. If state override is given, then return.
if (state_override.has_value())
@ -1442,8 +1439,8 @@ URL URLParser::basic_parse(StringView raw_input, Optional<URL> const& base_url,
return {};
// 3. If host is "localhost", then set host to the empty string.
if (host.value() == "localhost")
host = "";
if (host.value().has<String>() && host.value().get<String>() == "localhost"sv)
host = String {};
// 4. Set urls host to host.
url->m_host = host.release_value();
@ -1498,7 +1495,7 @@ URL URLParser::basic_parse(StringView raw_input, Optional<URL> const& base_url,
continue;
}
// 5. Otherwise, if state override is given and urls host is null, append the empty string to urls path.
else if (state_override.has_value() && url->host().is_empty()) {
else if (state_override.has_value() && url->host().has<Empty>()) {
url->append_slash();
}
break;

View file

@ -49,7 +49,7 @@ void LocationEdit::highlight_location()
if (url.is_valid() && !hasFocus()) {
if (url.scheme() == "http" || url.scheme() == "https" || url.scheme() == "gemini") {
int host_start = (url.scheme().length() + 3) - cursorPosition();
auto host_length = url.host().length();
auto host_length = url.serialized_host().release_value_but_fixme_should_propagate_errors().bytes().size();
// FIXME: Maybe add a generator to use https://publicsuffix.org/list/public_suffix_list.dat
// for now just highlight the whole host

View file

@ -53,7 +53,7 @@ void WebSocketImplQt::connect(WebSocket::ConnectionInfo const& connection_info)
if (connection_info.is_secure()) {
auto ssl_socket = make<QSslSocket>();
ssl_socket->connectToHostEncrypted(
qstring_from_ak_deprecated_string(connection_info.url().host()),
qstring_from_ak_string(connection_info.url().serialized_host().release_value_but_fixme_should_propagate_errors()),
connection_info.url().port_or_default());
QObject::connect(ssl_socket.ptr(), &QSslSocket::alertReceived, [this](QSsl::AlertLevel level, QSsl::AlertType, QString const&) {
if (level == QSsl::AlertLevel::Fatal)
@ -63,7 +63,7 @@ void WebSocketImplQt::connect(WebSocket::ConnectionInfo const& connection_info)
} else {
m_socket = make<QTcpSocket>();
m_socket->connectToHost(
qstring_from_ak_deprecated_string(connection_info.url().host()),
qstring_from_ak_string(connection_info.url().serialized_host().release_value_but_fixme_should_propagate_errors()),
connection_info.url().port_or_default());
}

View file

@ -22,7 +22,7 @@ TEST_CASE(basic)
URL url("http://www.serenityos.org"sv);
EXPECT_EQ(url.is_valid(), true);
EXPECT_EQ(url.scheme(), "http");
EXPECT_EQ(url.host(), "www.serenityos.org");
EXPECT_EQ(MUST(url.serialized_host()), "www.serenityos.org");
EXPECT_EQ(url.port_or_default(), 80);
EXPECT_EQ(url.serialize_path(), "/");
EXPECT(url.query().is_null());
@ -32,7 +32,7 @@ TEST_CASE(basic)
URL url("https://www.serenityos.org/index.html"sv);
EXPECT_EQ(url.is_valid(), true);
EXPECT_EQ(url.scheme(), "https");
EXPECT_EQ(url.host(), "www.serenityos.org");
EXPECT_EQ(MUST(url.serialized_host()), "www.serenityos.org");
EXPECT_EQ(url.port_or_default(), 443);
EXPECT_EQ(url.serialize_path(), "/index.html");
EXPECT(url.query().is_null());
@ -42,7 +42,7 @@ TEST_CASE(basic)
URL url("https://www.serenityos.org1/index.html"sv);
EXPECT_EQ(url.is_valid(), true);
EXPECT_EQ(url.scheme(), "https");
EXPECT_EQ(url.host(), "www.serenityos.org1");
EXPECT_EQ(MUST(url.serialized_host()), "www.serenityos.org1");
EXPECT_EQ(url.port_or_default(), 443);
EXPECT_EQ(url.serialize_path(), "/index.html");
EXPECT(url.query().is_null());
@ -52,7 +52,7 @@ TEST_CASE(basic)
URL url("https://localhost:1234/~anon/test/page.html"sv);
EXPECT_EQ(url.is_valid(), true);
EXPECT_EQ(url.scheme(), "https");
EXPECT_EQ(url.host(), "localhost");
EXPECT_EQ(MUST(url.serialized_host()), "localhost");
EXPECT_EQ(url.port_or_default(), 1234);
EXPECT_EQ(url.serialize_path(), "/~anon/test/page.html");
EXPECT(url.query().is_null());
@ -62,7 +62,7 @@ TEST_CASE(basic)
URL url("http://www.serenityos.org/index.html?#"sv);
EXPECT_EQ(url.is_valid(), true);
EXPECT_EQ(url.scheme(), "http");
EXPECT_EQ(url.host(), "www.serenityos.org");
EXPECT_EQ(MUST(url.serialized_host()), "www.serenityos.org");
EXPECT_EQ(url.port_or_default(), 80);
EXPECT_EQ(url.serialize_path(), "/index.html");
EXPECT_EQ(url.query(), "");
@ -72,7 +72,7 @@ TEST_CASE(basic)
URL url("http://www.serenityos.org/index.html?foo=1&bar=2"sv);
EXPECT_EQ(url.is_valid(), true);
EXPECT_EQ(url.scheme(), "http");
EXPECT_EQ(url.host(), "www.serenityos.org");
EXPECT_EQ(MUST(url.serialized_host()), "www.serenityos.org");
EXPECT_EQ(url.port_or_default(), 80);
EXPECT_EQ(url.serialize_path(), "/index.html");
EXPECT_EQ(url.query(), "foo=1&bar=2");
@ -82,7 +82,7 @@ TEST_CASE(basic)
URL url("http://www.serenityos.org/index.html#fragment"sv);
EXPECT_EQ(url.is_valid(), true);
EXPECT_EQ(url.scheme(), "http");
EXPECT_EQ(url.host(), "www.serenityos.org");
EXPECT_EQ(MUST(url.serialized_host()), "www.serenityos.org");
EXPECT_EQ(url.port_or_default(), 80);
EXPECT_EQ(url.serialize_path(), "/index.html");
EXPECT(url.query().is_null());
@ -92,7 +92,7 @@ TEST_CASE(basic)
URL url("http://www.serenityos.org/index.html?foo=1&bar=2&baz=/?#frag/ment?test#"sv);
EXPECT_EQ(url.is_valid(), true);
EXPECT_EQ(url.scheme(), "http");
EXPECT_EQ(url.host(), "www.serenityos.org");
EXPECT_EQ(MUST(url.serialized_host()), "www.serenityos.org");
EXPECT_EQ(url.port_or_default(), 80);
EXPECT_EQ(url.serialize_path(), "/index.html");
EXPECT_EQ(url.query(), "foo=1&bar=2&baz=/?");
@ -128,7 +128,7 @@ TEST_CASE(file_url_with_hostname)
URL url("file://courage/my/file"sv);
EXPECT(url.is_valid());
EXPECT_EQ(url.scheme(), "file");
EXPECT_EQ(url.host(), "courage");
EXPECT_EQ(MUST(url.serialized_host()), "courage");
EXPECT_EQ(url.port_or_default(), 0);
EXPECT_EQ(url.serialize_path(), "/my/file");
EXPECT_EQ(url.serialize(), "file://courage/my/file");
@ -141,7 +141,7 @@ TEST_CASE(file_url_with_localhost)
URL url("file://localhost/my/file"sv);
EXPECT(url.is_valid());
EXPECT_EQ(url.scheme(), "file");
EXPECT_EQ(url.host(), "");
EXPECT_EQ(MUST(url.serialized_host()), "");
EXPECT_EQ(url.serialize_path(), "/my/file");
EXPECT_EQ(url.serialize(), "file:///my/file");
}
@ -151,7 +151,7 @@ TEST_CASE(file_url_without_hostname)
URL url("file:///my/file"sv);
EXPECT(url.is_valid());
EXPECT_EQ(url.scheme(), "file");
EXPECT_EQ(url.host(), "");
EXPECT_EQ(MUST(url.serialized_host()), "");
EXPECT_EQ(url.serialize_path(), "/my/file");
EXPECT_EQ(url.serialize(), "file:///my/file");
}
@ -205,7 +205,7 @@ TEST_CASE(about_url)
URL url("about:blank"sv);
EXPECT(url.is_valid());
EXPECT_EQ(url.scheme(), "about");
EXPECT(url.host().is_null());
EXPECT(url.host().has<Empty>());
EXPECT_EQ(url.serialize_path(), "blank");
EXPECT(url.query().is_null());
EXPECT(url.fragment().is_null());
@ -217,7 +217,7 @@ TEST_CASE(mailto_url)
URL url("mailto:mail@example.com"sv);
EXPECT(url.is_valid());
EXPECT_EQ(url.scheme(), "mailto");
EXPECT(url.host().is_null());
EXPECT(url.host().has<Empty>());
EXPECT_EQ(url.port_or_default(), 0);
EXPECT_EQ(url.path_segment_count(), 1u);
EXPECT_EQ(url.path_segment_at_index(0), "mail@example.com");
@ -231,7 +231,7 @@ TEST_CASE(data_url)
URL url("data:text/html,test"sv);
EXPECT(url.is_valid());
EXPECT_EQ(url.scheme(), "data");
EXPECT(url.host().is_null());
EXPECT(url.host().has<Empty>());
EXPECT_EQ(url.data_mime_type(), "text/html");
EXPECT_EQ(url.data_payload(), "test");
EXPECT(!url.data_payload_is_base64());
@ -243,7 +243,7 @@ TEST_CASE(data_url_default_mime_type)
URL url("data:,test"sv);
EXPECT(url.is_valid());
EXPECT_EQ(url.scheme(), "data");
EXPECT(url.host().is_null());
EXPECT(url.host().has<Empty>());
EXPECT_EQ(url.data_mime_type(), "text/plain");
EXPECT_EQ(url.data_payload(), "test");
EXPECT(!url.data_payload_is_base64());
@ -255,7 +255,7 @@ TEST_CASE(data_url_encoded)
URL url("data:text/html,Hello%20friends%2C%0X%X0"sv);
EXPECT(url.is_valid());
EXPECT_EQ(url.scheme(), "data");
EXPECT(url.host().is_null());
EXPECT(url.host().has<Empty>());
EXPECT_EQ(url.data_mime_type(), "text/html");
EXPECT_EQ(url.data_payload(), "Hello friends,%0X%X0");
EXPECT(!url.data_payload_is_base64());
@ -267,7 +267,7 @@ TEST_CASE(data_url_base64_encoded)
URL url("data:text/html;base64,test"sv);
EXPECT(url.is_valid());
EXPECT_EQ(url.scheme(), "data");
EXPECT(url.host().is_null());
EXPECT(url.host().has<Empty>());
EXPECT_EQ(url.data_mime_type(), "text/html");
EXPECT_EQ(url.data_payload(), "test");
EXPECT(url.data_payload_is_base64());
@ -279,7 +279,7 @@ TEST_CASE(data_url_base64_encoded_default_mime_type)
URL url("data:;base64,test"sv);
EXPECT(url.is_valid());
EXPECT_EQ(url.scheme(), "data");
EXPECT(url.host().is_null());
EXPECT(url.host().has<Empty>());
EXPECT_EQ(url.data_mime_type(), "text/plain");
EXPECT_EQ(url.data_payload(), "test");
EXPECT(url.data_payload_is_base64());
@ -291,7 +291,7 @@ TEST_CASE(data_url_base64_encoded_with_whitespace)
URL url("data: text/html ; bAsE64 , test with whitespace "sv);
EXPECT(url.is_valid());
EXPECT_EQ(url.scheme(), "data");
EXPECT(url.host().is_null());
EXPECT(url.host().has<Empty>());
EXPECT_EQ(url.data_mime_type(), "text/html");
EXPECT_EQ(url.data_payload(), " test with whitespace ");
EXPECT(url.data_payload_is_base64());
@ -303,7 +303,7 @@ TEST_CASE(data_url_base64_encoded_with_inline_whitespace)
URL url("data:text/javascript;base64,%20ZD%20Qg%0D%0APS%20An%20Zm91cic%0D%0A%207%20"sv);
EXPECT(url.is_valid());
EXPECT_EQ(url.scheme(), "data");
EXPECT(url.host().is_null());
EXPECT(url.host().has<Empty>());
EXPECT_EQ(url.data_mime_type(), "text/javascript");
EXPECT(url.data_payload_is_base64());
EXPECT_EQ(url.data_payload(), " ZD Qg\r\nPS An Zm91cic\r\n 7 "sv);
@ -371,7 +371,7 @@ TEST_CASE(complete_url)
URL url = base_url.complete_url("test.html"sv);
EXPECT(url.is_valid());
EXPECT_EQ(url.scheme(), "http");
EXPECT_EQ(url.host(), "serenityos.org");
EXPECT_EQ(MUST(url.serialized_host()), "serenityos.org");
EXPECT_EQ(url.serialize_path(), "/test.html");
EXPECT(url.query().is_null());
EXPECT(url.query().is_null());
@ -445,6 +445,7 @@ TEST_CASE(ipv6_address)
constexpr auto ipv6_url = "http://[::1]/index.html"sv;
URL url(ipv6_url);
EXPECT(url.is_valid());
EXPECT_EQ(MUST(url.serialized_host()), "[::1]"sv);
EXPECT_EQ(url, ipv6_url);
}
@ -452,6 +453,7 @@ TEST_CASE(ipv6_address)
constexpr auto ipv6_url = "http://[0:f:0:0:f:f:0:0]/index.html"sv;
URL url(ipv6_url);
EXPECT(url.is_valid());
EXPECT_EQ(MUST(url.serialized_host()), "[0:f::f:f:0:0]"sv);
EXPECT_EQ(url, ipv6_url);
}
@ -459,6 +461,7 @@ TEST_CASE(ipv6_address)
constexpr auto ipv6_url = "https://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]/index.html"sv;
URL url(ipv6_url);
EXPECT(url.is_valid());
EXPECT_EQ(MUST(url.serialized_host()), "[2001:db8:85a3::8a2e:370:7334]"sv);
EXPECT_EQ(url, ipv6_url);
}
@ -475,14 +478,14 @@ TEST_CASE(ipv4_address)
constexpr auto ipv4_url = "http://127.0.0.1/index.html"sv;
URL url(ipv4_url);
EXPECT(url.is_valid());
EXPECT_EQ(url.host(), "127.0.0.1"sv);
EXPECT_EQ(MUST(url.serialized_host()), "127.0.0.1"sv);
}
{
constexpr auto ipv4_url = "http://0x.0x.0"sv;
URL url(ipv4_url);
EXPECT(url.is_valid());
EXPECT_EQ(url.host(), "0.0.0.0"sv);
EXPECT_EQ(MUST(url.serialized_host()), "0.0.0.0"sv);
}
{
@ -495,13 +498,13 @@ TEST_CASE(ipv4_address)
constexpr auto ipv4_url = "http://256"sv;
URL url(ipv4_url);
EXPECT(url.is_valid());
EXPECT_EQ(url.host(), "0.0.1.0"sv);
EXPECT_EQ(MUST(url.serialized_host()), "0.0.1.0"sv);
}
{
constexpr auto ipv4_url = "http://888888888"sv;
URL url(ipv4_url);
EXPECT(url.is_valid());
EXPECT_EQ(url.host(), "52.251.94.56"sv);
EXPECT_EQ(MUST(url.serialized_host()), "52.251.94.56"sv);
}
}

View file

@ -248,8 +248,8 @@ void URLProvider::query(DeprecatedString const& query, Function<void(Vector<Nonn
if (url.scheme().is_empty())
url.set_scheme("http");
if (url.host().is_empty())
url.set_host(query);
if (url.host().has<Empty>() || url.host() == String {})
url.set_host(String::from_deprecated_string(query).release_value_but_fixme_should_propagate_errors());
if (url.path_segment_count() == 0)
url.set_paths({ "" });

View file

@ -208,7 +208,10 @@ Optional<DeprecatedString> CookieJar::canonicalize_domain(const URL& url)
return {};
// FIXME: Implement RFC 5890 to "Convert each label that is not a Non-Reserved LDH (NR-LDH) label to an A-label".
return url.host().to_lowercase();
if (url.host().has<Empty>())
return {};
return url.serialized_host().release_value_but_fixme_should_propagate_errors().to_deprecated_string().to_lowercase();
}
bool CookieJar::domain_matches(DeprecatedString const& string, DeprecatedString const& domain_string)

View file

@ -107,10 +107,10 @@ void Tab::update_status(Optional<String> text_override, i32 count_waiting)
if (count_waiting == 0) {
// ex: "Loading google.com"
m_statusbar->set_text(String::formatted("Loading {}", m_navigating_url->host()).release_value_but_fixme_should_propagate_errors());
m_statusbar->set_text(String::formatted("Loading {}", m_navigating_url->serialized_host().release_value_but_fixme_should_propagate_errors()).release_value_but_fixme_should_propagate_errors());
} else {
// ex: "google.com is waiting on 5 resources"
m_statusbar->set_text(String::formatted("{} is waiting on {} resource{}", m_navigating_url->host(), count_waiting, count_waiting == 1 ? ""sv : "s"sv).release_value_but_fixme_should_propagate_errors());
m_statusbar->set_text(String::formatted("{} is waiting on {} resource{}", m_navigating_url->serialized_host().release_value_but_fixme_should_propagate_errors(), count_waiting, count_waiting == 1 ? ""sv : "s"sv).release_value_but_fixme_should_propagate_errors());
}
}

View file

@ -83,7 +83,7 @@ HelpWindow::HelpWindow(GUI::Window* parent)
m_webview = splitter.add<WebView::OutOfProcessWebView>();
m_webview->on_link_click = [this](auto& url, auto&, auto&&) {
VERIFY(url.scheme() == "spreadsheet");
if (url.host() == "example") {
if (url.host().template has<String>() && url.host().template get<String>() == "example"sv) {
auto example_path = url.serialize_path();
auto entry = LexicalPath::basename(example_path);
auto doc_option = m_docs.get_object(entry);
@ -122,11 +122,11 @@ HelpWindow::HelpWindow(GUI::Window* parent)
widget->add_sheet(sheet.release_nonnull());
window->show();
} else if (url.host() == "doc") {
} else if (url.host() == String::from_utf8_short_string("doc"sv)) {
auto entry = LexicalPath::basename(url.serialize_path());
m_webview->load(URL::create_with_data("text/html", render(entry)));
} else {
dbgln("Invalid spreadsheet action domain '{}'", url.host());
dbgln("Invalid spreadsheet action domain '{}'", url.serialized_host().release_value_but_fixme_should_propagate_errors());
}
};

View file

@ -264,7 +264,7 @@ Optional<Position> Sheet::position_from_url(const URL& url) const
return {};
}
if (url.scheme() != "spreadsheet" || url.host() != "cell") {
if (url.scheme() != "spreadsheet" || url.host() != String::from_utf8_short_string("cell"sv)) {
dbgln("Bad url: {}", url.to_deprecated_string());
return {};
}
@ -757,7 +757,7 @@ URL Position::to_url(Sheet const& sheet) const
{
URL url;
url.set_scheme("spreadsheet");
url.set_host("cell");
url.set_host(String::from_utf8_short_string("cell"sv));
url.set_paths({ DeprecatedString::number(getpid()) });
url.set_fragment(to_cell_identifier(sheet));
return url;

View file

@ -36,10 +36,9 @@ struct ProxyData {
proxy_data.type = ProxyData::Type::SOCKS5;
auto host_ipv4 = IPv4Address::from_string(url.host());
if (!host_ipv4.has_value())
if (!url.host().has<URL::IPv4Address>())
return Error::from_string_literal("Invalid proxy host, must be an IPv4 address");
proxy_data.host_ipv4 = host_ipv4->to_u32();
proxy_data.host_ipv4 = url.host().get<URL::IPv4Address>();
auto port = url.port();
if (!port.has_value())

View file

@ -179,8 +179,9 @@ void UrlBox::highlight_url()
if (url.is_valid() && !is_focused()) {
if (url.scheme() == "http" || url.scheme() == "https" || url.scheme() == "gemini") {
auto serialized_host = url.serialized_host().release_value_but_fixme_should_propagate_errors().to_deprecated_string();
auto host_start = url.scheme().length() + 3;
auto host_length = url.host().length();
auto host_length = serialized_host.length();
// FIXME: Maybe add a generator to use https://publicsuffix.org/list/public_suffix_list.dat
// for now just highlight the whole host

View file

@ -7,6 +7,7 @@
#include <AK/Base64.h>
#include <AK/StringBuilder.h>
#include <AK/URLParser.h>
#include <LibHTTP/HttpRequest.h>
#include <LibHTTP/Job.h>
@ -57,7 +58,7 @@ ErrorOr<ByteBuffer> HttpRequest::to_raw_request() const
TRY(builder.try_append(m_url.query()));
}
TRY(builder.try_append(" HTTP/1.1\r\nHost: "sv));
TRY(builder.try_append(m_url.host()));
TRY(builder.try_append(TRY(m_url.serialized_host())));
if (m_url.port().has_value())
TRY(builder.try_appendff(":{}", *m_url.port()));
TRY(builder.try_append("\r\n"sv));

View file

@ -82,7 +82,7 @@ ErrorOr<NonnullRefPtr<PageNode const>> Node::try_create_from_query(Vector<String
ErrorOr<NonnullRefPtr<Node const>> Node::try_find_from_help_url(URL const& url)
{
if (url.host() != "man")
if (url.host() != String::from_utf8_short_string("man"sv))
return Error::from_string_view("Bad help operation"sv);
if (url.path_segment_count() < 2)
return Error::from_string_view("Bad help page URL"sv);

View file

@ -2316,8 +2316,7 @@ DeprecatedString Document::domain() const
return DeprecatedString::empty();
// 3. Return effectiveDomain, serialized.
// FIXME: Implement host serialization.
return effective_domain.release_value();
return URLParser::serialize_host(effective_domain.release_value()).release_value_but_fixme_should_propagate_errors().to_deprecated_string();
}
void Document::set_domain(DeprecatedString const& domain)

View file

@ -256,7 +256,7 @@ WebIDL::ExceptionOr<Optional<JS::NonnullGCPtr<PendingResponse>>> main_fetch(JS::
// - requests current URLs scheme is "http"
request->current_url().scheme() == "http"sv
// - requests current URLs host is a domain
&& URL::host_is_domain(request->current_url().host())
&& URL::host_is_domain(request->current_url().serialized_host().release_value_but_fixme_should_propagate_errors())
// FIXME: - Matching requests current URLs host per Known HSTS Host Domain Name Matching results in either a
// superdomain match with an asserted includeSubDomains directive or a congruent match (with or without an
// asserted includeSubDomains directive) [HSTS]; or DNS resolution for the request finds a matching HTTPS RR

View file

@ -44,7 +44,7 @@ static bool url_matches_about_blank(AK::URL const& url)
&& url.serialize_path() == "blank"sv
&& url.username().is_empty()
&& url.password().is_empty()
&& url.host().is_null();
&& url.host().has<Empty>();
}
// FIXME: This is an outdated older version of "determining the origin" and should be removed.

View file

@ -167,15 +167,15 @@ DeprecatedString HTMLHyperlinkElementUtils::host() const
auto& url = m_url;
// 3. If url or url's host is null, return the empty string.
if (!url.has_value() || url->host().is_null())
if (!url.has_value() || url->host().has<Empty>())
return DeprecatedString::empty();
// 4. If url's port is null, return url's host, serialized.
if (!url->port().has_value())
return url->host();
return url->serialized_host().release_value_but_fixme_should_propagate_errors().to_deprecated_string();
// 5. Return url's host, serialized, followed by ":" and url's port, serialized.
return DeprecatedString::formatted("{}:{}", url->host(), url->port().value());
return DeprecatedString::formatted("{}:{}", url->serialized_host().release_value_but_fixme_should_propagate_errors(), url->port().value());
}
// https://html.spec.whatwg.org/multipage/links.html#dom-hyperlink-host
@ -205,11 +205,14 @@ DeprecatedString HTMLHyperlinkElementUtils::hostname() const
// 1. Reinitialize url.
//
// 2. Let url be this element's url.
//
AK::URL url(href());
// 3. If url or url's host is null, return the empty string.
//
if (url.host().has<Empty>())
return DeprecatedString::empty();
// 4. Return url's host, serialized.
return AK::URL(href()).host();
return url.serialized_host().release_value_but_fixme_should_propagate_errors().to_deprecated_string();
}
void HTMLHyperlinkElementUtils::set_hostname(DeprecatedString hostname)

View file

@ -153,15 +153,15 @@ WebIDL::ExceptionOr<String> Location::host() const
auto url = this->url();
// 3. If url's host is null, return the empty string.
if (url.host().is_null())
if (url.host().has<Empty>())
return String {};
// 4. If url's port is null, return url's host, serialized.
if (!url.port().has_value())
return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(url.host()));
return TRY_OR_THROW_OOM(vm, url.serialized_host());
// 5. Return url's host, serialized, followed by ":" and url's port, serialized.
return TRY_OR_THROW_OOM(vm, String::formatted("{}:{}", url.host(), *url.port()));
return TRY_OR_THROW_OOM(vm, String::formatted("{}:{}", TRY_OR_THROW_OOM(vm, url.serialized_host()), *url.port()));
}
WebIDL::ExceptionOr<void> Location::set_host(String const&)
@ -183,11 +183,11 @@ WebIDL::ExceptionOr<String> Location::hostname() const
auto url = this->url();
// 2. If this's url's host is null, return the empty string.
if (url.host().is_null())
if (url.host().has<Empty>())
return String {};
// 3. Return this's url's host, serialized.
return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(url.host()));
return TRY_OR_THROW_OOM(vm, url.serialized_host());
}
WebIDL::ExceptionOr<void> Location::set_hostname(String const&)

View file

@ -217,7 +217,7 @@ static bool url_matches_about_blank(AK::URL const& url)
&& url.serialize_path() == "blank"sv
&& url.username().is_empty()
&& url.password().is_empty()
&& url.host().is_null();
&& url.host().has<Empty>();
}
// https://html.spec.whatwg.org/multipage/iframe-embed-object.html#shared-attribute-processing-steps-for-iframe-and-frame-elements

View file

@ -8,13 +8,15 @@
#pragma once
#include <AK/DeprecatedString.h>
#include <AK/URL.h>
#include <AK/URLParser.h>
namespace Web::HTML {
class Origin {
public:
Origin() = default;
Origin(DeprecatedString const& scheme, DeprecatedString const& host, u16 port)
Origin(DeprecatedString const& scheme, AK::URL::Host const& host, u16 port)
: m_scheme(scheme)
, m_host(host)
, m_port(port)
@ -22,10 +24,10 @@ public:
}
// https://html.spec.whatwg.org/multipage/origin.html#concept-origin-opaque
bool is_opaque() const { return m_scheme.is_null() && m_host.is_null() && m_port == 0; }
bool is_opaque() const { return m_scheme.is_null() && m_host.has<Empty>() && m_port == 0; }
DeprecatedString const& scheme() const { return m_scheme; }
DeprecatedString const& host() const { return m_host; }
AK::URL::Host const& host() const { return m_host; }
u16 port() const { return m_port; }
// https://html.spec.whatwg.org/multipage/origin.html#same-origin
@ -81,7 +83,7 @@ public:
result.append("://"sv);
// 4. Append origin's host, serialized, to result.
result.append(host());
result.append(URLParser::serialize_host(host()).release_value_but_fixme_should_propagate_errors().to_deprecated_string());
// 5. If origin's port is non-null, append a U+003A COLON character (:), and origin's port, serialized, to result.
if (port() != 0) {
@ -93,11 +95,11 @@ public:
}
// https://html.spec.whatwg.org/multipage/origin.html#concept-origin-effective-domain
Optional<DeprecatedString> effective_domain() const
Optional<AK::URL::Host> effective_domain() const
{
// 1. If origin is an opaque origin, then return null.
if (is_opaque())
return Optional<DeprecatedString> {};
return {};
// FIXME: 2. If origin's domain is non-null, then return origin's domain.
@ -109,7 +111,7 @@ public:
private:
DeprecatedString m_scheme;
DeprecatedString m_host;
AK::URL::Host m_host;
u16 m_port { 0 };
};
@ -120,7 +122,10 @@ template<>
struct Traits<Web::HTML::Origin> : public GenericTraits<Web::HTML::Origin> {
static unsigned hash(Web::HTML::Origin const& origin)
{
return pair_int_hash(origin.scheme().hash(), pair_int_hash(int_hash(origin.port()), origin.host().hash()));
auto hash_without_host = pair_int_hash(origin.scheme().hash(), origin.port());
if (origin.host().has<Empty>())
return hash_without_host;
return pair_int_hash(hash_without_host, URLParser::serialize_host(origin.host()).release_value_but_fixme_should_propagate_errors().hash());
}
};
} // namespace AK

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/URLParser.h>
#include <LibWeb/HTML/WorkerGlobalScope.h>
#include <LibWeb/HTML/WorkerLocation.h>
@ -43,15 +44,15 @@ WebIDL::ExceptionOr<String> WorkerLocation::host() const
auto const& url = m_global_scope->url();
// 2. If url's host is null, return the empty string.
if (url.host().is_empty())
if (url.host().has<Empty>())
return String {};
// 3. If url's port is null, return url's host, serialized.
if (!url.port().has_value())
return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(url.host()));
return TRY_OR_THROW_OOM(vm, url.serialized_host());
// 4. Return url's host, serialized, followed by ":" and url's port, serialized.
return TRY_OR_THROW_OOM(vm, String::formatted("{}:{}", url.host().view(), url.port().value()));
return TRY_OR_THROW_OOM(vm, String::formatted("{}:{}", TRY_OR_THROW_OOM(vm, url.serialized_host()), url.port().value()));
}
// https://html.spec.whatwg.org/multipage/workers.html#dom-workerlocation-hostname
@ -64,11 +65,11 @@ WebIDL::ExceptionOr<String> WorkerLocation::hostname() const
auto const& host = m_global_scope->url().host();
// 2. If host is null, return the empty string.
if (host.is_empty())
if (host.has<Empty>())
return String {};
// 3. Return host, serialized.
return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(host));
return TRY_OR_THROW_OOM(vm, URLParser::serialize_host(host));
}
// https://html.spec.whatwg.org/multipage/workers.html#dom-workerlocation-port

View file

@ -28,20 +28,29 @@ Trustworthiness is_origin_potentially_trustworthy(HTML::Origin const& origin)
return Trustworthiness::PotentiallyTrustworthy;
// 4. If origins host matches one of the CIDR notations 127.0.0.0/8 or ::1/128 [RFC4632], return "Potentially Trustworthy".
if (auto ipv4_address = IPv4Address::from_string(origin.host()); ipv4_address.has_value() && (ipv4_address->to_u32() & 0xff000000) != 0)
return Trustworthiness::PotentiallyTrustworthy;
if (auto ipv6_address = IPv6Address::from_string(origin.host()); ipv6_address.has_value() && ipv6_address == IPv6Address::loopback())
return Trustworthiness::PotentiallyTrustworthy;
// FIXME: This would be nicer if URL::IPv4Address and URL::IPv6Address were instances of AK::IPv4Address and AK::IPv6Address
if (origin.host().has<AK::URL::IPv4Address>()) {
if ((origin.host().get<AK::URL::IPv4Address>() & 0xff000000) != 0)
return Trustworthiness::PotentiallyTrustworthy;
} else if (origin.host().has<AK::URL::IPv6Address>()) {
auto ipv6_address = origin.host().get<AK::URL::IPv6Address>();
static constexpr AK::URL::IPv6Address loopback { 0, 0, 0, 0, 0, 0, 0, 1 };
if (ipv6_address == loopback)
return Trustworthiness::PotentiallyTrustworthy;
}
// 5. If the user agent conforms to the name resolution rules in [let-localhost-be-localhost] and one of the following is true:
// - origins host is "localhost" or "localhost."
// - origins host ends with ".localhost" or ".localhost."
// then return "Potentially Trustworthy".
// Note: See §5.2 localhost for details on the requirements here.
if (origin.host().is_one_of("localhost"sv, "localhost.")
|| origin.host().ends_with(".localhost"sv)
|| origin.host().ends_with(".localhost."sv)) {
return Trustworthiness::PotentiallyTrustworthy;
if (origin.host().has<String>()) {
auto const& host = origin.host().get<String>();
if (host.is_one_of("localhost"sv, "localhost.")
|| host.ends_with_bytes(".localhost"sv)
|| host.ends_with_bytes(".localhost."sv)) {
return Trustworthiness::PotentiallyTrustworthy;
}
}
// 6. If origins scheme is "file", return "Potentially Trustworthy".

View file

@ -235,15 +235,15 @@ WebIDL::ExceptionOr<String> URL::host() const
auto& url = m_url;
// 2. If urls host is null, then return the empty string.
if (url.host().is_null())
if (url.host().has<Empty>())
return String {};
// 3. If urls port is null, return urls host, serialized.
if (!url.port().has_value())
return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(url.host()));
return TRY_OR_THROW_OOM(vm, url.serialized_host());
// 4. Return urls host, serialized, followed by U+003A (:) and urls port, serialized.
return TRY_OR_THROW_OOM(vm, String::formatted("{}:{}", url.host(), *url.port()));
return TRY_OR_THROW_OOM(vm, String::formatted("{}:{}", TRY_OR_THROW_OOM(vm, url.serialized_host()), *url.port()));
}
// https://url.spec.whatwg.org/#dom-url-hostref-for-dom-url-host%E2%91%A0
@ -265,11 +265,11 @@ WebIDL::ExceptionOr<String> URL::hostname() const
auto& vm = realm().vm();
// 1. If thiss URLs host is null, then return the empty string.
if (m_url.host().is_null())
if (m_url.host().has<Empty>())
return String {};
// 2. Return thiss URLs host, serialized.
return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(m_url.host()));
return TRY_OR_THROW_OOM(vm, m_url.serialized_host());
}
// https://url.spec.whatwg.org/#ref-for-dom-url-hostname①
@ -473,7 +473,7 @@ HTML::Origin url_origin(AK::URL const& url)
if (url.scheme() == "file"sv) {
// Unfortunate as it is, this is left as an exercise to the reader. When in doubt, return a new opaque origin.
// Note: We must return an origin with the `file://' protocol for `file://' iframes to work from `file://' pages.
return HTML::Origin(url.scheme(), DeprecatedString(), 0);
return HTML::Origin(url.scheme(), String {}, 0);
}
// -> Otherwise

View file

@ -388,7 +388,7 @@ WebIDL::ExceptionOr<void> XMLHttpRequest::open(String const& method_string, Stri
// NOTE: This is handled in the overload lacking the async argument.
// 8. If parsedURLs host is non-null, then:
if (!parsed_url.host().is_null()) {
if (!parsed_url.host().has<Empty>()) {
// 1. If the username argument is not null, set the username given parsedURL and username.
if (username.has_value())
parsed_url.set_username(username.value().to_deprecated_string());

View file

@ -42,17 +42,19 @@ void WebSocketImplSerenity::connect(ConnectionInfo const& connection_info)
VERIFY(on_connection_error);
VERIFY(on_ready_to_read);
auto socket_result = [&]() -> ErrorOr<NonnullOwnPtr<Core::BufferedSocketBase>> {
auto host = TRY(connection_info.url().serialized_host()).to_deprecated_string();
if (connection_info.is_secure()) {
TLS::Options options;
options.set_alert_handler([this](auto) {
on_connection_error();
});
return TRY(Core::BufferedSocket<TLS::TLSv12>::create(
TRY(TLS::TLSv12::connect(connection_info.url().host(), connection_info.url().port_or_default(), move(options)))));
TRY(TLS::TLSv12::connect(host, connection_info.url().port_or_default(), move(options)))));
}
return TRY(Core::BufferedTCPSocket::create(
TRY(Core::TCPSocket::connect(connection_info.url().host(), connection_info.url().port_or_default()))));
TRY(Core::TCPSocket::connect(host, connection_info.url().port_or_default()))));
}();
if (socket_result.is_error()) {

View file

@ -171,7 +171,7 @@ void WebSocket::send_client_handshake()
// 4. Host
auto url = m_connection.url();
builder.appendff("Host: {}", url.host());
builder.appendff("Host: {}", url.serialized_host().release_value_but_fixme_should_propagate_errors());
if (!m_connection.is_secure() && url.port_or_default() != 80)
builder.appendff(":{}", url.port_or_default());
else if (m_connection.is_secure() && url.port_or_default() != 443)

View file

@ -344,8 +344,8 @@ void ViewImplementation::handle_web_content_process_crash()
builder.append(escape_html_entities(m_url.to_deprecated_string()));
builder.append("</title></head><body>"sv);
builder.append("<h1>Web page crashed"sv);
if (!m_url.host().is_empty()) {
builder.appendff(" on {}", escape_html_entities(m_url.host()));
if (!m_url.host().has<Empty>()) {
builder.appendff(" on {}", escape_html_entities(m_url.serialized_host().release_value_but_fixme_should_propagate_errors()));
}
builder.append("</h1>"sv);
auto escaped_url = escape_html_entities(m_url.to_deprecated_string());

View file

@ -23,7 +23,7 @@ void request_did_finish(URL const& url, Core::Socket const* socket)
dbgln_if(REQUESTSERVER_DEBUG, "Request for {} finished", url);
ConnectionKey partial_key { url.host(), url.port_or_default() };
ConnectionKey partial_key { url.serialized_host().release_value_but_fixme_should_propagate_errors().to_deprecated_string(), url.port_or_default() };
auto fire_off_next_job = [&](auto& cache) {
auto it = find_if(cache.begin(), cache.end(), [&](auto& connection) { return connection.key.hostname == partial_key.hostname && connection.key.port == partial_key.port; });
if (it == cache.end()) {

View file

@ -37,14 +37,14 @@ struct Proxy {
ErrorOr<NonnullOwnPtr<StorageType>> tunnel(URL const& url, Args&&... args)
{
if (data.type == Core::ProxyData::Direct) {
return TRY(SocketType::connect(url.host(), url.port_or_default(), forward<Args>(args)...));
return TRY(SocketType::connect(url.serialized_host().release_value_but_fixme_should_propagate_errors().to_deprecated_string(), url.port_or_default(), forward<Args>(args)...));
}
if (data.type == Core::ProxyData::SOCKS5) {
if constexpr (requires { SocketType::connect(declval<DeprecatedString>(), *proxy_client_storage, forward<Args>(args)...); }) {
proxy_client_storage = TRY(Core::SOCKSProxyClient::connect(data.host_ipv4, data.port, Core::SOCKSProxyClient::Version::V5, url.host(), url.port_or_default()));
return TRY(SocketType::connect(url.host(), *proxy_client_storage, forward<Args>(args)...));
proxy_client_storage = TRY(Core::SOCKSProxyClient::connect(data.host_ipv4, data.port, Core::SOCKSProxyClient::Version::V5, url.serialized_host().release_value_but_fixme_should_propagate_errors().to_deprecated_string(), url.port_or_default()));
return TRY(SocketType::connect(url.serialized_host().release_value_but_fixme_should_propagate_errors().to_deprecated_string(), *proxy_client_storage, forward<Args>(args)...));
} else if constexpr (IsSame<SocketType, Core::TCPSocket>) {
return TRY(Core::SOCKSProxyClient::connect(data.host_ipv4, data.port, Core::SOCKSProxyClient::Version::V5, url.host(), url.port_or_default()));
return TRY(Core::SOCKSProxyClient::connect(data.host_ipv4, data.port, Core::SOCKSProxyClient::Version::V5, url.serialized_host().release_value_but_fixme_should_propagate_errors().to_deprecated_string(), url.port_or_default()));
} else {
return Error::from_string_literal("SOCKS5 not supported for this socket type");
}
@ -173,7 +173,7 @@ ErrorOr<void> recreate_socket_if_needed(T& connection, URL const& url)
decltype(auto) get_or_create_connection(auto& cache, URL const& url, auto& job, Core::ProxyData proxy_data = {})
{
using CacheEntryType = RemoveCVReference<decltype(*cache.begin()->value)>;
auto& sockets_for_url = *cache.ensure({ url.host(), url.port_or_default(), proxy_data }, [] { return make<CacheEntryType>(); });
auto& sockets_for_url = *cache.ensure({ url.serialized_host().release_value_but_fixme_should_propagate_errors().to_deprecated_string(), url.port_or_default(), proxy_data }, [] { return make<CacheEntryType>(); });
Proxy proxy { proxy_data };

View file

@ -147,7 +147,7 @@ void ConnectionFromClient::ensure_connection(URL const& url, ::RequestServer::Ca
}
if (cache_level == CacheLevel::ResolveOnly) {
return Core::deferred_invoke([host = url.host()] {
return Core::deferred_invoke([host = url.serialized_host().release_value_but_fixme_should_propagate_errors().to_deprecated_string()] {
dbgln("EnsureConnection: DNS-preload for {}", host);
(void)gethostbyname(host.characters());
});
@ -156,7 +156,8 @@ void ConnectionFromClient::ensure_connection(URL const& url, ::RequestServer::Ca
auto& job = Job::ensure(url);
dbgln("EnsureConnection: Pre-connect to {}", url);
auto do_preconnect = [&](auto& cache) {
auto it = cache.find({ url.host(), url.port_or_default() });
auto serialized_host = url.serialized_host().release_value_but_fixme_should_propagate_errors().to_deprecated_string();
auto it = cache.find({ serialized_host, url.port_or_default() });
if (it == cache.end() || it->value->is_empty())
ConnectionCache::get_or_create_connection(cache, url, job);
};

View file

@ -112,10 +112,10 @@ void ConnectionFromClient::load_url(const URL& url)
#if defined(AK_OS_SERENITY)
DeprecatedString process_name;
if (url.host().is_empty())
if (url.host().has<Empty>() || url.host() == String {})
process_name = "WebContent";
else
process_name = DeprecatedString::formatted("WebContent: {}", url.host());
process_name = DeprecatedString::formatted("WebContent: {}", url.serialized_host().release_value_but_fixme_should_propagate_errors());
pthread_setname_np(pthread_self(), process_name.characters());
#endif

View file

@ -679,7 +679,7 @@ ErrorOr<void> BarewordLiteral::highlight_in_editor(Line::Editor& editor, Shell&
if (FileSystem::exists(m_text)) {
auto realpath = shell.resolve_path(m_text.bytes_as_string_view());
auto url = URL::create_with_file_scheme(realpath);
url.set_host(shell.hostname);
url.set_host(TRY(String::from_deprecated_string(shell.hostname)));
editor.stylize({ m_position.start_offset, m_position.end_offset }, { Line::Style::Hyperlink(url.to_deprecated_string()) });
}
return {};
@ -2620,7 +2620,7 @@ ErrorOr<void> PathRedirectionNode::highlight_in_editor(Line::Editor& editor, She
if (!path.starts_with('/'))
path = String::formatted("{}/{}", shell.cwd, path).release_value_but_fixme_should_propagate_errors();
auto url = URL::create_with_file_scheme(path.to_deprecated_string());
url.set_host(shell.hostname);
url.set_host(TRY(String::from_deprecated_string(shell.hostname)));
editor.stylize({ position.start_offset, position.end_offset }, { Line::Style::Hyperlink(url.to_deprecated_string()) });
}
return {};
@ -3214,7 +3214,7 @@ ErrorOr<void> Juxtaposition::highlight_in_editor(Line::Editor& editor, Shell& sh
if (FileSystem::exists(path)) {
auto realpath = shell.resolve_path(path);
auto url = URL::create_with_file_scheme(realpath);
url.set_host(shell.hostname);
url.set_host(TRY(String::from_deprecated_string(shell.hostname)));
editor.stylize({ m_position.start_offset, m_position.end_offset }, { Line::Style::Hyperlink(url.to_deprecated_string()) });
}

View file

@ -191,7 +191,7 @@ RecursionDecision MarkdownLinkage::visit(Markdown::Text::LinkNode const& link_no
return RecursionDecision::Recurse;
}
if (url.scheme() == "help") {
if (url.host() != "man") {
if (url.host() != String::from_utf8_short_string("man"sv)) {
warnln("help:// URL without 'man': {}", href);
m_has_invalid_link = true;
return RecursionDecision::Recurse;

View file

@ -343,7 +343,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
if (output_name.is_empty() || output_name == "/") {
int i = -1;
do {
output_name = url.host();
output_name = url.serialized_host().release_value_but_fixme_should_propagate_errors().to_deprecated_string();
if (i > -1)
output_name = DeprecatedString::formatted("{}.{}", output_name, i);
++i;