UNPKG

@network-utils/tcp-ping

Version:

A simple TCP ping util, written in Typescript, to test the reachability and latency of a host.

153 lines (152 loc) 6.29 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.probe = exports.ping = void 0; const net_1 = require("net"); /** * Attempts to connect to the given host and returns the `IConnectionResult` * @param options The `IPingOptions` to use for this connection */ function connect({ address, port, timeout }) { return new Promise((resolve) => { // Create a new tcp scoket const socket = new net_1.Socket(); // Save the current time so we can calculate latency const startTime = process.hrtime(); // Connect to the given host socket.connect(port, address, () => { // Calculate the latency of the connection const [seconds, nanoseconds] = process.hrtime(startTime); // Convert the latency from nanoseconds to milliseconds // so that the output is easier to work with const timeToConnect = (seconds * 1e9 + nanoseconds) / 1e6; // We don't need the socket anymore // so we should destroy it socket.destroy(); // Resolve with the latency of this attempt resolve({ time: timeToConnect }); }); // Make sure we catch any errors thrown by the socket socket.on('error', (error) => { // We don't need the socket anymore // so we should destroy it socket.destroy(); // Resolve with the error resolve({ error }); }); // Set the timeout for the connection socket.setTimeout(timeout, () => { // We don't need the socket anymore // so we should destroy it socket.destroy(); // Resolve with a timeout error resolve({ error: Error('Request timeout') }); }); }); } /** * Pings the given device and report the statistics * in the form of an `IPingResult` object * @param options The `IPingOptions` object */ function ping(options, progress) { return __awaiter(this, void 0, void 0, function* () { // Default ping options const opts = Object.assign({ address: '127.0.0.1', attempts: 10, port: 80, timeout: 3000 }, options); // Removed to allow pinging hostnames // Make sure this is a real IP address // if (!isIP(opts.address)) throw Error('Invalid IP') if (opts.port < 1) throw RangeError('Negative port'); /** * An array of all the connection attempts */ const connectionResults = []; // Try to connect to the given host for (let i = 0; i < opts.attempts; i++) { connectionResults.push({ // i + 1 so the first attempt is `attempt 1` // instead of `attempt 0` attemptNumber: i + 1, result: yield connect(opts), }); if (typeof progress === 'function') progress(i + 1, opts.attempts); } /** * The result of this ping */ const result = { averageLatency: NaN, errors: [], maximumLatency: 0, minimumLatency: Infinity, options: opts, }; /** * The sum of the latency of all * the successful ping attempts */ let latencySum = 0; // Loop over all the connection results for (const attempt of connectionResults) { // If `time` is undefined then // assume there's an error if (typeof attempt.result.time === 'undefined') { // Push the error onto the errors array result.errors.push({ attempt: attempt.attemptNumber, // If error is undefined then throw an unknown error error: attempt.result.error || Error('Unknown error'), }); // We're done with this iteration continue; } // Get the latency of this attempt const { time } = attempt.result; // Add it to the sum latencySum += time; // If this attempts latency is less // then the current `minimumLatency` then we // update `minimumLatency` if (time < result.minimumLatency) result.minimumLatency = time; // If this attempts latency is greater // then the current `maximumLatency` then we // update `maximumLatency` if (time > result.maximumLatency) result.maximumLatency = time; } // Calculate the average latency of all the attempts // (excluding the attempts that errored because those // didn't return a latency) result.averageLatency = latencySum / (connectionResults.length - result.errors.length); // Finally, resolve with the result return result; }); } exports.ping = ping; /** * Makes one attempt to reach the host and returns * a `boolean` indicating whether or not it was successful. * @param port The port to probe * @param address The address to probe * @param timeout The timeout of the probe */ function probe(port, address = '127.0.0.1', timeout = 3000) { return __awaiter(this, void 0, void 0, function* () { // Ping the host const result = yield ping({ address, port, timeout, attempts: 1 }); // If there aren't any error then the device is reachable return result.errors.length === 0; }); } exports.probe = probe;