Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fs,url: refactor FileURLToPath and FromNamespacedPath #50090

Closed
wants to merge 3 commits into from
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
fs,url: move FileURLToPath to node_url
anonrig committed Oct 25, 2023

Verified

This commit was signed with the committer’s verified signature. The key has expired.
anonrig Yagiz Nizipli
commit 9e7afcc0c4f5b8ecd16877d8903ab86bc4fb136f
130 changes: 9 additions & 121 deletions src/node_file.cc
Original file line number Diff line number Diff line change
@@ -29,6 +29,7 @@
#include "node_metadata.h"
#include "node_process-inl.h"
#include "node_stat_watcher.h"
#include "node_url.h"
#include "permission/permission.h"
#include "util-inl.h"

@@ -2812,123 +2813,6 @@ static void GetFormatOfExtensionlessFile(
return args.GetReturnValue().Set(EXTENSIONLESS_FORMAT_JAVASCRIPT);
}

static bool FileURLToPath(
Environment* env,
const ada::url_aggregator& file_url,
/* The linter can't detect the assign for result_file_path
So we need to ignore since it suggest to put const */
// NOLINTNEXTLINE(runtime/references)
std::string& result_file_path) {
if (file_url.type != ada::scheme::FILE) {
env->isolate()->ThrowException(ERR_INVALID_URL_SCHEME(env->isolate()));

return false;
}

std::string_view pathname = file_url.get_pathname();
#ifdef _WIN32
size_t first_percent = std::string::npos;
size_t pathname_size = pathname.size();
std::string pathname_escaped_slash;

for (size_t i = 0; i < pathname_size; i++) {
if (pathname[i] == '/') {
pathname_escaped_slash += '\\';
} else {
pathname_escaped_slash += pathname[i];
}

if (pathname[i] != '%') continue;

if (first_percent == std::string::npos) {
first_percent = i;
}

// just safe-guard against access the pathname
// outside the bounds
if ((i + 2) >= pathname_size) continue;

char third = pathname[i + 2] | 0x20;

bool is_slash = pathname[i + 1] == '2' && third == 102;
bool is_forward_slash = pathname[i + 1] == '5' && third == 99;

if (!is_slash && !is_forward_slash) continue;

env->isolate()->ThrowException(ERR_INVALID_FILE_URL_PATH(
env->isolate(),
"File URL path must not include encoded \\ or / characters"));

return false;
}

std::string_view hostname = file_url.get_hostname();
std::string decoded_pathname = ada::unicode::percent_decode(
std::string_view(pathname_escaped_slash), first_percent);

if (hostname.size() > 0) {
// If hostname is set, then we have a UNC path
// Pass the hostname through domainToUnicode just in case
// it is an IDN using punycode encoding. We do not need to worry
// about percent encoding because the URL parser will have
// already taken care of that for us. Note that this only
// causes IDNs with an appropriate `xn--` prefix to be decoded.
result_file_path =
"\\\\" + ada::unicode::to_unicode(hostname) + decoded_pathname;

return true;
}

char letter = decoded_pathname[1] | 0x20;
char sep = decoded_pathname[2];

// a..z A..Z
if (letter < 'a' || letter > 'z' || sep != ':') {
env->isolate()->ThrowException(ERR_INVALID_FILE_URL_PATH(
env->isolate(), "File URL path must be absolute"));

return false;
}

result_file_path = decoded_pathname.substr(1);

return true;
#else // _WIN32
std::string_view hostname = file_url.get_hostname();

if (hostname.size() > 0) {
std::string error_message =
std::string("File URL host must be \"localhost\" or empty on ") +
std::string(per_process::metadata.platform);
env->isolate()->ThrowException(
ERR_INVALID_FILE_URL_HOST(env->isolate(), error_message.c_str()));

return false;
}

size_t first_percent = std::string::npos;
for (size_t i = 0; (i + 2) < pathname.size(); i++) {
if (pathname[i] != '%') continue;

if (first_percent == std::string::npos) {
first_percent = i;
}

if (pathname[i + 1] == '2' && (pathname[i + 2] | 0x20) == 102) {
env->isolate()->ThrowException(ERR_INVALID_FILE_URL_PATH(
env->isolate(),
"File URL path must not include encoded / characters"));

return false;
}
}

result_file_path = ada::unicode::percent_decode(pathname, first_percent);

return true;
#endif // _WIN32
}

