From 9611c248995ad3bacda1d0a93d0b5e1e6605d162 Mon Sep 17 00:00:00 2001 From: Egor Tensin Date: Sat, 28 Feb 2026 21:59:12 +0100 Subject: upgrade actions/core to version 2 --- node_modules/undici/lib/core/request.js | 292 ++++++++++---------------------- 1 file changed, 94 insertions(+), 198 deletions(-) (limited to 'node_modules/undici/lib/core/request.js') diff --git a/node_modules/undici/lib/core/request.js b/node_modules/undici/lib/core/request.js index 3697e6a..7800303 100644 --- a/node_modules/undici/lib/core/request.js +++ b/node_modules/undici/lib/core/request.js @@ -4,52 +4,29 @@ const { InvalidArgumentError, NotSupportedError } = require('./errors') -const assert = require('assert') -const { kHTTP2BuildRequest, kHTTP2CopyHeaders, kHTTP1BuildRequest } = require('./symbols') -const util = require('./util') - -// tokenRegExp and headerCharRegex have been lifted from -// https://github.com/nodejs/node/blob/main/lib/_http_common.js - -/** - * Verifies that the given val is a valid HTTP token - * per the rules defined in RFC 7230 - * See https://tools.ietf.org/html/rfc7230#section-3.2.6 - */ -const tokenRegExp = /^[\^_`a-zA-Z\-0-9!#$%&'*+.|~]+$/ - -/** - * Matches if val contains an invalid field-vchar - * field-value = *( field-content / obs-fold ) - * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] - * field-vchar = VCHAR / obs-text - */ -const headerCharRegex = /[^\t\x20-\x7e\x80-\xff]/ +const assert = require('node:assert') +const { + isValidHTTPToken, + isValidHeaderValue, + isStream, + destroy, + isBuffer, + isFormDataLike, + isIterable, + isBlobLike, + buildURL, + validateHandler, + getServerName, + normalizedMethodRecords +} = require('./util') +const { channels } = require('./diagnostics.js') +const { headerNameLowerCasedRecord } = require('./constants') // Verifies that a given path is valid does not contain control chars \x00 to \x20 const invalidPathRegex = /[^\u0021-\u00ff]/ const kHandler = Symbol('handler') -const channels = {} - -let extractBody - -try { - const diagnosticsChannel = require('diagnostics_channel') - channels.create = diagnosticsChannel.channel('undici:request:create') - channels.bodySent = diagnosticsChannel.channel('undici:request:bodySent') - channels.headers = diagnosticsChannel.channel('undici:request:headers') - channels.trailers = diagnosticsChannel.channel('undici:request:trailers') - channels.error = diagnosticsChannel.channel('undici:request:error') -} catch { - channels.create = { hasSubscribers: false } - channels.bodySent = { hasSubscribers: false } - channels.headers = { hasSubscribers: false } - channels.trailers = { hasSubscribers: false } - channels.error = { hasSubscribers: false } -} - class Request { constructor (origin, { path, @@ -64,7 +41,8 @@ class Request { bodyTimeout, reset, throwOnError, - expectContinue + expectContinue, + servername }, handler) { if (typeof path !== 'string') { throw new InvalidArgumentError('path must be a string') @@ -74,13 +52,13 @@ class Request { method !== 'CONNECT' ) { throw new InvalidArgumentError('path must be an absolute URL or start with a slash') - } else if (invalidPathRegex.exec(path) !== null) { + } else if (invalidPathRegex.test(path)) { throw new InvalidArgumentError('invalid request path') } if (typeof method !== 'string') { throw new InvalidArgumentError('method must be a string') - } else if (tokenRegExp.exec(method) === null) { + } else if (normalizedMethodRecords[method] === undefined && !isValidHTTPToken(method)) { throw new InvalidArgumentError('invalid request method') } @@ -116,13 +94,13 @@ class Request { if (body == null) { this.body = null - } else if (util.isStream(body)) { + } else if (isStream(body)) { this.body = body const rState = this.body._readableState if (!rState || !rState.autoDestroy) { this.endHandler = function autoDestroy () { - util.destroy(this) + destroy(this) } this.body.on('end', this.endHandler) } @@ -135,7 +113,7 @@ class Request { } } this.body.on('error', this.errorHandler) - } else if (util.isBuffer(body)) { + } else if (isBuffer(body)) { this.body = body.byteLength ? body : null } else if (ArrayBuffer.isView(body)) { this.body = body.buffer.byteLength ? Buffer.from(body.buffer, body.byteOffset, body.byteLength) : null @@ -143,7 +121,7 @@ class Request { this.body = body.byteLength ? Buffer.from(body) : null } else if (typeof body === 'string') { this.body = body.length ? Buffer.from(body) : null - } else if (util.isFormDataLike(body) || util.isIterable(body) || util.isBlobLike(body)) { + } else if (isFormDataLike(body) || isIterable(body) || isBlobLike(body)) { this.body = body } else { throw new InvalidArgumentError('body must be a string, a Buffer, a Readable stream, an iterable, or an async iterable') @@ -155,7 +133,7 @@ class Request { this.upgrade = upgrade || null - this.path = query ? util.buildURL(path, query) : path + this.path = query ? buildURL(path, query) : path this.origin = origin @@ -173,7 +151,7 @@ class Request { this.contentType = null - this.headers = '' + this.headers = [] // Only for H2 this.expectContinue = expectContinue != null ? expectContinue : false @@ -186,39 +164,26 @@ class Request { processHeader(this, headers[i], headers[i + 1]) } } else if (headers && typeof headers === 'object') { - const keys = Object.keys(headers) - for (let i = 0; i < keys.length; i++) { - const key = keys[i] - processHeader(this, key, headers[key]) + if (headers[Symbol.iterator]) { + for (const header of headers) { + if (!Array.isArray(header) || header.length !== 2) { + throw new InvalidArgumentError('headers must be in key-value pair format') + } + processHeader(this, header[0], header[1]) + } + } else { + const keys = Object.keys(headers) + for (let i = 0; i < keys.length; ++i) { + processHeader(this, keys[i], headers[keys[i]]) + } } } else if (headers != null) { throw new InvalidArgumentError('headers must be an object or an array') } - if (util.isFormDataLike(this.body)) { - if (util.nodeMajor < 16 || (util.nodeMajor === 16 && util.nodeMinor < 8)) { - throw new InvalidArgumentError('Form-Data bodies are only supported in node v16.8 and newer.') - } + validateHandler(handler, method, upgrade) - if (!extractBody) { - extractBody = require('../fetch/body.js').extractBody - } - - const [bodyStream, contentType] = extractBody(body) - if (this.contentType == null) { - this.contentType = contentType - this.headers += `content-type: ${contentType}\r\n` - } - this.body = bodyStream.stream - this.contentLength = bodyStream.length - } else if (util.isBlobLike(body) && this.contentType == null && body.type) { - this.contentType = body.type - this.headers += `content-type: ${body.type}\r\n` - } - - util.validateHandler(handler, method, upgrade) - - this.servername = util.getServerName(this.host) + this.servername = servername || getServerName(this.host) this[kHandler] = handler @@ -263,6 +228,10 @@ class Request { } } + onResponseStarted () { + return this[kHandler].onResponseStarted?.() + } + onHeaders (statusCode, headers, resume, statusText) { assert(!this.aborted) assert(!this.completed) @@ -342,157 +311,84 @@ class Request { } } - // TODO: adjust to support H2 addHeader (key, value) { processHeader(this, key, value) return this } - - static [kHTTP1BuildRequest] (origin, opts, handler) { - // TODO: Migrate header parsing here, to make Requests - // HTTP agnostic - return new Request(origin, opts, handler) - } - - static [kHTTP2BuildRequest] (origin, opts, handler) { - const headers = opts.headers - opts = { ...opts, headers: null } - - const request = new Request(origin, opts, handler) - - request.headers = {} - - if (Array.isArray(headers)) { - if (headers.length % 2 !== 0) { - throw new InvalidArgumentError('headers array must be even') - } - for (let i = 0; i < headers.length; i += 2) { - processHeader(request, headers[i], headers[i + 1], true) - } - } else if (headers && typeof headers === 'object') { - const keys = Object.keys(headers) - for (let i = 0; i < keys.length; i++) { - const key = keys[i] - processHeader(request, key, headers[key], true) - } - } else if (headers != null) { - throw new InvalidArgumentError('headers must be an object or an array') - } - - return request - } - - static [kHTTP2CopyHeaders] (raw) { - const rawHeaders = raw.split('\r\n') - const headers = {} - - for (const header of rawHeaders) { - const [key, value] = header.split(': ') - - if (value == null || value.length === 0) continue - - if (headers[key]) headers[key] += `,${value}` - else headers[key] = value - } - - return headers - } } -function processHeaderValue (key, val, skipAppend) { - if (val && typeof val === 'object') { +function processHeader (request, key, val) { + if (val && (typeof val === 'object' && !Array.isArray(val))) { throw new InvalidArgumentError(`invalid ${key} header`) + } else if (val === undefined) { + return } - val = val != null ? `${val}` : '' + let headerName = headerNameLowerCasedRecord[key] - if (headerCharRegex.exec(val) !== null) { - throw new InvalidArgumentError(`invalid ${key} header`) + if (headerName === undefined) { + headerName = key.toLowerCase() + if (headerNameLowerCasedRecord[headerName] === undefined && !isValidHTTPToken(headerName)) { + throw new InvalidArgumentError('invalid header key') + } } - return skipAppend ? val : `${key}: ${val}\r\n` -} - -function processHeader (request, key, val, skipAppend = false) { - if (val && (typeof val === 'object' && !Array.isArray(val))) { - throw new InvalidArgumentError(`invalid ${key} header`) - } else if (val === undefined) { - return + if (Array.isArray(val)) { + const arr = [] + for (let i = 0; i < val.length; i++) { + if (typeof val[i] === 'string') { + if (!isValidHeaderValue(val[i])) { + throw new InvalidArgumentError(`invalid ${key} header`) + } + arr.push(val[i]) + } else if (val[i] === null) { + arr.push('') + } else if (typeof val[i] === 'object') { + throw new InvalidArgumentError(`invalid ${key} header`) + } else { + arr.push(`${val[i]}`) + } + } + val = arr + } else if (typeof val === 'string') { + if (!isValidHeaderValue(val)) { + throw new InvalidArgumentError(`invalid ${key} header`) + } + } else if (val === null) { + val = '' + } else { + val = `${val}` } - if ( - request.host === null && - key.length === 4 && - key.toLowerCase() === 'host' - ) { - if (headerCharRegex.exec(val) !== null) { - throw new InvalidArgumentError(`invalid ${key} header`) + if (request.host === null && headerName === 'host') { + if (typeof val !== 'string') { + throw new InvalidArgumentError('invalid host header') } // Consumed by Client request.host = val - } else if ( - request.contentLength === null && - key.length === 14 && - key.toLowerCase() === 'content-length' - ) { + } else if (request.contentLength === null && headerName === 'content-length') { request.contentLength = parseInt(val, 10) if (!Number.isFinite(request.contentLength)) { throw new InvalidArgumentError('invalid content-length header') } - } else if ( - request.contentType === null && - key.length === 12 && - key.toLowerCase() === 'content-type' - ) { + } else if (request.contentType === null && headerName === 'content-type') { request.contentType = val - if (skipAppend) request.headers[key] = processHeaderValue(key, val, skipAppend) - else request.headers += processHeaderValue(key, val) - } else if ( - key.length === 17 && - key.toLowerCase() === 'transfer-encoding' - ) { - throw new InvalidArgumentError('invalid transfer-encoding header') - } else if ( - key.length === 10 && - key.toLowerCase() === 'connection' - ) { + request.headers.push(key, val) + } else if (headerName === 'transfer-encoding' || headerName === 'keep-alive' || headerName === 'upgrade') { + throw new InvalidArgumentError(`invalid ${headerName} header`) + } else if (headerName === 'connection') { const value = typeof val === 'string' ? val.toLowerCase() : null if (value !== 'close' && value !== 'keep-alive') { throw new InvalidArgumentError('invalid connection header') - } else if (value === 'close') { + } + + if (value === 'close') { request.reset = true } - } else if ( - key.length === 10 && - key.toLowerCase() === 'keep-alive' - ) { - throw new InvalidArgumentError('invalid keep-alive header') - } else if ( - key.length === 7 && - key.toLowerCase() === 'upgrade' - ) { - throw new InvalidArgumentError('invalid upgrade header') - } else if ( - key.length === 6 && - key.toLowerCase() === 'expect' - ) { + } else if (headerName === 'expect') { throw new NotSupportedError('expect header not supported') - } else if (tokenRegExp.exec(key) === null) { - throw new InvalidArgumentError('invalid header key') } else { - if (Array.isArray(val)) { - for (let i = 0; i < val.length; i++) { - if (skipAppend) { - if (request.headers[key]) request.headers[key] += `,${processHeaderValue(key, val[i], skipAppend)}` - else request.headers[key] = processHeaderValue(key, val[i], skipAppend) - } else { - request.headers += processHeaderValue(key, val[i]) - } - } - } else { - if (skipAppend) request.headers[key] = processHeaderValue(key, val, skipAppend) - else request.headers += processHeaderValue(key, val) - } + request.headers.push(key, val) } } -- cgit v1.3