Skip to content

Commit 32a3e47

Browse files
committed
url: offload URLSearchParams initialization
1 parent f915fa3 commit 32a3e47

File tree

3 files changed

+46
-26
lines changed

3 files changed

+46
-26
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, isURLThis } = 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 (isURLThis(input)) {
138137
// url.URL instance
139138
input = urlToHttpOptions(input);
140139
} 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, isURLThis } = require('internal/url');
5656
const { validateObject } = require('internal/validators');
5757

5858
function Server(opts, requestListener) {
@@ -350,9 +350,7 @@ function request(...args) {
350350
if (typeof args[0] === 'string') {
351351
const urlStr = ArrayPrototypeShift(args);
352352
options = urlToHttpOptions(new URL(urlStr));
353-
} else if (args[0] && args[0][searchParamsSymbol] &&
354-
args[0][searchParamsSymbol][searchParamsSymbol]) {
355-
// url.URL instance
353+
} else if (isURLThis(args[0])) {
356354
options = urlToHttpOptions(ArrayPrototypeShift(args));
357355
}
358356

lib/internal/url.js

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ const FORWARD_SLASH = /\//g;
100100

101101
const context = Symbol('context');
102102
const searchParams = Symbol('query');
103+
const kDirty = Symbol('dirty');
103104

104105
const updateActions = {
105106
kProtocol: 0,
@@ -224,11 +225,12 @@ class URLSearchParams {
224225
} else {
225226
// USVString
226227
init = toUSVString(init);
227-
initSearchParams(this, init);
228+
this[searchParams] = init ? parseParams(init) : [];
228229
}
229230

230231
// "associated url object"
231232
this[context] = null;
233+
this[kDirty] = false;
232234
}
233235

234236
[inspect.custom](recurseTimes, ctx) {
@@ -604,11 +606,34 @@ class URL {
604606
ctx.password = password;
605607
ctx.port = port;
606608
ctx.hash = hash;
607-
if (!this[searchParams]) { // Invoked from URL constructor
608-
this[searchParams] = new URLSearchParams();
609+
if (this[searchParams]) {
610+
// Update `kDirty` property to recalculate searchParams on access.
611+
// This is done to reduce the overhead of initializing the URL.
612+
this[searchParams][kDirty] = true;
613+
}
614+
};
615+
616+
#onSearchUpdate = (href, origin, protocol, hostname, pathname,
617+
search, username, password, port, hash) => {
618+
const ctx = this[context];
619+
ctx.href = href;
620+
ctx.origin = origin;
621+
ctx.protocol = protocol;
622+
ctx.hostname = hostname;
623+
ctx.pathname = pathname;
624+
ctx.search = search;
625+
ctx.username = username;
626+
ctx.password = password;
627+
ctx.port = port;
628+
ctx.hash = hash;
629+
630+
if (this[searchParams] == null) {
631+
this[searchParams] = new URLSearchParams(this[context].search);
609632
this[searchParams][context] = this;
633+
} else {
634+
this[searchParams][searchParams] = this[context].search ? parseParams(this[context].search) : [];
635+
this[searchParams][kDirty] = false;
610636
}
611-
initSearchParams(this[searchParams], ctx.search);
612637
};
613638

614639
toString() {
@@ -729,18 +754,25 @@ class URL {
729754
return this[context].search;
730755
}
731756

732-
set search(search) {
757+
set search(value) {
733758
if (!isURLThis(this))
734759
throw new ERR_INVALID_THIS('URL');
735-
search = toUSVString(search);
736-
updateUrl(this[context].href, updateActions.kSearch, search, this.#onParseComplete);
737-
initSearchParams(this[searchParams], this[context].search);
760+
updateUrl(this[context].href, updateActions.kSearch, toUSVString(value), this.#onSearchUpdate);
738761
}
739762

740763
// readonly
741764
get searchParams() {
742765
if (!isURLThis(this))
743766
throw new ERR_INVALID_THIS('URL');
767+
// Create URLSearchParams on demand to greatly improve the URL performance.
768+
if (this[searchParams] == null) {
769+
this[searchParams] = new URLSearchParams(this[context].search);
770+
this[searchParams][context] = this;
771+
} else if (this[searchParams][kDirty]) {
772+
const updated = this[context].search;
773+
this[searchParams][searchParams] = updated ? parseParams(updated) : [];
774+
this[searchParams][kDirty] = false;
775+
}
744776
return this[searchParams];
745777
}
746778

@@ -815,14 +847,6 @@ ObjectDefineProperties(URL, {
815847
revokeObjectURL: kEnumerableProperty,
816848
});
817849

818-
function initSearchParams(url, init) {
819-
if (!init) {
820-
url[searchParams] = [];
821-
return;
822-
}
823-
url[searchParams] = parseParams(init);
824-
}
825-
826850
// application/x-www-form-urlencoded parser
827851
// Ref: https://url.spec.whatwg.org/#concept-urlencoded-parser
828852
function parseParams(qs) {
@@ -1141,8 +1165,7 @@ function domainToUnicode(domain) {
11411165
function urlToHttpOptions(url) {
11421166
const options = {
11431167
protocol: url.protocol,
1144-
hostname: typeof url.hostname === 'string' &&
1145-
StringPrototypeStartsWith(url.hostname, '[') ?
1168+
hostname: url.hostname && StringPrototypeStartsWith(url.hostname, '[') ?
11461169
StringPrototypeSlice(url.hostname, 1, -1) :
11471170
url.hostname,
11481171
hash: url.hash,
@@ -1313,6 +1336,6 @@ module.exports = {
13131336
domainToASCII,
13141337
domainToUnicode,
13151338
urlToHttpOptions,
1316-
searchParamsSymbol: searchParams,
13171339
encodeStr,
1340+
isURLThis,
13181341
};

0 commit comments

Comments
 (0)