From a6980d5108e589cc712d50ece7d664f18667f663 Mon Sep 17 00:00:00 2001 From: Sander Declerck Date: Thu, 2 Oct 2025 09:06:35 +0200 Subject: [PATCH] Add upstream proxy support --- package-lock.json | 1 + packages/safe-chain/package.json | 1 + .../src/registryProxy/mitmRequestHandler.js | 6 ++ .../src/registryProxy/tunnelRequestHandler.js | 68 +++++++++++++++++++ 4 files changed, 76 insertions(+) diff --git a/package-lock.json b/package-lock.json index f111431..6b74d53 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4886,6 +4886,7 @@ "dependencies": { "abbrev": "3.0.1", "chalk": "5.4.1", + "https-proxy-agent": "7.0.6", "make-fetch-happen": "14.0.3", "node-forge": "1.3.1", "npm-registry-fetch": "18.0.2", diff --git a/packages/safe-chain/package.json b/packages/safe-chain/package.json index c74d44e..d28fe73 100644 --- a/packages/safe-chain/package.json +++ b/packages/safe-chain/package.json @@ -30,6 +30,7 @@ "dependencies": { "abbrev": "3.0.1", "chalk": "5.4.1", + "https-proxy-agent": "7.0.6", "make-fetch-happen": "14.0.3", "node-forge": "1.3.1", "npm-registry-fetch": "18.0.2", diff --git a/packages/safe-chain/src/registryProxy/mitmRequestHandler.js b/packages/safe-chain/src/registryProxy/mitmRequestHandler.js index 750815e..4be9987 100644 --- a/packages/safe-chain/src/registryProxy/mitmRequestHandler.js +++ b/packages/safe-chain/src/registryProxy/mitmRequestHandler.js @@ -1,5 +1,6 @@ import https from "https"; import { generateCertForHost } from "./certUtils.js"; +import { HttpsProxyAgent } from "https-proxy-agent"; export function mitmConnect(req, clientSocket, isAllowed) { const { hostname } = new URL(`http://${req.url}`); @@ -75,6 +76,11 @@ function createProxyRequest(hostname, req, res) { delete options.headers.host; + const httpsProxy = process.env.HTTPS_PROXY || process.env.https_proxy; + if (httpsProxy) { + options.agent = new HttpsProxyAgent(httpsProxy); + } + const proxyReq = https.request(options, (proxyRes) => { res.writeHead(proxyRes.statusCode, proxyRes.headers); proxyRes.pipe(res); diff --git a/packages/safe-chain/src/registryProxy/tunnelRequestHandler.js b/packages/safe-chain/src/registryProxy/tunnelRequestHandler.js index 8579729..1609664 100644 --- a/packages/safe-chain/src/registryProxy/tunnelRequestHandler.js +++ b/packages/safe-chain/src/registryProxy/tunnelRequestHandler.js @@ -2,6 +2,16 @@ import * as net from "net"; import { ui } from "../environment/userInteraction.js"; export function tunnelRequest(req, clientSocket, head) { + const httpsProxy = process.env.HTTPS_PROXY || process.env.https_proxy; + + if (httpsProxy) { + tunnelRequestViaProxy(req, clientSocket, head, httpsProxy); + } else { + tunnelRequestToDestination(req, clientSocket, head); + } +} + +function tunnelRequestToDestination(req, clientSocket, head) { const { port, hostname } = new URL(`http://${req.url}`); const serverSocket = net.connect(port || 443, hostname, () => { @@ -18,3 +28,61 @@ export function tunnelRequest(req, clientSocket, head) { clientSocket.end("HTTP/1.1 502 Bad Gateway\r\n\r\n"); }); } + +function tunnelRequestViaProxy(req, clientSocket, head, proxyUrl) { + const { port, hostname } = new URL(`http://${req.url}`); + const proxy = new URL(proxyUrl); + + // Connect to proxy server + const proxySocket = net.connect({ + host: proxy.hostname, + port: proxy.port, + }); + + proxySocket.on("connect", () => { + // Send CONNECT request to proxy + const connectRequest = [ + `CONNECT ${hostname}:${port || 443} HTTP/1.1`, + `Host: ${hostname}:${port || 443}`, + "", + "", + ].join("\r\n"); + + proxySocket.write(connectRequest); + }); + + let isConnected = false; + proxySocket.once("data", (data) => { + const response = data.toString(); + + // Check if CONNECT succeeded (HTTP/1.1 200) + if (response.startsWith("HTTP/1.1 200")) { + isConnected = true; + clientSocket.write("HTTP/1.1 200 Connection Established\r\n\r\n"); + proxySocket.write(head); + proxySocket.pipe(clientSocket); + clientSocket.pipe(proxySocket); + } else { + ui.writeError( + `Safe-chain: proxy CONNECT failed: ${response.split("\r\n")[0]}` + ); + clientSocket.end("HTTP/1.1 502 Bad Gateway\r\n\r\n"); + proxySocket.end(); + } + }); + + proxySocket.on("error", (err) => { + if (!isConnected) { + ui.writeError( + `Safe-chain: error connecting to proxy ${proxy.hostname}:${ + proxy.port || 8080 + } - ${err.message}` + ); + clientSocket.end("HTTP/1.1 502 Bad Gateway\r\n\r\n"); + } + }); + + clientSocket.on("error", () => { + proxySocket.end(); + }); +}