diff --git a/.changeset/swift-lizards-poke.md b/.changeset/swift-lizards-poke.md new file mode 100644 index 00000000..01410f02 --- /dev/null +++ b/.changeset/swift-lizards-poke.md @@ -0,0 +1,5 @@ +--- +'pac-proxy-agent': patch +--- + +Properly forward WebSocket requests via PAC agents that resolve to HTTP proxies diff --git a/packages/pac-proxy-agent/src/index.ts b/packages/pac-proxy-agent/src/index.ts index 57aa06d5..585249ff 100644 --- a/packages/pac-proxy-agent/src/index.ts +++ b/packages/pac-proxy-agent/src/index.ts @@ -180,6 +180,7 @@ export class PacProxyAgent extends Agent { opts: AgentConnectOpts ): Promise { const { secureEndpoint } = opts; + const isWebSocket = req.getHeader('upgrade') === 'websocket'; // First, get a generated `FindProxyForURL()` function, // either cached or retrieved from the source @@ -261,7 +262,7 @@ export class PacProxyAgent extends Agent { const proxyURL = `${ type === 'HTTPS' ? 'https' : 'http' }://${target}`; - if (secureEndpoint) { + if (secureEndpoint || isWebSocket) { agent = new HttpsProxyAgent(proxyURL, this.opts); } else { agent = new HttpProxyAgent(proxyURL, this.opts); diff --git a/packages/pac-proxy-agent/test/test.ts b/packages/pac-proxy-agent/test/test.ts index e3f2e5e5..3455fa40 100644 --- a/packages/pac-proxy-agent/test/test.ts +++ b/packages/pac-proxy-agent/test/test.ts @@ -174,6 +174,27 @@ describe('PacProxyAgent', () => { assert('via' in data); }); + it('should work over an HTTP proxy for WebSockets', async () => { + httpServer.once('request', function (req, res) { + res.end(JSON.stringify(req.headers)); + }); + + function FindProxyForURL() { + return 'PROXY localhost:PORT;'; + } + + const uri = `data:,${encodeURIComponent( + FindProxyForURL.toString().replace('PORT', proxyServerUrl.port) + )}`; + const agent = new PacProxyAgent(uri); + + const res = await req(new URL('/test', httpServerUrl), { agent, headers: { upgrade: 'websocket' } }); + const data = await json(res); + assert.equal(httpServerUrl.host, data.host); + assert(!('via' in data)); // Used CONNECT rather than plain HTTP proxy + assert.equal(data.upgrade, 'websocket'); + }); + it('should work over a SOCKS proxy', async () => { httpServer.once('request', function (req, res) { res.end(JSON.stringify(req.headers));