Skip to content

Commit fc3356f

Browse files
committed
url: offload URLSearchParams initialization
PR-URL: nodejs#46867 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Antoine du Hamel <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Tiancheng "Timothy" Gu <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: Filip Skokan <[email protected]>
1 parent 8fa78d8 commit fc3356f

File tree

4 files changed

+48
-52
lines changed

4 files changed

+48
-52
lines changed

lib/_http_client.js

Lines changed: 2 additions & 3 deletions
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,
@@ -138,8 +138,7 @@ function ClientRequest(input, options, cb) {
138138
if (typeof input === 'string') {
139139
const urlStr = input;
140140
input = urlToHttpOptions(new URL(urlStr));
141-
} else if (input && input[searchParamsSymbol] &&
142-
input[searchParamsSymbol][searchParamsSymbol]) {
141+
} else if (isURL(input)) {
143142
// url.URL instance
144143
input = urlToHttpOptions(input);
145144
} else {

lib/https.js

Lines changed: 2 additions & 4 deletions
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

Lines changed: 37 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ class URLSearchParams {
226226
} else {
227227
// USVString
228228
init = toUSVString(init);
229-
initSearchParams(this, init);
229+
this[searchParams] = init ? parseParams(init) : [];
230230
}
231231

232232
// "associated url object"
@@ -536,7 +536,7 @@ ObjectDefineProperties(URLSearchParams.prototype, {
536536
},
537537
});
538538

539-
function isURLThis(self) {
539+
function isURL(self) {
540540
return self != null && ObjectPrototypeHasOwnProperty(self, context);
541541
}
542542

@@ -605,160 +605,161 @@ class URL {
605605
ctx.password = password;
606606
ctx.port = port;
607607
ctx.hash = hash;
608-
if (!this[searchParams]) { // Invoked from URL constructor
609-
this[searchParams] = new URLSearchParams();
610-
this[searchParams][context] = this;
608+
if (this[searchParams]) {
609+
this[searchParams][searchParams] = parseParams(search);
611610
}
612-
initSearchParams(this[searchParams], ctx.search);
613611
};
614612

615613
toString() {
616-
if (!isURLThis(this))
614+
if (!isURL(this))
617615
throw new ERR_INVALID_THIS('URL');
618616
return this[context].href;
619617
}
620618

621619
get href() {
622-
if (!isURLThis(this))
620+
if (!isURL(this))
623621
throw new ERR_INVALID_THIS('URL');
624622
return this[context].href;
625623
}
626624

627625
set href(value) {
628-
if (!isURLThis(this))
626+
if (!isURL(this))
629627
throw new ERR_INVALID_THIS('URL');
630628
const valid = updateUrl(this[context].href, updateActions.kHref, `${value}`, this.#onParseComplete);
631629
if (!valid) { throw ERR_INVALID_URL(`${value}`); }
632630
}
633631

634632
// readonly
635633
get origin() {
636-
if (!isURLThis(this))
634+
if (!isURL(this))
637635
throw new ERR_INVALID_THIS('URL');
638636
return this[context].origin;
639637
}
640638

641639
get protocol() {
642-
if (!isURLThis(this))
640+
if (!isURL(this))
643641
throw new ERR_INVALID_THIS('URL');
644642
return this[context].protocol;
645643
}
646644

647645
set protocol(value) {
648-
if (!isURLThis(this))
646+
if (!isURL(this))
649647
throw new ERR_INVALID_THIS('URL');
650648
updateUrl(this[context].href, updateActions.kProtocol, `${value}`, this.#onParseComplete);
651649
}
652650

653651
get username() {
654-
if (!isURLThis(this))
652+
if (!isURL(this))
655653
throw new ERR_INVALID_THIS('URL');
656654
return this[context].username;
657655
}
658656

659657
set username(value) {
660-
if (!isURLThis(this))
658+
if (!isURL(this))
661659
throw new ERR_INVALID_THIS('URL');
662660
updateUrl(this[context].href, updateActions.kUsername, `${value}`, this.#onParseComplete);
663661
}
664662

665663
get password() {
666-
if (!isURLThis(this))
664+
if (!isURL(this))
667665
throw new ERR_INVALID_THIS('URL');
668666
return this[context].password;
669667
}
670668

671669
set password(value) {
672-
if (!isURLThis(this))
670+
if (!isURL(this))
673671
throw new ERR_INVALID_THIS('URL');
674672
updateUrl(this[context].href, updateActions.kPassword, `${value}`, this.#onParseComplete);
675673
}
676674

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

685683
set host(value) {
686-
if (!isURLThis(this))
684+
if (!isURL(this))
687685
throw new ERR_INVALID_THIS('URL');
688686
updateUrl(this[context].href, updateActions.kHost, `${value}`, this.#onParseComplete);
689687
}
690688

691689
get hostname() {
692-
if (!isURLThis(this))
690+
if (!isURL(this))
693691
throw new ERR_INVALID_THIS('URL');
694692
return this[context].hostname;
695693
}
696694

697695
set hostname(value) {
698-
if (!isURLThis(this))
696+
if (!isURL(this))
699697
throw new ERR_INVALID_THIS('URL');
700698
updateUrl(this[context].href, updateActions.kHostname, `${value}`, this.#onParseComplete);
701699
}
702700

703701
get port() {
704-
if (!isURLThis(this))
702+
if (!isURL(this))
705703
throw new ERR_INVALID_THIS('URL');
706704
return this[context].port;
707705
}
708706

709707
set port(value) {
710-
if (!isURLThis(this))
708+
if (!isURL(this))
711709
throw new ERR_INVALID_THIS('URL');
712710
updateUrl(this[context].href, updateActions.kPort, `${value}`, this.#onParseComplete);
713711
}
714712

715713
get pathname() {
716-
if (!isURLThis(this))
714+
if (!isURL(this))
717715
throw new ERR_INVALID_THIS('URL');
718716
return this[context].pathname;
719717
}
720718

721719
set pathname(value) {
722-
if (!isURLThis(this))
720+
if (!isURL(this))
723721
throw new ERR_INVALID_THIS('URL');
724722
updateUrl(this[context].href, updateActions.kPathname, `${value}`, this.#onParseComplete);
725723
}
726724

727725
get search() {
728-
if (!isURLThis(this))
726+
if (!isURL(this))
729727
throw new ERR_INVALID_THIS('URL');
730728
return this[context].search;
731729
}
732730

733-
set search(search) {
734-
if (!isURLThis(this))
731+
set search(value) {
732+
if (!isURL(this))
735733
throw new ERR_INVALID_THIS('URL');
736-
search = toUSVString(search);
737-
updateUrl(this[context].href, updateActions.kSearch, search, this.#onParseComplete);
738-
initSearchParams(this[searchParams], this[context].search);
734+
updateUrl(this[context].href, updateActions.kSearch, toUSVString(value), this.#onParseComplete);
739735
}
740736

741737
// readonly
742738
get searchParams() {
743-
if (!isURLThis(this))
739+
if (!isURL(this))
744740
throw new ERR_INVALID_THIS('URL');
741+
// Create URLSearchParams on demand to greatly improve the URL performance.
742+
if (this[searchParams] == null) {
743+
this[searchParams] = new URLSearchParams(this[context].search);
744+
this[searchParams][context] = this;
745+
}
745746
return this[searchParams];
746747
}
747748

748749
get hash() {
749-
if (!isURLThis(this))
750+
if (!isURL(this))
750751
throw new ERR_INVALID_THIS('URL');
751752
return this[context].hash;
752753
}
753754

754755
set hash(value) {
755-
if (!isURLThis(this))
756+
if (!isURL(this))
756757
throw new ERR_INVALID_THIS('URL');
757758
updateUrl(this[context].href, updateActions.kHash, `${value}`, this.#onParseComplete);
758759
}
759760

760761
toJSON() {
761-
if (!isURLThis(this))
762+
if (!isURL(this))
762763
throw new ERR_INVALID_THIS('URL');
763764
return this[context].href;
764765
}
@@ -816,14 +817,6 @@ ObjectDefineProperties(URL, {
816817
revokeObjectURL: kEnumerableProperty,
817818
});
818819

819-
function initSearchParams(url, init) {
820-
if (!init) {
821-
url[searchParams] = [];
822-
return;
823-
}
824-
url[searchParams] = parseParams(init);
825-
}
826-
827820
// application/x-www-form-urlencoded parser
828821
// Ref: https://url.spec.whatwg.org/#concept-urlencoded-parser
829822
function parseParams(qs) {
@@ -1142,8 +1135,7 @@ function domainToUnicode(domain) {
11421135
function urlToHttpOptions(url) {
11431136
const options = {
11441137
protocol: url.protocol,
1145-
hostname: typeof url.hostname === 'string' &&
1146-
StringPrototypeStartsWith(url.hostname, '[') ?
1138+
hostname: url.hostname && StringPrototypeStartsWith(url.hostname, '[') ?
11471139
StringPrototypeSlice(url.hostname, 1, -1) :
11481140
url.hostname,
11491141
hash: url.hash,
@@ -1314,6 +1306,6 @@ module.exports = {
13141306
domainToASCII,
13151307
domainToUnicode,
13161308
urlToHttpOptions,
1317-
searchParamsSymbol: searchParams,
13181309
encodeStr,
1310+
isURL,
13191311
};

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

Lines changed: 7 additions & 0 deletions
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)