From 851158798b9f349325310d15da8020ab2517c2f0 Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Sat, 28 Jun 2025 18:59:22 +0900 Subject: [PATCH 01/13] refactor: enable ts strict checks --- src/_utils.ts | 118 ++++++++++++++++++++++++--------- src/middleware/_utils.ts | 42 ++++++++---- src/middleware/web-incoming.ts | 22 +++--- src/middleware/web-outgoing.ts | 34 +++++----- src/middleware/ws-incoming.ts | 19 +++--- src/server.ts | 63 ++++++++++++------ src/types.ts | 4 +- tsconfig.json | 2 +- 8 files changed, 201 insertions(+), 103 deletions(-) diff --git a/src/_utils.ts b/src/_utils.ts index 9700f26..ff0be5d 100644 --- a/src/_utils.ts +++ b/src/_utils.ts @@ -1,3 +1,15 @@ +import httpNative from "node:http"; +import httpsNative from "node:https"; +import type tls from "node:tls"; +import net from "node:net"; +import type { + NormalizedProxyTarget, + NormalizedProxyTargetUrl, + ProxyServerOptions, + ProxyTargetDetailed, +} from "./types"; +import type url from "node:url"; + const upgradeHeader = /(^|,)\s*upgrade\s*($|,)/i; /** @@ -15,19 +27,29 @@ export const isSSL = /^https|wss/; * common.setupOutgoing(outgoing, options, req) * // => { host: ..., hostname: ...} * - * @param {Object} Outgoing Base object to be filled with required properties - * @param {Object} Options Config object passed to the proxy - * @param {ClientRequest} Req Request Object - * @param {String} Forward String to select forward or target + * @param outgoing Base object to be filled with required properties + * @param options Config object passed to the proxy + * @param req Request Object + * @param forward String to select forward or target * - * @return {Object} Outgoing Object with all required properties set + * @return Outgoing Object with all required properties set * * @api private */ -export function setupOutgoing(outgoing, options, req, forward?) { +export function setupOutgoing( + outgoing: httpNative.RequestOptions & httpsNative.RequestOptions, + options: ProxyServerOptions & { + target: NormalizedProxyTarget; + forward: NormalizedProxyTargetUrl; + }, + req: httpNative.IncomingMessage, + forward?: "forward" | "target", +): httpNative.RequestOptions | httpsNative.RequestOptions { outgoing.port = options[forward || "target"].port || - (isSSL.test(options[forward || "target"].protocol) ? 443 : 80); + (isSSL.test(options[forward || "target"].protocol ?? "undefined") + ? 443 + : 80); for (const e of [ "host", @@ -40,10 +62,13 @@ export function setupOutgoing(outgoing, options, req, forward?) { "ca", "ciphers", "secureProtocol", - ]) { - outgoing[e] = options[forward || "target"][e]; + ] as const) { + const value = (options[forward || "target"] as ProxyTargetDetailed)[e]; + // @ts-expect-error -- this mapping is valid + outgoing[e] = value; } + // @ts-expect-error - options.method is undocumented outgoing.method = options.method || req.method; outgoing.headers = { ...req.headers }; @@ -55,11 +80,13 @@ export function setupOutgoing(outgoing, options, req, forward?) { outgoing.auth = options.auth; } + // @ts-expect-error - options.ca is undocumented if (options.ca) { + // @ts-expect-error - options.ca is undocumented outgoing.ca = options.ca; } - if (isSSL.test(options[forward || "target"].protocol)) { + if (isSSL.test(options[forward || "target"].protocol ?? "undefined")) { outgoing.rejectUnauthorized = options.secure === undefined ? true : options.secure; } @@ -85,10 +112,13 @@ export function setupOutgoing(outgoing, options, req, forward?) { const target = options[forward || "target"]; const targetPath = target && options.prependPath !== false - ? target.pathname || target.path || "" + ? (target as URL).pathname || + // NOTE: http-proxy receives url.Url instead of URL + (target as Partial).path || + "" : ""; - const parsed = new URL(req.url, "http://localhost"); + const parsed = new URL(req.url!, "http://localhost"); let outgoingPath = options.toProxy ? req.url : parsed.pathname + parsed.search || ""; @@ -106,7 +136,7 @@ export function setupOutgoing(outgoing, options, req, forward?) { requiresPort(outgoing.port, options[forward || "target"].protocol) && !hasPort(outgoing.host) ? outgoing.host + ":" + outgoing.port - : outgoing.host; + : (outgoing.host as string | undefined); } return outgoing; } @@ -144,14 +174,14 @@ export function joinURL( * common.setupSocket(socket) * // => Socket * - * @param {Socket} Socket instance to setup + * @param socket instance to setup * - * @return {Socket} Return the configured socket. + * @return Return the configured socket. * * @api private */ -export function setupSocket(socket) { +export function setupSocket(socket: net.Socket): net.Socket { socket.setTimeout(0); socket.setNoDelay(true); @@ -163,13 +193,13 @@ export function setupSocket(socket) { /** * Get the port number from the host. Or guess it based on the connection type. * - * @param {Request} req Incoming HTTP request. + * @param req Incoming HTTP request. * - * @return {String} The port number. + * @return The port number. * * @api private */ -export function getPort(req) { +export function getPort(req: httpNative.IncomingMessage): string { const res = req.headers.host ? req.headers.host.match(/:(\d+)/) : ""; if (res) { return res[1]; @@ -180,26 +210,45 @@ export function getPort(req) { /** * Check if the request has an encrypted connection. * - * @param {Request} req Incoming HTTP request. + * @param req Incoming HTTP request. * - * @return {Boolean} Whether the connection is encrypted or not. + * @return Whether the connection is encrypted or not. * * @api private */ -export function hasEncryptedConnection(req) { - return Boolean(req.connection.encrypted || req.connection.pair); +export function hasEncryptedConnection( + req: httpNative.IncomingMessage, +): boolean { + return Boolean( + // req.connection.pair probably does not exist anymore + (req.connection as tls.TLSSocket).encrypted || (req.connection as any).pair, + ); } /** * Rewrites or removes the domain of a cookie header * - * @param {String|Array} Header - * @param {Object} Config, mapping of domain to rewritten domain. - * '*' key to match any domain, null value to remove the domain. + * @param header + * @param config, mapping of domain to rewritten domain. + * '*' key to match any domain, null value to remove the domain. * * @api private */ -export function rewriteCookieProperty(header, config, property) { +export function rewriteCookieProperty( + header: string, + config: Record, + property: string, +): string; +export function rewriteCookieProperty( + header: string | string[], + config: Record, + property: string, +): string | string[]; +export function rewriteCookieProperty( + header: string | string[], + config: Record, + property: string, +): string | string[] { if (Array.isArray(header)) { return header.map(function (headerElement) { return rewriteCookieProperty(headerElement, config, property); @@ -226,12 +275,12 @@ export function rewriteCookieProperty(header, config, property) { /** * Check the host and see if it potentially has a port in it (keep it simple) * - * @returns {Boolean} Whether we have one or not + * @returns Whether we have one or not * * @api private */ -export function hasPort(host: string) { - return !!~host.indexOf(":"); +export function hasPort(host: string | null | undefined): boolean { + return host ? !!~host.indexOf(":") : false; } /** @@ -239,12 +288,15 @@ export function hasPort(host: string) { * * Ported from https://github.com/unshiftio/requires-port/blob/master/index.js * - * @returns {Boolean} Whether the port is required for the protocol + * @returns Whether the port is required for the protocol * * @api private */ -export function requiresPort(_port: string, _protocol: string) { - const protocol = _protocol.split(":")[0]; +export function requiresPort( + _port: string | number, + _protocol: string | undefined, +): boolean { + const protocol = _protocol?.split(":")[0]; const port = +_port; if (!port) return false; diff --git a/src/middleware/_utils.ts b/src/middleware/_utils.ts index 076d138..b866276 100644 --- a/src/middleware/_utils.ts +++ b/src/middleware/_utils.ts @@ -1,22 +1,35 @@ import type { IncomingMessage, OutgoingMessage } from "node:http"; import type { ProxyServer } from "../server"; -import type { ProxyServerOptions } from "../types"; +import type { + NormalizedProxyTarget, + NormalizedProxyTargetUrl, + ProxyServerOptions, +} from "../types"; +import type { Socket } from "node:net"; -export type ProxyMiddleware = ( +export type ResOfType = T extends "ws" + ? T extends "web" + ? OutgoingMessage | Socket + : Socket + : T extends "web" + ? OutgoingMessage + : never; + +export type ProxyMiddleware = ( req: IncomingMessage, - res: OutgoingMessage, - opts: ProxyServerOptions & { target: URL; forward: URL }, - server?: ProxyServer, + res: T, + opts: ProxyServerOptions & { + target: NormalizedProxyTarget; + forward: NormalizedProxyTargetUrl; + }, + server: ProxyServer, head?: Buffer, - callback?: ( - err: any, - req: IncomingMessage, - socket: OutgoingMessage, - url?: any, - ) => void, + callback?: (err: any, req: IncomingMessage, socket: T, url?: any) => void, ) => void | true; -export function defineProxyMiddleware(m: ProxyMiddleware) { +export function defineProxyMiddleware< + T extends OutgoingMessage | Socket = OutgoingMessage, +>(m: ProxyMiddleware) { return m; } @@ -24,7 +37,10 @@ export type ProxyOutgoingMiddleware = ( req: IncomingMessage, res: OutgoingMessage, proxyRes: IncomingMessage, - opts: ProxyServerOptions & { target: URL; forward: URL }, + opts: ProxyServerOptions & { + target: NormalizedProxyTarget; + forward: NormalizedProxyTargetUrl; + }, ) => void | true; export function defineProxyOutgoingMiddleware(m: ProxyOutgoingMiddleware) { diff --git a/src/middleware/web-incoming.ts b/src/middleware/web-incoming.ts index 04bd6c0..33c2641 100644 --- a/src/middleware/web-incoming.ts +++ b/src/middleware/web-incoming.ts @@ -3,6 +3,7 @@ import httpsNative from "node:https"; import { getPort, hasEncryptedConnection, setupOutgoing } from "../_utils"; import { webOutgoingMiddleware } from "./web-outgoing"; import { ProxyMiddleware, defineProxyMiddleware } from "./_utils"; +import type { NormalizedProxyTarget } from "../types"; const nativeAgents = { http: httpNative, https: httpsNative }; @@ -43,7 +44,7 @@ const XHeaders = defineProxyMiddleware((req, res, options) => { proto: encrypted ? "https" : "http", }; - for (const header of ["for", "port", "proto"]) { + for (const header of ["for", "port", "proto"] as const) { req.headers["x-forwarded-" + header] = (req.headers["x-forwarded-" + header] || "") + (req.headers["x-forwarded-" + header] ? "," : "") + @@ -95,7 +96,7 @@ const stream = defineProxyMiddleware( ).request(setupOutgoing(options.ssl || {}, options, req)); // Enable developers to modify the proxyReq before headers are sent - proxyReq.on("socket", (socket) => { + proxyReq.on("socket", (_socket) => { if (server && !proxyReq.getHeader("expect")) { server.emit("proxyReq", proxyReq, req, res, options); } @@ -119,9 +120,12 @@ const stream = defineProxyMiddleware( req.on("error", proxyError); proxyReq.on("error", proxyError); - function createErrorHandler(proxyReq, url) { - return function proxyError(err) { - if (req.socket.destroyed && err.code === "ECONNRESET") { + function createErrorHandler(proxyReq: httpNative.ClientRequest, url: NormalizedProxyTarget) { + return function proxyError(err: Error) { + if ( + req.socket.destroyed && + (err as NodeJS.ErrnoException).code === "ECONNRESET" + ) { server.emit("econnreset", err, req, res, url); return proxyReq.abort(); } @@ -173,9 +177,5 @@ const stream = defineProxyMiddleware( }, ); -export const webIncomingMiddleware: readonly ProxyMiddleware[] = [ - deleteLength, - timeout, - XHeaders, - stream, -] as const; +export const webIncomingMiddleware: readonly ProxyMiddleware[] = + [deleteLength, timeout, XHeaders, stream] as const; diff --git a/src/middleware/web-outgoing.ts b/src/middleware/web-outgoing.ts index 5afeac5..273c0a4 100644 --- a/src/middleware/web-outgoing.ts +++ b/src/middleware/web-outgoing.ts @@ -34,7 +34,7 @@ const setRedirectHostRewrite = defineProxyOutgoingMiddleware( proxyRes.headers.location && redirectRegex.test(String(proxyRes.statusCode)) ) { - const target = new URL(options.target); + const target = options.target; const u = new URL(proxyRes.headers.location); // Make sure the redirected host matches the target host before rewriting @@ -45,7 +45,7 @@ const setRedirectHostRewrite = defineProxyOutgoingMiddleware( if (options.hostRewrite) { u.host = options.hostRewrite; } else if (options.autoRewrite) { - u.host = req.headers.host; + u.host = req.headers.host ?? "undefined"; } if (options.protocolRewrite) { u.protocol = options.protocolRewrite; @@ -69,11 +69,23 @@ const setRedirectHostRewrite = defineProxyOutgoingMiddleware( */ const writeHeaders = defineProxyOutgoingMiddleware( (req, res, proxyRes, options) => { - let rewriteCookieDomainConfig = options.cookieDomainRewrite; - let rewriteCookiePathConfig = options.cookiePathRewrite; + const rewriteCookieDomainConfig = + typeof options.cookieDomainRewrite === "string" + ? // also test for '' + { "*": options.cookieDomainRewrite } + : options.cookieDomainRewrite; + const rewriteCookiePathConfig = + typeof options.cookiePathRewrite === "string" + ? // also test for '' + { "*": options.cookiePathRewrite } + : options.cookiePathRewrite; + const preserveHeaderKeyCase = options.preserveHeaderKeyCase; - let rawHeaderKeyMap; - const setHeader = function (key, header) { + let rawHeaderKeyMap: Record | undefined; + const setHeader = function ( + key: string, + header: string | string[] | undefined, + ) { if (header === undefined) { return; } @@ -90,16 +102,6 @@ const writeHeaders = defineProxyOutgoingMiddleware( res.setHeader(String(key).trim(), header); }; - if (typeof rewriteCookieDomainConfig === "string") { - // also test for '' - rewriteCookieDomainConfig = { "*": rewriteCookieDomainConfig }; - } - - if (typeof rewriteCookiePathConfig === "string") { - // also test for '' - rewriteCookiePathConfig = { "*": rewriteCookiePathConfig }; - } - // message.rawHeaders is added in: v0.11.6 // https://nodejs.org/api/http.html#http_message_rawheaders if (preserveHeaderKeyCase && proxyRes.rawHeaders !== undefined) { diff --git a/src/middleware/ws-incoming.ts b/src/middleware/ws-incoming.ts index d800a71..6855f83 100644 --- a/src/middleware/ws-incoming.ts +++ b/src/middleware/ws-incoming.ts @@ -8,12 +8,13 @@ import { setupSocket, } from "../_utils"; import { ProxyMiddleware, defineProxyMiddleware } from "./_utils"; +import type { Socket } from "node:net"; /** * WebSocket requests must have the `GET` method and * the `upgrade:websocket` header */ -const checkMethodAndHeader = defineProxyMiddleware((req, socket) => { +const checkMethodAndHeader = defineProxyMiddleware((req, socket) => { if (req.method !== "GET" || !req.headers.upgrade) { socket.destroy(); return true; @@ -28,7 +29,7 @@ const checkMethodAndHeader = defineProxyMiddleware((req, socket) => { /** * Sets `x-forwarded-*` headers if specified in config. */ -const XHeaders = defineProxyMiddleware((req, socket, options) => { +const XHeaders = defineProxyMiddleware((req, socket, options) => { if (!options.xfwd) { return; } @@ -39,7 +40,7 @@ const XHeaders = defineProxyMiddleware((req, socket, options) => { proto: hasEncryptedConnection(req) ? "wss" : "ws", }; - for (const header of ["for", "port", "proto"]) { + for (const header of ["for", "port", "proto"] as const) { req.headers["x-forwarded-" + header] = (req.headers["x-forwarded-" + header] || "") + (req.headers["x-forwarded-" + header] ? "," : "") + @@ -51,9 +52,12 @@ const XHeaders = defineProxyMiddleware((req, socket, options) => { * Does the actual proxying. Make the request and upgrade it * send the Switching Protocols request and pipe the sockets. */ -const stream = defineProxyMiddleware( +const stream = defineProxyMiddleware( (req, socket, options, server, head, callback) => { - const createHttpHeader = function (line, headers) { + const createHttpHeader = function ( + line: string, + headers: http.OutgoingHttpHeaders, + ) { return ( Object.keys(headers) // eslint-disable-next-line unicorn/no-array-reduce @@ -80,12 +84,11 @@ const stream = defineProxyMiddleware( setupSocket(socket); if (head && head.length > 0) { - // @ts-expect-error socket.unshift(head); } const proxyReq = ( - isSSL.test(options.target.protocol) ? https : http + isSSL.test(options.target.protocol || "undefined") ? https : http ).request(setupOutgoing(options.ssl || {}, options, req)); // Enable developers to modify the proxyReq before headers are sent @@ -162,7 +165,7 @@ const stream = defineProxyMiddleware( }, ); -export const websocketIncomingMiddleware: readonly ProxyMiddleware[] = [ +export const websocketIncomingMiddleware: readonly ProxyMiddleware[] = [ checkMethodAndHeader, XHeaders, stream, diff --git a/src/server.ts b/src/server.ts index b54a416..8f29f3a 100644 --- a/src/server.ts +++ b/src/server.ts @@ -5,14 +5,17 @@ import { EventEmitter } from "node:events"; import { webIncomingMiddleware } from "./middleware/web-incoming"; import { websocketIncomingMiddleware } from "./middleware/ws-incoming"; import { ProxyServerOptions } from "./types"; -import { ProxyMiddleware } from "./middleware/_utils"; +import { ProxyMiddleware, type ResOfType } from "./middleware/_utils"; +import type net from "node:net"; // eslint-disable-next-line unicorn/prefer-event-target export class ProxyServer extends EventEmitter { private _server?: http.Server | https.Server; - _webPasses: ProxyMiddleware[] = [...webIncomingMiddleware]; - _wsPasses: ProxyMiddleware[] = [...websocketIncomingMiddleware]; + _webPasses: ProxyMiddleware[] = [ + ...webIncomingMiddleware, + ]; + _wsPasses: ProxyMiddleware[] = [...websocketIncomingMiddleware]; options: ProxyServerOptions; @@ -25,7 +28,7 @@ export class ProxyServer extends EventEmitter { ws: ( req: http.IncomingMessage, - socket: http.OutgoingMessage, + socket: net.Socket, opts: ProxyServerOptions, head?: any, ) => Promise; @@ -50,7 +53,7 @@ export class ProxyServer extends EventEmitter { * @param hostname - The hostname to listen on */ listen(port: number, hostname: string) { - const closure = (req, res) => { + const closure = (req: http.IncomingMessage, res: http.ServerResponse) => { this.web(req, res); }; @@ -85,11 +88,15 @@ export class ProxyServer extends EventEmitter { } } - before(type: "ws" | "web", passName: string, pass: ProxyMiddleware) { + before( + type: Type, + passName: string, + pass: ProxyMiddleware>, + ) { if (type !== "ws" && type !== "web") { throw new Error("type must be `web` or `ws`"); } - const passes = type === "ws" ? this._wsPasses : this._webPasses; + const passes = this._getPasses(type); let i: false | number = false; for (const [idx, v] of passes.entries()) { if (v.name === passName) { @@ -102,11 +109,15 @@ export class ProxyServer extends EventEmitter { passes.splice(i, 0, pass); } - after(type: "ws" | "web", passName: string, pass: ProxyMiddleware) { + after( + type: Type, + passName: string, + pass: ProxyMiddleware>, + ) { if (type !== "ws" && type !== "web") { throw new Error("type must be `web` or `ws`"); } - const passes = type === "ws" ? this._wsPasses : this._webPasses; + const passes = this._getPasses(type); let i: boolean | number = false; for (const [idx, v] of passes.entries()) { if (v.name === passName) { @@ -118,6 +129,15 @@ export class ProxyServer extends EventEmitter { } passes.splice(i++, 0, pass); } + + /** @internal */ + _getPasses( + type: Type, + ): ProxyMiddleware>[] { + return (type === "ws" + ? this._wsPasses + : this._webPasses) as unknown as ProxyMiddleware>[]; + } } /** @@ -140,29 +160,32 @@ export function createProxyServer(options: ProxyServerOptions = {}) { // --- Internal --- -function _createProxyFn(type: "web" | "ws", server: ProxyServer) { +function _createProxyFn( + type: Type, + server: ProxyServer, +) { + type Res = ResOfType; return function ( + this: ProxyServer, req: http.IncomingMessage, - res: http.OutgoingMessage, - opts: ProxyServerOptions, - head: any, + res: Res, + opts?: ProxyServerOptions, + head?: any, ): Promise { const requestOptions = { ...opts, ...server.options }; - for (const key of ["target", "forward"]) { + for (const key of ["target", "forward"] as const) { if (typeof requestOptions[key] === "string") { requestOptions[key] = new URL(requestOptions[key]); } } if (!requestOptions.target && !requestOptions.forward) { - return this.emit( - "error", - new Error("Must provide a proper URL as target"), - ); + this.emit("error", new Error("Must provide a proper URL as target")); + return Promise.resolve(); } - let _resolve: () => void; + let _resolve!: () => void; let _reject: (error: any) => void; const callbackPromise = new Promise((resolve, reject) => { _resolve = resolve; @@ -176,7 +199,7 @@ function _createProxyFn(type: "web" | "ws", server: ProxyServer) { _reject(error); }); - for (const pass of type === "ws" ? server._wsPasses : server._webPasses) { + for (const pass of server._getPasses(type)) { const stop = pass( req, res, diff --git a/src/types.ts b/src/types.ts index 23fbb65..d179017 100644 --- a/src/types.ts +++ b/src/types.ts @@ -16,8 +16,10 @@ export interface ProxyTargetDetailed { } type ProxyTarget = ProxyTargetUrl | ProxyTargetDetailed; +export type NormalizedProxyTarget = Exclude; -type ProxyTargetUrl = string | Partial; +type ProxyTargetUrl = string | URL; +export type NormalizedProxyTargetUrl = Exclude; export interface ProxyServerOptions { /** URL string to be parsed with the url module. */ diff --git a/tsconfig.json b/tsconfig.json index 462b4d6..e9588d9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,7 +4,7 @@ "module": "ESNext", "moduleResolution": "Node", "esModuleInterop": true, - "strict": false + "strict": true }, "include": ["src"] } From 91e6731abd260acd5b2116c2b3231c7f882d6821 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sat, 28 Jun 2025 10:00:42 +0000 Subject: [PATCH 02/13] chore: apply automated updates --- src/middleware/web-incoming.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/middleware/web-incoming.ts b/src/middleware/web-incoming.ts index 33c2641..0fe36b4 100644 --- a/src/middleware/web-incoming.ts +++ b/src/middleware/web-incoming.ts @@ -120,7 +120,10 @@ const stream = defineProxyMiddleware( req.on("error", proxyError); proxyReq.on("error", proxyError); - function createErrorHandler(proxyReq: httpNative.ClientRequest, url: NormalizedProxyTarget) { + function createErrorHandler( + proxyReq: httpNative.ClientRequest, + url: NormalizedProxyTarget, + ) { return function proxyError(err: Error) { if ( req.socket.destroyed && From 62d76419b0d5afb1a9b65b719420682b7935685f Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Wed, 24 Sep 2025 18:15:23 +0200 Subject: [PATCH 03/13] Update src/_utils.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 翠 --- src/_utils.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/_utils.ts b/src/_utils.ts index ff0be5d..20179091 100644 --- a/src/_utils.ts +++ b/src/_utils.ts @@ -113,8 +113,6 @@ export function setupOutgoing( const targetPath = target && options.prependPath !== false ? (target as URL).pathname || - // NOTE: http-proxy receives url.Url instead of URL - (target as Partial).path || "" : ""; From fe2b9b8eb12a2a122c1c1ab461daa367fb2df90e Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Wed, 24 Sep 2025 16:15:50 +0000 Subject: [PATCH 04/13] chore: apply automated updates --- src/_utils.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/_utils.ts b/src/_utils.ts index 20179091..2be7f9b 100644 --- a/src/_utils.ts +++ b/src/_utils.ts @@ -112,8 +112,7 @@ export function setupOutgoing( const target = options[forward || "target"]; const targetPath = target && options.prependPath !== false - ? (target as URL).pathname || - "" + ? (target as URL).pathname || "" : ""; const parsed = new URL(req.url!, "http://localhost"); From 7cbde8b43198e6d2c7c955cbce42c7dd5700cd62 Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Wed, 24 Sep 2025 18:25:21 +0200 Subject: [PATCH 05/13] Apply suggestion from @sapphi-red MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 翠 --- src/middleware/web-outgoing.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/middleware/web-outgoing.ts b/src/middleware/web-outgoing.ts index 273c0a4..7ef684e 100644 --- a/src/middleware/web-outgoing.ts +++ b/src/middleware/web-outgoing.ts @@ -45,7 +45,9 @@ const setRedirectHostRewrite = defineProxyOutgoingMiddleware( if (options.hostRewrite) { u.host = options.hostRewrite; } else if (options.autoRewrite) { - u.host = req.headers.host ?? "undefined"; + if (req.headers.host) { + u.host = req.headers.host; + } } if (options.protocolRewrite) { u.protocol = options.protocolRewrite; From d7b969a34adee2ff4edb70f29b87ac613c195d45 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Wed, 24 Sep 2025 16:25:49 +0000 Subject: [PATCH 06/13] chore: apply automated updates --- src/middleware/web-outgoing.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/middleware/web-outgoing.ts b/src/middleware/web-outgoing.ts index 7ef684e..3606406 100644 --- a/src/middleware/web-outgoing.ts +++ b/src/middleware/web-outgoing.ts @@ -44,10 +44,8 @@ const setRedirectHostRewrite = defineProxyOutgoingMiddleware( if (options.hostRewrite) { u.host = options.hostRewrite; - } else if (options.autoRewrite) { - if (req.headers.host) { - u.host = req.headers.host; - } + } else if (options.autoRewrite && req.headers.host) { + u.host = req.headers.host; } if (options.protocolRewrite) { u.protocol = options.protocolRewrite; From f89085521654976ea93b265ac461522f9db68f29 Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Wed, 24 Sep 2025 18:31:28 +0200 Subject: [PATCH 07/13] simplify types --- src/_utils.ts | 4 +--- src/middleware/_utils.ts | 12 ++++-------- src/middleware/web-incoming.ts | 6 +++--- src/middleware/ws-incoming.ts | 8 ++++---- src/types.ts | 12 ++++-------- 5 files changed, 16 insertions(+), 26 deletions(-) diff --git a/src/_utils.ts b/src/_utils.ts index 2be7f9b..4248d07 100644 --- a/src/_utils.ts +++ b/src/_utils.ts @@ -4,11 +4,9 @@ import type tls from "node:tls"; import net from "node:net"; import type { NormalizedProxyTarget, - NormalizedProxyTargetUrl, ProxyServerOptions, ProxyTargetDetailed, } from "./types"; -import type url from "node:url"; const upgradeHeader = /(^|,)\s*upgrade\s*($|,)/i; @@ -40,7 +38,7 @@ export function setupOutgoing( outgoing: httpNative.RequestOptions & httpsNative.RequestOptions, options: ProxyServerOptions & { target: NormalizedProxyTarget; - forward: NormalizedProxyTargetUrl; + forward: URL; }, req: httpNative.IncomingMessage, forward?: "forward" | "target", diff --git a/src/middleware/_utils.ts b/src/middleware/_utils.ts index b866276..dddb0f9 100644 --- a/src/middleware/_utils.ts +++ b/src/middleware/_utils.ts @@ -1,11 +1,7 @@ import type { IncomingMessage, OutgoingMessage } from "node:http"; -import type { ProxyServer } from "../server"; -import type { - NormalizedProxyTarget, - NormalizedProxyTargetUrl, - ProxyServerOptions, -} from "../types"; import type { Socket } from "node:net"; +import type { ProxyServer } from "../server"; +import type { NormalizedProxyTarget, ProxyServerOptions } from "../types"; export type ResOfType = T extends "ws" ? T extends "web" @@ -20,7 +16,7 @@ export type ProxyMiddleware = ( res: T, opts: ProxyServerOptions & { target: NormalizedProxyTarget; - forward: NormalizedProxyTargetUrl; + forward: URL; }, server: ProxyServer, head?: Buffer, @@ -39,7 +35,7 @@ export type ProxyOutgoingMiddleware = ( proxyRes: IncomingMessage, opts: ProxyServerOptions & { target: NormalizedProxyTarget; - forward: NormalizedProxyTargetUrl; + forward: URL; }, ) => void | true; diff --git a/src/middleware/web-incoming.ts b/src/middleware/web-incoming.ts index 0fe36b4..b47c236 100644 --- a/src/middleware/web-incoming.ts +++ b/src/middleware/web-incoming.ts @@ -1,11 +1,11 @@ -import httpNative from "node:http"; -import httpsNative from "node:https"; +import nodeHTTP from "node:http"; +import nodeHTTPS from "node:https"; import { getPort, hasEncryptedConnection, setupOutgoing } from "../_utils"; import { webOutgoingMiddleware } from "./web-outgoing"; import { ProxyMiddleware, defineProxyMiddleware } from "./_utils"; import type { NormalizedProxyTarget } from "../types"; -const nativeAgents = { http: httpNative, https: httpsNative }; +const nativeAgents = { http: nodeHTTP, https: nodeHTTPS }; /** * Sets `content-length` to '0' if request is of DELETE type. diff --git a/src/middleware/ws-incoming.ts b/src/middleware/ws-incoming.ts index 6855f83..dc5c90e 100644 --- a/src/middleware/ws-incoming.ts +++ b/src/middleware/ws-incoming.ts @@ -1,5 +1,5 @@ -import http from "node:http"; -import https from "node:https"; +import nodeHTTP from "node:http"; +import nodeHTTPS from "node:https"; import { getPort, hasEncryptedConnection, @@ -56,7 +56,7 @@ const stream = defineProxyMiddleware( (req, socket, options, server, head, callback) => { const createHttpHeader = function ( line: string, - headers: http.OutgoingHttpHeaders, + headers: nodeHTTP.OutgoingHttpHeaders, ) { return ( Object.keys(headers) @@ -88,7 +88,7 @@ const stream = defineProxyMiddleware( } const proxyReq = ( - isSSL.test(options.target.protocol || "undefined") ? https : http + isSSL.test(options.target.protocol || "undefined") ? nodeHTTPS : nodeHTTP ).request(setupOutgoing(options.ssl || {}, options, req)); // Enable developers to modify the proxyReq before headers are sent diff --git a/src/types.ts b/src/types.ts index d179017..bffcc0b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -15,17 +15,13 @@ export interface ProxyTargetDetailed { secureProtocol?: string; } -type ProxyTarget = ProxyTargetUrl | ProxyTargetDetailed; -export type NormalizedProxyTarget = Exclude; - -type ProxyTargetUrl = string | URL; -export type NormalizedProxyTargetUrl = Exclude; +export type NormalizedProxyTarget = URL | ProxyTargetDetailed; export interface ProxyServerOptions { /** URL string to be parsed with the url module. */ - target?: ProxyTarget; - /** URL string to be parsed with the url module. */ - forward?: ProxyTargetUrl; + target?: string | URL | ProxyTargetDetailed; + /** URL string to be parsed. */ + forward?: string | URL; /** Object to be passed to http(s).request. */ agent?: any; /** Object to be passed to https.createServer(). */ From 6d46330ab6263a4863c597e9b55deab1a799052e Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Wed, 24 Sep 2025 18:32:13 +0200 Subject: [PATCH 08/13] Update src/_utils.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 翠 --- src/_utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_utils.ts b/src/_utils.ts index 4248d07..182b6c0 100644 --- a/src/_utils.ts +++ b/src/_utils.ts @@ -45,7 +45,7 @@ export function setupOutgoing( ): httpNative.RequestOptions | httpsNative.RequestOptions { outgoing.port = options[forward || "target"].port || - (isSSL.test(options[forward || "target"].protocol ?? "undefined") + (isSSL.test(options[forward || "target"].protocol ?? "http") ? 443 : 80); From 70c559c01c76dd4fcc655c729976389b0a81d624 Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Wed, 24 Sep 2025 18:32:26 +0200 Subject: [PATCH 09/13] Update src/_utils.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 翠 --- src/_utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_utils.ts b/src/_utils.ts index 182b6c0..4398f51 100644 --- a/src/_utils.ts +++ b/src/_utils.ts @@ -131,7 +131,7 @@ export function setupOutgoing( requiresPort(outgoing.port, options[forward || "target"].protocol) && !hasPort(outgoing.host) ? outgoing.host + ":" + outgoing.port - : (outgoing.host as string | undefined); + : outgoing.host ?? undefined; } return outgoing; } From 419e6867d911cde3abca888bcce76aac96ce7ae9 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Wed, 24 Sep 2025 16:32:53 +0000 Subject: [PATCH 10/13] chore: apply automated updates --- src/_utils.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/_utils.ts b/src/_utils.ts index 4398f51..8f1a66c 100644 --- a/src/_utils.ts +++ b/src/_utils.ts @@ -45,9 +45,7 @@ export function setupOutgoing( ): httpNative.RequestOptions | httpsNative.RequestOptions { outgoing.port = options[forward || "target"].port || - (isSSL.test(options[forward || "target"].protocol ?? "http") - ? 443 - : 80); + (isSSL.test(options[forward || "target"].protocol ?? "http") ? 443 : 80); for (const e of [ "host", @@ -131,7 +129,7 @@ export function setupOutgoing( requiresPort(outgoing.port, options[forward || "target"].protocol) && !hasPort(outgoing.host) ? outgoing.host + ":" + outgoing.port - : outgoing.host ?? undefined; + : (outgoing.host ?? undefined); } return outgoing; } From 86045dca086dcccff60dfcc33fce90c7e7589ab3 Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Wed, 24 Sep 2025 18:36:43 +0200 Subject: [PATCH 11/13] update --- src/_utils.ts | 8 ++------ src/middleware/_utils.ts | 6 +++--- src/middleware/web-incoming.ts | 9 +++++---- src/middleware/ws-incoming.ts | 2 +- src/types.ts | 2 -- 5 files changed, 11 insertions(+), 16 deletions(-) diff --git a/src/_utils.ts b/src/_utils.ts index 8f1a66c..748c668 100644 --- a/src/_utils.ts +++ b/src/_utils.ts @@ -2,11 +2,7 @@ import httpNative from "node:http"; import httpsNative from "node:https"; import type tls from "node:tls"; import net from "node:net"; -import type { - NormalizedProxyTarget, - ProxyServerOptions, - ProxyTargetDetailed, -} from "./types"; +import type { ProxyServerOptions, ProxyTargetDetailed } from "./types"; const upgradeHeader = /(^|,)\s*upgrade\s*($|,)/i; @@ -37,7 +33,7 @@ export const isSSL = /^https|wss/; export function setupOutgoing( outgoing: httpNative.RequestOptions & httpsNative.RequestOptions, options: ProxyServerOptions & { - target: NormalizedProxyTarget; + target: URL | ProxyTargetDetailed; forward: URL; }, req: httpNative.IncomingMessage, diff --git a/src/middleware/_utils.ts b/src/middleware/_utils.ts index dddb0f9..7dd9d35 100644 --- a/src/middleware/_utils.ts +++ b/src/middleware/_utils.ts @@ -1,7 +1,7 @@ import type { IncomingMessage, OutgoingMessage } from "node:http"; import type { Socket } from "node:net"; import type { ProxyServer } from "../server"; -import type { NormalizedProxyTarget, ProxyServerOptions } from "../types"; +import type { ProxyServerOptions, ProxyTargetDetailed } from "../types"; export type ResOfType = T extends "ws" ? T extends "web" @@ -15,7 +15,7 @@ export type ProxyMiddleware = ( req: IncomingMessage, res: T, opts: ProxyServerOptions & { - target: NormalizedProxyTarget; + target: URL | ProxyTargetDetailed; forward: URL; }, server: ProxyServer, @@ -34,7 +34,7 @@ export type ProxyOutgoingMiddleware = ( res: OutgoingMessage, proxyRes: IncomingMessage, opts: ProxyServerOptions & { - target: NormalizedProxyTarget; + target: URL | ProxyTargetDetailed; forward: URL; }, ) => void | true; diff --git a/src/middleware/web-incoming.ts b/src/middleware/web-incoming.ts index b47c236..2596b44 100644 --- a/src/middleware/web-incoming.ts +++ b/src/middleware/web-incoming.ts @@ -1,9 +1,10 @@ +import type { ClientRequest, OutgoingMessage } from "node:http"; +import type { ProxyTargetDetailed } from "../types"; import nodeHTTP from "node:http"; import nodeHTTPS from "node:https"; import { getPort, hasEncryptedConnection, setupOutgoing } from "../_utils"; import { webOutgoingMiddleware } from "./web-outgoing"; import { ProxyMiddleware, defineProxyMiddleware } from "./_utils"; -import type { NormalizedProxyTarget } from "../types"; const nativeAgents = { http: nodeHTTP, https: nodeHTTPS }; @@ -121,8 +122,8 @@ const stream = defineProxyMiddleware( proxyReq.on("error", proxyError); function createErrorHandler( - proxyReq: httpNative.ClientRequest, - url: NormalizedProxyTarget, + proxyReq: ClientRequest, + url: URL | ProxyTargetDetailed, ) { return function proxyError(err: Error) { if ( @@ -180,5 +181,5 @@ const stream = defineProxyMiddleware( }, ); -export const webIncomingMiddleware: readonly ProxyMiddleware[] = +export const webIncomingMiddleware: readonly ProxyMiddleware[] = [deleteLength, timeout, XHeaders, stream] as const; diff --git a/src/middleware/ws-incoming.ts b/src/middleware/ws-incoming.ts index dc5c90e..8d0426a 100644 --- a/src/middleware/ws-incoming.ts +++ b/src/middleware/ws-incoming.ts @@ -88,7 +88,7 @@ const stream = defineProxyMiddleware( } const proxyReq = ( - isSSL.test(options.target.protocol || "undefined") ? nodeHTTPS : nodeHTTP + isSSL.test(options.target.protocol || "http") ? nodeHTTPS : nodeHTTP ).request(setupOutgoing(options.ssl || {}, options, req)); // Enable developers to modify the proxyReq before headers are sent diff --git a/src/types.ts b/src/types.ts index bffcc0b..9b3dd4a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -15,8 +15,6 @@ export interface ProxyTargetDetailed { secureProtocol?: string; } -export type NormalizedProxyTarget = URL | ProxyTargetDetailed; - export interface ProxyServerOptions { /** URL string to be parsed with the url module. */ target?: string | URL | ProxyTargetDetailed; From fb4229ad9d2aca863cbcac490eed5b247e8f014a Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Wed, 24 Sep 2025 21:02:38 +0200 Subject: [PATCH 12/13] fix utils --- src/_utils.ts | 27 ++++++++++----- src/types.ts | 13 ++++++-- test/_utils.test.ts | 80 +++++++++++++++++++++++---------------------- 3 files changed, 69 insertions(+), 51 deletions(-) diff --git a/src/_utils.ts b/src/_utils.ts index 748c668..069c388 100644 --- a/src/_utils.ts +++ b/src/_utils.ts @@ -1,8 +1,13 @@ import httpNative from "node:http"; import httpsNative from "node:https"; import type tls from "node:tls"; +import type { Url as LegacyURL } from "node:url"; import net from "node:net"; -import type { ProxyServerOptions, ProxyTargetDetailed } from "./types"; +import type { + ProxyServerOptions, + ProxyTarget, + ProxyTargetDetailed, +} from "./types"; const upgradeHeader = /(^|,)\s*upgrade\s*($|,)/i; @@ -33,15 +38,17 @@ export const isSSL = /^https|wss/; export function setupOutgoing( outgoing: httpNative.RequestOptions & httpsNative.RequestOptions, options: ProxyServerOptions & { - target: URL | ProxyTargetDetailed; - forward: URL; + target: ProxyTarget; + forward?: ProxyTarget; }, req: httpNative.IncomingMessage, forward?: "forward" | "target", ): httpNative.RequestOptions | httpsNative.RequestOptions { outgoing.port = - options[forward || "target"].port || - (isSSL.test(options[forward || "target"].protocol ?? "http") ? 443 : 80); + (options[forward || "target"] as URL).port || + (isSSL.test((options[forward || "target"] as URL).protocol ?? "http") + ? 443 + : 80); for (const e of [ "host", @@ -78,7 +85,7 @@ export function setupOutgoing( outgoing.ca = options.ca; } - if (isSSL.test(options[forward || "target"].protocol ?? "undefined")) { + if (isSSL.test((options[forward || "target"] as URL).protocol ?? "http")) { outgoing.rejectUnauthorized = options.secure === undefined ? true : options.secure; } @@ -104,7 +111,7 @@ export function setupOutgoing( const target = options[forward || "target"]; const targetPath = target && options.prependPath !== false - ? (target as URL).pathname || "" + ? (target as URL).pathname || (target as LegacyURL).path || "" : ""; const parsed = new URL(req.url!, "http://localhost"); @@ -122,8 +129,10 @@ export function setupOutgoing( if (options.changeOrigin) { outgoing.headers.host = - requiresPort(outgoing.port, options[forward || "target"].protocol) && - !hasPort(outgoing.host) + requiresPort( + outgoing.port, + (options[forward || "target"] as URL).protocol, + ) && !hasPort(outgoing.host) ? outgoing.host + ":" + outgoing.port : (outgoing.host ?? undefined); } diff --git a/src/types.ts b/src/types.ts index 9b3dd4a..4cb53d9 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,4 +1,5 @@ import type * as stream from "node:stream"; +import type { Url as LegacyURL } from "node:url"; export interface ProxyTargetDetailed { host: string; @@ -15,11 +16,17 @@ export interface ProxyTargetDetailed { secureProtocol?: string; } +export type ProxyTarget = + | string + | URL + | Partial + | ProxyTargetDetailed; + export interface ProxyServerOptions { - /** URL string to be parsed with the url module. */ - target?: string | URL | ProxyTargetDetailed; /** URL string to be parsed. */ - forward?: string | URL; + target?: ProxyTarget; + /** URL string to be parsed. */ + forward?: Exclude; /** Object to be passed to http(s).request. */ agent?: any; /** Object to be passed to https.createServer(). */ diff --git a/test/_utils.test.ts b/test/_utils.test.ts index ea7e16a..6237ca5 100644 --- a/test/_utils.test.ts +++ b/test/_utils.test.ts @@ -20,6 +20,7 @@ describe("lib/http-proxy/common.js", () => { socketPath: "are", port: "you", }, + // @ts-expect-error headers: { fizz: "bang", overwritten: true }, localAddress: "local.address", auth: "username:pass", @@ -65,7 +66,7 @@ describe("lib/http-proxy/common.js", () => { method: "i", url: "am", headers: { pro: "xy", overwritten: false }, - }, + } as any, ); expect(outgoing.headers.connection).to.eql("upgrade"); }); @@ -81,14 +82,14 @@ describe("lib/http-proxy/common.js", () => { hostname: "how", socketPath: "are", port: "you", - }, + } as any, headers: { connection: "keep-alive, upgrade" }, // this is what Firefox sets }, { method: "i", url: "am", headers: { pro: "xy", overwritten: false }, - }, + } as any, ); expect(outgoing.headers.connection).to.eql("keep-alive, upgrade"); }); @@ -112,7 +113,7 @@ describe("lib/http-proxy/common.js", () => { method: "i", url: "am", headers: { pro: "xy", overwritten: false }, - }, + } as any, ); expect(outgoing.headers.connection).to.eql("close"); }); @@ -135,18 +136,16 @@ describe("lib/http-proxy/common.js", () => { method: "i", url: "am", headers: { pro: "xy", overwritten: false }, - }, + } as any, ); expect(outgoing.headers.connection).to.eql("close"); }); it("should set the agent to false if none is given", () => { const outgoing = {} as any; - common.setupOutgoing( - outgoing, - { target: "http://localhost" }, - { url: "/" }, - ); + common.setupOutgoing(outgoing, { target: "http://localhost" }, { + url: "/", + } as any); expect(outgoing.agent).to.eql(false); }); @@ -167,7 +166,7 @@ describe("lib/http-proxy/common.js", () => { method: "i", url: "am", headers: { pro: "xy" }, - }, + } as any, ); expect(outgoing.host).to.eql("how"); @@ -184,11 +183,9 @@ describe("lib/http-proxy/common.js", () => { it("should keep the original target path in the outgoing path", () => { const outgoing = {} as any; - common.setupOutgoing( - outgoing, - { target: { path: "some-path" } }, - { url: "am" }, - ); + common.setupOutgoing(outgoing, { target: { path: "some-path" } }, { + url: "am", + } as any); expect(outgoing.path).to.eql("some-path/am"); }); @@ -205,7 +202,7 @@ describe("lib/http-proxy/common.js", () => { }, { url: "am", - }, + } as any, "forward", ); @@ -222,7 +219,7 @@ describe("lib/http-proxy/common.js", () => { host: "whatever.com", }, }, - { url: "/" }, + { url: "/" } as any, ); expect(outgoing.port).to.eql(443); @@ -236,7 +233,7 @@ describe("lib/http-proxy/common.js", () => { target: { path: "hellothere" }, prependPath: false, }, - { url: "hi" }, + { url: "hi" } as any, ); expect(outgoing.path).to.eql("/hi"); @@ -249,7 +246,7 @@ describe("lib/http-proxy/common.js", () => { { target: { path: "/forward" }, }, - { url: "/static/path" }, + { url: "/static/path" } as any, ); expect(outgoing.path).to.eql("/forward/static/path"); @@ -262,7 +259,9 @@ describe("lib/http-proxy/common.js", () => { { target: { path: "/forward" }, }, - { url: "/?foo=bar//&target=http://foobar.com/?a=1%26b=2&other=2" }, + { + url: "/?foo=bar//&target=http://foobar.com/?a=1%26b=2&other=2", + } as any, ); expect(outgoing.path).to.eql( @@ -279,10 +278,10 @@ describe("lib/http-proxy/common.js", () => { common.setupOutgoing( outgoing, { - target: URL.parse("http://sometarget.com:80"), + target: URL.parse("http://sometarget.com:80")!, toProxy: true, }, - { url: google }, + { url: google } as any, ); expect(outgoing.path).to.eql("/" + google); @@ -295,10 +294,10 @@ describe("lib/http-proxy/common.js", () => { common.setupOutgoing( outgoing, { - target: URL.parse("http://sometarget.com:80"), + target: URL.parse("http://sometarget.com:80")!, toProxy: true, }, - { url: google }, + { url: google } as any, ); expect(outgoing.path).to.eql("/" + google); @@ -311,10 +310,10 @@ describe("lib/http-proxy/common.js", () => { common.setupOutgoing( outgoing, { - target: URL.parse("http://sometarget.com:80"), + target: URL.parse("http://sometarget.com:80")!, toProxy: true, }, - { url: google }, + { url: google } as any, ); expect(outgoing.path).to.eql("/" + google); @@ -327,10 +326,10 @@ describe("lib/http-proxy/common.js", () => { common.setupOutgoing( outgoing, { - target: URL.parse(myEndpoint), + target: URL.parse(myEndpoint)!, ignorePath: true, }, - { url: "/more/crazy/pathness" }, + { url: "/more/crazy/pathness" } as any, ); expect(outgoing.path).to.eql("/some/crazy/path/whoooo"); @@ -342,11 +341,11 @@ describe("lib/http-proxy/common.js", () => { common.setupOutgoing( outgoing, { - target: URL.parse(myEndpoint), + target: URL.parse(myEndpoint)!, ignorePath: true, prependPath: false, }, - { url: "/more/crazy/pathness" }, + { url: "/more/crazy/pathness" } as any, ); expect(outgoing.path).to.eql("/"); @@ -360,10 +359,10 @@ describe("lib/http-proxy/common.js", () => { common.setupOutgoing( outgoing, { - target: URL.parse(myEndpoint), + target: URL.parse(myEndpoint)!, changeOrigin: true, }, - { url: "/" }, + { url: "/" } as any, ); expect(outgoing.headers.host).to.eql("mycouch.com:6984"); @@ -381,7 +380,7 @@ describe("lib/http-proxy/common.js", () => { }, changeOrigin: true, }, - { url: "/" }, + { url: "/" } as any, ); expect(outgoing.headers.host).to.eql("mycouch.com:6984"); }); @@ -410,7 +409,7 @@ describe("lib/http-proxy/common.js", () => { { method: "i", url: "am", - }, + } as any, ); expect(outgoing.pfx).eql("my-pfx"); @@ -427,10 +426,11 @@ describe("lib/http-proxy/common.js", () => { common.setupOutgoing( outgoing, { - target: URL.parse("https://whooooo.com"), + target: "https://whooooo.com", + // @ts-expect-error method: "POST", }, - { method: "GET", url: "" }, + { method: "GET", url: "" } as any, ); expect(outgoing.method).eql("POST"); @@ -439,7 +439,9 @@ describe("lib/http-proxy/common.js", () => { // url.parse('').path => null it("should not pass null as last arg to #urlJoin", () => { const outgoing = {} as any; - common.setupOutgoing(outgoing, { target: { path: "" } }, { url: "" }); + common.setupOutgoing(outgoing, { target: { path: "" } }, { + url: "", + } as any); expect(outgoing.path).toBe("/"); // leading slash is new in httpxy }); @@ -463,7 +465,7 @@ describe("lib/http-proxy/common.js", () => { socketConfig.keepalive = bol; }, }; - const returnValue = common.setupSocket(stubSocket); + const returnValue = common.setupSocket(stubSocket as any); expect(socketConfig.timeout).to.eql(0); expect(socketConfig.nodelay).to.eql(true); From a7a9717465e46f6cdfe20539010237e02c157d2d Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Wed, 24 Sep 2025 21:04:58 +0200 Subject: [PATCH 13/13] fix web-outgoing --- src/middleware/web-outgoing.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/middleware/web-outgoing.ts b/src/middleware/web-outgoing.ts index 82c8b74..e124c6d 100644 --- a/src/middleware/web-outgoing.ts +++ b/src/middleware/web-outgoing.ts @@ -38,7 +38,10 @@ export const setRedirectHostRewrite = defineProxyOutgoingMiddleware( proxyRes.headers.location && redirectRegex.test(String(proxyRes.statusCode)) ) { - const target = options.target; + const target = + options.target instanceof URL + ? options.target + : new URL(options.target as string | URL); // TODO: handle legacy url? const u = new URL(proxyRes.headers.location); // Make sure the redirected host matches the target host before rewriting