diff --git a/test/test.ts b/test/test.ts index b0e828f..ea7e08a 100644 --- a/test/test.ts +++ b/test/test.ts @@ -10,7 +10,8 @@ tap.test('should create a valid instance of SmartNetwork', async () => { tap.test('should perform a speedtest', async () => { const result = await testSmartNetwork.getSpeed(); - console.log(result); + console.log(`Download speed for this instance is ${result.downloadSpeed}`); + console.log(`Upload speed for this instance is ${result.uploadSpeed}`); }); tap.test('should determine wether a port is free', async () => { diff --git a/ts/smartnetwork.classes.cloudflarespeed.ts b/ts/smartnetwork.classes.cloudflarespeed.ts index 59355f1..929fe85 100644 --- a/ts/smartnetwork.classes.cloudflarespeed.ts +++ b/ts/smartnetwork.classes.cloudflarespeed.ts @@ -6,18 +6,39 @@ export class CloudflareSpeed { public async speedTest() { const latency = await this.measureLatency(); - + const serverLocations = await this.fetchServerLocations(); const cgiData = await this.fetchCfCdnCgiTrace(); + const testDown1 = await this.measureDownload(101000, 10); + + const testDown2 = await this.measureDownload(1001000, 8); + + const testDown3 = await this.measureDownload(10001000, 6); + + const testDown4 = await this.measureDownload(25001000, 4); + + const testDown5 = await this.measureDownload(100001000, 1); + + const downloadTests = [...testDown1, ...testDown2, ...testDown3, ...testDown4, ...testDown5]; + const speedDownload = stats.median(downloadTests).toFixed(2); + + const testUp1 = await this.measureUpload(11000, 10); + const testUp2 = await this.measureUpload(101000, 10); + const testUp3 = await this.measureUpload(1001000, 8); + const uploadTests = [...testUp1, ...testUp2, ...testUp3]; + const speedUpload = stats.median(uploadTests).toFixed(2); + return { ...latency, ip: cgiData.ip, serverLocation: { shortId: cgiData.colo, name: serverLocations[cgiData.colo], - availableLocations: serverLocations, - } + availableLocations: serverLocations, + }, + downloadSpeed: speedDownload, + uploadSpeed: speedUpload, }; } @@ -42,12 +63,50 @@ export class CloudflareSpeed { averageTime: stats.average(measurements), medianTime: stats.median(measurements), jitter: stats.jitter(measurements), - } - - ; + }; } - public async fetchServerLocations(): Promise<{[key: string]: string}> { + public async measureDownload(bytes: number, iterations: number) { + const measurements = []; + + for (let i = 0; i < iterations; i += 1) { + await this.download(bytes).then( + async (response) => { + const transferTime = response[5] - response[4]; + measurements.push(await this.measureSpeed(bytes, transferTime)); + }, + (error) => { + console.log(`Error: ${error}`); + } + ); + } + + return measurements; + } + + public async measureUpload(bytes: number, iterations: number) { + const measurements = []; + + for (let i = 0; i < iterations; i += 1) { + await this.upload(bytes).then( + async (response) => { + const transferTime = response[6]; + measurements.push(await this.measureSpeed(bytes, transferTime)); + }, + (error) => { + console.log(`Error: ${error}`); + } + ); + } + + return measurements; + } + + public async measureSpeed(bytes: number, duration: number) { + return (bytes * 8) / (duration / 1000) / 1e6; + } + + public async fetchServerLocations(): Promise<{ [key: string]: string }> { const res = JSON.parse(await this.get('speed.cloudflare.com', '/locations')); return res.reduce((data, { iata, city }) => { @@ -99,6 +158,20 @@ export class CloudflareSpeed { return this.request(options); } + public async upload(bytes: number) { + const data = '0'.repeat(bytes); + const options = { + hostname: 'speed.cloudflare.com', + path: '/__up', + method: 'POST', + headers: { + 'Content-Length': Buffer.byteLength(data), + }, + }; + + return this.request(options, data); + } + public async request(options, data = '') { let started; let dnsLookup; @@ -150,19 +223,19 @@ export class CloudflareSpeed { } 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, + 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