diff --git a/src/_utils.ts b/src/_utils.ts index 4e67d5e..5c4ea97 100644 --- a/src/_utils.ts +++ b/src/_utils.ts @@ -116,8 +116,9 @@ export function setupOutgoing( if (options.agent !== undefined) { outgoing.agent = options.agent || false; - } else if (req.httpVersionMajor > 1) { - // HTTP/2 incoming requests: keep-alive agents can conflict with stream lifecycle + } else if (req.httpVersionMajor > 1 || upgradeHeader.test(req.headers.connection || "")) { + // WebSocket upgrades and HTTP/2 incoming requests: agents conflict with + // the socket lifecycle (upgrade handoff / stream multiplexing). outgoing.agent = false; } else { // Use default keep-alive agents for connection reuse diff --git a/test/_utils.test.ts b/test/_utils.test.ts index 447e09d..9e1026f 100644 --- a/test/_utils.test.ts +++ b/test/_utils.test.ts @@ -196,6 +196,21 @@ describe("lib/http-proxy/common.js", () => { expect(outgoing.agent).to.eql(false); }); + it("should not use keep-alive agent for websocket upgrade requests", () => { + const outgoing = createOutgoing(); + common.setupOutgoing( + outgoing, + { target: "http://localhost" }, + stubIncomingMessage({ + url: "/", + headers: { connection: "Upgrade", upgrade: "websocket" }, + }), + ); + // WebSocket upgrades take ownership of the socket after 101, + // so a keep-alive agent must not be used. + expect(outgoing.agent).to.eql(false); + }); + it("set the port according to the protocol", () => { const outgoing = createOutgoing(); common.setupOutgoing(