fix(core): update

This commit is contained in:
Philipp Kunz 2021-04-28 13:41:55 +00:00
parent d8044507ed
commit 260f000304
9 changed files with 269 additions and 42 deletions

23
license Normal file
View File

@ -0,0 +1,23 @@
The MIT License (MIT)
Copyright (c) 2015 Lossless GmbH
Copyright (c) 2020 Tomás Arias
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

5
package-lock.json generated
View File

@ -11866,11 +11866,6 @@
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
"dev": true "dev": true
}, },
"speed-cloudflare-cli": {
"version": "2.0.3",
"resolved": "https://verdaccio.lossless.one/speed-cloudflare-cli/-/speed-cloudflare-cli-2.0.3.tgz",
"integrity": "sha512-aYnaj7ZhasoW+zhsVrO0tmGTxAfBY6eYFoIdoBJdJ0yZE1F/g11pt2AJqIuST0Qbe0x/pzwsEed267tCR6HuOA=="
},
"speedtest-net": { "speedtest-net": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://verdaccio.lossless.one/speedtest-net/-/speedtest-net-2.1.1.tgz", "resolved": "https://verdaccio.lossless.one/speedtest-net/-/speedtest-net-2.1.1.tgz",

View File

@ -24,7 +24,6 @@
"@types/default-gateway": "^3.0.1", "@types/default-gateway": "^3.0.1",
"isopen": "^1.3.0", "isopen": "^1.3.0",
"public-ip": "^4.0.3", "public-ip": "^4.0.3",
"speed-cloudflare-cli": "^2.0.3",
"speedtest-net": "^2.1.1", "speedtest-net": "^2.1.1",
"systeminformation": "^5.6.12" "systeminformation": "^5.6.12"
}, },
@ -43,4 +42,4 @@
"browserslist": [ "browserslist": [
"last 1 chrome versions" "last 1 chrome versions"
] ]
} }

View File

