@socketsecurity/lib
Version:
Core utilities and infrastructure for Socket.dev security tools
338 lines (337 loc) • 10.4 kB
JavaScript
;
/* Socket Lib - Built with esbuild */
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var http_request_exports = {};
__export(http_request_exports, {
httpDownload: () => httpDownload,
httpGetJson: () => httpGetJson,
httpGetText: () => httpGetText,
httpRequest: () => httpRequest
});
module.exports = __toCommonJS(http_request_exports);
var import_fs = require("fs");
let _http;
let _https;
// @__NO_SIDE_EFFECTS__
function getHttp() {
if (_http === void 0) {
_http = require("node:http");
}
return _http;
}
// @__NO_SIDE_EFFECTS__
function getHttps() {
if (_https === void 0) {
_https = require("node:https");
}
return _https;
}
async function httpRequest(url, options) {
const {
body,
followRedirects = true,
headers = {},
maxRedirects = 5,
method = "GET",
retries = 0,
retryDelay = 1e3,
timeout = 3e4
} = { __proto__: null, ...options };
let lastError;
for (let attempt = 0; attempt <= retries; attempt++) {
try {
return await httpRequestAttempt(url, {
body,
followRedirects,
headers,
maxRedirects,
method,
timeout
});
} catch (e) {
lastError = e;
if (attempt === retries) {
break;
}
const delayMs = retryDelay * 2 ** attempt;
await new Promise((resolve) => setTimeout(resolve, delayMs));
}
}
throw lastError || new Error("Request failed after retries");
}
async function httpRequestAttempt(url, options) {
const {
body,
followRedirects = true,
headers = {},
maxRedirects = 5,
method = "GET",
timeout = 3e4
} = { __proto__: null, ...options };
return await new Promise((resolve, reject) => {
const parsedUrl = new URL(url);
const isHttps = parsedUrl.protocol === "https:";
const httpModule = isHttps ? /* @__PURE__ */ getHttps() : /* @__PURE__ */ getHttp();
const requestOptions = {
headers: {
"User-Agent": "socket-registry/1.0",
...headers
},
hostname: parsedUrl.hostname,
method,
path: parsedUrl.pathname + parsedUrl.search,
port: parsedUrl.port,
timeout
};
const request = httpModule.request(
requestOptions,
(res) => {
if (followRedirects && res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
if (maxRedirects <= 0) {
reject(
new Error(
`Too many redirects (exceeded maximum: ${maxRedirects})`
)
);
return;
}
const redirectUrl = res.headers.location.startsWith("http") ? res.headers.location : new URL(res.headers.location, url).toString();
resolve(
httpRequestAttempt(redirectUrl, {
body,
followRedirects,
headers,
maxRedirects: maxRedirects - 1,
method,
timeout
})
);
return;
}
const chunks = [];
res.on("data", (chunk) => {
chunks.push(chunk);
});
res.on("end", () => {
const responseBody = Buffer.concat(chunks);
const ok = res.statusCode !== void 0 && res.statusCode >= 200 && res.statusCode < 300;
const response = {
arrayBuffer() {
return responseBody.buffer.slice(
responseBody.byteOffset,
responseBody.byteOffset + responseBody.byteLength
);
},
body: responseBody,
headers: res.headers,
json() {
return JSON.parse(responseBody.toString("utf8"));
},
ok,
status: res.statusCode || 0,
statusText: res.statusMessage || "",
text() {
return responseBody.toString("utf8");
}
};
resolve(response);
});
}
);
request.on("error", (error) => {
const code = error.code;
let message = `HTTP request failed for ${url}: ${error.message}
`;
if (code === "ENOTFOUND") {
message += "DNS lookup failed. Check the hostname and your network connection.";
} else if (code === "ECONNREFUSED") {
message += "Connection refused. Verify the server is running and accessible.";
} else if (code === "ETIMEDOUT") {
message += "Request timed out. Check your network or increase the timeout value.";
} else if (code === "ECONNRESET") {
message += "Connection reset. The server may have closed the connection unexpectedly.";
} else {
message += "Check your network connection and verify the URL is correct.";
}
reject(new Error(message, { cause: error }));
});
request.on("timeout", () => {
request.destroy();
reject(new Error(`Request timed out after ${timeout}ms`));
});
if (body) {
request.write(body);
}
request.end();
});
}
async function httpDownload(url, destPath, options) {
const {
headers = {},
onProgress,
retries = 0,
retryDelay = 1e3,
timeout = 12e4
} = { __proto__: null, ...options };
let lastError;
for (let attempt = 0; attempt <= retries; attempt++) {
try {
return await httpDownloadAttempt(url, destPath, {
headers,
onProgress,
timeout
});
} catch (e) {
lastError = e;
if (attempt === retries) {
break;
}
const delayMs = retryDelay * 2 ** attempt;
await new Promise((resolve) => setTimeout(resolve, delayMs));
}
}
throw lastError || new Error("Download failed after retries");
}
async function httpDownloadAttempt(url, destPath, options) {
const {
headers = {},
onProgress,
timeout = 12e4
} = { __proto__: null, ...options };
return await new Promise((resolve, reject) => {
const parsedUrl = new URL(url);
const isHttps = parsedUrl.protocol === "https:";
const httpModule = isHttps ? /* @__PURE__ */ getHttps() : /* @__PURE__ */ getHttp();
const requestOptions = {
headers: {
"User-Agent": "socket-registry/1.0",
...headers
},
hostname: parsedUrl.hostname,
method: "GET",
path: parsedUrl.pathname + parsedUrl.search,
port: parsedUrl.port,
timeout
};
let fileStream;
let streamClosed = false;
const closeStream = () => {
if (!streamClosed && fileStream) {
streamClosed = true;
fileStream.close();
}
};
const request = httpModule.request(
requestOptions,
(res) => {
if (!res.statusCode || res.statusCode < 200 || res.statusCode >= 300) {
closeStream();
reject(
new Error(
`Download failed: HTTP ${res.statusCode} ${res.statusMessage}`
)
);
return;
}
const totalSize = Number.parseInt(
res.headers["content-length"] || "0",
10
);
let downloadedSize = 0;
fileStream = (0, import_fs.createWriteStream)(destPath);
fileStream.on("error", (error) => {
closeStream();
const err = new Error(`Failed to write file: ${error.message}`, {
cause: error
});
reject(err);
});
res.on("data", (chunk) => {
downloadedSize += chunk.length;
if (onProgress && totalSize > 0) {
onProgress(downloadedSize, totalSize);
}
});
res.on("end", () => {
fileStream?.close(() => {
streamClosed = true;
resolve({
path: destPath,
size: downloadedSize
});
});
});
res.on("error", (error) => {
closeStream();
reject(error);
});
res.pipe(fileStream);
}
);
request.on("error", (error) => {
closeStream();
const code = error.code;
let message = `HTTP download failed for ${url}: ${error.message}
`;
if (code === "ENOTFOUND") {
message += "DNS lookup failed. Check the hostname and your network connection.";
} else if (code === "ECONNREFUSED") {
message += "Connection refused. Verify the server is running and accessible.";
} else if (code === "ETIMEDOUT") {
message += "Request timed out. Check your network or increase the timeout value.";
} else if (code === "ECONNRESET") {
message += "Connection reset. The server may have closed the connection unexpectedly.";
} else {
message += "Check your network connection and verify the URL is correct.";
}
reject(new Error(message, { cause: error }));
});
request.on("timeout", () => {
request.destroy();
closeStream();
reject(new Error(`Download timed out after ${timeout}ms`));
});
request.end();
});
}
async function httpGetJson(url, options) {
const response = await httpRequest(url, { ...options, method: "GET" });
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
try {
return response.json();
} catch (e) {
throw new Error("Failed to parse JSON response", { cause: e });
}
}
async function httpGetText(url, options) {
const response = await httpRequest(url, { ...options, method: "GET" });
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return response.text();
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
httpDownload,
httpGetJson,
httpGetText,
httpRequest
});