From fe5ea15a78e672ef4bc7e40094ac19086be89e51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ramon=20R=C3=BCttimann?= Date: Fri, 14 Oct 2022 13:09:45 +0200 Subject: [PATCH] fix: do not proxy traffic to sockets This commit implements a proxy-exception for traffic targeting local sockets instead of a "host:port" combination. If such traffic would be forwarded through the proxy, the socket destination info would be lost, while a "localhost:80" destination would be added. This means that the proxy cannot handle such requests correctly either way, which makes sending traffic to the proxy pointless. Because of that, if the agent receives traffic destined for a socket, it will simply use the fallback agent to handle that traffic. --- src/classes/Agent.ts | 13 +++++ .../factories/createGlobalProxyAgent.ts | 48 +++++++++++++++---- 2 files changed, 51 insertions(+), 10 deletions(-) diff --git a/src/classes/Agent.ts b/src/classes/Agent.ts index 9d2d5aea..07e45a8a 100644 --- a/src/classes/Agent.ts +++ b/src/classes/Agent.ts @@ -80,6 +80,19 @@ abstract class Agent { requestUrl = this.protocol + '//' + (configuration.hostname ?? configuration.host) + (configuration.port === 80 ?? configuration.port === 443 ? '' : ':' + configuration.port) + request.path; } + // If a request should go to a local socket, proxying it through an HTTP + // server does not make sense as the information about the target socket + // will be lost and the proxy won't be able to correctly handle the request. + if (configuration.socketPath) { + log.trace({ + destination: configuration.socketPath, + }, "not proxying request; destination is a socket"); + + // @ts-expect-error seems like we are using wrong type for fallbackAgent. + this.fallbackAgent.addRequest(request, configuration); + return; + } + if (!this.isProxyConfigured()) { log.trace({ destination: requestUrl, diff --git a/test/global-agent/factories/createGlobalProxyAgent.ts b/test/global-agent/factories/createGlobalProxyAgent.ts index 15c3e40b..07160521 100644 --- a/test/global-agent/factories/createGlobalProxyAgent.ts +++ b/test/global-agent/factories/createGlobalProxyAgent.ts @@ -270,44 +270,72 @@ serial('proxies HTTPS request with dedicated proxy', async (t) => { globalProxyAgent.HTTPS_PROXY = proxyServer.url; const response: HttpResponseType = await new Promise((resolve) => { - https.get('https://127.0.0.1', {}, createHttpResponseResolver(resolve)); + https.get("https://127.0.0.1", {}, createHttpResponseResolver(resolve)); }); - t.is(response.body, 'OK'); + t.is(response.body, "OK"); }); -serial('ignores dedicated HTTPS proxy for HTTP urls', async (t) => { +serial("ignores dedicated HTTPS proxy for HTTP urls", async (t) => { const globalProxyAgent = createGlobalProxyAgent(); const proxyServer = await createProxyServer(); globalProxyAgent.HTTP_PROXY = proxyServer.url; - globalProxyAgent.HTTPS_PROXY = 'http://example.org'; + globalProxyAgent.HTTPS_PROXY = "http://example.org"; const response: HttpResponseType = await new Promise((resolve) => { - http.get('http://127.0.0.1', {}, createHttpResponseResolver(resolve)); + http.get("http://127.0.0.1", {}, createHttpResponseResolver(resolve)); }); - t.is(response.body, 'OK'); + t.is(response.body, "OK"); }); -serial('forwards requests matching NO_PROXY', async (t) => { +serial("forwards requests matching NO_PROXY", async (t) => { const globalProxyAgent = createGlobalProxyAgent(); const proxyServer = await createProxyServer(); const httpServer = await createHttpServer(); globalProxyAgent.HTTP_PROXY = proxyServer.url; - globalProxyAgent.NO_PROXY = '127.0.0.1'; + globalProxyAgent.NO_PROXY = "127.0.0.1"; const response: HttpResponseType = await new Promise((resolve) => { http.get(httpServer.url, createHttpResponseResolver(resolve)); }); - t.is(response.body, 'DIRECT'); + t.is(response.body, "DIRECT"); +}); + +serial("forwards requests that go to a socket", async (t) => { + const globalProxyAgent = createGlobalProxyAgent(); + + // not relevant as traffic shouldn't go through proxy + globalProxyAgent.HTTP_PROXY = "localhost:10324"; + + var server = http.createServer(function (req, res) { + res.writeHead(200); + res.write("OK"); + res.end(); + }); + + t.teardown(() => { + server.close(); + }); + server.listen("/tmp/test.sock"); + + const response: HttpResponseType = await new Promise((resolve) => { + http.get({ + socketPath: "/tmp/test.sock", + path: "/endpoint", + method: "get", + }, createHttpResponseResolver(resolve) + ); + }); + t.is(response.body, "OK"); }); -serial('proxies HTTP request (using http.get(host))', async (t) => { +serial("proxies HTTP request (using http.get(host))", async (t) => { const globalProxyAgent = createGlobalProxyAgent(); const proxyServer = await createProxyServer();