diff --git a/src/inspector_agent.cc b/src/inspector_agent.cc index c54b2ab17fcb6e..c101bf80d2a6e9 100644 --- a/src/inspector_agent.cc +++ b/src/inspector_agent.cc @@ -638,9 +638,7 @@ class NodeInspectorClient : public V8InspectorClient { if (!IsFilePath(resource_name)) return nullptr; node::url::URL url = node::url::URL::FromFilePath(resource_name); - // TODO(ak239spb): replace this code with url.href(). - // Refs: https://github.com/nodejs/node/issues/22610 - return Utf8ToStringView(url.protocol() + "/" + url.path()); + return Utf8ToStringView(url.href()); } node::Environment* env_; diff --git a/src/node_url.cc b/src/node_url.cc index 226fa69693558e..e3f860ef311677 100644 --- a/src/node_url.cc +++ b/src/node_url.cc @@ -2081,6 +2081,48 @@ void URL::Parse(const char* input, } } // NOLINT(readability/fn_size) +// https://url.spec.whatwg.org/#url-serializing +std::string URL::SerializeURL(const struct url_data* url, + bool exclude = false) { + std::string output = url->scheme; + if (url->flags & URL_FLAGS_HAS_HOST) { + output += "//"; + if (url->flags & URL_FLAGS_HAS_USERNAME || + url->flags & URL_FLAGS_HAS_PASSWORD) { + if (url->flags & URL_FLAGS_HAS_USERNAME) { + output += url->username; + } + if (url->flags & URL_FLAGS_HAS_PASSWORD) { + output += ":" + url->password; + } + output += "@"; + } + output += url->host; + if (url->port != -1) { + output += ":" + std::to_string(url->port); + } + } + if (url->flags & URL_FLAGS_CANNOT_BE_BASE) { + output += url->path[0]; + } else { + if (!(url->flags & URL_FLAGS_HAS_HOST) && + url->path.size() > 1 && + url->path[0].empty()) { + output += "/."; + } + for (size_t i = 1; i < url->path.size(); i++) { + output += "/" + url->path[i]; + } + } + if (url->flags & URL_FLAGS_HAS_QUERY) { + output = "?" + url->query; + } + if (!exclude && url->flags & URL_FLAGS_HAS_FRAGMENT) { + output = "#" + url->fragment; + } + return output; +} + namespace { void SetArgs(Environment* env, Local argv[ARG_COUNT], diff --git a/src/node_url.h b/src/node_url.h index 6439a4a087be77..e42c2627179fcc 100644 --- a/src/node_url.h +++ b/src/node_url.h @@ -71,6 +71,7 @@ struct url_data { std::string query; std::string fragment; std::vector path; + std::string href; }; class URL { @@ -83,6 +84,8 @@ class URL { const struct url_data* base, bool has_base); + static std::string SerializeURL(const struct url_data* url, bool exclude); + URL(const char* input, const size_t len) { Parse(input, len, kUnknownState, &context_, false, nullptr, false); } @@ -160,6 +163,10 @@ class URL { return ret; } + std::string href() const { + return SerializeURL(&context_, false); + } + // Get the path of the file: URL in a format consumable by native file system // APIs. Returns an empty string if something went wrong. std::string ToFilePath() const; diff --git a/test/cctest/test_url.cc b/test/cctest/test_url.cc index 99b4b4f39d5daf..686ebcd11b301e 100644 --- a/test/cctest/test_url.cc +++ b/test/cctest/test_url.cc @@ -137,25 +137,31 @@ TEST_F(URLTest, FromFilePath) { file_url = URL::FromFilePath("C:\\Program Files\\"); EXPECT_EQ("file:", file_url.protocol()); EXPECT_EQ("//C:/Program%20Files/", file_url.path()); + EXPECT_EQ("file:///C:/Program%20Files/", file_url.href()); file_url = URL::FromFilePath("C:\\a\\b\\c"); EXPECT_EQ("file:", file_url.protocol()); EXPECT_EQ("//C:/a/b/c", file_url.path()); + EXPECT_EQ("file:///C:/a/b/c", file_url.href()); file_url = URL::FromFilePath("b:\\a\\%%.js"); EXPECT_EQ("file:", file_url.protocol()); EXPECT_EQ("//b:/a/%25%25.js", file_url.path()); + EXPECT_EQ("file:///b:/a/%25%25.js", file_url.href()); #else file_url = URL::FromFilePath("/"); EXPECT_EQ("file:", file_url.protocol()); EXPECT_EQ("//", file_url.path()); + EXPECT_EQ("file:///", file_url.href()); file_url = URL::FromFilePath("/a/b/c"); EXPECT_EQ("file:", file_url.protocol()); EXPECT_EQ("//a/b/c", file_url.path()); + EXPECT_EQ("file:///a/b/c", file_url.href()); file_url = URL::FromFilePath("/a/%%.js"); EXPECT_EQ("file:", file_url.protocol()); EXPECT_EQ("//a/%25%25.js", file_url.path()); + EXPECT_EQ("file:///a/%25%25.js", file_url.href()); #endif }