@ -31,13 +31,17 @@ const testSmartNetwork = new smartnetwork.SmartNetwork();
const run = async () => { const run = async () => {
// measure average speed over a period of 5 seconds // measure average speed over a period of 5 seconds
// the structure of speedResult is self explanatory using TypeScript (or the linked TypeDoc above) // the structure of speedResult is self explanatory using TypeScript (or the linked TypeDoc above)
const speedResult = testSmartNetwork.getSpeed(5000); const speedResult: smartnetwork.SpeedResult = testSmartNetwork.getSpeed(5000);
// // you can check for local ports before trying to bind to it from your nodejs program
const isLocalPortAvailable: boolean = await testSmartNetwork.isLocalPortAvailable(1234); const isLocalPortUnused: boolean = await testSmartNetwork.isLocalPortUnused(1234);
// you can run basic port checks on remote domains.
const isRemotePortAvailable: boolean = await testSmartNetwork.isRemotePortAvailable( const isRemotePortAvailable: boolean = await testSmartNetwork.isRemotePortAvailable(
'google.com:80' 'google.com:80'
); );
// just another way to call for the same thing as above
const isRemotePortAvailable: boolean = await testSmartNetwork.isRemotePortAvailable( const isRemotePortAvailable: boolean = await testSmartNetwork.isRemotePortAvailable(
'google.com', 'google.com',
80 80

View File

@ -10,8 +10,7 @@ tap.test('should create a valid instance of SmartNetwork', async () => {
tap.test('should perform a speedtest', async () => { tap.test('should perform a speedtest', async () => {
const result = await testSmartNetwork.getSpeed(); const result = await testSmartNetwork.getSpeed();
// console.log(`Download speed for this instance is ${result.download.bandwidth}`); console.log(result);
// console.log(`Upload speed for this instance is ${result.download.bandwidth}`);
}); });
tap.test('should determine wether a port is free', async () => { tap.test('should determine wether a port is free', async () => {

43
ts/helpers/stats.ts Normal file
View File

@ -0,0 +1,43 @@
export function average(values) {
let total = 0;
for (let i = 0; i < values.length; i += 1) {
total += values[i];
}
return total / values.length;
}
export function median(values) {
const half = Math.floor(values.length / 2);
values.sort((a, b) => a - b);
if (values.length % 2) return values[half];
return (values[half - 1] + values[half]) / 2;
}
export function quartile(values, percentile) {
values.sort((a, b) => a - b);
const pos = (values.length - 1) * percentile;
const base = Math.floor(pos);
const rest = pos - base;
if (values[base + 1] !== undefined) {
return values[base] + rest * (values[base + 1] - values[base]);
}
return values[base];
}
export function jitter(values) {
// Average distance between consecutive latency measurements...
let jitters = [];
for (let i = 0; i < values.length - 1; i += 1) {
jitters.push(Math.abs(values[i] - values[i + 1]));
}
return average(jitters);
}

View File

@ -0,0 +1,188 @@
import * as plugins from './smartnetwork.plugins';
import * as stats from './helpers/stats';
export class CloudflareSpeed {
constructor() {}
public async speedTest() {
const latency = await this.measureLatency();
const serverLocations = await this.fetchServerLocations();
const cgiData = await this.fetchCfCdnCgiTrace();
return {
...latency,
ip: cgiData.ip,
serverLocation: {
shortId: cgiData.colo,
name: serverLocations[cgiData.colo],
availableLocations: serverLocations,
}
};
}
public async measureLatency() {
const measurements: number[] = [];
for (let i = 0; i < 20; i += 1) {
await this.download(1000).then(
(response) => {
// TTFB - Server processing time
measurements.push(response[4] - response[0] - response[6]);
},
(error) => {
console.log(`Error: ${error}`);
}
);
}
return {
maxTime: Math.max(...measurements),
minTime: Math.min(...measurements),
averageTime: stats.average(measurements),
medianTime: stats.median(measurements),
jitter: stats.jitter(measurements),
}
;
}
public async fetchServerLocations(): Promise<{[key: string]: string}> {
const res = JSON.parse(await this.get('speed.cloudflare.com', '/locations'));
return res.reduce((data, { iata, city }) => {
// Bypass prettier "no-assign-param" rules
const data1 = data;
data1[iata] = city;
return data1;
}, {});
}
public async get(hostname: string, path: string): Promise<string> {
return new Promise((resolve, reject) => {
const req = plugins.https.request(
{
hostname,
path,
method: 'GET',
},
(res) => {
const body = [];
res.on('data', (chunk) => {
body.push(chunk);
});
res.on('end', () => {
try {
resolve(Buffer.concat(body).toString());
} catch (e) {
reject(e);
}
});
req.on('error', (err) => {
reject(err);
});
}
);
req.end();
});
}
public async download(bytes) {
const options = {
hostname: 'speed.cloudflare.com',
path: `/__down?bytes=${bytes}`,
method: 'GET',
};
return this.request(options);
}
public async request(options, data = '') {
let started;
let dnsLookup;
let tcpHandshake;
let sslHandshake;
let ttfb;
let ended;
return new Promise((resolve, reject) => {
started = plugins.perfHooks.performance.now();
const req = plugins.https.request(options, (res) => {
res.once('readable', () => {
ttfb = plugins.perfHooks.performance.now();
});
res.on('data', () => {});
res.on('end', () => {
ended = plugins.perfHooks.performance.now();
resolve([
started,
dnsLookup,
tcpHandshake,
sslHandshake,
ttfb,
ended,
parseFloat(res.headers['server-timing'].slice(22) as any),
]);
});
});
req.on('socket', (socket) => {
socket.on('lookup', () => {
dnsLookup = plugins.perfHooks.performance.now();
});
socket.on('connect', () => {
tcpHandshake = plugins.perfHooks.performance.now();
});
socket.on('secureConnect', () => {
sslHandshake = plugins.perfHooks.performance.now();
});
});
req.on('error', (error) => {
reject(error);
});
req.write(data);
req.end();
});
}
public async fetchCfCdnCgiTrace(): Promise<{
fl: string,
h: string,
ip: string,
ts: string,
visit_scheme: string,
uag: string,
colo: string,
http: string,
loc: string,
tls: string,
sni: string,
warp: string,
gateway: string,
}> {
const parseCfCdnCgiTrace = (text) =>
text
.split('\n')
.map((i) => {
const j = i.split('=');
return [j[0], j[1]];
})
.reduce((data, [k, v]) => {
if (v === undefined) return data;
// Bypass prettier "no-assign-param" rules
const data1 = data;
// Object.fromEntries is only supported by Node.js 12 or newer
data1[k] = v;
return data1;
}, {});
return this.get('speed.cloudflare.com', '/cdn-cgi/trace').then(parseCfCdnCgiTrace);
}
}

View File

@ -1,33 +1,6 @@
import * as plugins from './smartnetwork.plugins'; import * as plugins from './smartnetwork.plugins';
export interface ISpeedtestData { import { CloudflareSpeed } from './smartnetwork.classes.cloudflarespeed';
timestamp: Date;
ping: { jitter: number; latency: number };
download: { bandwidth: number; bytes: number; elapsed: number };
upload: { bandwidth: number; bytes: number; elapsed: number };
packetLoss: number;
isp: string;
interface: {
internalIp: string;
name: string;
macAddr: string;
isVpn: false;
externalIp: string;
};
server: {
id: number;
name: string;
location: string;
country: string;
host: string;
port: number;
ip: string;
};
result: {
id: string;
url: string;
};
}
/** /**
* SmartNetwork simplifies actions within the network * SmartNetwork simplifies actions within the network
@ -38,7 +11,8 @@ export class SmartNetwork {
* @param measurementTime * @param measurementTime
*/ */
public async getSpeed() { public async getSpeed() {
const test = null; const cloudflareSpeedInstance = new CloudflareSpeed();
const test = await cloudflareSpeedInstance.speedTest();
return test; return test;
} }

View File

@ -1,7 +1,9 @@
// native scope // native scope
import * as os from 'os'; import * as os from 'os';
import * as https from 'https';
import * as perfHooks from 'perf_hooks';
export { os }; export { os, https, perfHooks };
// @pushrocks scope // @pushrocks scope
import * as smartpromise from '@pushrocks/smartpromise'; import * as smartpromise from '@pushrocks/smartpromise';