Skip to content

Commit 1fa6352

Browse files
anonrigtargos
authored andcommitted
url: offload URLSearchParams initialization
PR-URL: #46867 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Filip Skokan <panva.ip@gmail.com>
1 parent 5c7fc92 commit 1fa6352

File tree

4 files changed

+48
-52
lines changed

4 files changed

+48
-52
lines changed

lib/_http_client.js

+2-3
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ const {
6363
const Agent = require('_http_agent');
6464
const { Buffer } = require('buffer');
6565
const { defaultTriggerAsyncIdScope } = require('internal/async_hooks');
66-
const { URL, urlToHttpOptions, searchParamsSymbol } = require('internal/url');
66+
const { URL, urlToHttpOptions, isURL } = require('internal/url');
6767
const {
6868
kOutHeaders,
6969
kNeedDrain,
@@ -133,8 +133,7 @@ function ClientRequest(input, options, cb) {
133133
if (typeof input === 'string') {
134134
const urlStr = input;
135135
input = urlToHttpOptions(new URL(urlStr));
136-
} else if (input && input[searchParamsSymbol] &&
137-
input[searchParamsSymbol][searchParamsSymbol]) {
136+
} else if (isURL(input)) {
138137
// url.URL instance
139138
input = urlToHttpOptions(input);
140139
} else {

lib/https.js

+2-4
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ const { ClientRequest } = require('_http_client');
5252
let debug = require('internal/util/debuglog').debuglog('https', (fn) => {
5353
debug = fn;
5454
});
55-
const { URL, urlToHttpOptions, searchParamsSymbol } = require('internal/url');
55+
const { URL, urlToHttpOptions, isURL } = require('internal/url');
5656

5757
function Server(opts, requestListener) {
5858
if (!(this instanceof Server)) return new Server(opts, requestListener);
@@ -344,9 +344,7 @@ function request(...args) {
344344
if (typeof args[0] === 'string') {
345345
const urlStr = ArrayPrototypeShift(args);
346346
options = urlToHttpOptions(new URL(urlStr));
347-
} else if (args[0] && args[0][searchParamsSymbol] &&
348-
args[0][searchParamsSymbol][searchParamsSymbol]) {
349-
// url.URL instance
347+
} else if (isURL(args[0])) {
350348
options = urlToHttpOptions(ArrayPrototypeShift(args));
351349
}
352350

lib/internal/url.js

+37-45
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ class URLSearchParams {
224224
} else {
225225
// USVString
226226
init = toUSVString(init);
227-
initSearchParams(this, init);
227+
this[searchParams] = init ? parseParams(init) : [];
228228
}
229229

230230
// "associated url object"
@@ -534,7 +534,7 @@ ObjectDefineProperties(URLSearchParams.prototype, {
534534
},
535535
});
536536

537-
function isURLThis(self) {
537+
function isURL(self) {
538538
return self != null && ObjectPrototypeHasOwnProperty(self, context);
539539
}
540540

@@ -602,160 +602,161 @@ class URL {
602602
ctx.password = password;
603603
ctx.port = port;
604604
ctx.hash = hash;
605-
if (!this[searchParams]) { // Invoked from URL constructor
606-
this[searchParams] = new URLSearchParams();
607-
this[searchParams][context] = this;
605+
if (this[searchParams]) {
606+
this[searchParams][searchParams] = parseParams(search);
608607
}
609-
initSearchParams(this[searchParams], ctx.search);
610608
};
611609

612610
toString() {
613-
if (!isURLThis(this))
611+
if (!isURL(this))
614612
throw new ERR_INVALID_THIS('URL');
615613
return this[context].href;
616614
}
617615

618616
get href() {
619-
if (!isURLThis(this))
617+
if (!isURL(this))
620618
throw new ERR_INVALID_THIS('URL');
621619
return this[context].href;
622620
}
623621

624622
set href(value) {
625-
if (!isURLThis(this))
623+
if (!isURL(this))
626624
throw new ERR_INVALID_THIS('URL');
627625
const valid = updateUrl(this[context].href, updateActions.kHref, `${value}`, this.#onParseComplete);
628626
if (!valid) { throw ERR_INVALID_URL(`${value}`); }
629627
}
630628

631629
// readonly
632630
get origin() {
633-
if (!isURLThis(this))
631+
if (!isURL(this))
634632
throw new ERR_INVALID_THIS('URL');
635633
return this[context].origin;
636634
}
637635

638636
get protocol() {
639-
if (!isURLThis(this))
637+
if (!isURL(this))
640638
throw new ERR_INVALID_THIS('URL');
641639
return this[context].protocol;
642640
}
643641

644642
set protocol(value) {
645-
if (!isURLThis(this))
643+
if (!isURL(this))
646644
throw new ERR_INVALID_THIS('URL');
647645
updateUrl(this[context].href, updateActions.kProtocol, `${value}`, this.#onParseComplete);
648646
}
649647

650648
get username() {
651-
if (!isURLThis(this))
649+
if (!isURL(this))
652650
throw new ERR_INVALID_THIS('URL');
653651
return this[context].username;
654652
}
655653

656654
set username(value) {
657-
if (!isURLThis(this))
655+
if (!isURL(this))
658656
throw new ERR_INVALID_THIS('URL');
659657
updateUrl(this[context].href, updateActions.kUsername, `${value}`, this.#onParseComplete);
660658
}
661659

662660
get password() {
663-
if (!isURLThis(this))
661+
if (!isURL(this))
664662
throw new ERR_INVALID_THIS('URL');
665663
return this[context].password;
666664
}
667665

668666
set password(value) {
669-
if (!isURLThis(this))
667+
if (!isURL(this))
670668
throw new ERR_INVALID_THIS('URL');
671669
updateUrl(this[context].href, updateActions.kPassword, `${value}`, this.#onParseComplete);
672670
}
673671

674672
get host() {
675-
if (!isURLThis(this))
673+
if (!isURL(this))
676674
throw new ERR_INVALID_THIS('URL');
677675
const port = this[context].port;
678676
const suffix = port.length > 0 ? `:${port}` : '';
679677
return this[context].hostname + suffix;
680678
}
681679

682680
set host(value) {
683-
if (!isURLThis(this))
681+
if (!isURL(this))
684682
throw new ERR_INVALID_THIS('URL');
685683
updateUrl(this[context].href, updateActions.kHost, `${value}`, this.#onParseComplete);
686684
}
687685

688686
get hostname() {
689-
if (!isURLThis(this))
687+
if (!isURL(this))
690688
throw new ERR_INVALID_THIS('URL');
691689
return this[context].hostname;
692690
}
693691

694692
set hostname(value) {
695-
if (!isURLThis(this))
693+
if (!isURL(this))
696694
throw new ERR_INVALID_THIS('URL');
697695
updateUrl(this[context].href, updateActions.kHostname, `${value}`, this.#onParseComplete);
698696
}
699697

700698
get port() {
701-
if (!isURLThis(this))
699+
if (!isURL(this))
702700
throw new ERR_INVALID_THIS('URL');
703701
return this[context].port;
704702
}
705703

706704
set port(value) {
707-
if (!isURLThis(this))
705+
if (!isURL(this))
708706
throw new ERR_INVALID_THIS('URL');
709707
updateUrl(this[context].href, updateActions.kPort, `${value}`, this.#onParseComplete);
710708
}
711709

712710
get pathname() {
713-
if (!isURLThis(this))
711+
if (!isURL(this))
714712
throw new ERR_INVALID_THIS('URL');
715713
return this[context].pathname;
716714
}
717715

718716
set pathname(value) {
719-
if (!isURLThis(this))
717+
if (!isURL(this))
720718
throw new ERR_INVALID_THIS('URL');
721719
updateUrl(this[context].href, updateActions.kPathname, `${value}`, this.#onParseComplete);
722720
}
723721

724722
get search() {
725-
if (!isURLThis(this))
723+
if (!isURL(this))
726724
throw new ERR_INVALID_THIS('URL');
727725
return this[context].search;
728726
}
729727

730-
set search(search) {
731-
if (!isURLThis(this))
728+
set search(value) {
729+
if (!isURL(this))
732730
throw new ERR_INVALID_THIS('URL');
733-
search = toUSVString(search);
734-
updateUrl(this[context].href, updateActions.kSearch, search, this.#onParseComplete);
735-
initSearchParams(this[searchParams], this[context].search);
731+
updateUrl(this[context].href, updateActions.kSearch, toUSVString(value), this.#onParseComplete);
736732
}
737733

738734
// readonly
739735
get searchParams() {
740-
if (!isURLThis(this))
736+
if (!isURL(this))
741737
throw new ERR_INVALID_THIS('URL');
738+
// Create URLSearchParams on demand to greatly improve the URL performance.
739+
if (this[searchParams] == null) {
740+
this[searchParams] = new URLSearchParams(this[context].search);
741+
this[searchParams][context] = this;
742+
}
742743
return this[searchParams];
743744
}
744745

745746
get hash() {
746-
if (!isURLThis(this))
747+
if (!isURL(this))
747748
throw new ERR_INVALID_THIS('URL');
748749
return this[context].hash;
749750
}
750751

751752
set hash(value) {
752-
if (!isURLThis(this))
753+
if (!isURL(this))
753754
throw new ERR_INVALID_THIS('URL');
754755
updateUrl(this[context].href, updateActions.kHash, `${value}`, this.#onParseComplete);
755756
}
756757

757758
toJSON() {
758-
if (!isURLThis(this))
759+
if (!isURL(this))
759760
throw new ERR_INVALID_THIS('URL');
760761
return this[context].href;
761762
}
@@ -813,14 +814,6 @@ ObjectDefineProperties(URL, {
813814
revokeObjectURL: kEnumerableProperty,
814815
});
815816

816-
function initSearchParams(url, init) {
817-
if (!init) {
818-
url[searchParams] = [];
819-
return;
820-
}
821-
url[searchParams] = parseParams(init);
822-
}
823-
824817
// application/x-www-form-urlencoded parser
825818
// Ref: https://url.spec.whatwg.org/#concept-urlencoded-parser
826819
function parseParams(qs) {
@@ -1139,8 +1132,7 @@ function domainToUnicode(domain) {
11391132
function urlToHttpOptions(url) {
11401133
const options = {
11411134
protocol: url.protocol,
1142-
hostname: typeof url.hostname === 'string' &&
1143-
StringPrototypeStartsWith(url.hostname, '[') ?
1135+
hostname: url.hostname && StringPrototypeStartsWith(url.hostname, '[') ?
11441136
StringPrototypeSlice(url.hostname, 1, -1) :
11451137
url.hostname,
11461138
hash: url.hash,
@@ -1311,6 +1303,6 @@ module.exports = {
13111303
domainToASCII,
13121304
domainToUnicode,
13131305
urlToHttpOptions,
1314-
searchParamsSymbol: searchParams,
13151306
encodeStr,
1307+
isURL,
13161308
};

test/parallel/test-whatwg-url-properties.js

+7
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,13 @@ const { URL, URLSearchParams, format } = require('url');
7373
assert.strictEqual(params.size, 3);
7474
}
7575

76+
{
77+
const u = new URL('https://abc.com/?q=old');
78+
const s = u.searchParams;
79+
u.href = 'http://abc.com/?q=new';
80+
assert.strictEqual(s.get('q'), 'new');
81+
}
82+
7683
function stringifyName(name) {
7784
if (typeof name === 'symbol') {
7885
const { description } = name;

0 commit comments

Comments
 (0)