diff --git a/src/middleware/web-incoming.ts b/src/middleware/web-incoming.ts index f02a1bc..bc8f8ea 100644 --- a/src/middleware/web-incoming.ts +++ b/src/middleware/web-incoming.ts @@ -134,7 +134,7 @@ export const stream = defineProxyMiddleware((req, res, options, server, head, ca function createErrorHandler(proxyReq: ClientRequest, url: URL | ProxyTargetDetailed) { return function proxyError(err: Error) { - if (req.socket.destroyed && (err as NodeJS.ErrnoException).code === "ECONNRESET") { + if (req.socket?.destroyed && (err as NodeJS.ErrnoException).code === "ECONNRESET") { server.emit("econnreset", err, req, res, url); return proxyReq.abort(); } diff --git a/test/middleware/web-incoming.test.ts b/test/middleware/web-incoming.test.ts index 2260ce3..5b61378 100644 --- a/test/middleware/web-incoming.test.ts +++ b/test/middleware/web-incoming.test.ts @@ -630,6 +630,46 @@ describe("#client abort propagation", () => { }); }); +// Regression: upstream http-party/node-http-proxy#1634 +// When req.socket is undefined, the error handler in stream middleware +// would throw TypeError: Cannot read properties of undefined (reading 'destroyed') +describe("#req.socket undefined", () => { + it("should not crash when req.socket is undefined and error occurs", async () => { + const { resolve, promise } = Promise.withResolvers(); + + // Use a port that refuses connections to trigger the error handler + const proxy = httpProxy.createProxyServer({ + target: "http://127.0.0.1:1", + }); + + const proxyServer = http.createServer((req, res) => { + // Set req.socket to undefined after proxyReq is emitted but before + // the error handler fires (simulates HTTP/2 or edge-case teardown) + proxy.once("proxyReq", () => { + Object.defineProperty(req, "socket", { + value: undefined, + writable: true, + configurable: true, + }); + }); + proxy.web(req, res); + }); + const proxyPort = await listenOn(proxyServer); + + proxy.once("error", () => { + // Should reach here without TypeError crash + proxyServer.close(); + resolve(); + }); + + const req = http.request(`http://127.0.0.1:${proxyPort}`, () => {}); + req.on("error", () => {}); // Ignore client-side errors + req.end(); + + await promise; + }); +}); + describe("#followRedirects", () => { it("should follow 301 redirect", async () => { const { resolve, promise } = Promise.withResolvers();