diff --git a/include/upa/url.h b/include/upa/url.h index 6a08ddf..5a51097 100644 --- a/include/upa/url.h +++ b/include/upa/url.h @@ -31,7 +31,7 @@ #include #include #include // uint8_t -#include // std::next +#include #include #include #include @@ -3264,7 +3264,15 @@ inline std::string path_from_file_url(const url& file_url, file_path_format form throw url_error(validation_errc::file_url_unsupported_host, "UNC path cannot have \".\" hostname"); // UNC path path.append("\\\\"); - path.append(hostname); + if (file_url.host_type() == HostType::IPv6) { + // Form an IPV6 address host-name by substituting hyphens for the colons and appending ".ipv6-literal.net" + // https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/62e862f4-2a51-452e-8eeb-dc4ff5ee33cc + std::replace_copy(std::next(hostname.begin()), std::prev(hostname.end()), + std::back_inserter(path), ':', '-'); + path.append(".ipv6-literal.net"); + } else { + path.append(hostname); + } } // percent decode pathname and normalize slashes diff --git a/test/test-url.cpp b/test/test-url.cpp index 35a1249..5ed33e5 100644 --- a/test/test-url.cpp +++ b/test/test-url.cpp @@ -761,6 +761,9 @@ TEST_CASE("path_from_file_url") { CHECK(path_from_file_url("file://host/path", upa::file_path_format::windows) == "\\\\host\\path"); CHECK(path_from_file_url("file:////host/path", upa::file_path_format::windows) == "\\\\host\\path"); CHECK(path_from_file_url("file://///host/path", upa::file_path_format::windows) == "\\\\host\\path"); + // UNC: IPv4 and IPv6 hostnames + CHECK(path_from_file_url("file://127.0.0.1/path", upa::file_path_format::windows) == "\\\\127.0.0.1\\path"); + CHECK(path_from_file_url("file://[::1]/path", upa::file_path_format::windows) == "\\\\--1.ipv6-literal.net\\path"); // Invalid UNC CHECK_THROWS_AS(path_from_file_url("file://host", upa::file_path_format::windows), upa::url_error); CHECK_THROWS_AS(path_from_file_url("file://host/", upa::file_path_format::windows), upa::url_error);