UNPKG

cf-speedtest

Version:

CLI tool for testing internet speed using Cloudflare's network

181 lines (168 loc) 5.11 kB
// src/constants/index.ts var CLOUDFLARE_SPEEDTEST_HOSTNAME = "speed.cloudflare.com"; var FILE_SIZE = 1e8; // src/logger/logger.ts import chalk from "chalk"; var logServerLocation = (city, ip) => { console.log(chalk.bold.green("\u{1F310} Server Location:")); console.log(`${chalk.blue("City :")} ${chalk.green(city)}`); console.log(`${chalk.blue("Public IP:")} ${chalk.green(ip)}`); }; var logLatency = (stats) => { const { average, packetLoss } = stats; console.log( chalk.bold.cyan("\u{1F3D3} Latency:"), `${chalk.green(`${average.toFixed(2)}ms`)}` ); console.log( chalk.bold.red("\u{1F6AB} Packet Loss:"), `${chalk.red(`${packetLoss.toFixed(2)}%`)}` ); }; var logDownload = ({ average }) => { console.log( chalk.bold.yellow( "\u2B07\uFE0F Download:", `${chalk.green(average.toFixed(2), "Mbps")}` ) ); }; var logUpload = ({ average }) => { console.log( chalk.bold.magenta("\u2B06\uFE0F Upload:"), `${chalk.green(average.toFixed(2), "Mbps")}` ); }; // src/measurements/measure-download.ts import https from "https"; // src/utils/convert-to-mbps.ts var convertToMbps = (bytes, duration) => bytes * 8 / (duration / 1e3) / 1e6; // src/utils/measure-bandwidth.ts var measureBandwidth = async (handler, bytes) => { const start = performance.now(); const transferred = await handler(bytes); const duration = performance.now() - start; const average = convertToMbps(transferred, duration); return { average }; }; // src/measurements/measure-transfer.ts var measureTransfer = async (handler, bytes, logger) => { const stats = await measureBandwidth(handler, bytes); logger(stats); return stats; }; // src/measurements/measure-download.ts var download = (bytes) => new Promise((resolve, reject) => { const options = { hostname: CLOUDFLARE_SPEEDTEST_HOSTNAME, path: `/__down?bytes=${bytes}`, method: "GET" }; const req = https.request(options, (res) => { if (res.statusCode !== 200) { reject(new Error(`Download failed with status ${res.statusCode}`)); return; } let total = 0; res.on("data", (chunk) => { total += chunk.length; }); res.on("end", () => { resolve(total); }); }); req.on("error", (error) => reject(new Error(error.message))); req.end(); }); var measureDownload = (bytes) => measureTransfer(download, bytes, logDownload); // src/measurements/measure-latency.ts var measureLatency = async (runs = 20) => { const url = "https://speed.cloudflare.com/cdn-cgi/trace"; const measurements = []; for (let i = 0; i < runs; i++) { try { const start = performance.now(); await fetch(url, { method: "HEAD" }); const duration = performance.now() - start; measurements.push(duration); } catch (error) { console.error("Error measuring latency:", error); } } const latencies = measurements; const sum = latencies.reduce((acc, latency) => acc + latency, 0); const average = sum / latencies.length; const diffs = latencies.slice(1).map((v, i) => Math.abs(v - (latencies[i] ?? 0))); const jitter = diffs.length ? diffs.reduce((acc, diff) => acc + diff, 0) / diffs.length : 0; const lost = runs - measurements.length; const packetLoss = lost / runs * 100; return { average, jitter, packetLoss, measurements: latencies }; }; // src/measurements/measure-upload.ts import { Buffer } from "buffer"; var measureUpload = (bytes) => measureTransfer( async (bytes2) => { const body = Buffer.allocUnsafe(bytes2); const res = await fetch( `https://${CLOUDFLARE_SPEEDTEST_HOSTNAME}/__up?bytes=${bytes2}`, { method: "POST", body } ); if (!res.ok) { throw new Error(`Upload failed with status ${res.status}`); } return bytes2; }, bytes, logUpload ); // src/utils/connection-metadata.ts var parseToJson = (text) => { const lines = text.split("\n"); const result = {}; for (const line of lines) { const [key, value] = line.split("="); if (key && value) { result[key] = value; } } return result; }; var connectionMetadata = async () => { const res = await fetch( `https://${CLOUDFLARE_SPEEDTEST_HOSTNAME}/cdn-cgi/trace` ).then((res2) => res2.text()); return parseToJson(res); }; // src/utils/get-city.ts var getCity = async (iata) => { const res = await fetch(`https://${CLOUDFLARE_SPEEDTEST_HOSTNAME}/locations`); if (!res.ok) { throw new Error(res.statusText); } const serverLocation = await res.json(); const matchedLocation = serverLocation.find( (location) => location.iata === iata ); if (matchedLocation) { return matchedLocation.city; } return "Unknown Location"; }; // src/index.ts var runCLI = async () => { const [latencyStats, metadata] = await Promise.all([ measureLatency(), connectionMetadata() ]); const { colo, ip } = metadata; const city = await getCity(colo); logServerLocation(city, ip); logLatency(latencyStats); await Promise.all([measureDownload(FILE_SIZE), measureUpload(FILE_SIZE)]); }; export { runCLI };