@push.rocks/smartnetwork 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,8 @@
1
+ /**
2
+ * autocreated commitinfo by @pushrocks/commitinfo
3
+ */
4
+ export declare const commitinfo: {
5
+ name: string;
6
+ version: string;
7
+ description: string;
8
+ };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * autocreated commitinfo by @pushrocks/commitinfo
3
+ */
4
+ export const commitinfo = {
5
+ name: '@pushrocks/smartnetwork',
6
+ version: '3.0.2',
7
+ description: 'network diagnostics'
8
+ };
9
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx5QkFBeUI7SUFDL0IsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLHFCQUFxQjtDQUNuQyxDQUFBIn0=
@@ -0,0 +1,4 @@
1
+ export declare function average(values: number[]): number;
2
+ export declare function median(values: number[]): number;
3
+ export declare function quartile(values: number[], percentile: number): number;
4
+ export declare function jitter(values: number[]): number;
@@ -0,0 +1,33 @@
1
+ export function average(values) {
2
+ let total = 0;
3
+ for (let i = 0; i < values.length; i += 1) {
4
+ total += values[i];
5
+ }
6
+ return total / values.length;
7
+ }
8
+ export function median(values) {
9
+ const half = Math.floor(values.length / 2);
10
+ values.sort((a, b) => a - b);
11
+ if (values.length % 2)
12
+ return values[half];
13
+ return (values[half - 1] + values[half]) / 2;
14
+ }
15
+ export function quartile(values, percentile) {
16
+ values.sort((a, b) => a - b);
17
+ const pos = (values.length - 1) * percentile;
18
+ const base = Math.floor(pos);
19
+ const rest = pos - base;
20
+ if (values[base + 1] !== undefined) {
21
+ return values[base] + rest * (values[base + 1] - values[base]);
22
+ }
23
+ return values[base];
24
+ }
25
+ export function jitter(values) {
26
+ // Average distance between consecutive latency measurements...
27
+ let jitters = [];
28
+ for (let i = 0; i < values.length - 1; i += 1) {
29
+ jitters.push(Math.abs(values[i] - values[i + 1]));
30
+ }
31
+ return average(jitters);
32
+ }
33
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RhdHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9oZWxwZXJzL3N0YXRzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE1BQU0sVUFBVSxPQUFPLENBQUMsTUFBZ0I7SUFDdEMsSUFBSSxLQUFLLEdBQUcsQ0FBQyxDQUFDO0lBRWQsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxNQUFNLEVBQUUsQ0FBQyxJQUFJLENBQUMsRUFBRTtRQUN6QyxLQUFLLElBQUksTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDO0tBQ3BCO0lBRUQsT0FBTyxLQUFLLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQztBQUMvQixDQUFDO0FBRUQsTUFBTSxVQUFVLE1BQU0sQ0FBQyxNQUFnQjtJQUNyQyxNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUM7SUFFM0MsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztJQUU3QixJQUFJLE1BQU0sQ0FBQyxNQUFNLEdBQUcsQ0FBQztRQUFFLE9BQU8sTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDO0lBRTNDLE9BQU8sQ0FBQyxNQUFNLENBQUMsSUFBSSxHQUFHLENBQUMsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQztBQUMvQyxDQUFDO0FBRUQsTUFBTSxVQUFVLFFBQVEsQ0FBQyxNQUFnQixFQUFFLFVBQWtCO0lBQzNELE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7SUFDN0IsTUFBTSxHQUFHLEdBQUcsQ0FBQyxNQUFNLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQyxHQUFHLFVBQVUsQ0FBQztJQUM3QyxNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQzdCLE1BQU0sSUFBSSxHQUFHLEdBQUcsR0FBRyxJQUFJLENBQUM7SUFFeEIsSUFBSSxNQUFNLENBQUMsSUFBSSxHQUFHLENBQUMsQ0FBQyxLQUFLLFNBQVMsRUFBRTtRQUNsQyxPQUFPLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxJQUFJLEdBQUcsQ0FBQyxNQUFNLENBQUMsSUFBSSxHQUFHLENBQUMsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO0tBQ2hFO0lBRUQsT0FBTyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUM7QUFDdEIsQ0FBQztBQUVELE1BQU0sVUFBVSxNQUFNLENBQUMsTUFBZ0I7SUFDckMsK0RBQStEO0lBQy9ELElBQUksT0FBTyxHQUFHLEVBQUUsQ0FBQztJQUVqQixLQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsTUFBTSxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsRUFBRTtRQUM3QyxPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0tBQ25EO0lBRUQsT0FBTyxPQUFPLENBQUMsT0FBTyxDQUFDLENBQUM7QUFDMUIsQ0FBQyJ9
@@ -0,0 +1 @@
1
+ export * from './smartnetwork.classes.smartnetwork.js';
@@ -0,0 +1,2 @@
1
+ export * from './smartnetwork.classes.smartnetwork.js';
2
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLHdDQUF3QyxDQUFDIn0=
@@ -0,0 +1,54 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ import * as plugins from './smartnetwork.plugins.js';
3
+ export declare class CloudflareSpeed {
4
+ constructor();
5
+ speedTest(): Promise<{
6
+ ip: string;
7
+ serverLocation: {
8
+ shortId: string;
9
+ name: string;
10
+ availableLocations: {
11
+ [key: string]: string;
12
+ };
13
+ };
14
+ downloadSpeed: string;
15
+ uploadSpeed: string;
16
+ maxTime: number;
17
+ minTime: number;
18
+ averageTime: number;
19
+ medianTime: number;
20
+ jitter: number;
21
+ }>;
22
+ measureLatency(): Promise<{
23
+ maxTime: number;
24
+ minTime: number;
25
+ averageTime: number;
26
+ medianTime: number;
27
+ jitter: number;
28
+ }>;
29
+ measureDownload(bytes: number, iterations: number): Promise<number[]>;
30
+ measureUpload(bytes: number, iterations: number): Promise<number[]>;
31
+ measureSpeed(bytes: number, duration: number): Promise<number>;
32
+ fetchServerLocations(): Promise<{
33
+ [key: string]: string;
34
+ }>;
35
+ get(hostname: string, path: string): Promise<string>;
36
+ download(bytes: number): Promise<number[]>;
37
+ upload(bytes: number): Promise<number[]>;
38
+ request(options: plugins.https.RequestOptions, data?: string): Promise<number[]>;
39
+ fetchCfCdnCgiTrace(): Promise<{
40
+ fl: string;
41
+ h: string;
42
+ ip: string;
43
+ ts: string;
44
+ visit_scheme: string;
45
+ uag: string;
46
+ colo: string;
47
+ http: string;
48
+ loc: string;
49
+ tls: string;
50
+ sni: string;
51
+ warp: string;
52
+ gateway: string;
53
+ }>;
54
+ }
@@ -0,0 +1,199 @@
1
+ import * as plugins from './smartnetwork.plugins.js';
2
+ import * as stats from './helpers/stats.js';
3
+ export class CloudflareSpeed {
4
+ constructor() { }
5
+ async speedTest() {
6
+ const latency = await this.measureLatency();
7
+ const serverLocations = await this.fetchServerLocations();
8
+ const cgiData = await this.fetchCfCdnCgiTrace();
9
+ // lets test the download speed
10
+ const testDown1 = await this.measureDownload(101000, 10);
11
+ const testDown2 = await this.measureDownload(1001000, 8);
12
+ const testDown3 = await this.measureDownload(10001000, 6);
13
+ const testDown4 = await this.measureDownload(25001000, 4);
14
+ const testDown5 = await this.measureDownload(100001000, 1);
15
+ const downloadTests = [...testDown1, ...testDown2, ...testDown3, ...testDown4, ...testDown5];
16
+ const speedDownload = stats.quartile(downloadTests, 0.9).toFixed(2);
17
+ // lets test the upload speed
18
+ const testUp1 = await this.measureUpload(11000, 10);
19
+ const testUp2 = await this.measureUpload(101000, 10);
20
+ const testUp3 = await this.measureUpload(1001000, 8);
21
+ const uploadTests = [...testUp1, ...testUp2, ...testUp3];
22
+ const speedUpload = stats.quartile(uploadTests, 0.9).toFixed(2);
23
+ return {
24
+ ...latency,
25
+ ip: cgiData.ip,
26
+ serverLocation: {
27
+ shortId: cgiData.colo,
28
+ name: serverLocations[cgiData.colo],
29
+ availableLocations: serverLocations,
30
+ },
31
+ downloadSpeed: speedDownload,
32
+ uploadSpeed: speedUpload,
33
+ };
34
+ }
35
+ async measureLatency() {
36
+ const measurements = [];
37
+ for (let i = 0; i < 20; i += 1) {
38
+ await this.download(1000).then((response) => {
39
+ // TTFB - Server processing time
40
+ measurements.push(response[4] - response[0] - response[6]);
41
+ }, (error) => {
42
+ console.log(`Error: ${error}`);
43
+ });
44
+ }
45
+ return {
46
+ maxTime: Math.max(...measurements),
47
+ minTime: Math.min(...measurements),
48
+ averageTime: stats.average(measurements),
49
+ medianTime: stats.median(measurements),
50
+ jitter: stats.jitter(measurements),
51
+ };
52
+ }
53
+ async measureDownload(bytes, iterations) {
54
+ const measurements = [];
55
+ for (let i = 0; i < iterations; i += 1) {
56
+ await this.download(bytes).then(async (response) => {
57
+ const transferTime = response[5] - response[4];
58
+ measurements.push(await this.measureSpeed(bytes, transferTime));
59
+ }, (error) => {
60
+ console.log(`Error: ${error}`);
61
+ });
62
+ }
63
+ return measurements;
64
+ }
65
+ async measureUpload(bytes, iterations) {
66
+ const measurements = [];
67
+ for (let i = 0; i < iterations; i += 1) {
68
+ await this.upload(bytes).then(async (response) => {
69
+ const transferTime = response[6];
70
+ measurements.push(await this.measureSpeed(bytes, transferTime));
71
+ }, (error) => {
72
+ console.log(`Error: ${error}`);
73
+ });
74
+ }
75
+ return measurements;
76
+ }
77
+ async measureSpeed(bytes, duration) {
78
+ return (bytes * 8) / (duration / 1000) / 1e6;
79
+ }
80
+ async fetchServerLocations() {
81
+ const res = JSON.parse(await this.get('speed.cloudflare.com', '/locations'));
82
+ return res.reduce((data, optionsArg) => {
83
+ // Bypass prettier "no-assign-param" rules
84
+ const data1 = data;
85
+ data1[optionsArg.iata] = optionsArg.city;
86
+ return data1;
87
+ }, {});
88
+ }
89
+ async get(hostname, path) {
90
+ return new Promise((resolve, reject) => {
91
+ const req = plugins.https.request({
92
+ hostname,
93
+ path,
94
+ method: 'GET',
95
+ }, (res) => {
96
+ const body = [];
97
+ res.on('data', (chunk) => {
98
+ body.push(chunk);
99
+ });
100
+ res.on('end', () => {
101
+ try {
102
+ resolve(Buffer.concat(body).toString());
103
+ }
104
+ catch (e) {
105
+ reject(e);
106
+ }
107
+ });
108
+ req.on('error', (err) => {
109
+ reject(err);
110
+ });
111
+ });
112
+ req.end();
113
+ });
114
+ }
115
+ async download(bytes) {
116
+ const options = {
117
+ hostname: 'speed.cloudflare.com',
118
+ path: `/__down?bytes=${bytes}`,
119
+ method: 'GET',
120
+ };
121
+ return this.request(options);
122
+ }
123
+ async upload(bytes) {
124
+ const data = '0'.repeat(bytes);
125
+ const options = {
126
+ hostname: 'speed.cloudflare.com',
127
+ path: '/__up',
128
+ method: 'POST',
129
+ headers: {
130
+ 'Content-Length': Buffer.byteLength(data),
131
+ },
132
+ };
133
+ return this.request(options, data);
134
+ }
135
+ async request(options, data = '') {
136
+ let started;
137
+ let dnsLookup;
138
+ let tcpHandshake;
139
+ let sslHandshake;
140
+ let ttfb;
141
+ let ended;
142
+ return new Promise((resolve, reject) => {
143
+ started = plugins.perfHooks.performance.now();
144
+ const req = plugins.https.request(options, (res) => {
145
+ res.once('readable', () => {
146
+ ttfb = plugins.perfHooks.performance.now();
147
+ });
148
+ res.on('data', () => { });
149
+ res.on('end', () => {
150
+ ended = plugins.perfHooks.performance.now();
151
+ resolve([
152
+ started,
153
+ dnsLookup,
154
+ tcpHandshake,
155
+ sslHandshake,
156
+ ttfb,
157
+ ended,
158
+ parseFloat(res.headers['server-timing'].slice(22)),
159
+ ]);
160
+ });
161
+ });
162
+ req.on('socket', (socket) => {
163
+ socket.on('lookup', () => {
164
+ dnsLookup = plugins.perfHooks.performance.now();
165
+ });
166
+ socket.on('connect', () => {
167
+ tcpHandshake = plugins.perfHooks.performance.now();
168
+ });
169
+ socket.on('secureConnect', () => {
170
+ sslHandshake = plugins.perfHooks.performance.now();
171
+ });
172
+ });
173
+ req.on('error', (error) => {
174
+ reject(error);
175
+ });
176
+ req.write(data);
177
+ req.end();
178
+ });
179
+ }
180
+ async fetchCfCdnCgiTrace() {
181
+ const parseCfCdnCgiTrace = (text) => text
182
+ .split('\n')
183
+ .map((i) => {
184
+ const j = i.split('=');
185
+ return [j[0], j[1]];
186
+ })
187
+ .reduce((data, [k, v]) => {
188
+ if (v === undefined)
189
+ return data;
190
+ // Bypass prettier "no-assign-param" rules
191
+ const data1 = data;
192
+ // Object.fromEntries is only supported by Node.js 12 or newer
193
+ data1[k] = v;
194
+ return data1;
195
+ }, {});
196
+ return this.get('speed.cloudflare.com', '/cdn-cgi/trace').then(parseCfCdnCgiTrace);
197
+ }
198
+ }
199
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRuZXR3b3JrLmNsYXNzZXMuY2xvdWRmbGFyZXNwZWVkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvc21hcnRuZXR3b3JrLmNsYXNzZXMuY2xvdWRmbGFyZXNwZWVkLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sMkJBQTJCLENBQUM7QUFDckQsT0FBTyxLQUFLLEtBQUssTUFBTSxvQkFBb0IsQ0FBQztBQUU1QyxNQUFNLE9BQU8sZUFBZTtJQUMxQixnQkFBZSxDQUFDO0lBRVQsS0FBSyxDQUFDLFNBQVM7UUFDcEIsTUFBTSxPQUFPLEdBQUcsTUFBTSxJQUFJLENBQUMsY0FBYyxFQUFFLENBQUM7UUFFNUMsTUFBTSxlQUFlLEdBQUcsTUFBTSxJQUFJLENBQUMsb0JBQW9CLEVBQUUsQ0FBQztRQUMxRCxNQUFNLE9BQU8sR0FBRyxNQUFNLElBQUksQ0FBQyxrQkFBa0IsRUFBRSxDQUFDO1FBRWhELCtCQUErQjtRQUMvQixNQUFNLFNBQVMsR0FBRyxNQUFNLElBQUksQ0FBQyxlQUFlLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBQ3pELE1BQU0sU0FBUyxHQUFHLE1BQU0sSUFBSSxDQUFDLGVBQWUsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDLENBQUM7UUFDekQsTUFBTSxTQUFTLEdBQUcsTUFBTSxJQUFJLENBQUMsZUFBZSxDQUFDLFFBQVEsRUFBRSxDQUFDLENBQUMsQ0FBQztRQUMxRCxNQUFNLFNBQVMsR0FBRyxNQUFNLElBQUksQ0FBQyxlQUFlLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FBQyxDQUFDO1FBQzFELE1BQU0sU0FBUyxHQUFHLE1BQU0sSUFBSSxDQUFDLGVBQWUsQ0FBQyxTQUFTLEVBQUUsQ0FBQyxDQUFDLENBQUM7UUFDM0QsTUFBTSxhQUFhLEdBQUcsQ0FBQyxHQUFHLFNBQVMsRUFBRSxHQUFHLFNBQVMsRUFBRSxHQUFHLFNBQVMsRUFBRSxHQUFHLFNBQVMs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
@@ -0,0 +1,50 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ /// <reference types="node" resolution-mode="require"/>
3
+ import * as plugins from './smartnetwork.plugins.js';
4
+ /**
5
+ * SmartNetwork simplifies actions within the network
6
+ */
7
+ export declare class SmartNetwork {
8
+ /**
9
+ * get network speed
10
+ * @param measurementTime
11
+ */
12
+ getSpeed(): Promise<{
13
+ ip: string;
14
+ serverLocation: {
15
+ shortId: string;
16
+ name: string;
17
+ availableLocations: {
18
+ [key: string]: string;
19
+ };
20
+ };
21
+ downloadSpeed: string;
22
+ uploadSpeed: string;
23
+ maxTime: number;
24
+ minTime: number;
25
+ averageTime: number;
26
+ medianTime: number;
27
+ jitter: number;
28
+ }>;
29
+ ping(hostArg: string, timeoutArg?: number): Promise<ReturnType<typeof plugins.smartping.Smartping.prototype.ping>>;
30
+ /**
31
+ * returns a promise with a boolean answer
32
+ * note: false also resolves with false as argument
33
+ * @param port
34
+ */
35
+ isLocalPortUnused(port: number): Promise<boolean>;
36
+ /**
37
+ * checks wether a remote port is available
38
+ * @param domainArg
39
+ */
40
+ isRemotePortAvailable(domainArg: string, portArg?: number): Promise<boolean>;
41
+ getGateways(): Promise<NodeJS.Dict<plugins.os.NetworkInterfaceInfo[]>>;
42
+ getDefaultGateway(): Promise<{
43
+ ipv4: plugins.os.NetworkInterfaceInfo;
44
+ ipv6: plugins.os.NetworkInterfaceInfo;
45
+ }>;
46
+ getPublicIps(): Promise<{
47
+ v4: any;
48
+ v6: any;
49
+ }>;
50
+ }
@@ -0,0 +1,123 @@
1
+ import * as plugins from './smartnetwork.plugins.js';
2
+ import { CloudflareSpeed } from './smartnetwork.classes.cloudflarespeed.js';
3
+ /**
4
+ * SmartNetwork simplifies actions within the network
5
+ */
6
+ export class SmartNetwork {
7
+ /**
8
+ * get network speed
9
+ * @param measurementTime
10
+ */
11
+ async getSpeed() {
12
+ const cloudflareSpeedInstance = new CloudflareSpeed();
13
+ const test = await cloudflareSpeedInstance.speedTest();
14
+ return test;
15
+ }
16
+ async ping(hostArg, timeoutArg = 500) {
17
+ const smartpingInstance = new plugins.smartping.Smartping();
18
+ const pingResult = await smartpingInstance.ping(hostArg, timeoutArg);
19
+ return pingResult;
20
+ }
21
+ /**
22
+ * returns a promise with a boolean answer
23
+ * note: false also resolves with false as argument
24
+ * @param port
25
+ */
26
+ async isLocalPortUnused(port) {
27
+ const doneIpV4 = plugins.smartpromise.defer();
28
+ const doneIpV6 = plugins.smartpromise.defer();
29
+ const net = await import('net'); // creates only one instance of net ;) even on multiple calls
30
+ // test IPv4 space
31
+ const ipv4Test = net.createServer();
32
+ ipv4Test.once('error', (err) => {
33
+ if (err.code !== 'EADDRINUSE') {
34
+ doneIpV4.resolve(false);
35
+ return;
36
+ }
37
+ doneIpV4.resolve(false);
38
+ });
39
+ ipv4Test.once('listening', () => {
40
+ ipv4Test.once('close', () => {
41
+ doneIpV4.resolve(true);
42
+ });
43
+ ipv4Test.close();
44
+ });
45
+ ipv4Test.listen(port, '0.0.0.0');
46
+ await doneIpV4.promise;
47
+ // test IPv6 space
48
+ const ipv6Test = net.createServer();
49
+ ipv6Test.once('error', function (err) {
50
+ if (err.code !== 'EADDRINUSE') {
51
+ doneIpV6.resolve(false);
52
+ return;
53
+ }
54
+ doneIpV6.resolve(false);
55
+ });
56
+ ipv6Test.once('listening', () => {
57
+ ipv6Test.once('close', () => {
58
+ doneIpV6.resolve(true);
59
+ });
60
+ ipv6Test.close();
61
+ });
62
+ ipv6Test.listen(port, '::');
63
+ // lets wait for the result
64
+ const resultIpV4 = await doneIpV4.promise;
65
+ const resultIpV6 = await doneIpV6.promise;
66
+ const result = resultIpV4 === true && resultIpV6 === true;
67
+ return result;
68
+ }
69
+ /**
70
+ * checks wether a remote port is available
71
+ * @param domainArg
72
+ */
73
+ async isRemotePortAvailable(domainArg, portArg) {
74
+ const done = plugins.smartpromise.defer();
75
+ const domainPart = domainArg.split(':')[0];
76
+ const port = portArg ? portArg : parseInt(domainArg.split(':')[1], 10);
77
+ plugins.isopen(domainPart, port, (response) => {
78
+ console.log(response);
79
+ if (response[port.toString()].isOpen) {
80
+ done.resolve(true);
81
+ }
82
+ else {
83
+ done.resolve(false);
84
+ }
85
+ });
86
+ const result = await done.promise;
87
+ return result;
88
+ }
89
+ async getGateways() {
90
+ const result = plugins.os.networkInterfaces();
91
+ return result;
92
+ }
93
+ async getDefaultGateway() {
94
+ const defaultGatewayName = await plugins.systeminformation.networkInterfaceDefault();
95
+ if (!defaultGatewayName) {
96
+ console.log('Cannot determine default gateway');
97
+ return null;
98
+ }
99
+ const gateways = await this.getGateways();
100
+ const defaultGateway = gateways[defaultGatewayName];
101
+ return {
102
+ ipv4: defaultGateway[0],
103
+ ipv6: defaultGateway[1],
104
+ };
105
+ }
106
+ async getPublicIps() {
107
+ return {
108
+ v4: await plugins.publicIp.publicIpv4({
109
+ timeout: 1000,
110
+ onlyHttps: true,
111
+ }).catch(async (err) => {
112
+ return null;
113
+ }),
114
+ v6: await plugins.publicIp.publicIpv6({
115
+ timeout: 1000,
116
+ onlyHttps: true,
117
+ }).catch(async (err) => {
118
+ return null;
119
+ })
120
+ };
121
+ }
122
+ }
123
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRuZXR3b3JrLmNsYXNzZXMuc21hcnRuZXR3b3JrLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvc21hcnRuZXR3b3JrLmNsYXNzZXMuc21hcnRuZXR3b3JrLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sMkJBQTJCLENBQUM7QUFFckQsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLDJDQUEyQyxDQUFDO0FBRTVFOztHQUVHO0FBQ0gsTUFBTSxPQUFPLFlBQVk7SUFDdkI7OztPQUdHO0lBQ0ksS0FBSyxDQUFDLFFBQVE7UUFDbkIsTUFBTSx1QkFBdUIsR0FBRyxJQUFJLGVBQWUsRUFBRSxDQUFDO1FBQ3RELE1BQU0sSUFBSSxHQUFHLE1BQU0sdUJBQXVCLENBQUMsU0FBUyxFQUFFLENBQUM7UUFDdkQsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBRU0sS0FBSyxDQUFDLElBQUksQ0FBQyxPQUFlLEVBQUUsYUFBcUIsR0FBRztRQUN6RCxNQUFNLGlCQUFpQixHQUFHLElBQUksT0FBTyxDQUFDLFNBQVMsQ0FBQyxTQUFTLEVBQUUsQ0FBQztRQUM1RCxNQUFNLFVBQVUsR0FBRyxNQUFNLGlCQUFpQixDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsVUFBVSxDQUFDLENBQUM7UUFDckUsT0FBTyxVQUFVLENBQUM7SUFDcEIsQ0FBQztJQUVEOzs7O09BSUc7SUFDSSxLQUFLLENBQUMsaUJBQWlCLENBQUMsSUFBWTtRQUN6QyxNQUFNLFFBQVEsR0FBRyxPQUFPLENBQUMsWUFBWSxDQUFDLEtBQUssRUFBVyxDQUFDO1FBQ3ZELE1BQU0sUUFBUSxHQUFHLE9BQU8sQ0FBQyxZQUFZLENBQUMsS0FBSyxFQUFXLENBQUM7UUFDdkQsTUFBTSxHQUFHLEdBQUcsTUFBTSxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyw2REFBNkQ7UUFFOUYsa0JBQWtCO1FBQ2xCLE1BQU0sUUFBUSxHQUFHLEdBQUcs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
@@ -0,0 +1,12 @@
1
+ import * as os from 'os';
2
+ import * as https from 'https';
3
+ import * as perfHooks from 'perf_hooks';
4
+ export { os, https, perfHooks };
5
+ import * as smartping from '@pushrocks/smartping';
6
+ import * as smartpromise from '@pushrocks/smartpromise';
7
+ import * as smartstring from '@pushrocks/smartstring';
8
+ export { smartpromise, smartping, smartstring };
9
+ import isopen from 'isopen';
10
+ import * as publicIp from 'public-ip';
11
+ import * as systeminformation from 'systeminformation';
12
+ export { isopen, publicIp, systeminformation };
@@ -0,0 +1,18 @@
1
+ // native scope
2
+ import * as os from 'os';
3
+ import * as https from 'https';
4
+ import * as perfHooks from 'perf_hooks';
5
+ export { os, https, perfHooks };
6
+ // @pushrocks scope
7
+ import * as smartping from '@pushrocks/smartping';
8
+ import * as smartpromise from '@pushrocks/smartpromise';
9
+ import * as smartstring from '@pushrocks/smartstring';
10
+ export { smartpromise, smartping, smartstring };
11
+ // @third party scope
12
+ // @ts-ignore
13
+ import isopen from 'isopen';
14
+ // @ts-ignore
15
+ import * as publicIp from 'public-ip';
16
+ import * as systeminformation from 'systeminformation';
17
+ export { isopen, publicIp, systeminformation };
18
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRuZXR3b3JrLnBsdWdpbnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydG5ldHdvcmsucGx1Z2lucy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxlQUFlO0FBQ2YsT0FBTyxLQUFLLEVBQUUsTUFBTSxJQUFJLENBQUM7QUFDekIsT0FBTyxLQUFLLEtBQUssTUFBTSxPQUFPLENBQUM7QUFDL0IsT0FBTyxLQUFLLFNBQVMsTUFBTSxZQUFZLENBQUM7QUFFeEMsT0FBTyxFQUFFLEVBQUUsRUFBRSxLQUFLLEVBQUUsU0FBUyxFQUFFLENBQUM7QUFFaEMsbUJBQW1CO0FBQ25CLE9BQU8sS0FBSyxTQUFTLE1BQU0sc0JBQXNCLENBQUM7QUFDbEQsT0FBTyxLQUFLLFlBQVksTUFBTSx5QkFBeUIsQ0FBQztBQUN4RCxPQUFPLEtBQUssV0FBVyxNQUFNLHdCQUF3QixDQUFDO0FBRXRELE9BQU8sRUFBRSxZQUFZLEVBQUUsU0FBUyxFQUFFLFdBQVcsRUFBRSxDQUFDO0FBRWhELHFCQUFxQjtBQUNyQixhQUFhO0FBQ2IsT0FBTyxNQUFNLE1BQU0sUUFBUSxDQUFDO0FBQzVCLGFBQWE7QUFDYixPQUFPLEtBQUssUUFBUSxNQUFNLFdBQVcsQ0FBQztBQUN0QyxPQUFPLEtBQUssaUJBQWlCLE1BQU0sbUJBQW1CLENBQUM7QUFFdkQsT0FBTyxFQUFFLE1BQU0sRUFBRSxRQUFRLEVBQUUsaUJBQWlCLEVBQUUsQ0FBQyJ9
package/license ADDED
@@ -0,0 +1,23 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Lossless GmbH
4
+ Copyright (c) 2020 Tomás Arias
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
23
+
package/npmextra.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "npmci": {
3
+ "npmGlobalTools": [],
4
+ "npmAccessLevel": "public"
5
+ },
6
+ "gitzone": {
7
+ "projectType": "npm",
8
+ "module": {
9
+ "githost": "gitlab.com",
10
+ "gitscope": "pushrocks",
11
+ "gitrepo": "smartnetwork",
12
+ "description": "network diagnostics",
13
+ "npmPackagename": "@pushrocks/smartnetwork",
14
+ "license": "MIT"
15
+ }
16
+ }
17
+ }
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@push.rocks/smartnetwork",
3
+ "version": "3.0.2",
4
+ "private": false,
5
+ "description": "network diagnostics",
6
+ "main": "dist_ts/index.js",
7
+ "typings": "dist_ts/index.d.ts",
8
+ "type": "module",
9
+ "author": "Lossless GmbH",
10
+ "license": "MIT",
11
+ "scripts": {
12
+ "test": "(tstest test/)",
13
+ "build": "(tsbuild --web --allowimplicitany)"
14
+ },
15
+ "devDependencies": {
16
+ "@gitzone/tsbuild": "^2.1.61",
17
+ "@gitzone/tsrun": "^1.2.39",
18
+ "@gitzone/tstest": "^1.0.69",
19
+ "@pushrocks/smartenv": "^5.0.0",
20
+ "@pushrocks/tapbundle": "^5.0.3",
21
+ "@types/node": "^18.11.3"
22
+ },
23
+ "dependencies": {
24
+ "@pushrocks/smartping": "^1.0.7",
25
+ "@pushrocks/smartpromise": "^3.1.7",
26
+ "@pushrocks/smartstring": "^4.0.2",
27
+ "@types/default-gateway": "^3.0.1",
28
+ "isopen": "^1.3.0",
29
+ "public-ip": "^6.0.1",
30
+ "systeminformation": "^5.11.9"
31
+ },
32
+ "files": [
33
+ "ts/**/*",
34
+ "ts_web/**/*",
35
+ "dist/**/*",
36
+ "dist_*/**/*",
37
+ "dist_ts/**/*",
38
+ "dist_ts_web/**/*",
39
+ "assets/**/*",
40
+ "cli.js",
41
+ "npmextra.json",
42
+ "readme.md"
43
+ ],
44
+ "browserslist": [
45
+ "last 1 chrome versions"
46
+ ]
47
+ }
package/readme.md ADDED
@@ -0,0 +1,61 @@
1
+ # @pushrocks/smartnetwork
2
+ network diagnostics
3
+
4
+ ## Availabililty and Links
5
+ * [npmjs.org (npm package)](https://www.npmjs.com/package/@pushrocks/smartnetwork)
6
+ * [gitlab.com (source)](https://gitlab.com/pushrocks/smartnetwork)
7
+ * [github.com (source mirror)](https://github.com/pushrocks/smartnetwork)
8
+ * [docs (typedoc)](https://pushrocks.gitlab.io/smartnetwork/)
9
+
10
+ ## Status for master
11
+
12
+ Status Category | Status Badge
13
+ -- | --
14
+ GitLab Pipelines | [![pipeline status](https://gitlab.com/pushrocks/smartnetwork/badges/master/pipeline.svg)](https://lossless.cloud)
15
+ GitLab Pipline Test Coverage | [![coverage report](https://gitlab.com/pushrocks/smartnetwork/badges/master/coverage.svg)](https://lossless.cloud)
16
+ npm | [![npm downloads per month](https://badgen.net/npm/dy/@pushrocks/smartnetwork)](https://lossless.cloud)
17
+ Snyk | [![Known Vulnerabilities](https://badgen.net/snyk/pushrocks/smartnetwork)](https://lossless.cloud)
18
+ TypeScript Support | [![TypeScript](https://badgen.net/badge/TypeScript/>=%203.x/blue?icon=typescript)](https://lossless.cloud)
19
+ node Support | [![node](https://img.shields.io/badge/node->=%2010.x.x-blue.svg)](https://nodejs.org/dist/latest-v10.x/docs/api/)
20
+ Code Style | [![Code Style](https://badgen.net/badge/style/prettier/purple)](https://lossless.cloud)
21
+ PackagePhobia (total standalone install weight) | [![PackagePhobia](https://badgen.net/packagephobia/install/@pushrocks/smartnetwork)](https://lossless.cloud)
22
+ PackagePhobia (package size on registry) | [![PackagePhobia](https://badgen.net/packagephobia/publish/@pushrocks/smartnetwork)](https://lossless.cloud)
23
+ BundlePhobia (total size when bundled) | [![BundlePhobia](https://badgen.net/bundlephobia/minzip/@pushrocks/smartnetwork)](https://lossless.cloud)
24
+ Platform support | [![Supports Windows 10](https://badgen.net/badge/supports%20Windows%2010/yes/green?icon=windows)](https://lossless.cloud) [![Supports Mac OS X](https://badgen.net/badge/supports%20Mac%20OS%20X/yes/green?icon=apple)](https://lossless.cloud)
25
+
26
+ ## Usage
27
+
28
+ ```typescript
29
+ import * as smartnetwork from 'smartnetwork';
30
+ const testSmartNetwork = new smartnetwork.SmartNetwork();
31
+ const run = async () => {
32
+ // measure average speed over a period of 5 seconds
33
+ // the structure of speedResult is self explanatory using TypeScript (or the linked TypeDoc above)
34
+ const speedResult: smartnetwork.SpeedResult = testSmartNetwork.getSpeed(5000);
35
+
36
+ // you can check for local ports before trying to bind to it from your nodejs program
37
+ const isLocalPortUnused: boolean = await testSmartNetwork.isLocalPortUnused(1234);
38
+
39
+ // you can run basic port checks on remote domains.
40
+ const isRemotePortAvailable: boolean = await testSmartNetwork.isRemotePortAvailable(
41
+ 'google.com:80'
42
+ );
43
+
44
+ // just another way to call for the same thing as above
45
+ const isRemotePortAvailable: boolean = await testSmartNetwork.isRemotePortAvailable(
46
+ 'google.com',
47
+ 80
48
+ );
49
+ };
50
+ ```
51
+
52
+ ## Contribution
53
+
54
+ We are always happy for code contributions. If you are not the code contributing type that is ok. Still, maintaining Open Source repositories takes considerable time and thought. If you like the quality of what we do and our modules are useful to you we would appreciate a little monthly contribution: You can [contribute one time](https://lossless.link/contribute-onetime) or [contribute monthly](https://lossless.link/contribute). :)
55
+
56
+ For further information read the linked docs at the top of this readme.
57
+
58
+ > MIT licensed | **&copy;** [Lossless GmbH](https://lossless.gmbh)
59
+ | By using this npm module you agree to our [privacy policy](https://lossless.gmbH/privacy)
60
+
61
+ [![repo-footer](https://lossless.gitlab.io/publicrelations/repofooter.svg)](https://maintainedby.lossless.com)
@@ -0,0 +1,8 @@
1
+ /**
2
+ * autocreated commitinfo by @pushrocks/commitinfo
3
+ */
4
+ export const commitinfo = {
5
+ name: '@pushrocks/smartnetwork',
6
+ version: '3.0.2',
7
+ description: 'network diagnostics'
8
+ }
@@ -0,0 +1,43 @@
1
+ export function average(values: number[]) {
2
+ let total = 0;
3
+
4
+ for (let i = 0; i < values.length; i += 1) {
5
+ total += values[i];
6
+ }
7
+
8
+ return total / values.length;
9
+ }
10
+
11
+ export function median(values: number[]) {
12
+ const half = Math.floor(values.length / 2);
13
+
14
+ values.sort((a, b) => a - b);
15
+
16
+ if (values.length % 2) return values[half];
17
+
18
+ return (values[half - 1] + values[half]) / 2;
19
+ }
20
+
21
+ export function quartile(values: number[], percentile: number) {
22
+ values.sort((a, b) => a - b);
23
+ const pos = (values.length - 1) * percentile;
24
+ const base = Math.floor(pos);
25
+ const rest = pos - base;
26
+
27
+ if (values[base + 1] !== undefined) {
28
+ return values[base] + rest * (values[base + 1] - values[base]);
29
+ }
30
+
31
+ return values[base];
32
+ }
33
+
34
+ export function jitter(values: number[]) {
35
+ // Average distance between consecutive latency measurements...
36
+ let jitters = [];
37
+
38
+ for (let i = 0; i < values.length - 1; i += 1) {
39
+ jitters.push(Math.abs(values[i] - values[i + 1]));
40
+ }
41
+
42
+ return average(jitters);
43
+ }
package/ts/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './smartnetwork.classes.smartnetwork.js';
@@ -0,0 +1,258 @@
1
+ import * as plugins from './smartnetwork.plugins.js';
2
+ import * as stats from './helpers/stats.js';
3
+
4
+ export class CloudflareSpeed {
5
+ constructor() {}
6
+
7
+ public async speedTest() {
8
+ const latency = await this.measureLatency();
9
+
10
+ const serverLocations = await this.fetchServerLocations();
11
+ const cgiData = await this.fetchCfCdnCgiTrace();
12
+
13
+ // lets test the download speed
14
+ const testDown1 = await this.measureDownload(101000, 10);
15
+ const testDown2 = await this.measureDownload(1001000, 8);
16
+ const testDown3 = await this.measureDownload(10001000, 6);
17
+ const testDown4 = await this.measureDownload(25001000, 4);
18
+ const testDown5 = await this.measureDownload(100001000, 1);
19
+ const downloadTests = [...testDown1, ...testDown2, ...testDown3, ...testDown4, ...testDown5];
20
+ const speedDownload = stats.quartile(downloadTests, 0.9).toFixed(2);
21
+
22
+ // lets test the upload speed
23
+ const testUp1 = await this.measureUpload(11000, 10);
24
+ const testUp2 = await this.measureUpload(101000, 10);
25
+ const testUp3 = await this.measureUpload(1001000, 8);
26
+ const uploadTests = [...testUp1, ...testUp2, ...testUp3];
27
+ const speedUpload = stats.quartile(uploadTests, 0.9).toFixed(2);
28
+
29
+ return {
30
+ ...latency,
31
+ ip: cgiData.ip,
32
+ serverLocation: {
33
+ shortId: cgiData.colo,
34
+ name: serverLocations[cgiData.colo],
35
+ availableLocations: serverLocations,
36
+ },
37
+ downloadSpeed: speedDownload,
38
+ uploadSpeed: speedUpload,
39
+ };
40
+ }
41
+
42
+ public async measureLatency() {
43
+ const measurements: number[] = [];
44
+
45
+ for (let i = 0; i < 20; i += 1) {
46
+ await this.download(1000).then(
47
+ (response) => {
48
+ // TTFB - Server processing time
49
+ measurements.push(response[4] - response[0] - response[6]);
50
+ },
51
+ (error) => {
52
+ console.log(`Error: ${error}`);
53
+ }
54
+ );
55
+ }
56
+
57
+ return {
58
+ maxTime: Math.max(...measurements),
59
+ minTime: Math.min(...measurements),
60
+ averageTime: stats.average(measurements),
61
+ medianTime: stats.median(measurements),
62
+ jitter: stats.jitter(measurements),
63
+ };
64
+ }
65
+
66
+ public async measureDownload(bytes: number, iterations: number) {
67
+ const measurements: number[] = [];
68
+
69
+ for (let i = 0; i < iterations; i += 1) {
70
+ await this.download(bytes).then(
71
+ async (response) => {
72
+ const transferTime = response[5] - response[4];
73
+ measurements.push(await this.measureSpeed(bytes, transferTime));
74
+ },
75
+ (error) => {
76
+ console.log(`Error: ${error}`);
77
+ }
78
+ );
79
+ }
80
+
81
+ return measurements;
82
+ }
83
+
84
+ public async measureUpload(bytes: number, iterations: number) {
85
+ const measurements: number[] = [];
86
+
87
+ for (let i = 0; i < iterations; i += 1) {
88
+ await this.upload(bytes).then(
89
+ async (response) => {
90
+ const transferTime = response[6];
91
+ measurements.push(await this.measureSpeed(bytes, transferTime));
92
+ },
93
+ (error) => {
94
+ console.log(`Error: ${error}`);
95
+ }
96
+ );
97
+ }
98
+
99
+ return measurements;
100
+ }
101
+
102
+ public async measureSpeed(bytes: number, duration: number) {
103
+ return (bytes * 8) / (duration / 1000) / 1e6;
104
+ }
105
+
106
+ public async fetchServerLocations(): Promise<{ [key: string]: string }> {
107
+ const res = JSON.parse(await this.get('speed.cloudflare.com', '/locations'));
108
+
109
+ return res.reduce((data: any, optionsArg: { iata: string; city: string }) => {
110
+ // Bypass prettier "no-assign-param" rules
111
+ const data1 = data;
112
+
113
+ data1[optionsArg.iata] = optionsArg.city;
114
+ return data1;
115
+ }, {});
116
+ }
117
+
118
+ public async get(hostname: string, path: string): Promise<string> {
119
+ return new Promise((resolve, reject) => {
120
+ const req = plugins.https.request(
121
+ {
122
+ hostname,
123
+ path,
124
+ method: 'GET',
125
+ },
126
+ (res) => {
127
+ const body: Array<Buffer> = [];
128
+ res.on('data', (chunk) => {
129
+ body.push(chunk);
130
+ });
131
+ res.on('end', () => {
132
+ try {
133
+ resolve(Buffer.concat(body).toString());
134
+ } catch (e) {
135
+ reject(e);
136
+ }
137
+ });
138
+ req.on('error', (err) => {
139
+ reject(err);
140
+ });
141
+ }
142
+ );
143
+
144
+ req.end();
145
+ });
146
+ }
147
+
148
+ public async download(bytes: number) {
149
+ const options = {
150
+ hostname: 'speed.cloudflare.com',
151
+ path: `/__down?bytes=${bytes}`,
152
+ method: 'GET',
153
+ };
154
+
155
+ return this.request(options);
156
+ }
157
+
158
+ public async upload(bytes: number) {
159
+ const data = '0'.repeat(bytes);
160
+ const options = {
161
+ hostname: 'speed.cloudflare.com',
162
+ path: '/__up',
163
+ method: 'POST',
164
+ headers: {
165
+ 'Content-Length': Buffer.byteLength(data),
166
+ },
167
+ };
168
+
169
+ return this.request(options, data);
170
+ }
171
+
172
+ public async request(options: plugins.https.RequestOptions, data = ''): Promise<number[]> {
173
+ let started: number;
174
+ let dnsLookup: number;
175
+ let tcpHandshake: number;
176
+ let sslHandshake: number;
177
+ let ttfb: number;
178
+ let ended: number;
179
+
180
+ return new Promise((resolve, reject) => {
181
+ started = plugins.perfHooks.performance.now();
182
+ const req = plugins.https.request(options, (res) => {
183
+ res.once('readable', () => {
184
+ ttfb = plugins.perfHooks.performance.now();
185
+ });
186
+ res.on('data', () => {});
187
+ res.on('end', () => {
188
+ ended = plugins.perfHooks.performance.now();
189
+ resolve([
190
+ started,
191
+ dnsLookup,
192
+ tcpHandshake,
193
+ sslHandshake,
194
+ ttfb,
195
+ ended,
196
+ parseFloat(res.headers['server-timing'].slice(22) as any),
197
+ ]);
198
+ });
199
+ });
200
+
201
+ req.on('socket', (socket) => {
202
+ socket.on('lookup', () => {
203
+ dnsLookup = plugins.perfHooks.performance.now();
204
+ });
205
+ socket.on('connect', () => {
206
+ tcpHandshake = plugins.perfHooks.performance.now();
207
+ });
208
+ socket.on('secureConnect', () => {
209
+ sslHandshake = plugins.perfHooks.performance.now();
210
+ });
211
+ });
212
+
213
+ req.on('error', (error) => {
214
+ reject(error);
215
+ });
216
+
217
+ req.write(data);
218
+ req.end();
219
+ });
220
+ }
221
+
222
+ public async fetchCfCdnCgiTrace(): Promise<{
223
+ fl: string;
224
+ h: string;
225
+ ip: string;
226
+ ts: string;
227
+ visit_scheme: string;
228
+ uag: string;
229
+ colo: string;
230
+ http: string;
231
+ loc: string;
232
+ tls: string;
233
+ sni: string;
234
+ warp: string;
235
+ gateway: string;
236
+ }> {
237
+ const parseCfCdnCgiTrace = (text: string) =>
238
+ text
239
+ .split('\n')
240
+ .map((i) => {
241
+ const j = i.split('=');
242
+
243
+ return [j[0], j[1]];
244
+ })
245
+ .reduce((data: any, [k, v]) => {
246
+ if (v === undefined) return data;
247
+
248
+ // Bypass prettier "no-assign-param" rules
249
+ const data1 = data;
250
+ // Object.fromEntries is only supported by Node.js 12 or newer
251
+ data1[k] = v;
252
+
253
+ return data1;
254
+ }, {});
255
+
256
+ return this.get('speed.cloudflare.com', '/cdn-cgi/trace').then(parseCfCdnCgiTrace);
257
+ }
258
+ }
@@ -0,0 +1,137 @@
1
+ import * as plugins from './smartnetwork.plugins.js';
2
+
3
+ import { CloudflareSpeed } from './smartnetwork.classes.cloudflarespeed.js';
4
+
5
+ /**
6
+ * SmartNetwork simplifies actions within the network
7
+ */
8
+ export class SmartNetwork {
9
+ /**
10
+ * get network speed
11
+ * @param measurementTime
12
+ */
13
+ public async getSpeed() {
14
+ const cloudflareSpeedInstance = new CloudflareSpeed();
15
+ const test = await cloudflareSpeedInstance.speedTest();
16
+ return test;
17
+ }
18
+
19
+ public async ping(hostArg: string, timeoutArg: number = 500): Promise<ReturnType<typeof plugins.smartping.Smartping.prototype.ping>> {
20
+ const smartpingInstance = new plugins.smartping.Smartping();
21
+ const pingResult = await smartpingInstance.ping(hostArg, timeoutArg);
22
+ return pingResult;
23
+ }
24
+
25
+ /**
26
+ * returns a promise with a boolean answer
27
+ * note: false also resolves with false as argument
28
+ * @param port
29
+ */
30
+ public async isLocalPortUnused(port: number): Promise<boolean> {
31
+ const doneIpV4 = plugins.smartpromise.defer<boolean>();
32
+ const doneIpV6 = plugins.smartpromise.defer<boolean>();
33
+ const net = await import('net'); // creates only one instance of net ;) even on multiple calls
34
+
35
+ // test IPv4 space
36
+ const ipv4Test = net.createServer();
37
+ ipv4Test.once('error', (err: any) => {
38
+ if (err.code !== 'EADDRINUSE') {
39
+ doneIpV4.resolve(false);
40
+ return;
41
+ }
42
+ doneIpV4.resolve(false);
43
+ });
44
+ ipv4Test.once('listening', () => {
45
+ ipv4Test.once('close', () => {
46
+ doneIpV4.resolve(true);
47
+ });
48
+ ipv4Test.close();
49
+ });
50
+ ipv4Test.listen(port, '0.0.0.0');
51
+
52
+ await doneIpV4.promise;
53
+
54
+ // test IPv6 space
55
+ const ipv6Test = net.createServer();
56
+ ipv6Test.once('error', function (err: any) {
57
+ if (err.code !== 'EADDRINUSE') {
58
+ doneIpV6.resolve(false);
59
+ return;
60
+ }
61
+ doneIpV6.resolve(false);
62
+ });
63
+ ipv6Test.once('listening', () => {
64
+ ipv6Test.once('close', () => {
65
+ doneIpV6.resolve(true);
66
+ });
67
+ ipv6Test.close();
68
+ });
69
+ ipv6Test.listen(port, '::');
70
+
71
+ // lets wait for the result
72
+ const resultIpV4 = await doneIpV4.promise;
73
+ const resultIpV6 = await doneIpV6.promise;
74
+ const result = resultIpV4 === true && resultIpV6 === true;
75
+ return result;
76
+ }
77
+
78
+ /**
79
+ * checks wether a remote port is available
80
+ * @param domainArg
81
+ */
82
+ public async isRemotePortAvailable(domainArg: string, portArg?: number): Promise<boolean> {
83
+ const done = plugins.smartpromise.defer<boolean>();
84
+ const domainPart = domainArg.split(':')[0];
85
+ const port = portArg ? portArg : parseInt(domainArg.split(':')[1], 10);
86
+
87
+ plugins.isopen(domainPart, port, (response: any) => {
88
+ console.log(response);
89
+ if (response[port.toString()].isOpen) {
90
+ done.resolve(true);
91
+ } else {
92
+ done.resolve(false);
93
+ }
94
+ });
95
+ const result = await done.promise;
96
+ return result;
97
+ }
98
+
99
+ public async getGateways() {
100
+ const result = plugins.os.networkInterfaces();
101
+ return result;
102
+ }
103
+
104
+ public async getDefaultGateway(): Promise<{
105
+ ipv4: plugins.os.NetworkInterfaceInfo;
106
+ ipv6: plugins.os.NetworkInterfaceInfo;
107
+ }> {
108
+ const defaultGatewayName = await plugins.systeminformation.networkInterfaceDefault();
109
+ if (!defaultGatewayName) {
110
+ console.log('Cannot determine default gateway');
111
+ return null;
112
+ }
113
+ const gateways = await this.getGateways();
114
+ const defaultGateway = gateways[defaultGatewayName];
115
+ return {
116
+ ipv4: defaultGateway[0],
117
+ ipv6: defaultGateway[1],
118
+ };
119
+ }
120
+
121
+ public async getPublicIps() {
122
+ return {
123
+ v4: await plugins.publicIp.publicIpv4({
124
+ timeout: 1000,
125
+ onlyHttps: true,
126
+ }).catch(async (err) => {
127
+ return null
128
+ }),
129
+ v6: await plugins.publicIp.publicIpv6({
130
+ timeout: 1000,
131
+ onlyHttps: true,
132
+ }).catch(async (err) => {
133
+ return null
134
+ })
135
+ };
136
+ }
137
+ }
@@ -0,0 +1,22 @@
1
+ // native scope
2
+ import * as os from 'os';
3
+ import * as https from 'https';
4
+ import * as perfHooks from 'perf_hooks';
5
+
6
+ export { os, https, perfHooks };
7
+
8
+ // @pushrocks scope
9
+ import * as smartping from '@pushrocks/smartping';
10
+ import * as smartpromise from '@pushrocks/smartpromise';
11
+ import * as smartstring from '@pushrocks/smartstring';
12
+
13
+ export { smartpromise, smartping, smartstring };
14
+
15
+ // @third party scope
16
+ // @ts-ignore
17
+ import isopen from 'isopen';
18
+ // @ts-ignore
19
+ import * as publicIp from 'public-ip';
20
+ import * as systeminformation from 'systeminformation';
21
+
22
+ export { isopen, publicIp, systeminformation };
@@ -0,0 +1,5 @@
1
+ {
2
+ "compilerOptions": {
3
+ "noImplicitAny": true
4
+ }
5
+ }