Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 5a3ad87

Browse filesBrowse files
anonrigtargos
authored andcommitted
url: simplify and improve url formatting
PR-URL: #46736 Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent 4e18e0a commit 5a3ad87
Copy full SHA for 5a3ad87

File tree

Expand file treeCollapse file tree

4 files changed

+111
-110
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

4 files changed

+111
-110
lines changed
Open diff view settings
Collapse file

‎lib/internal/url.js‎

Copy file name to clipboardExpand all lines: lib/internal/url.js
+13-85Lines changed: 13 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ const path = require('path');
7777

7878
const {
7979
validateFunction,
80-
validateObject,
8180
} = require('internal/validators');
8281

8382
const querystring = require('querystring');
@@ -150,8 +149,6 @@ class URLContext {
150149
password = '';
151150
port = '';
152151
hash = '';
153-
hasHost = false;
154-
hasOpaquePath = false;
155152
}
156153

157154
function isURLSearchParams(self) {
@@ -282,7 +279,9 @@ class URLSearchParams {
282279
name = toUSVString(name);
283280
value = toUSVString(value);
284281
ArrayPrototypePush(this[searchParams], name, value);
285-
update(this[context], this);
282+
if (this[context]) {
283+
this[context].search = this.toString();
284+
}
286285
}
287286

288287
delete(name) {
@@ -303,7 +302,9 @@ class URLSearchParams {
303302
i += 2;
304303
}
305304
}
306-
update(this[context], this);
305+
if (this[context]) {
306+
this[context].search = this.toString();
307+
}
307308
}
308309

309310
get(name) {
@@ -398,7 +399,9 @@ class URLSearchParams {
398399
ArrayPrototypePush(list, name, value);
399400
}
400401

401-
update(this[context], this);
402+
if (this[context]) {
403+
this[context].search = this.toString();
404+
}
402405
}
403406

404407
sort() {
@@ -442,7 +445,9 @@ class URLSearchParams {
442445
}
443446
}
444447

445-
update(this[context], this);
448+
if (this[context]) {
449+
this[context].search = this.toString();
450+
}
446451
}
447452

448453
// https://heycam.github.io/webidl/#es-iterators
@@ -528,46 +533,6 @@ function isURLThis(self) {
528533
return (self !== undefined && self !== null && self[context] !== undefined);
529534
}
530535

