Skip to content

Commit 0c45748

Browse files
committed
url: refactor FileURLToPath
1 parent fce8fba commit 0c45748

File tree

3 files changed

+127
-121
lines changed

3 files changed

+127
-121
lines changed

src/node_file.cc

+13-121
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "node_metadata.h"
3030
#include "node_process-inl.h"
3131
#include "node_stat_watcher.h"
32+
#include "node_url.h"
3233
#include "permission/permission.h"
3334
#include "util-inl.h"
3435

@@ -2842,123 +2843,6 @@ static void GetFormatOfExtensionlessFile(
28422843
return args.GetReturnValue().Set(EXTENSIONLESS_FORMAT_JAVASCRIPT);
28432844
}
28442845

2845-
static bool FileURLToPath(
2846-
Environment* env,
2847-
const ada::url_aggregator& file_url,
2848-
/* The linter can't detect the assign for result_file_path
2849-
So we need to ignore since it suggest to put const */
2850-
// NOLINTNEXTLINE(runtime/references)
2851-
std::string& result_file_path) {
2852-
if (file_url.type != ada::scheme::FILE) {
2853-
env->isolate()->ThrowException(ERR_INVALID_URL_SCHEME(env->isolate()));
2854-
2855-
return false;
2856-
}
2857-
2858-
std::string_view pathname = file_url.get_pathname();
2859-
#ifdef _WIN32
2860-
size_t first_percent = std::string::npos;
2861-
size_t pathname_size = pathname.size();
2862-
std::string pathname_escaped_slash;
2863-
2864-
for (size_t i = 0; i < pathname_size; i++) {
2865-
if (pathname[i] == '/') {
2866-
pathname_escaped_slash += '\\';
2867-
} else {
2868-
pathname_escaped_slash += pathname[i];
2869-
}
2870-
2871-
if (pathname[i] != '%') continue;
2872-
2873-
if (first_percent == std::string::npos) {
2874-
first_percent = i;
2875-
}
2876-
2877-
// just safe-guard against access the pathname
2878-
// outside the bounds
2879-
if ((i + 2) >= pathname_size) continue;
2880-
2881-
char third = pathname[i + 2] | 0x20;
2882-
2883-
bool is_slash = pathname[i + 1] == '2' && third == 102;
2884-
bool is_forward_slash = pathname[i + 1] == '5' && third == 99;
2885-
2886-
if (!is_slash && !is_forward_slash) continue;
2887-
2888-
env->isolate()->ThrowException(ERR_INVALID_FILE_URL_PATH(
2889-
env->isolate(),
2890-
"File URL path must not include encoded \\ or / characters"));
2891-
2892-
return false;
2893-
}
2894-
2895-
std::string_view hostname = file_url.get_hostname();
2896-
std::string decoded_pathname = ada::unicode::percent_decode(
2897-
std::string_view(pathname_escaped_slash), first_percent);
2898-
2899-
if (hostname.size() > 0) {
2900-
// If hostname is set, then we have a UNC path
2901-
// Pass the hostname through domainToUnicode just in case
2902-
// it is an IDN using punycode encoding. We do not need to worry
2903-
// about percent encoding because the URL parser will have
2904-
// already taken care of that for us. Note that this only
2905-
// causes IDNs with an appropriate `xn--` prefix to be decoded.
2906-
result_file_path =
2907-
"\\\\" + ada::unicode::to_unicode(hostname) + decoded_pathname;
2908-
2909-
return true;
2910-
}
2911-
2912-
char letter = decoded_pathname[1] | 0x20;
2913-
char sep = decoded_pathname[2];
2914-
2915-
// a..z A..Z
2916-
if (letter < 'a' || letter > 'z' || sep != ':') {
2917-
env->isolate()->ThrowException(ERR_INVALID_FILE_URL_PATH(
2918-
env->isolate(), "File URL path must be absolute"));
2919-
2920-
return false;
2921-
}
2922-
2923-
result_file_path = decoded_pathname.substr(1);
2924-
2925-
return true;
2926-
#else // _WIN32
2927-
std::string_view hostname = file_url.get_hostname();
2928-
2929-
if (hostname.size() > 0) {
2930-
std::string error_message =
2931-
std::string("File URL host must be \"localhost\" or empty on ") +
2932-
std::string(per_process::metadata.platform);
2933-
env->isolate()->ThrowException(
2934-
ERR_INVALID_FILE_URL_HOST(env->isolate(), error_message.c_str()));
2935-
2936-
return false;
2937-
}
2938-
2939-
size_t first_percent = std::string::npos;
2940-
for (size_t i = 0; (i + 2) < pathname.size(); i++) {
2941-
if (pathname[i] != '%') continue;
2942-
2943-
if (first_percent == std::string::npos) {
2944-
first_percent = i;
2945-
}
2946-
2947-
if (pathname[i + 1] == '2' && (pathname[i + 2] | 0x20) == 102) {
2948-
env->isolate()->ThrowException(ERR_INVALID_FILE_URL_PATH(
2949-
env->isolate(),
2950-
"File URL path must not include encoded / characters"));
2951-
2952-
return false;
2953-
}
2954-
}
2955-
2956-
result_file_path = ada::unicode::percent_decode(pathname, first_percent);
2957-
2958-
return true;
2959-
#endif // _WIN32
2960-
}
2961-
29622846
BindingData::FilePathIsFileReturnType BindingData::FilePathIsFile(
29632847
Environment* env, const std::string& file_path) {
29642848
THROW_IF_INSUFFICIENT_PERMISSIONS(
@@ -3046,7 +2930,9 @@ void BindingData::LegacyMainResolve(const FunctionCallbackInfo<Value>& args) {
30462930
return;
30472931
}
30482932

3049-
if (!FileURLToPath(env, file_path_url.value(), initial_file_path)) return;
2933+
if (!node::url::FileURLToPath(env, *file_path_url, &initial_file_path)) {
2934+
return;
2935+
}
30502936

30512937
FromNamespacedPath(&initial_file_path);
30522938

@@ -3080,7 +2966,9 @@ void BindingData::LegacyMainResolve(const FunctionCallbackInfo<Value>& args) {
30802966
return;
30812967
}
30822968

3083-
if (!FileURLToPath(env, file_path_url.value(), initial_file_path)) return;
2969+
if (!node::url::FileURLToPath(env, *file_path_url, &initial_file_path)) {
2970+
return;
2971+
}
30842972

30852973
FromNamespacedPath(&initial_file_path);
30862974

@@ -3107,7 +2995,9 @@ void BindingData::LegacyMainResolve(const FunctionCallbackInfo<Value>& args) {
31072995
std::string module_path;
31082996
std::string module_base;
31092997

3110-
if (!FileURLToPath(env, package_json_url.value(), module_path)) return;
2998+
if (!node::url::FileURLToPath(env, *package_json_url, &module_path)) {
2999+
return;
3000+
}
31113001

31123002
if (args.Length() >= 3 && !args[2]->IsNullOrUndefined() &&
31133003
args[2]->IsString()) {
@@ -3122,7 +3012,9 @@ void BindingData::LegacyMainResolve(const FunctionCallbackInfo<Value>& args) {
31223012
return;
31233013
}
31243014

3125-
if (!FileURLToPath(env, base_url.value(), module_base)) return;
3015+
if (!node::url::FileURLToPath(env, *base_url, &module_base)) {
3016+
return;
3017+
}
31263018
} else {
31273019
std::string err_arg_message =
31283020
"The \"base\" argument must be of type string or an instance of URL.";

src/node_url.cc

+111
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
#include "node_errors.h"
55
#include "node_external_reference.h"
66
#include "node_i18n.h"
7+
#include "node_metadata.h"
8+
#include "node_process-inl.h"
79
#include "util-inl.h"
810
#include "v8-fast-api-calls.h"
911
#include "v8.h"
@@ -426,6 +428,115 @@ std::string FromFilePath(const std::string_view file_path) {
426428
return ada::href_from_file(escaped_file_path);
427429
}
428430

431+
bool FileURLToPath(Environment* env,
432+
const ada::url_aggregator& file_url,
433+
std::string* result_file_path) {
434+
if (file_url.type != ada::scheme::FILE) {
435+
THROW_ERR_INVALID_URL_SCHEME(env->isolate());
436+
return false;
437+
}
438+
439+
std::string_view pathname = file_url.get_pathname();
440+
#ifdef _WIN32
441+
size_t first_percent = std::string::npos;
442+
size_t pathname_size = pathname.size();
443+
std::string pathname_escaped_slash;
444+
445+
for (size_t i = 0; i < pathname_size; i++) {
446+
if (pathname[i] == '/') {
447+
pathname_escaped_slash += '\\';
448+
} else {
449+
pathname_escaped_slash += pathname[i];
450+
}
451+
452+
if (pathname[i] != '%') continue;
453+
454+
if (first_percent == std::string::npos) {
455+
first_percent = i;
456+
}
457+
458+
// just safe-guard against access the pathname
459+
// outside the bounds
460+
if ((i + 2) >= pathname_size) continue;
461+
462+
char third = pathname[i + 2] | 0x20;
463+
464+
bool is_slash = pathname[i + 1] == '2' && third == 102;
465+
bool is_forward_slash = pathname[i + 1] == '5' && third == 99;
466+
467+
if (!is_slash && !is_forward_slash) continue;
468+
469+
THROW_ERR_INVALID_FILE_URL_PATH(
470+
env->isolate(),
471+
"File URL path must not include encoded \\ or / characters");
472+
473+
return false;
474+
}
475+
476+
std::string_view hostname = file_url.get_hostname();
477+
std::string decoded_pathname =
478+
ada::unicode::percent_decode(pathname_escaped_slash, first_percent);
479+
480+
if (hostname.size() > 0) {
481+
// If hostname is set, then we have a UNC path
482+
// Pass the hostname through domainToUnicode just in case
483+
// it is an IDN using punycode encoding. We do not need to worry
484+
// about percent encoding because the URL parser will have
485+
// already taken care of that for us. Note that this only
486+
// causes IDNs with an appropriate `xn--` prefix to be decoded.
487+
*result_file_path =
488+
"\\\\" + ada::unicode::to_unicode(hostname) + decoded_pathname;
489+
490+
return true;
491+
}
492+
493+
char letter = decoded_pathname[1] | 0x20;
494+
char sep = decoded_pathname[2];
495+
496+
// a..z A..Z
497+
if (letter < 'a' || letter > 'z' || sep != ':') {
498+
env->isolate()->ThrowException(ERR_INVALID_FILE_URL_PATH(
499+
env->isolate(), "File URL path must be absolute"));
500+
501+
return false;
502+
}
503+
504+
*result_file_path = decoded_pathname.substr(1);
505+
506+
return true;
507+
#else // _WIN32
508+
std::string_view hostname = file_url.get_hostname();
509+
510+
if (hostname.size() > 0) {
511+
THROW_ERR_INVALID_FILE_URL_HOST(
512+
env->isolate(),
513+
"File URL host must be \"localhost\" or empty on %s",
514+
std::string(per_process::metadata.platform));
515+
return false;
516+
}
517+
518+
size_t first_percent = std::string::npos;
519+
for (size_t i = 0; (i + 2) < pathname.size(); i++) {
520+
if (pathname[i] != '%') continue;
521+
522+
if (first_percent == std::string::npos) {
523+
first_percent = i;
524+
}
525+
526+
if (pathname[i + 1] == '2' && (pathname[i + 2] | 0x20) == 102) {
527+
THROW_ERR_INVALID_FILE_URL_PATH(
528+
env->isolate(),
529+
"File URL path must not include encoded / characters");
530+
return false;
531+
}
532+
}
533+
534+
*result_file_path = ada::unicode::percent_decode(pathname, first_percent);
535+
536+
return true;
537+
#endif // _WIN32
538+
}
539+
429540
} // namespace url
430541

431542
} // namespace node

src/node_url.h

+3
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ class BindingData : public SnapshotableObject {
8282
};
8383

8484
std::string FromFilePath(const std::string_view file_path);
85+
bool FileURLToPath(Environment* env,
86+
const ada::url_aggregator& file_url,
87+
std::string* result_file_path);
8588

8689
} // namespace url
8790

0 commit comments

Comments
 (0)