BindingData::FilePathIsFileReturnType BindingData::FilePathIsFile(
Environment* env, const std::string& file_path) {
THROW_IF_INSUFFICIENT_PERMISSIONS(
@@ -3016,7 +2900,9 @@ void BindingData::LegacyMainResolve(const FunctionCallbackInfo<Value>& args) {
return;
}

if (!FileURLToPath(env, file_path_url.value(), initial_file_path)) return;
if (!node::url::FileURLToPath(
env, file_path_url.value(), initial_file_path))
return;

FromNamespacedPath(&initial_file_path);

@@ -3050,7 +2936,8 @@ void BindingData::LegacyMainResolve(const FunctionCallbackInfo<Value>& args) {
return;
}

if (!FileURLToPath(env, file_path_url.value(), initial_file_path)) return;
if (!node::url::FileURLToPath(env, file_path_url.value(), initial_file_path))
return;

FromNamespacedPath(&initial_file_path);

@@ -3077,7 +2964,8 @@ void BindingData::LegacyMainResolve(const FunctionCallbackInfo<Value>& args) {
std::string module_path;
std::string module_base;

if (!FileURLToPath(env, package_json_url.value(), module_path)) return;
if (!node::url::FileURLToPath(env, package_json_url.value(), module_path))
return;

if (args.Length() >= 3 && !args[2]->IsNullOrUndefined() &&
args[2]->IsString()) {
@@ -3092,7 +2980,7 @@ void BindingData::LegacyMainResolve(const FunctionCallbackInfo<Value>& args) {
return;
}

if (!FileURLToPath(env, base_url.value(), module_base)) return;
if (!node::url::FileURLToPath(env, base_url.value(), module_base)) return;
} else {
std::string err_arg_message =
"The \"base\" argument must be of type string or an instance of URL.";
118 changes: 118 additions & 0 deletions src/node_url.cc
Original file line number Diff line number Diff line change
@@ -4,6 +4,8 @@
#include "node_errors.h"
#include "node_external_reference.h"
#include "node_i18n.h"
#include "node_metadata.h"
#include "node_process-inl.h"
#include "util-inl.h"
#include "v8-fast-api-calls.h"
#include "v8.h"
@@ -435,6 +437,122 @@ std::string FromFilePath(std::string_view file_path) {
return ada::href_from_file(escaped_file_path);
}

bool FileURLToPath(Environment* env,
const ada::url_aggregator& file_url,
/* The linter can't detect the assign for result_file_path
So we need to ignore since it suggest to put const */
// NOLINTNEXTLINE(runtime/references)
std::string& result_file_path) {
if (file_url.type != ada::scheme::FILE) {
env->isolate()->ThrowException(ERR_INVALID_URL_SCHEME(env->isolate()));

return false;
}

std::string_view pathname = file_url.get_pathname();
#ifdef _WIN32
size_t first_percent = std::string::npos;
size_t pathname_size = pathname.size();
std::string pathname_escaped_slash;

for (size_t i = 0; i < pathname_size; i++) {
if (pathname[i] == '/') {
pathname_escaped_slash += '\\';
} else {
pathname_escaped_slash += pathname[i];
}

if (pathname[i] != '%') continue;

if (first_percent == std::string::npos) {
first_percent = i;
}

// just safe-guard against access the pathname
// outside the bounds
if ((i + 2) >= pathname_size) continue;

char third = pathname[i + 2] | 0x20;

bool is_slash = pathname[i + 1] == '2' && third == 102;
bool is_forward_slash = pathname[i + 1] == '5' && third == 99;

if (!is_slash && !is_forward_slash) continue;

env->isolate()->ThrowException(ERR_INVALID_FILE_URL_PATH(
env->isolate(),
"File URL path must not include encoded \\ or / characters"));

return false;
}

std::string_view hostname = file_url.get_hostname();
std::string decoded_pathname = ada::unicode::percent_decode(
std::string_view(pathname_escaped_slash), first_percent);

if (hostname.size() > 0) {
// If hostname is set, then we have a UNC path
// Pass the hostname through domainToUnicode just in case
// it is an IDN using punycode encoding. We do not need to worry
// about percent encoding because the URL parser will have
// already taken care of that for us. Note that this only
// causes IDNs with an appropriate `xn--` prefix to be decoded.
result_file_path =
"\\\\" + ada::unicode::to_unicode(hostname) + decoded_pathname;

return true;
}

char letter = decoded_pathname[1] | 0x20;
char sep = decoded_pathname[2];

// a..z A..Z
if (letter < 'a' || letter > 'z' || sep != ':') {
env->isolate()->ThrowException(ERR_INVALID_FILE_URL_PATH(
env->isolate(), "File URL path must be absolute"));

return false;
}

result_file_path = decoded_pathname.substr(1);

return true;
#else // _WIN32
std::string_view hostname = file_url.get_hostname();

if (hostname.size() > 0) {
std::string error_message =
std::string("File URL host must be \"localhost\" or empty on ") +
std::string(per_process::metadata.platform);
env->isolate()->ThrowException(
ERR_INVALID_FILE_URL_HOST(env->isolate(), error_message.c_str()));

return false;
}

size_t first_percent = std::string::npos;
for (size_t i = 0; (i + 2) < pathname.size(); i++) {
if (pathname[i] != '%') continue;

if (first_percent == std::string::npos) {
first_percent = i;
}

if (pathname[i + 1] == '2' && (pathname[i + 2] | 0x20) == 102) {
env->isolate()->ThrowException(ERR_INVALID_FILE_URL_PATH(
env->isolate(),
"File URL path must not include encoded / characters"));

return false;
}
}

result_file_path = ada::unicode::percent_decode(pathname, first_percent);

return true;
#endif // _WIN32
}

} // namespace url

} // namespace node
7 changes: 6 additions & 1 deletion src/node_url.h
Original file line number Diff line number Diff line change
@@ -82,7 +82,12 @@ class BindingData : public SnapshotableObject {
};

std::string FromFilePath(std::string_view file_path);

bool FileURLToPath(Environment* env,
const ada::url_aggregator& file_url,
/* The linter can't detect the assign for result_file_path
So we need to ignore since it suggest to put const */
// NOLINTNEXTLINE(runtime/references)
std::string& result_file_path);
} // namespace url

} // namespace node