Skip to content

Commit

Permalink
url: offload URLSearchParams initialization
Browse files Browse the repository at this point in the history
  • Loading branch information
anonrig committed Feb 27, 2023
1 parent f915fa3 commit 32a3e47
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 26 deletions.
5 changes: 2 additions & 3 deletions lib/_http_client.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ const {
const Agent = require('_http_agent');
const { Buffer } = require('buffer');
const { defaultTriggerAsyncIdScope } = require('internal/async_hooks');
const { URL, urlToHttpOptions, searchParamsSymbol } = require('internal/url');
const { URL, urlToHttpOptions, isURLThis } = require('internal/url');
const {
kOutHeaders,
kNeedDrain,
Expand Down Expand Up @@ -133,8 +133,7 @@ function ClientRequest(input, options, cb) {
if (typeof input === 'string') {
const urlStr = input;
input = urlToHttpOptions(new URL(urlStr));
} else if (input && input[searchParamsSymbol] &&
input[searchParamsSymbol][searchParamsSymbol]) {
} else if (isURLThis(input)) {
// url.URL instance
input = urlToHttpOptions(input);
} else {
Expand Down
6 changes: 2 additions & 4 deletions lib/https.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ const { ClientRequest } = require('_http_client');
let debug = require('internal/util/debuglog').debuglog('https', (fn) => {
debug = fn;
});
const { URL, urlToHttpOptions, searchParamsSymbol } = require('internal/url');
const { URL, urlToHttpOptions, isURLThis } = require('internal/url');
const { validateObject } = require('internal/validators');

function Server(opts, requestListener) {
Expand Down Expand Up @@ -350,9 +350,7 @@ function request(...args) {
if (typeof args[0] === 'string') {
const urlStr = ArrayPrototypeShift(args);
options = urlToHttpOptions(new URL(urlStr));
} else if (args[0] && args[0][searchParamsSymbol] &&
args[0][searchParamsSymbol][searchParamsSymbol]) {
// url.URL instance
} else if (isURLThis(args[0])) {
options = urlToHttpOptions(ArrayPrototypeShift(args));
}

Expand Down
61 changes: 42 additions & 19 deletions lib/internal/url.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ const FORWARD_SLASH = /\//g;

const context = Symbol('context');
const searchParams = Symbol('query');
const kDirty = Symbol('dirty');

const updateActions = {
kProtocol: 0,
Expand Down Expand Up @@ -224,11 +225,12 @@ class URLSearchParams {
} else {
// USVString
init = toUSVString(init);
initSearchParams(this, init);
this[searchParams] = init ? parseParams(init) : [];
}

// "associated url object"
this[context] = null;
this[kDirty] = false;
}

[inspect.custom](recurseTimes, ctx) {
Expand Down Expand Up @@ -604,11 +606,34 @@ class URL {
ctx.password = password;
ctx.port = port;
ctx.hash = hash;
if (!this[searchParams]) { // Invoked from URL constructor
this[searchParams] = new URLSearchParams();
if (this[searchParams]) {
// Update `kDirty` property to recalculate searchParams on access.
// This is done to reduce the overhead of initializing the URL.
this[searchParams][kDirty] = true;
}
};

#onSearchUpdate = (href, origin, protocol, hostname, pathname,
search, username, password, port, hash) => {
const ctx = this[context];
ctx.href = href;
ctx.origin = origin;
ctx.protocol = protocol;
ctx.hostname = hostname;
ctx.pathname = pathname;
ctx.search = search;
ctx.username = username;
ctx.password = password;
ctx.port = port;
ctx.hash = hash;

if (this[searchParams] == null) {
this[searchParams] = new URLSearchParams(this[context].search);
this[searchParams][context] = this;
} else {
this[searchParams][searchParams] = this[context].search ? parseParams(this[context].search) : [];
this[searchParams][kDirty] = false;
}
initSearchParams(this[searchParams], ctx.search);
};

toString() {
Expand Down Expand Up @@ -729,18 +754,25 @@ class URL {
return this[context].search;
}

set search(search) {
set search(value) {
if (!isURLThis(this))
throw new ERR_INVALID_THIS('URL');
search = toUSVString(search);
updateUrl(this[context].href, updateActions.kSearch, search, this.#onParseComplete);
initSearchParams(this[searchParams], this[context].search);
updateUrl(this[context].href, updateActions.kSearch, toUSVString(value), this.#onSearchUpdate);
}

// readonly
get searchParams() {
if (!isURLThis(this))
throw new ERR_INVALID_THIS('URL');
// Create URLSearchParams on demand to greatly improve the URL performance.
if (this[searchParams] == null) {
this[searchParams] = new URLSearchParams(this[context].search);
this[searchParams][context] = this;
} else if (this[searchParams][kDirty]) {
const updated = this[context].search;
this[searchParams][searchParams] = updated ? parseParams(updated) : [];
this[searchParams][kDirty] = false;
}
return this[searchParams];
}

Expand Down Expand Up @@ -815,14 +847,6 @@ ObjectDefineProperties(URL, {
revokeObjectURL: kEnumerableProperty,
});

function initSearchParams(url, init) {
if (!init) {
url[searchParams] = [];
return;
}
url[searchParams] = parseParams(init);
}

// application/x-www-form-urlencoded parser
// Ref: https://url.spec.whatwg.org/#concept-urlencoded-parser
function parseParams(qs) {
Expand Down Expand Up @@ -1141,8 +1165,7 @@ function domainToUnicode(domain) {
function urlToHttpOptions(url) {
const options = {
protocol: url.protocol,
hostname: typeof url.hostname === 'string' &&
StringPrototypeStartsWith(url.hostname, '[') ?
hostname: url.hostname && StringPrototypeStartsWith(url.hostname, '[') ?
StringPrototypeSlice(url.hostname, 1, -1) :
url.hostname,
hash: url.hash,
Expand Down Expand Up @@ -1313,6 +1336,6 @@ module.exports = {
domainToASCII,
domainToUnicode,
urlToHttpOptions,
searchParamsSymbol: searchParams,
encodeStr,
isURLThis,
};

0 comments on commit 32a3e47

Please sign in to comment.