diff --git a/packages/safe-chain/src/main.js b/packages/safe-chain/src/main.js index e106e83..c3af410 100644 --- a/packages/safe-chain/src/main.js +++ b/packages/safe-chain/src/main.js @@ -11,6 +11,21 @@ export async function main(args) { const proxy = createSafeChainProxy(); await proxy.startServer(); + // Global error handlers to log unhandled errors + process.on("uncaughtException", (error) => { + ui.writeError(`Safe-chain: Uncaught exception: ${error.message}`); + ui.writeVerbose(`Stack trace: ${error.stack}`); + process.exit(1); + }); + + process.on("unhandledRejection", (reason) => { + ui.writeError(`Safe-chain: Unhandled promise rejection: ${reason}`); + if (reason instanceof Error) { + ui.writeVerbose(`Stack trace: ${reason.stack}`); + } + process.exit(1); + }); + try { // This parses all the --safe-chain arguments and removes them from the args array args = initializeCliArguments(args); diff --git a/packages/safe-chain/src/registryProxy/mitmRequestHandler.js b/packages/safe-chain/src/registryProxy/mitmRequestHandler.js index 63a8168..101fdd8 100644 --- a/packages/safe-chain/src/registryProxy/mitmRequestHandler.js +++ b/packages/safe-chain/src/registryProxy/mitmRequestHandler.js @@ -1,6 +1,7 @@ import https from "https"; import { generateCertForHost } from "./certUtils.js"; import { HttpsProxyAgent } from "https-proxy-agent"; +import { ui } from "../environment/userInteraction.js"; export function mitmConnect(req, clientSocket, isAllowed) { const { hostname } = new URL(`http://${req.url}`); @@ -37,13 +38,25 @@ function createHttpsServer(hostname, isAllowed) { forwardRequest(req, hostname, res); } - return https.createServer( + const server = https.createServer( { key: cert.privateKey, cert: cert.certificate, }, handleRequest ); + + server.on("error", (err) => { + ui.writeError(`Safe-chain: HTTPS server error: ${err.message}`); + if (!res.headersSent) { + res.writeHead(502); + res.end("Bad Gateway"); + } else if (res.writable) { + res.destroy(); + } + }); + + return server; } function getRequestPathAndQuery(url) { @@ -62,6 +75,11 @@ function forwardRequest(req, hostname, res) { res.end("Bad Gateway"); }); + req.on("error", (err) => { + ui.writeError(`Safe-chain: Error reading client request: ${err.message}`); + proxyReq.destroy(); + }); + req.on("data", (chunk) => { proxyReq.write(chunk); }); @@ -88,6 +106,16 @@ function createProxyRequest(hostname, req, res) { } const proxyReq = https.request(options, (proxyRes) => { + proxyRes.on("error", (err) => { + ui.writeError( + `Safe-chain: Error reading upstream response: ${err.message}` + ); + if (!res.headersSent) { + res.writeHead(502); + res.end("Bad Gateway"); + } + }); + res.writeHead(proxyRes.statusCode, proxyRes.headers); proxyRes.pipe(res); }); diff --git a/packages/safe-chain/src/registryProxy/plainHttpProxy.js b/packages/safe-chain/src/registryProxy/plainHttpProxy.js index e337b44..ac3dc69 100644 --- a/packages/safe-chain/src/registryProxy/plainHttpProxy.js +++ b/packages/safe-chain/src/registryProxy/plainHttpProxy.js @@ -43,8 +43,13 @@ export function handleHttpProxyRequest(req, res) { } ) .on("error", (err) => { - res.writeHead(502); - res.end(`Bad Gateway: ${err.message}`); + if (!res.headersSent) { + res.writeHead(502); + res.end(`Bad Gateway: ${err.message}`); + } else { + // Headers already sent, just destroy the response + res.destroy(); + } }); req.on("error", () => { diff --git a/packages/safe-chain/src/registryProxy/tunnelRequestHandler.js b/packages/safe-chain/src/registryProxy/tunnelRequestHandler.js index c28a022..5c764f5 100644 --- a/packages/safe-chain/src/registryProxy/tunnelRequestHandler.js +++ b/packages/safe-chain/src/registryProxy/tunnelRequestHandler.js @@ -24,12 +24,6 @@ export function tunnelRequest(req, clientSocket, head) { function tunnelRequestToDestination(req, clientSocket, head) { const { port, hostname } = new URL(`http://${req.url}`); - clientSocket.on("error", () => { - // NO-OP - // This can happen if the client TCP socket sends RST instead of FIN. - // Not subscribing to 'close' event will cause node to throw and crash. - }); - const serverSocket = net.connect(port || 443, hostname, () => { clientSocket.write("HTTP/1.1 200 Connection Established\r\n\r\n"); serverSocket.write(head); @@ -37,6 +31,14 @@ function tunnelRequestToDestination(req, clientSocket, head) { clientSocket.pipe(serverSocket); }); + clientSocket.on("error", () => { + // This can happen if the client TCP socket sends RST instead of FIN. + // Not subscribing to 'error' event will cause node to throw and crash. + if (serverSocket.writable) { + serverSocket.end(); + } + }); + serverSocket.on("error", (err) => { ui.writeError( `Safe-chain: error connecting to ${hostname}:${port} - ${err.message}` @@ -103,6 +105,13 @@ function tunnelRequestViaProxy(req, clientSocket, head, proxyUrl) { if (clientSocket.writable) { clientSocket.end("HTTP/1.1 502 Bad Gateway\r\n\r\n"); } + } else { + ui.writeError( + `Safe-chain: proxy socket error after connection - ${err.message}` + ); + if (clientSocket.writable) { + clientSocket.end(); + } } });