diff --git a/src/server.ts b/src/server.ts index fddcf16..3a50b1b 100644 --- a/src/server.ts +++ b/src/server.ts @@ -234,9 +234,9 @@ function _createProxyFn< http.ServerResponse | http2.Http2ServerResponse >, head, - (error) => { + (error, _req, _res, url) => { if (server.listenerCount("error") > 0) { - server.emit("error", error, req, res as ProxyServerRes | net.Socket); + server.emit("error", error, req, res as ProxyServerRes | net.Socket, url); _resolve(); } else { _reject(error); @@ -245,7 +245,13 @@ function _createProxyFn< ); } catch (error) { if (server.listenerCount("error") > 0) { - server.emit("error", error as Error, req, res as ProxyServerRes | net.Socket); + server.emit( + "error", + error as Error, + req, + res as ProxyServerRes | net.Socket, + requestOptions.target, + ); _resolve(); } else { _reject(error); diff --git a/test/http-proxy.test.ts b/test/http-proxy.test.ts index 20f72d2..7cd5d9e 100644 --- a/test/http-proxy.test.ts +++ b/test/http-proxy.test.ts @@ -333,6 +333,34 @@ describe("http-proxy", () => { await promise.catch(() => {}); }); + + it("should forward the target URL as the 4th argument of the error event", async () => { + const target = "http://127.0.0.1:1"; + const proxy = httpProxy.createProxyServer({ target }); + + const { promise, resolve } = Promise.withResolvers(); + proxy.on("error", (_err, _req, _res, url) => { + proxy.close(() => {}); + resolve(url as URL | undefined); + }); + + const proxyPort = await proxyListen(proxy); + + http + .request( + { + hostname: "127.0.0.1", + port: proxyPort, + method: "GET", + }, + () => {}, + ) + .end(); + + const url = await promise; + expect(url).toBeInstanceOf(URL); + expect((url as URL).href).toBe(new URL(target).href); + }); }); describe("#createProxyServer setting the correct timeout value", () => { diff --git a/test/index.test.ts b/test/index.test.ts index dc9e726..d3177d2 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -182,6 +182,76 @@ describe("middleware pass exceptions", () => { } }); + it("should forward the target URL as the 4th argument when a pass throws", async () => { + const target = await new Promise<{ + close: () => Promise; + url: string; + }>((resolve, reject) => { + const server = http.createServer((_req, res) => { + res.end("ok"); + }); + server.once("error", reject); + server.listen(0, "127.0.0.1", () => { + const { port } = server.address() as AddressInfo; + resolve({ + close: () => + new Promise((r, j) => { + server.close((e) => (e ? j(e) : r())); + }), + url: `http://127.0.0.1:${port}/`, + }); + }); + }); + + const proxy = createProxyServer({ target: target.url }); + + proxy.before("web", "", () => { + throw new TypeError("Invalid character in header"); + }); + + const proxyServer = await new Promise<{ + close: () => Promise; + url: string; + }>((resolve, reject) => { + const server = http.createServer((req, res) => { + void proxy.web(req, res); + }); + server.once("error", reject); + server.listen(0, "127.0.0.1", () => { + const { port } = server.address() as AddressInfo; + resolve({ + close: () => + new Promise((r, j) => { + server.close((e) => (e ? j(e) : r())); + }), + url: `http://127.0.0.1:${port}/`, + }); + }); + }); + + try { + const urlPromise = new Promise((resolve) => { + proxy.on("error", (_err, _req, res, url) => { + resolve(url as URL | undefined); + if (res && "writeHead" in res && !res.headersSent) { + res.writeHead(502); + res.end(); + } + }); + }); + + await $fetch(proxyServer.url, { ignoreResponseError: true }).catch(() => {}); + + const url = await urlPromise; + expect(url).toBeInstanceOf(URL); + expect((url as URL).href).toBe(new URL(target.url).href); + } finally { + proxy.close(); + await proxyServer.close(); + await target.close(); + } + }); + it("should reject promise when no error listener and pass throws", async () => { const target = await new Promise<{ close: () => Promise;