%PDF- %PDF-
Direktori : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/src/ |
Current File : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/src/node_url.cc |
#include "node_url.h" #include "ada.h" #include "base_object-inl.h" #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" #include <cstdint> #include <cstdio> #include <numeric> namespace node { namespace url { using v8::CFunction; using v8::Context; using v8::FastOneByteString; using v8::FunctionCallbackInfo; using v8::HandleScope; using v8::Isolate; using v8::Local; using v8::NewStringType; using v8::Object; using v8::ObjectTemplate; using v8::String; using v8::Value; void BindingData::MemoryInfo(MemoryTracker* tracker) const { tracker->TrackField("url_components_buffer", url_components_buffer_); } BindingData::BindingData(Realm* realm, v8::Local<v8::Object> object) : SnapshotableObject(realm, object, type_int), url_components_buffer_(realm->isolate(), kURLComponentsLength) { object ->Set(realm->context(), FIXED_ONE_BYTE_STRING(realm->isolate(), "urlComponents"), url_components_buffer_.GetJSArray()) .Check(); url_components_buffer_.MakeWeak(); } bool BindingData::PrepareForSerialization(v8::Local<v8::Context> context, v8::SnapshotCreator* creator) { // We'll just re-initialize the buffers in the constructor since their // contents can be thrown away once consumed in the previous call. url_components_buffer_.Release(); // Return true because we need to maintain the reference to the binding from // JS land. return true; } InternalFieldInfoBase* BindingData::Serialize(int index) { DCHECK_IS_SNAPSHOT_SLOT(index); InternalFieldInfo* info = InternalFieldInfoBase::New<InternalFieldInfo>(type()); return info; } void BindingData::Deserialize(v8::Local<v8::Context> context, v8::Local<v8::Object> holder, int index, InternalFieldInfoBase* info) { DCHECK_IS_SNAPSHOT_SLOT(index); v8::HandleScope scope(context->GetIsolate()); Realm* realm = Realm::GetCurrent(context); BindingData* binding = realm->AddBindingData<BindingData>(holder); CHECK_NOT_NULL(binding); } void BindingData::DomainToASCII(const FunctionCallbackInfo<Value>& args) { Environment* env = Environment::GetCurrent(args); CHECK_GE(args.Length(), 1); CHECK(args[0]->IsString()); std::string input = Utf8Value(env->isolate(), args[0]).ToString(); if (input.empty()) { return args.GetReturnValue().Set(String::Empty(env->isolate())); } // It is important to have an initial value that contains a special scheme. // Since it will change the implementation of `set_hostname` according to URL // spec. auto out = ada::parse<ada::url>("ws://x"); DCHECK(out); if (!out->set_hostname(input)) { return args.GetReturnValue().Set(String::Empty(env->isolate())); } std::string host = out->get_hostname(); args.GetReturnValue().Set( String::NewFromUtf8(env->isolate(), host.c_str()).ToLocalChecked()); } void BindingData::DomainToUnicode(const FunctionCallbackInfo<Value>& args) { Environment* env = Environment::GetCurrent(args); CHECK_GE(args.Length(), 1); CHECK(args[0]->IsString()); std::string input = Utf8Value(env->isolate(), args[0]).ToString(); if (input.empty()) { return args.GetReturnValue().Set(String::Empty(env->isolate())); } // It is important to have an initial value that contains a special scheme. // Since it will change the implementation of `set_hostname` according to URL // spec. auto out = ada::parse<ada::url>("ws://x"); DCHECK(out); if (!out->set_hostname(input)) { return args.GetReturnValue().Set(String::Empty(env->isolate())); } std::string result = ada::unicode::to_unicode(out->get_hostname()); args.GetReturnValue().Set(String::NewFromUtf8(env->isolate(), result.c_str(), NewStringType::kNormal, result.length()) .ToLocalChecked()); } void BindingData::GetOrigin(const v8::FunctionCallbackInfo<Value>& args) { CHECK_GE(args.Length(), 1); CHECK(args[0]->IsString()); // input Environment* env = Environment::GetCurrent(args); HandleScope handle_scope(env->isolate()); Utf8Value input(env->isolate(), args[0]); std::string_view input_view = input.ToStringView(); auto out = ada::parse<ada::url_aggregator>(input_view); if (!out) { THROW_ERR_INVALID_URL(env, "Invalid URL"); return; } std::string origin = out->get_origin(); args.GetReturnValue().Set(String::NewFromUtf8(env->isolate(), origin.data(), NewStringType::kNormal, origin.length()) .ToLocalChecked()); } void BindingData::CanParse(const FunctionCallbackInfo<Value>& args) { CHECK_GE(args.Length(), 1); CHECK(args[0]->IsString()); // input // args[1] // base url Environment* env = Environment::GetCurrent(args); HandleScope handle_scope(env->isolate()); Utf8Value input(env->isolate(), args[0]); std::string_view input_view = input.ToStringView(); bool can_parse{}; if (args[1]->IsString()) { Utf8Value base(env->isolate(), args[1]); std::string_view base_view = base.ToStringView(); can_parse = ada::can_parse(input_view, &base_view); } else { can_parse = ada::can_parse(input_view); } args.GetReturnValue().Set(can_parse); } bool BindingData::FastCanParse(Local<Value> receiver, const FastOneByteString& input) { return ada::can_parse(std::string_view(input.data, input.length)); } bool BindingData::FastCanParseWithBase(Local<Value> receiver, const FastOneByteString& input, const FastOneByteString& base) { auto base_view = std::string_view(base.data, base.length); return ada::can_parse(std::string_view(input.data, input.length), &base_view); } CFunction BindingData::fast_can_parse_methods_[] = { CFunction::Make(FastCanParse), CFunction::Make(FastCanParseWithBase)}; void BindingData::Format(const FunctionCallbackInfo<Value>& args) { CHECK_GT(args.Length(), 4); CHECK(args[0]->IsString()); // url href Environment* env = Environment::GetCurrent(args); Isolate* isolate = env->isolate(); Utf8Value href(isolate, args[0].As<String>()); const bool hash = args[1]->IsTrue(); const bool unicode = args[2]->IsTrue(); const bool search = args[3]->IsTrue(); const bool auth = args[4]->IsTrue(); // ada::url provides a faster alternative to ada::url_aggregator if we // directly want to manipulate the url components without using the respective // setters. therefore we are using ada::url here. auto out = ada::parse<ada::url>(href.ToStringView()); CHECK(out); if (!hash) { out->hash = std::nullopt; } if (unicode && out->has_hostname()) { out->host = ada::idna::to_unicode(out->get_hostname()); } if (!search) { out->query = std::nullopt; } if (!auth) { out->username = ""; out->password = ""; } std::string result = out->get_href(); args.GetReturnValue().Set(String::NewFromUtf8(env->isolate(), result.data(), NewStringType::kNormal, result.length()) .ToLocalChecked()); } void BindingData::Parse(const FunctionCallbackInfo<Value>& args) { CHECK_GE(args.Length(), 1); CHECK(args[0]->IsString()); // input // args[1] // base url Realm* realm = Realm::GetCurrent(args); BindingData* binding_data = realm->GetBindingData<BindingData>(); Isolate* isolate = realm->isolate(); std::optional<std::string> base_{}; Utf8Value input(isolate, args[0]); ada::result<ada::url_aggregator> base; ada::url_aggregator* base_pointer = nullptr; if (args[1]->IsString()) { base_ = Utf8Value(isolate, args[1]).ToString(); base = ada::parse<ada::url_aggregator>(*base_); if (!base) { return ThrowInvalidURL(realm->env(), input.ToStringView(), base_); } base_pointer = &base.value(); } auto out = ada::parse<ada::url_aggregator>(input.ToStringView(), base_pointer); if (!out) { return ThrowInvalidURL(realm->env(), input.ToStringView(), base_); } binding_data->UpdateComponents(out->get_components(), out->type); args.GetReturnValue().Set( ToV8Value(realm->context(), out->get_href(), isolate).ToLocalChecked()); } void BindingData::Update(const FunctionCallbackInfo<Value>& args) { CHECK(args[0]->IsString()); // href CHECK(args[1]->IsNumber()); // action type CHECK(args[2]->IsString()); // new value Realm* realm = Realm::GetCurrent(args); BindingData* binding_data = realm->GetBindingData<BindingData>(); Isolate* isolate = realm->isolate(); enum url_update_action action = static_cast<enum url_update_action>( args[1]->Uint32Value(realm->context()).FromJust()); Utf8Value input(isolate, args[0].As<String>()); Utf8Value new_value(isolate, args[2].As<String>()); std::string_view new_value_view = new_value.ToStringView(); auto out = ada::parse<ada::url_aggregator>(input.ToStringView()); CHECK(out); bool result{true}; switch (action) { case kPathname: { result = out->set_pathname(new_value_view); break; } case kHash: { out->set_hash(new_value_view); break; } case kHost: { result = out->set_host(new_value_view); break; } case kHostname: { result = out->set_hostname(new_value_view); break; } case kHref: { result = out->set_href(new_value_view); break; } case kPassword: { result = out->set_password(new_value_view); break; } case kPort: { result = out->set_port(new_value_view); break; } case kProtocol: { result = out->set_protocol(new_value_view); break; } case kSearch: { out->set_search(new_value_view); break; } case kUsername: { result = out->set_username(new_value_view); break; } default: UNREACHABLE("Unsupported URL update action"); } if (!result) { return args.GetReturnValue().Set(false); } binding_data->UpdateComponents(out->get_components(), out->type); args.GetReturnValue().Set( ToV8Value(realm->context(), out->get_href(), isolate).ToLocalChecked()); } void BindingData::UpdateComponents(const ada::url_components& components, const ada::scheme::type type) { url_components_buffer_[0] = components.protocol_end; url_components_buffer_[1] = components.username_end; url_components_buffer_[2] = components.host_start; url_components_buffer_[3] = components.host_end; url_components_buffer_[4] = components.port; url_components_buffer_[5] = components.pathname_start; url_components_buffer_[6] = components.search_start; url_components_buffer_[7] = components.hash_start; url_components_buffer_[8] = type; static_assert(kURLComponentsLength == 9, "kURLComponentsLength should be up-to-date"); } void BindingData::CreatePerIsolateProperties(IsolateData* isolate_data, Local<ObjectTemplate> target) { Isolate* isolate = isolate_data->isolate(); SetMethodNoSideEffect(isolate, target, "domainToASCII", DomainToASCII); SetMethodNoSideEffect(isolate, target, "domainToUnicode", DomainToUnicode); SetMethodNoSideEffect(isolate, target, "format", Format); SetMethodNoSideEffect(isolate, target, "getOrigin", GetOrigin); SetMethod(isolate, target, "parse", Parse); SetMethod(isolate, target, "update", Update); SetFastMethodNoSideEffect( isolate, target, "canParse", CanParse, {fast_can_parse_methods_, 2}); } void BindingData::CreatePerContextProperties(Local<Object> target, Local<Value> unused, Local<Context> context, void* priv) { Realm* realm = Realm::GetCurrent(context); realm->AddBindingData<BindingData>(target); } void BindingData::RegisterExternalReferences( ExternalReferenceRegistry* registry) { registry->Register(DomainToASCII); registry->Register(DomainToUnicode); registry->Register(Format); registry->Register(GetOrigin); registry->Register(Parse); registry->Register(Update); registry->Register(CanParse); registry->Register(FastCanParse); registry->Register(FastCanParseWithBase); for (const CFunction& method : fast_can_parse_methods_) { registry->Register(method.GetTypeInfo()); } } void ThrowInvalidURL(node::Environment* env, std::string_view input, std::optional<std::string> base) { Local<Value> err = ERR_INVALID_URL(env->isolate(), "Invalid URL"); DCHECK(err->IsObject()); auto err_object = err.As<Object>(); USE(err_object->Set(env->context(), env->input_string(), v8::String::NewFromUtf8(env->isolate(), input.data(), v8::NewStringType::kNormal, input.size()) .ToLocalChecked())); if (base.has_value()) { USE(err_object->Set(env->context(), env->base_string(), v8::String::NewFromUtf8(env->isolate(), base.value().c_str(), v8::NewStringType::kNormal, base.value().size()) .ToLocalChecked())); } env->isolate()->ThrowException(err); } std::string FromFilePath(std::string_view file_path) { // Avoid unnecessary allocations. size_t pos = file_path.empty() ? std::string_view::npos : file_path.find('%'); if (pos == std::string_view::npos) { return ada::href_from_file(file_path); } // Escape '%' characters to a temporary string. std::string escaped_file_path; do { escaped_file_path += file_path.substr(0, pos + 1); escaped_file_path += "25"; file_path = file_path.substr(pos + 1); pos = file_path.empty() ? std::string_view::npos : file_path.find('%'); } while (pos != std::string_view::npos); escaped_file_path += file_path; return ada::href_from_file(escaped_file_path); } std::optional<std::string> FileURLToPath(Environment* env, const ada::url_aggregator& file_url) { if (file_url.type != ada::scheme::FILE) { THROW_ERR_INVALID_URL_SCHEME(env->isolate()); return std::nullopt; } 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; THROW_ERR_INVALID_FILE_URL_PATH( env->isolate(), "File URL path must not include encoded \\ or / characters"); return std::nullopt; } 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. return "\\\\" + ada::unicode::to_unicode(hostname) + decoded_pathname; } char letter = decoded_pathname[1] | 0x20; char sep = decoded_pathname[2]; // a..z A..Z if (letter < 'a' || letter > 'z' || sep != ':') { THROW_ERR_INVALID_FILE_URL_PATH(env->isolate(), "File URL path must be absolute"); return std::nullopt; } return decoded_pathname.substr(1); #else // _WIN32 std::string_view hostname = file_url.get_hostname(); if (hostname.size() > 0) { THROW_ERR_INVALID_FILE_URL_HOST( env->isolate(), "File URL host must be \"localhost\" or empty on ", std::string(per_process::metadata.platform)); return std::nullopt; } 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) { THROW_ERR_INVALID_FILE_URL_PATH( env->isolate(), "File URL path must not include encoded / characters"); return std::nullopt; } } return ada::unicode::percent_decode(pathname, first_percent); #endif // _WIN32 } // Reverse the logic applied by path.toNamespacedPath() to create a // namespace-prefixed path. void FromNamespacedPath(std::string* path) { #ifdef _WIN32 if (path->compare(0, 8, "\\\\?\\UNC\\", 8) == 0) { *path = path->substr(8); path->insert(0, "\\\\"); } else if (path->compare(0, 4, "\\\\?\\", 4) == 0) { *path = path->substr(4); } #endif } } // namespace url } // namespace node NODE_BINDING_CONTEXT_AWARE_INTERNAL( url, node::url::BindingData::CreatePerContextProperties) NODE_BINDING_PER_ISOLATE_INIT( url, node::url::BindingData::CreatePerIsolateProperties) NODE_BINDING_EXTERNAL_REFERENCE( url, node::url::BindingData::RegisterExternalReferences)