diff --git a/lib/core/request.js b/lib/core/request.js index 376b360dfdb..c9bf388c316 100644 --- a/lib/core/request.js +++ b/lib/core/request.js @@ -15,7 +15,7 @@ const { isIterable, isBlobLike, serializePathWithQuery, - validateHandler, + assertRequestHandler, getServerName, normalizedMethodRecords } = require('./util') @@ -183,7 +183,7 @@ class Request { throw new InvalidArgumentError('headers must be an object or an array') } - validateHandler(handler, method, upgrade) + assertRequestHandler(handler, method, upgrade) this.servername = servername || getServerName(this.host) || null diff --git a/lib/core/util.js b/lib/core/util.js index cdc73195714..c5676ae6b64 100644 --- a/lib/core/util.js +++ b/lib/core/util.js @@ -345,7 +345,7 @@ function isDestroyed (body) { /** * @param {import ('stream').Stream} stream * @param {Error} [err] - * @returns + * @returns {void} */ function destroy (stream, err) { if (stream == null || !isStream(stream) || isDestroyed(stream)) { @@ -478,12 +478,24 @@ function parseRawHeaders (headers) { return ret } +/** + * @param {*} buffer + * @returns {buffer is Buffer} + */ function isBuffer (buffer) { // See, https://github.com/mcollina/undici/pull/319 return buffer instanceof Uint8Array || Buffer.isBuffer(buffer) } -function validateHandler (handler, method, upgrade) { +/** + * Asserts that the handler object is a request handler. + * + * @param {object} handler + * @param {string} method + * @param {string} [upgrade] + * @returns {asserts handler is import('../api/api-request').RequestHandler} + */ +function assertRequestHandler (handler, method, upgrade) { if (!handler || typeof handler !== 'object') { throw new InvalidArgumentError('handler must be an object') } @@ -560,6 +572,7 @@ function getSocketInfo (socket) { } /** + * @param {Iterable} iterable * @returns {ReadableStream} */ function ReadableStreamFrom (iterable) { @@ -623,23 +636,51 @@ function addAbortListener (signal, listener) { return () => signal.removeListener('abort', listener) } -const hasToWellFormed = typeof String.prototype.toWellFormed === 'function' -const hasIsWellFormed = typeof String.prototype.isWellFormed === 'function' - /** - * @param {string} val + * @function + * @param {string} value + * @returns {string} */ -function toUSVString (val) { - return hasToWellFormed ? `${val}`.toWellFormed() : nodeUtil.toUSVString(val) -} +const toUSVString = (() => { + if (typeof String.prototype.toWellFormed === 'function') { + /** + * @function + * @param {string} value + * @returns {string} + */ + return (value) => `${value}`.toWellFormed() + } else { + /** + * @function + * @param {string} value + * @returns {string} + */ + return nodeUtil.toUSVString + } +})() /** - * @param {string} val + * @param {*} value + * @returns {boolean} */ // TODO: move this to webidl -function isUSVString (val) { - return hasIsWellFormed ? `${val}`.isWellFormed() : toUSVString(val) === `${val}` -} +const isUSVString = (() => { + if (typeof String.prototype.isWellFormed === 'function') { + /** + * @function + * @param {*} value + * @returns {boolean} + */ + return (value) => `${value}`.isWellFormed() + } else { + /** + * @function + * @param {*} value + * @returns {boolean} + */ + return (value) => toUSVString(value) === `${value}` + } +})() /** * @see https://tools.ietf.org/html/rfc7230#section-3.2.6 @@ -710,11 +751,18 @@ function isValidHeaderValue (characters) { const rangeHeaderRegex = /^bytes (\d+)-(\d+)\/(\d+)?$/ +/** + * @typedef {object} RangeHeader + * @property {number} start + * @property {number | null} end + * @property {number | null} size + */ + /** * Parse accordingly to RFC 9110 * @see https://www.rfc-editor.org/rfc/rfc9110#field.content-range * @param {string} [range] - * @returns + * @returns {RangeHeader|null} */ function parseRangeHeader (range) { if (range == null || range === '') return { start: 0, end: null, size: null } @@ -730,9 +778,11 @@ function parseRangeHeader (range) { } /** - * @param {Record} obj + * @template {import("events").EventEmitter} T + * @param {T} obj * @param {string} name - * @param {Function} listener + * @param {(...args: any[]) => void} listener + * @returns {T} */ function addListener (obj, name, listener) { const listeners = (obj[kListeners] ??= []) @@ -742,7 +792,9 @@ function addListener (obj, name, listener) { } /** - * @param {Record} obj + * @template {import("events").EventEmitter} T + * @param {T} obj + * @returns {T} */ function removeAllListeners (obj) { if (obj[kListeners] != null) { @@ -751,6 +803,7 @@ function removeAllListeners (obj) { } obj[kListeners] = null } + return obj } /** @@ -821,7 +874,7 @@ module.exports = { deepClone, ReadableStreamFrom, isBuffer, - validateHandler, + assertRequestHandler, getSocketInfo, isFormDataLike, serializePathWithQuery, diff --git a/lib/handler/redirect-handler.js b/lib/handler/redirect-handler.js index 3db7a476d45..7db5eaf385e 100644 --- a/lib/handler/redirect-handler.js +++ b/lib/handler/redirect-handler.js @@ -38,7 +38,7 @@ class RedirectHandler { throw new InvalidArgumentError('maxRedirections must be a positive number') } - util.validateHandler(handler, opts.method, opts.upgrade) + util.assertRequestHandler(handler, opts.method, opts.upgrade) this.dispatch = dispatch this.location = null