@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
JavaScript
;
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;