531-
function constructHref(ctx, options) {
532-
if (options)
533-
validateObject(options, 'options');
534-
535-
options = {
536-
fragment: true,
537-
unicode: false,
538-
search: true,
539-
auth: true,
540-
...options
541-
};
542-
543-
// https://url.spec.whatwg.org/#url-serializing
544-
let ret = ctx.protocol;
545-
if (ctx.hasHost) {
546-
ret += '//';
547-
const hasUsername = ctx.username !== '';
548-
const hasPassword = ctx.password !== '';
549-
if (options.auth && (hasUsername || hasPassword)) {
550-
if (hasUsername)
551-
ret += ctx.username;
552-
if (hasPassword)
553-
ret += `:${ctx.password}`;
554-
ret += '@';
555-
}
556-
ret += options.unicode ?
557-
domainToUnicode(ctx.hostname) : ctx.hostname;
558-
if (ctx.port !== '')
559-
ret += `:${ctx.port}`;
560-
} else if (!ctx.hasOpaquePath && ctx.pathname.lastIndexOf('/') !== 0 && ctx.pathname.startsWith('//')) {
561-
ret += '/.';
562-
}
563-
ret += ctx.pathname;
564-
if (options.search)
565-
ret += ctx.search;
566-
if (options.fragment)
567-
ret += ctx.hash;
568-
return ret;
569-
}
570-
571536
class URL {
572537
constructor(input, base = undefined) {
573538
// toUSVString is not needed.
@@ -620,14 +585,8 @@ class URL {
620585
return `${constructor.name} ${inspect(obj, opts)}`;
621586
}
622587

623-
[kFormat](options) {
624-
// TODO(@anonrig): Replace kFormat with actually calling setters.
625-
return constructHref(this[context], options);
626-
}
627-
628588
#onParseComplete = (href, origin, protocol, hostname, pathname,
629-
search, username, password, port, hash, hasHost,
630-
hasOpaquePath) => {
589+
search, username, password, port, hash) => {
631590
const ctx = this[context];
632591
ctx.href = href;
633592
ctx.origin = origin;
@@ -639,9 +598,6 @@ class URL {
639598
ctx.password = password;
640599
ctx.port = port;
641600
ctx.hash = hash;
642-
// TODO(@anonrig): Remove hasHost and hasOpaquePath when kFormat is removed.
643-
ctx.hasHost = hasHost;
644-
ctx.hasOpaquePath = hasOpaquePath;
645601
if (!this[searchParams]) { // Invoked from URL constructor
646602
this[searchParams] = new URLSearchParams();
647603
this[searchParams][context] = this;
@@ -854,33 +810,6 @@ ObjectDefineProperties(URL, {
854810
revokeObjectURL: kEnumerableProperty,
855811
});
856812

857-
function update(url, params) {
858-
if (!url)
859-
return;
860-
861-
const ctx = url[context];
862-
const serializedParams = params.toString();
863-
if (serializedParams.length > 0) {
864-
ctx.search = '?' + serializedParams;
865-
} else {
866-
ctx.search = '';
867-
868-
// Potentially strip trailing spaces from an opaque path
869-
if (ctx.hasOpaquePath && ctx.hash.length === 0) {
870-
let length = ctx.pathname.length;
871-
while (length > 0 && ctx.pathname.charCodeAt(length - 1) === 32) {
872-
length--;
873-
}
874-
875-
// No need to copy the whole string if there is no space at the end
876-
if (length !== ctx.pathname.length) {
877-
ctx.pathname = ctx.pathname.slice(0, length);
878-
}
879-
}
880-
}
881-
ctx.href = constructHref(ctx);
882-
}
883-
884813
function initSearchParams(url, init) {
885814
if (!init) {
886815
url[searchParams] = [];
@@ -1379,7 +1308,6 @@ module.exports = {
13791308
domainToASCII,
13801309
domainToUnicode,
13811310
urlToHttpOptions,
1382-
formatSymbol: kFormat,
13831311
searchParamsSymbol: searchParams,
13841312
encodeStr
13851313
};
Collapse file

‎lib/url.js‎

Copy file name to clipboardExpand all lines: lib/url.js
+38-8Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
'use strict';
2323

2424
const {
25+
Boolean,
2526
Int8Array,
2627
ObjectKeys,
2728
SafeSet,
@@ -37,7 +38,10 @@ const {
3738
ERR_INVALID_ARG_TYPE,
3839
ERR_INVALID_URL,
3940
} = require('internal/errors').codes;
40-
const { validateString } = require('internal/validators');
41+
const {
42+
validateString,
43+
validateObject,
44+
} = require('internal/validators');
4145

4246
// This ensures setURLConstructor() is called before the native
4347
// URL::ToObject() method is used.
@@ -50,11 +54,14 @@ const {
5054
domainToASCII,
5155
domainToUnicode,
5256
fileURLToPath,
53-
formatSymbol,
5457
pathToFileURL,
5558
urlToHttpOptions,
5659
} = require('internal/url');
5760

61+
const {
62+
formatUrl,
63+
} = internalBinding('url');
64+
5865
// Original url.parse() API
5966

6067
function Url() {
@@ -578,13 +585,36 @@ function urlFormat(urlObject, options) {
578585
} else if (typeof urlObject !== 'object' || urlObject === null) {
579586
throw new ERR_INVALID_ARG_TYPE('urlObject',
580587
['Object', 'string'], urlObject);
581-
} else if (!(urlObject instanceof Url)) {
582-
const format = urlObject[formatSymbol];
583-
return format ?
584-
format.call(urlObject, options) :
585-
Url.prototype.format.call(urlObject);
588+
} else if (urlObject instanceof URL) {
589+
let fragment = true;
590+
let unicode = false;
591+
let search = true;
592+
let auth = true;
593+
594+
if (options) {
595+
validateObject(options, 'options');
596+
597+
if (options.fragment != null) {
598+
fragment = Boolean(options.fragment);
599+
}
600+
601+
if (options.unicode != null) {
602+
unicode = Boolean(options.unicode);
603+
}
604+
605+
if (options.search != null) {
606+
search = Boolean(options.search);
607+
}
608+
609+
if (options.auth != null) {
610+
auth = Boolean(options.auth);
611+
}
612+
}
613+
614+
return formatUrl(urlObject.href, fragment, unicode, search, auth);
586615
}
587-
return urlObject.format();
616+
617+
return Url.prototype.format.call(urlObject);
588618
}
589619

590620
// These characters do not need escaping:
Collapse file

‎src/node_url.cc‎

Copy file name to clipboardExpand all lines: src/node_url.cc
+59-14Lines changed: 59 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
namespace node {
1313

14-
using v8::Boolean;
1514
using v8::Context;
1615
using v8::Function;
1716
using v8::FunctionCallbackInfo;
@@ -47,7 +46,7 @@ enum url_update_action {
4746
kHref = 9,
4847
};
4948

50-
void SetArgs(Environment* env, Local<Value> argv[12], const ada::result& url) {
49+
void SetArgs(Environment* env, Local<Value> argv[10], const ada::result& url) {
5150
Isolate* isolate = env->isolate();
5251
argv[0] = Utf8String(isolate, url->get_href());
5352
argv[1] = Utf8String(isolate, url->get_origin());
@@ -59,8 +58,6 @@ void SetArgs(Environment* env, Local<Value> argv[12], const ada::result& url) {
5958
argv[7] = Utf8String(isolate, url->get_password());
6059
argv[8] = Utf8String(isolate, url->get_port());
6160
argv[9] = Utf8String(isolate, url->get_hash());
62-
argv[10] = Boolean::New(isolate, url->host.has_value());
63-
argv[11] = Boolean::New(isolate, url->has_opaque_path);
6461
}
6562

6663
void Parse(const FunctionCallbackInfo<Value>& args) {
@@ -86,8 +83,7 @@ void Parse(const FunctionCallbackInfo<Value>& args) {
8683
}
8784
base_pointer = &base.value();
8885
}
89-
ada::result out =
90-
ada::parse(std::string_view(input.out(), input.length()), base_pointer);
86+
ada::result out = ada::parse(input.ToStringView(), base_pointer);
9187

9288
if (!out) {
9389
return args.GetReturnValue().Set(false);
@@ -105,8 +101,6 @@ void Parse(const FunctionCallbackInfo<Value>& args) {
105101
undef,
106102
undef,
107103
undef,
108-
undef,
109-
undef,
110104
};
111105
SetArgs(env, argv, out);
112106
USE(success_callback_->Call(
@@ -192,10 +186,8 @@ void UpdateUrl(const FunctionCallbackInfo<Value>& args) {
192186
Utf8Value new_value(isolate, args[2].As<String>());
193187
Local<Function> success_callback_ = args[3].As<Function>();
194188

195-
std::string_view new_value_view =
196-
std::string_view(new_value.out(), new_value.length());
197-
std::string_view input_view = std::string_view(input.out(), input.length());
198-
ada::result out = ada::parse(input_view);
189+
std::string_view new_value_view = new_value.ToStringView();
190+
ada::result out = ada::parse(input.ToStringView());
199191
CHECK(out);
200192

201193
bool result{true};
@@ -255,21 +247,73 @@ void UpdateUrl(const FunctionCallbackInfo<Value>& args) {
255247
undef,
256248
undef,
257249
undef,
258-
undef,
259-
undef,
260250
};
261251
SetArgs(env, argv, out);
262252
USE(success_callback_->Call(
263253
env->context(), args.This(), arraysize(argv), argv));
264254
args.GetReturnValue().Set(result);
265255
}
266256

257+
void FormatUrl(const FunctionCallbackInfo<Value>& args) {
258+
CHECK_GT(args.Length(), 4);
259+
CHECK(args[0]->IsString()); // url href
260+
261+
Environment* env = Environment::GetCurrent(args);
262+
Isolate* isolate = env->isolate();
263+
264+
Utf8Value href(isolate, args[0].As<String>());
265+
const bool fragment = args[1]->IsTrue();
266+
const bool unicode = args[2]->IsTrue();
267+
const bool search = args[3]->IsTrue();
268+
const bool auth = args[4]->IsTrue();
269+
270+
ada::result out = ada::parse(href.ToStringView());
271+
CHECK(out);
272+
273+
if (!fragment) {
274+
out->fragment = std::nullopt;
275+
}
276+
277+
if (unicode) {
278+
#if defined(NODE_HAVE_I18N_SUPPORT)
279+
std::string hostname = out->get_hostname();
280+
MaybeStackBuffer<char> buf;
281+
int32_t len = i18n::ToUnicode(&buf, hostname.data(), hostname.length());
282+
283+
if (len < 0) {
284+
out->host = "";
285+
} else {
286+
out->host = buf.ToString();
287+
}
288+
#else
289+
out->host = "";
290+
#endif
291+
}
292+
293+
if (!search) {
294+
out->query = std::nullopt;
295+
}
296+
297+
if (!auth) {
298+
out->username = "";
299+
out->password = "";
300+
}
301+
302+
std::string result = out->get_href();
303+
args.GetReturnValue().Set(String::NewFromUtf8(env->isolate(),
304+
result.data(),
305+
NewStringType::kNormal,
306+
result.length())
307+
.ToLocalChecked());
308+
}
309+
267310
void Initialize(Local<Object> target,
268311
Local<Value> unused,
269312
Local<Context> context,
270313
void* priv) {
271314
SetMethod(context, target, "parse", Parse);
272315
SetMethod(context, target, "updateUrl", UpdateUrl);
316+
SetMethod(context, target, "formatUrl", FormatUrl);
273317

274318
SetMethodNoSideEffect(context, target, "domainToASCII", DomainToASCII);
275319
SetMethodNoSideEffect(context, target, "domainToUnicode", DomainToUnicode);
@@ -279,6 +323,7 @@ void Initialize(Local<Object> target,
279323
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
280324
registry->Register(Parse);
281325
registry->Register(UpdateUrl);
326+
registry->Register(FormatUrl);
282327

283328
registry->Register(DomainToASCII);
284329
registry->Register(DomainToUnicode);

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.