Add tests and clarifying comments

This commit is contained in:
Sander Declerck 2025-12-05 12:09:19 +01:00
parent 22b93e91f6
commit 57a0e88fa4
No known key found for this signature in database
4 changed files with 188 additions and 32 deletions

View file

@ -1,5 +1,6 @@
import * as net from "net";
import { ui } from "../environment/userInteraction.js";
import { isImdsEndpoint } from "./isImdsEndpoint.js";
/** @type {string[]} */
let timedoutEndpoints = [];
@ -40,12 +41,19 @@ export function tunnelRequest(req, clientSocket, head) {
*/
function tunnelRequestToDestination(req, clientSocket, head) {
const { port, hostname } = new URL(`http://${req.url}`);
const isImds = isImdsEndpoint(hostname);
if (timedoutEndpoints.includes(hostname)) {
clientSocket.end("HTTP/1.1 502 Bad Gateway\r\n\r\n");
ui.writeError(
`Safe-chain: Closing connection because previously timedout connect to ${hostname}`
);
if (isImds) {
ui.writeVerbose(
`Safe-chain: Closing connection because previously timedout connect to ${hostname}`
);
} else {
ui.writeError(
`Safe-chain: Closing connection because previously timedout connect to ${hostname}`
);
}
return;
}
@ -60,13 +68,24 @@ function tunnelRequestToDestination(req, clientSocket, head) {
}
);
// Set explicit connection timeout to avoid waiting for OS default (~2 minutes).
// IMDS endpoints get shorter timeout (3s) since they're commonly unreachable outside cloud environments.
const connectTimeout = getConnectTimeout(hostname);
serverSocket.setTimeout(connectTimeout);
serverSocket.on("timeout", () => {
timedoutEndpoints.push(hostname);
ui.writeError(
`Safe-chain: connect to ${hostname}:${port} timed out after ${connectTimeout}ms`
);
// Suppress error logging for IMDS endpoints - timeouts are expected when not in cloud
if (isImdsEndpoint(hostname)) {
ui.writeVerbose(
`Safe-chain: connect to ${hostname}:${port || 443} timed out after ${connectTimeout}ms`
);
} else {
ui.writeError(
`Safe-chain: connect to ${hostname}:${port || 443} timed out after ${connectTimeout}ms`
);
}
serverSocket.destroy(); // Clean up socket to prevent event loop hanging
clientSocket.end("HTTP/1.1 502 Bad Gateway\r\n\r\n");
});
@ -79,9 +98,15 @@ function tunnelRequestToDestination(req, clientSocket, head) {
});
serverSocket.on("error", (err) => {
ui.writeError(
`Safe-chain: error connecting to ${hostname}:${port} - ${err.message}`
);
if (isImdsEndpoint(hostname)) {
ui.writeVerbose(
`Safe-chain: error connecting to ${hostname}:${port} - ${err.message}`
);
} else {
ui.writeError(
`Safe-chain: error connecting to ${hostname}:${port} - ${err.message}`
);
}
if (clientSocket.writable) {
clientSocket.end("HTTP/1.1 502 Bad Gateway\r\n\r\n");
}
@ -167,13 +192,13 @@ function tunnelRequestViaProxy(req, clientSocket, head, proxyUrl) {
});
}
const imdsEndpoints = [
"metadata.google.internal",
"metadata.goog",
"169.254.169.254",
];
/**
* Returns appropriate connection timeout for a host.
* - IMDS endpoints: 3s (fail fast when outside cloud, reduce 5min delay to ~20s)
* - Other endpoints: 30s (allow for slow networks while preventing indefinite hangs)
*/
function getConnectTimeout(/** @type {string} */ host) {
if (imdsEndpoints.includes(host)) {
if (isImdsEndpoint(host)) {
return 3000;
}
return 30000;