From 710887f4007150d26a717dc3b296cd6755ae1868 Mon Sep 17 00:00:00 2001 From: dosse91 Date: Tue, 13 Jun 2017 14:20:05 +0200 Subject: [PATCH 01/18] Added options for managing errors during test (abort/retry/ignore) --- doc.md | 10 ++++++++-- speedtest_worker.js | 31 +++++++++++++++++++++---------- speedtest_worker.min.js | 2 +- 3 files changed, 30 insertions(+), 13 deletions(-) diff --git a/doc.md b/doc.md index c4ad91d19..f94e52efd 100644 --- a/doc.md +++ b/doc.md @@ -1,7 +1,7 @@ # HTML5 Speedtest > by Federico Dossena -> Version 4.2.1, May 15 2017 +> Version 4.2.2, June 13 2017 > [https://github.com/adolfintel/speedtest/](https://github.com/adolfintel/speedtest/) @@ -164,6 +164,12 @@ w.postMessage('start {"param1": "value1", "param2": "value2", ...}') * Recommended: `>=1` * Default override: 1 on Firefox if enable_quirks is true * Default override: 10 on Safari if enable_quirks is true +* __xhr_ignoreErrors__: how to react to errors in download/upload streams and the ping test + * `0`: Fail test on error (behaviour of previous versions of this test) + * `1`: Restart a stream/ping when it fails + * `2`: Ignore all errors + * Default: `1` + * Recommended: `1` * __allow_fetchAPI__: allow the use of Fetch API for the download test instead of regular XHR. Experimental, not recommended. * Default: `false` * __force_fetchAPI__: forces the use of Fetch API on all browsers that support it @@ -182,7 +188,7 @@ w.postMessage('abort') This will terminate all network activity and stop the worker. -__Important:__ do not simply kill the worker while it's running, as it will leave pending XHR requests! +__Important:__ do not simply kill the worker while it's running, as it may leave pending XHR requests! ## Using the test without PHP diff --git a/speedtest_worker.js b/speedtest_worker.js index 19bc8aaf4..777c0bf6a 100644 --- a/speedtest_worker.js +++ b/speedtest_worker.js @@ -1,5 +1,5 @@ /* - HTML5 Speedtest v4.2.1 + HTML5 Speedtest v4.2.2 by Federico Dossena https://github.com/adolfintel/speedtest/ GNU LGPLv3 License @@ -24,6 +24,7 @@ var settings = { url_getIp: 'getIP.php', // path to getIP.php relative to this js file, or a similar thing that outputs the client's ip xhr_dlMultistream: 10, // number of download streams to use (can be different if enable_quirks is active) xhr_ulMultistream: 3, // number of upload streams to use (can be different if enable_quirks is active) + xhr_ignoreErrors: 1, // 0=fail on errors, 1=attempt to restart a stream if it fails, 2=ignore all errors xhr_dlUseBlob: false, // if set to true, it reduces ram usage but uses the hard drive (useful with large garbagePhp_chunkSize and/or high xhr_dlMultistream) garbagePhp_chunkSize: 20, // size of chunks sent by garbage.php (can be different if enable_quirks is active) enable_quirks: true, // enable quirks for specific browsers. currently it overrides settings to optimize for specific browsers, unless they are already being overridden with the start command @@ -180,10 +181,11 @@ function dlTest (done) { testStream(i, 0) }.bind(this) xhr[i].onerror = function () { - // error, abort - failed = true + // error + if (settings.xhr_ignoreErrors === 0) failed=true //abort try { xhr[i].abort() } catch (e) { } delete (xhr[i]) + if (settings.xhr_ignoreErrors === 1) testStream(i, 100) //restart stream after 100ms }.bind(this) // send xhr try { if (settings.xhr_dlUseBlob) xhr[i].responseType = 'blob'; else xhr[i].responseType = 'arraybuffer' } catch (e) { } @@ -251,9 +253,10 @@ function ulTest (done) { } xhr[i].onerror = function () { // error, abort - failed = true + if (settings.xhr_ignoreErrors === 0) failed = true //abort try { xhr[i].abort() } catch (e) { } delete (xhr[i]) + if (settings.xhr_ignoreErrors === 1) testStatus(i,100); //restart stream after 100ms } xhr[i].open('POST', settings.url_ul + '?r=' + Math.random(), true) // random string to prevent caching xhr[i].setRequestHeader('Content-Encoding', 'identity') // disable compression (some browsers may refuse it, but data is incompressible anyway) @@ -273,10 +276,10 @@ function ulTest (done) { testStream(i, 0) }.bind(this) xhr[i].upload.onerror = function () { - // error, abort - failed = true + if (settings.xhr_ignoreErrors === 0) failed=true //abort try { xhr[i].abort() } catch (e) { } delete (xhr[i]) + if (settings.xhr_ignoreErrors === 1) testStream(i, 100) //restart stream after 100ms }.bind(this) // send xhr xhr[i].open('POST', settings.url_ul + '?r=' + Math.random(), true) // random string to prevent caching @@ -337,10 +340,18 @@ function pingTest (done) { }.bind(this) xhr[0].onerror = function () { // a ping failed, cancel test - pingStatus = 'Fail' - jitterStatus = 'Fail' - clearRequests() - done() + if (settings.xhr_ignoreErrors === 0) { //abort + pingStatus = 'Fail' + jitterStatus = 'Fail' + clearRequests() + done() + } + if (settings.xhr_ignoreErrors === 1) doPing() //retry ping + + if(settings.xhr_ignoreErrors === 2){ //ignore failed ping + i++ + if (i < settings.count_ping) doPing(); else done() // more pings to do? + } }.bind(this) // sent xhr xhr[0].open('GET', settings.url_ping + '?r=' + Math.random(), true) // random string to prevent caching diff --git a/speedtest_worker.min.js b/speedtest_worker.min.js index fab73a0fb..2591c25ce 100644 --- a/speedtest_worker.min.js +++ b/speedtest_worker.min.js @@ -1 +1 @@ -function clearRequests(){if(xhr){for(var i=0;iloadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].onload=function(){try{xhr[i].abort()}catch(e){}testStream(i,0)}.bind(this),xhr[i].onerror=function(){failed=!0;try{xhr[i].abort()}catch(e){}delete xhr[i]}.bind(this);try{settings.xhr_dlUseBlob?xhr[i].responseType="blob":xhr[i].responseType="arraybuffer"}catch(e){}xhr[i].open("GET",settings.url_dl+"?r="+Math.random()+"&ckSize="+settings.garbagePhp_chunkSize,!0),xhr[i].send()}}.bind(this),1+delay)}.bind(this),i=0;it)){var speed=totLoaded/(t/1e3);dlStatus=(8*speed/925e3).toFixed(2),(t/1e3>settings.time_dl||failed)&&((failed||isNaN(dlStatus))&&(dlStatus="Fail"),clearRequests(),clearInterval(interval),done())}}.bind(this),200)}}function ulTest(done){if(!ulCalled){ulCalled=!0;var totLoaded=0,startT=(new Date).getTime(),failed=!1;xhr=[];for(var testStream=function(i,delay){setTimeout(function(){if(3===testStatus){var prevLoaded=0,x=new XMLHttpRequest;xhr[i]=x;var ie11workaround;try{xhr[i].upload.onprogress,ie11workaround=!1}catch(e){ie11workaround=!0}ie11workaround?(xhr[i].onload=function(){totLoaded+=262144,testStream(i,0)},xhr[i].onerror=function(){failed=!0;try{xhr[i].abort()}catch(e){}delete xhr[i]},xhr[i].open("POST",settings.url_ul+"?r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(reqsmall)):(xhr[i].upload.onprogress=function(event){if(3!==testStatus)try{x.abort()}catch(e){}var loadDiff=event.loaded<=0?0:event.loaded-prevLoaded;isNaN(loadDiff)||!isFinite(loadDiff)||0>loadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].upload.onload=function(){testStream(i,0)}.bind(this),xhr[i].upload.onerror=function(){failed=!0;try{xhr[i].abort()}catch(e){}delete xhr[i]}.bind(this),xhr[i].open("POST",settings.url_ul+"?r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(req))}}.bind(this),1)}.bind(this),i=0;it)){var speed=totLoaded/(t/1e3);ulStatus=(8*speed/925e3).toFixed(2),(t/1e3>settings.time_ul||failed)&&((failed||isNaN(ulStatus))&&(ulStatus="Fail"),clearRequests(),clearInterval(interval),done())}}.bind(this),200)}}function pingTest(done){if(!ptCalled){ptCalled=!0;var prevT=null,ping=0,jitter=0,i=0,prevInstspd=0;xhr=[];var doPing=function(){prevT=(new Date).getTime(),xhr[0]=new XMLHttpRequest,xhr[0].onload=function(){if(0===i)prevT=(new Date).getTime();else{var instspd=((new Date).getTime()-prevT)/2,instjitter=Math.abs(instspd-prevInstspd);1===i?ping=instspd:(ping=.9*ping+.1*instspd,jitter=instjitter>jitter?.2*jitter+.8*instjitter:.9*jitter+.1*instjitter),prevInstspd=instspd}pingStatus=ping.toFixed(2),jitterStatus=jitter.toFixed(2),i++,ii;i++)req.push(r);req=new Blob(req),r=new ArrayBuffer(262144);try{r=new Float32Array(r);for(var i=0;iloadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].onload=function(){try{xhr[i].abort()}catch(e){}testStream(i,0)}.bind(this),xhr[i].onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this);try{settings.xhr_dlUseBlob?xhr[i].responseType="blob":xhr[i].responseType="arraybuffer"}catch(e){}xhr[i].open("GET",settings.url_dl+"?r="+Math.random()+"&ckSize="+settings.garbagePhp_chunkSize,!0),xhr[i].send()}}.bind(this),1+delay)}.bind(this),i=0;it)){var speed=totLoaded/(t/1e3);dlStatus=(8*speed/925e3).toFixed(2),(t/1e3>settings.time_dl||failed)&&((failed||isNaN(dlStatus))&&(dlStatus="Fail"),clearRequests(),clearInterval(interval),done())}}.bind(this),200)}}function ulTest(done){if(!ulCalled){ulCalled=!0;var totLoaded=0,startT=(new Date).getTime(),failed=!1;xhr=[];for(var testStream=function(i,delay){setTimeout(function(){if(3===testStatus){var prevLoaded=0,x=new XMLHttpRequest;xhr[i]=x;var ie11workaround;try{xhr[i].upload.onprogress,ie11workaround=!1}catch(e){ie11workaround=!0}ie11workaround?(xhr[i].onload=function(){totLoaded+=262144,testStream(i,0)},xhr[i].onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStatus(i,100)},xhr[i].open("POST",settings.url_ul+"?r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(reqsmall)):(xhr[i].upload.onprogress=function(event){if(3!==testStatus)try{x.abort()}catch(e){}var loadDiff=event.loaded<=0?0:event.loaded-prevLoaded;isNaN(loadDiff)||!isFinite(loadDiff)||0>loadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].upload.onload=function(){testStream(i,0)}.bind(this),xhr[i].upload.onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this),xhr[i].open("POST",settings.url_ul+"?r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(req))}}.bind(this),1)}.bind(this),i=0;it)){var speed=totLoaded/(t/1e3);ulStatus=(8*speed/925e3).toFixed(2),(t/1e3>settings.time_ul||failed)&&((failed||isNaN(ulStatus))&&(ulStatus="Fail"),clearRequests(),clearInterval(interval),done())}}.bind(this),200)}}function pingTest(done){if(!ptCalled){ptCalled=!0;var prevT=null,ping=0,jitter=0,i=0,prevInstspd=0;xhr=[];var doPing=function(){prevT=(new Date).getTime(),xhr[0]=new XMLHttpRequest,xhr[0].onload=function(){if(0===i)prevT=(new Date).getTime();else{var instspd=((new Date).getTime()-prevT)/2,instjitter=Math.abs(instspd-prevInstspd);1===i?ping=instspd:(ping=.9*ping+.1*instspd,jitter=instjitter>jitter?.2*jitter+.8*instjitter:.9*jitter+.1*instjitter),prevInstspd=instspd}pingStatus=ping.toFixed(2),jitterStatus=jitter.toFixed(2),i++,ii;i++)req.push(r);req=new Blob(req),r=new ArrayBuffer(262144);try{r=new Float32Array(r);for(var i=0;i Date: Wed, 14 Jun 2017 12:33:11 +0200 Subject: [PATCH 02/18] Improved ping/jitter test --- doc.md | 5 +++-- empty.php | 1 + speedtest_worker.js | 4 ++-- speedtest_worker.min.js | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/doc.md b/doc.md index f94e52efd..67aa26bff 100644 --- a/doc.md +++ b/doc.md @@ -1,7 +1,7 @@ # HTML5 Speedtest > by Federico Dossena -> Version 4.2.2, June 13 2017 +> Version 4.2.3, June 14 2017 > [https://github.com/adolfintel/speedtest/](https://github.com/adolfintel/speedtest/) @@ -206,7 +206,7 @@ It is important here to turn off compression, and generate incompressible data. A symlink to `/dev/urandom` is also ok. #### Replacement for `empty.php` -Your replacement must simply respond with a HTTP code 200 and send nothing else. You may want to send additional headers to disable caching. +Your replacement must simply respond with a HTTP code 200 and send nothing else. You may want to send additional headers to disable caching. The test assumes that Connection:keep-alive is sent by the server. #### Replacement for `getIP.php` Your replacement must simply respond with the client's IP as plaintext. Nothing fancy. @@ -220,6 +220,7 @@ w.postMessage('start {"url_dl": "newGarbageURL", "url_ul": "newEmptyURL", "url_p ## Known bugs and limitations +* The ping/jitter test is measured by seeing how long it takes for an empty XHR to complete. It is not an acutal ICMP ping * __Chrome:__ high CPU usage from XHR requests with very fast connections (like gigabit). For this reason, the test may report inaccurate results if your CPU is too slow. (Does not affect most computers) * __IE11:__ the upload test is not precise on very fast connections diff --git a/empty.php b/empty.php index 7395f6cc7..eb1fc85b6 100644 --- a/empty.php +++ b/empty.php @@ -3,4 +3,5 @@ header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0"); header("Cache-Control: post-check=0, pre-check=0", false); header("Pragma: no-cache"); +header("Connection: keep-alive"); ?> \ No newline at end of file diff --git a/speedtest_worker.js b/speedtest_worker.js index 777c0bf6a..63e9d76d4 100644 --- a/speedtest_worker.js +++ b/speedtest_worker.js @@ -1,5 +1,5 @@ /* - HTML5 Speedtest v4.2.2 + HTML5 Speedtest v4.2.3 by Federico Dossena https://github.com/adolfintel/speedtest/ GNU LGPLv3 License @@ -325,7 +325,7 @@ function pingTest (done) { if (i === 0) { prevT = new Date().getTime() // first pong } else { - var instspd = (new Date().getTime() - prevT) / 2 + var instspd = (new Date().getTime() - prevT) var instjitter = Math.abs(instspd - prevInstspd) if (i === 1) ping = instspd; /* first ping, can't tell jiutter yet*/ else { ping = ping * 0.9 + instspd * 0.1 // ping, weighted average diff --git a/speedtest_worker.min.js b/speedtest_worker.min.js index 2591c25ce..8bc21e5bc 100644 --- a/speedtest_worker.min.js +++ b/speedtest_worker.min.js @@ -1 +1 @@ -function clearRequests(){if(xhr){for(var i=0;iloadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].onload=function(){try{xhr[i].abort()}catch(e){}testStream(i,0)}.bind(this),xhr[i].onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this);try{settings.xhr_dlUseBlob?xhr[i].responseType="blob":xhr[i].responseType="arraybuffer"}catch(e){}xhr[i].open("GET",settings.url_dl+"?r="+Math.random()+"&ckSize="+settings.garbagePhp_chunkSize,!0),xhr[i].send()}}.bind(this),1+delay)}.bind(this),i=0;it)){var speed=totLoaded/(t/1e3);dlStatus=(8*speed/925e3).toFixed(2),(t/1e3>settings.time_dl||failed)&&((failed||isNaN(dlStatus))&&(dlStatus="Fail"),clearRequests(),clearInterval(interval),done())}}.bind(this),200)}}function ulTest(done){if(!ulCalled){ulCalled=!0;var totLoaded=0,startT=(new Date).getTime(),failed=!1;xhr=[];for(var testStream=function(i,delay){setTimeout(function(){if(3===testStatus){var prevLoaded=0,x=new XMLHttpRequest;xhr[i]=x;var ie11workaround;try{xhr[i].upload.onprogress,ie11workaround=!1}catch(e){ie11workaround=!0}ie11workaround?(xhr[i].onload=function(){totLoaded+=262144,testStream(i,0)},xhr[i].onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStatus(i,100)},xhr[i].open("POST",settings.url_ul+"?r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(reqsmall)):(xhr[i].upload.onprogress=function(event){if(3!==testStatus)try{x.abort()}catch(e){}var loadDiff=event.loaded<=0?0:event.loaded-prevLoaded;isNaN(loadDiff)||!isFinite(loadDiff)||0>loadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].upload.onload=function(){testStream(i,0)}.bind(this),xhr[i].upload.onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this),xhr[i].open("POST",settings.url_ul+"?r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(req))}}.bind(this),1)}.bind(this),i=0;it)){var speed=totLoaded/(t/1e3);ulStatus=(8*speed/925e3).toFixed(2),(t/1e3>settings.time_ul||failed)&&((failed||isNaN(ulStatus))&&(ulStatus="Fail"),clearRequests(),clearInterval(interval),done())}}.bind(this),200)}}function pingTest(done){if(!ptCalled){ptCalled=!0;var prevT=null,ping=0,jitter=0,i=0,prevInstspd=0;xhr=[];var doPing=function(){prevT=(new Date).getTime(),xhr[0]=new XMLHttpRequest,xhr[0].onload=function(){if(0===i)prevT=(new Date).getTime();else{var instspd=((new Date).getTime()-prevT)/2,instjitter=Math.abs(instspd-prevInstspd);1===i?ping=instspd:(ping=.9*ping+.1*instspd,jitter=instjitter>jitter?.2*jitter+.8*instjitter:.9*jitter+.1*instjitter),prevInstspd=instspd}pingStatus=ping.toFixed(2),jitterStatus=jitter.toFixed(2),i++,ii;i++)req.push(r);req=new Blob(req),r=new ArrayBuffer(262144);try{r=new Float32Array(r);for(var i=0;iloadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].onload=function(){try{xhr[i].abort()}catch(e){}testStream(i,0)}.bind(this),xhr[i].onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this);try{settings.xhr_dlUseBlob?xhr[i].responseType="blob":xhr[i].responseType="arraybuffer"}catch(e){}xhr[i].open("GET",settings.url_dl+"?r="+Math.random()+"&ckSize="+settings.garbagePhp_chunkSize,!0),xhr[i].send()}}.bind(this),1+delay)}.bind(this),i=0;it)){var speed=totLoaded/(t/1e3);dlStatus=(8*speed/925e3).toFixed(2),(t/1e3>settings.time_dl||failed)&&((failed||isNaN(dlStatus))&&(dlStatus="Fail"),clearRequests(),clearInterval(interval),done())}}.bind(this),200)}}function ulTest(done){if(!ulCalled){ulCalled=!0;var totLoaded=0,startT=(new Date).getTime(),failed=!1;xhr=[];for(var testStream=function(i,delay){setTimeout(function(){if(3===testStatus){var prevLoaded=0,x=new XMLHttpRequest;xhr[i]=x;var ie11workaround;try{xhr[i].upload.onprogress,ie11workaround=!1}catch(e){ie11workaround=!0}ie11workaround?(xhr[i].onload=function(){totLoaded+=262144,testStream(i,0)},xhr[i].onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStatus(i,100)},xhr[i].open("POST",settings.url_ul+"?r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(reqsmall)):(xhr[i].upload.onprogress=function(event){if(3!==testStatus)try{x.abort()}catch(e){}var loadDiff=event.loaded<=0?0:event.loaded-prevLoaded;isNaN(loadDiff)||!isFinite(loadDiff)||0>loadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].upload.onload=function(){testStream(i,0)}.bind(this),xhr[i].upload.onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this),xhr[i].open("POST",settings.url_ul+"?r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(req))}}.bind(this),1)}.bind(this),i=0;it)){var speed=totLoaded/(t/1e3);ulStatus=(8*speed/925e3).toFixed(2),(t/1e3>settings.time_ul||failed)&&((failed||isNaN(ulStatus))&&(ulStatus="Fail"),clearRequests(),clearInterval(interval),done())}}.bind(this),200)}}function pingTest(done){if(!ptCalled){ptCalled=!0;var prevT=null,ping=0,jitter=0,i=0,prevInstspd=0;xhr=[];var doPing=function(){prevT=(new Date).getTime(),xhr[0]=new XMLHttpRequest,xhr[0].onload=function(){if(0===i)prevT=(new Date).getTime();else{var instspd=(new Date).getTime()-prevT,instjitter=Math.abs(instspd-prevInstspd);1===i?ping=instspd:(ping=.9*ping+.1*instspd,jitter=instjitter>jitter?.2*jitter+.8*instjitter:.9*jitter+.1*instjitter),prevInstspd=instspd}pingStatus=ping.toFixed(2),jitterStatus=jitter.toFixed(2),i++,ii;i++)req.push(r);req=new Blob(req),r=new ArrayBuffer(262144);try{r=new Float32Array(r);for(var i=0;i Date: Thu, 15 Jun 2017 07:02:35 +0200 Subject: [PATCH 03/18] Added configuration for custom compensation factor; Fixed a problem where xhr_ignoreErrors could not be changed --- doc.md | 6 +++++- speedtest_worker.js | 11 +++++++---- speedtest_worker.min.js | 2 +- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/doc.md b/doc.md index 67aa26bff..e90aa4634 100644 --- a/doc.md +++ b/doc.md @@ -1,7 +1,7 @@ # HTML5 Speedtest > by Federico Dossena -> Version 4.2.3, June 14 2017 +> Version 4.2.4, June 15 2017 > [https://github.com/adolfintel/speedtest/](https://github.com/adolfintel/speedtest/) @@ -178,6 +178,10 @@ Fetch API are used if the following conditions are met: * allow_fetchAPI is true * Chromium-based browser with support for Fetch API and enable_quirks is true OR force_fetchAPI is true and the browser supports Fetch API +* __overheadCompensationFactor__: compensation for HTTP and network overhead. Default value assumes typical MTUs used over the Internet. You might want to change this if you're using this in your internal network with different MTUs, or if you're using IPv6 instead of IPv4. + * Default: `1.13359567567567567568` (1048576/925000) assumes HTTP+TCP+IPv4+ETH with typical MTUs used over the Internet + * `1460 / 1514`: TCP+IPv4+ETH, ignoring HTTP overhead (this measures the speed at which you actually download/upload files instead of estimating the speed of the connection) + * `1440 / 1514`: TCP+IPv6+ETH, ignoring HTTP overhead (this measures the speed at which you actually download/upload files instead of estimating the speed of the connection) ### Aborting the test prematurely The test can be aborted at any time by sending an abort command to the worker: diff --git a/speedtest_worker.js b/speedtest_worker.js index 63e9d76d4..98739b576 100644 --- a/speedtest_worker.js +++ b/speedtest_worker.js @@ -1,5 +1,5 @@ /* - HTML5 Speedtest v4.2.3 + HTML5 Speedtest v4.2.4 by Federico Dossena https://github.com/adolfintel/speedtest/ GNU LGPLv3 License @@ -29,7 +29,8 @@ var settings = { garbagePhp_chunkSize: 20, // size of chunks sent by garbage.php (can be different if enable_quirks is active) enable_quirks: true, // enable quirks for specific browsers. currently it overrides settings to optimize for specific browsers, unless they are already being overridden with the start command allow_fetchAPI: false, // enables Fetch API. currently disabled because it leaks memory like no tomorrow - force_fetchAPI: false // when Fetch API is enabled, it will force usage on every browser that supports it + force_fetchAPI: false, // when Fetch API is enabled, it will force usage on every browser that supports it + overheadCompensationFactor: 1048576/925000 //compensation for HTTP+TCP+IP+ETH overhead. 925000 is how much data is actually carried over 1048576 (1mb) bytes downloaded/uploaded. This default value assumes HTTP+TCP+IPv4+ETH with typical MTUs over the Internet. You may want to change this if you're going through your local network with a different MTU or if you're going over IPv6 (see doc.md for some other values) } var xhr = null // array of currently active xhr requests @@ -95,9 +96,11 @@ this.addEventListener('message', function (e) { if (typeof s.count_ping !== 'undefined') settings.count_ping = s.count_ping // number of pings for ping test if (typeof s.xhr_dlMultistream !== 'undefined') settings.xhr_dlMultistream = s.xhr_dlMultistream // number of download streams if (typeof s.xhr_ulMultistream !== 'undefined') settings.xhr_ulMultistream = s.xhr_ulMultistream // number of upload streams + if (typeof s.xhr_ignoreErrors !== 'undefined') settings.xhr_ignoreErrors = s.xhr_ignoreErrors // what to do in case of errors during the test if (typeof s.xhr_dlUseBlob !== 'undefined') settings.xhr_dlUseBlob = s.xhr_dlUseBlob // use blob for download test if (typeof s.garbagePhp_chunkSize !== 'undefined') settings.garbagePhp_chunkSize = s.garbagePhp_chunkSize // size of garbage.php chunks if (typeof s.force_fetchAPI !== 'undefined') settings.force_fetchAPI = s.force_fetchAPI // use fetch api on all browsers that support it if enabled + if (typeof s.overheadCompensationFactor !== 'undefined') settings.overheadCompensationFactor = s.overheadCompensationFactor //custom overhead compensation factor (default assumes HTTP+TCP+IP+ETH with typical MTUs) if (settings.allow_fetchAPI && settings.force_fetchAPI && (!!self.fetch)) useFetchAPI = true } catch (e) { } // run the tests @@ -203,7 +206,7 @@ function dlTest (done) { var t = new Date().getTime() - startT if (t < 200) return var speed = totLoaded / (t / 1000.0) - dlStatus = ((speed * 8) / 925000.0).toFixed(2) // 925000 instead of 1048576 to account for overhead + dlStatus = ((speed * 8 * settings.overheadCompensationFactor)/1048576).toFixed(2) // speed is multiplied by 8 to go from bytes to bits, overhead compensation is applied, then everything is divided by 1048576 to go to megabits/s if ((t / 1000.0) > settings.time_dl || failed) { // test is over, stop streams and timer if (failed || isNaN(dlStatus)) dlStatus = 'Fail' clearRequests() @@ -297,7 +300,7 @@ function ulTest (done) { var t = new Date().getTime() - startT if (t < 200) return var speed = totLoaded / (t / 1000.0) - ulStatus = ((speed * 8) / 925000.0).toFixed(2) // 925000 instead of 1048576 to account for overhead + ulStatus = ((speed * 8 * settings.overheadCompensationFactor)/1048576).toFixed(2) // speed is multiplied by 8 to go from bytes to bits, overhead compensation is applied, then everything is divided by 1048576 to go to megabits/s if ((t / 1000.0) > settings.time_ul || failed) { // test is over, stop streams and timer if (failed || isNaN(ulStatus)) ulStatus = 'Fail' clearRequests() diff --git a/speedtest_worker.min.js b/speedtest_worker.min.js index 8bc21e5bc..4bc7afb4c 100644 --- a/speedtest_worker.min.js +++ b/speedtest_worker.min.js @@ -1 +1 @@ -function clearRequests(){if(xhr){for(var i=0;iloadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].onload=function(){try{xhr[i].abort()}catch(e){}testStream(i,0)}.bind(this),xhr[i].onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this);try{settings.xhr_dlUseBlob?xhr[i].responseType="blob":xhr[i].responseType="arraybuffer"}catch(e){}xhr[i].open("GET",settings.url_dl+"?r="+Math.random()+"&ckSize="+settings.garbagePhp_chunkSize,!0),xhr[i].send()}}.bind(this),1+delay)}.bind(this),i=0;it)){var speed=totLoaded/(t/1e3);dlStatus=(8*speed/925e3).toFixed(2),(t/1e3>settings.time_dl||failed)&&((failed||isNaN(dlStatus))&&(dlStatus="Fail"),clearRequests(),clearInterval(interval),done())}}.bind(this),200)}}function ulTest(done){if(!ulCalled){ulCalled=!0;var totLoaded=0,startT=(new Date).getTime(),failed=!1;xhr=[];for(var testStream=function(i,delay){setTimeout(function(){if(3===testStatus){var prevLoaded=0,x=new XMLHttpRequest;xhr[i]=x;var ie11workaround;try{xhr[i].upload.onprogress,ie11workaround=!1}catch(e){ie11workaround=!0}ie11workaround?(xhr[i].onload=function(){totLoaded+=262144,testStream(i,0)},xhr[i].onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStatus(i,100)},xhr[i].open("POST",settings.url_ul+"?r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(reqsmall)):(xhr[i].upload.onprogress=function(event){if(3!==testStatus)try{x.abort()}catch(e){}var loadDiff=event.loaded<=0?0:event.loaded-prevLoaded;isNaN(loadDiff)||!isFinite(loadDiff)||0>loadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].upload.onload=function(){testStream(i,0)}.bind(this),xhr[i].upload.onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this),xhr[i].open("POST",settings.url_ul+"?r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(req))}}.bind(this),1)}.bind(this),i=0;it)){var speed=totLoaded/(t/1e3);ulStatus=(8*speed/925e3).toFixed(2),(t/1e3>settings.time_ul||failed)&&((failed||isNaN(ulStatus))&&(ulStatus="Fail"),clearRequests(),clearInterval(interval),done())}}.bind(this),200)}}function pingTest(done){if(!ptCalled){ptCalled=!0;var prevT=null,ping=0,jitter=0,i=0,prevInstspd=0;xhr=[];var doPing=function(){prevT=(new Date).getTime(),xhr[0]=new XMLHttpRequest,xhr[0].onload=function(){if(0===i)prevT=(new Date).getTime();else{var instspd=(new Date).getTime()-prevT,instjitter=Math.abs(instspd-prevInstspd);1===i?ping=instspd:(ping=.9*ping+.1*instspd,jitter=instjitter>jitter?.2*jitter+.8*instjitter:.9*jitter+.1*instjitter),prevInstspd=instspd}pingStatus=ping.toFixed(2),jitterStatus=jitter.toFixed(2),i++,ii;i++)req.push(r);req=new Blob(req),r=new ArrayBuffer(262144);try{r=new Float32Array(r);for(var i=0;iloadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].onload=function(){try{xhr[i].abort()}catch(e){}testStream(i,0)}.bind(this),xhr[i].onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this);try{settings.xhr_dlUseBlob?xhr[i].responseType="blob":xhr[i].responseType="arraybuffer"}catch(e){}xhr[i].open("GET",settings.url_dl+"?r="+Math.random()+"&ckSize="+settings.garbagePhp_chunkSize,!0),xhr[i].send()}}.bind(this),1+delay)}.bind(this),i=0;it)){var speed=totLoaded/(t/1e3);dlStatus=(8*speed*settings.overheadCompensationFactor/1048576).toFixed(2),(t/1e3>settings.time_dl||failed)&&((failed||isNaN(dlStatus))&&(dlStatus="Fail"),clearRequests(),clearInterval(interval),done())}}.bind(this),200)}}function ulTest(done){if(!ulCalled){ulCalled=!0;var totLoaded=0,startT=(new Date).getTime(),failed=!1;xhr=[];for(var testStream=function(i,delay){setTimeout(function(){if(3===testStatus){var prevLoaded=0,x=new XMLHttpRequest;xhr[i]=x;var ie11workaround;try{xhr[i].upload.onprogress,ie11workaround=!1}catch(e){ie11workaround=!0}ie11workaround?(xhr[i].onload=function(){totLoaded+=262144,testStream(i,0)},xhr[i].onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStatus(i,100)},xhr[i].open("POST",settings.url_ul+"?r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(reqsmall)):(xhr[i].upload.onprogress=function(event){if(3!==testStatus)try{x.abort()}catch(e){}var loadDiff=event.loaded<=0?0:event.loaded-prevLoaded;isNaN(loadDiff)||!isFinite(loadDiff)||0>loadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].upload.onload=function(){testStream(i,0)}.bind(this),xhr[i].upload.onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this),xhr[i].open("POST",settings.url_ul+"?r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(req))}}.bind(this),1)}.bind(this),i=0;it)){var speed=totLoaded/(t/1e3);ulStatus=(8*speed*settings.overheadCompensationFactor/1048576).toFixed(2),(t/1e3>settings.time_ul||failed)&&((failed||isNaN(ulStatus))&&(ulStatus="Fail"),clearRequests(),clearInterval(interval),done())}}.bind(this),200)}}function pingTest(done){if(!ptCalled){ptCalled=!0;var prevT=null,ping=0,jitter=0,i=0,prevInstspd=0;xhr=[];var doPing=function(){prevT=(new Date).getTime(),xhr[0]=new XMLHttpRequest,xhr[0].onload=function(){if(0===i)prevT=(new Date).getTime();else{var instspd=(new Date).getTime()-prevT,instjitter=Math.abs(instspd-prevInstspd);1===i?ping=instspd:(ping=.9*ping+.1*instspd,jitter=instjitter>jitter?.2*jitter+.8*instjitter:.9*jitter+.1*instjitter),prevInstspd=instspd}pingStatus=ping.toFixed(2),jitterStatus=jitter.toFixed(2),i++,ii;i++)req.push(r);req=new Blob(req),r=new ArrayBuffer(262144);try{r=new Float32Array(r);for(var i=0;i Date: Thu, 15 Jun 2017 07:25:26 +0200 Subject: [PATCH 04/18] Fixed a typo in IE11-specific code --- speedtest_worker.js | 2 +- speedtest_worker.min.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/speedtest_worker.js b/speedtest_worker.js index 98739b576..dade6c8ff 100644 --- a/speedtest_worker.js +++ b/speedtest_worker.js @@ -259,7 +259,7 @@ function ulTest (done) { if (settings.xhr_ignoreErrors === 0) failed = true //abort try { xhr[i].abort() } catch (e) { } delete (xhr[i]) - if (settings.xhr_ignoreErrors === 1) testStatus(i,100); //restart stream after 100ms + if (settings.xhr_ignoreErrors === 1) testStream(i,100); //restart stream after 100ms } xhr[i].open('POST', settings.url_ul + '?r=' + Math.random(), true) // random string to prevent caching xhr[i].setRequestHeader('Content-Encoding', 'identity') // disable compression (some browsers may refuse it, but data is incompressible anyway) diff --git a/speedtest_worker.min.js b/speedtest_worker.min.js index 4bc7afb4c..4387e1eaa 100644 --- a/speedtest_worker.min.js +++ b/speedtest_worker.min.js @@ -1 +1 @@ -function clearRequests(){if(xhr){for(var i=0;iloadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].onload=function(){try{xhr[i].abort()}catch(e){}testStream(i,0)}.bind(this),xhr[i].onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this);try{settings.xhr_dlUseBlob?xhr[i].responseType="blob":xhr[i].responseType="arraybuffer"}catch(e){}xhr[i].open("GET",settings.url_dl+"?r="+Math.random()+"&ckSize="+settings.garbagePhp_chunkSize,!0),xhr[i].send()}}.bind(this),1+delay)}.bind(this),i=0;it)){var speed=totLoaded/(t/1e3);dlStatus=(8*speed*settings.overheadCompensationFactor/1048576).toFixed(2),(t/1e3>settings.time_dl||failed)&&((failed||isNaN(dlStatus))&&(dlStatus="Fail"),clearRequests(),clearInterval(interval),done())}}.bind(this),200)}}function ulTest(done){if(!ulCalled){ulCalled=!0;var totLoaded=0,startT=(new Date).getTime(),failed=!1;xhr=[];for(var testStream=function(i,delay){setTimeout(function(){if(3===testStatus){var prevLoaded=0,x=new XMLHttpRequest;xhr[i]=x;var ie11workaround;try{xhr[i].upload.onprogress,ie11workaround=!1}catch(e){ie11workaround=!0}ie11workaround?(xhr[i].onload=function(){totLoaded+=262144,testStream(i,0)},xhr[i].onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStatus(i,100)},xhr[i].open("POST",settings.url_ul+"?r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(reqsmall)):(xhr[i].upload.onprogress=function(event){if(3!==testStatus)try{x.abort()}catch(e){}var loadDiff=event.loaded<=0?0:event.loaded-prevLoaded;isNaN(loadDiff)||!isFinite(loadDiff)||0>loadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].upload.onload=function(){testStream(i,0)}.bind(this),xhr[i].upload.onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this),xhr[i].open("POST",settings.url_ul+"?r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(req))}}.bind(this),1)}.bind(this),i=0;it)){var speed=totLoaded/(t/1e3);ulStatus=(8*speed*settings.overheadCompensationFactor/1048576).toFixed(2),(t/1e3>settings.time_ul||failed)&&((failed||isNaN(ulStatus))&&(ulStatus="Fail"),clearRequests(),clearInterval(interval),done())}}.bind(this),200)}}function pingTest(done){if(!ptCalled){ptCalled=!0;var prevT=null,ping=0,jitter=0,i=0,prevInstspd=0;xhr=[];var doPing=function(){prevT=(new Date).getTime(),xhr[0]=new XMLHttpRequest,xhr[0].onload=function(){if(0===i)prevT=(new Date).getTime();else{var instspd=(new Date).getTime()-prevT,instjitter=Math.abs(instspd-prevInstspd);1===i?ping=instspd:(ping=.9*ping+.1*instspd,jitter=instjitter>jitter?.2*jitter+.8*instjitter:.9*jitter+.1*instjitter),prevInstspd=instspd}pingStatus=ping.toFixed(2),jitterStatus=jitter.toFixed(2),i++,ii;i++)req.push(r);req=new Blob(req),r=new ArrayBuffer(262144);try{r=new Float32Array(r);for(var i=0;iloadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].onload=function(){try{xhr[i].abort()}catch(e){}testStream(i,0)}.bind(this),xhr[i].onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this);try{settings.xhr_dlUseBlob?xhr[i].responseType="blob":xhr[i].responseType="arraybuffer"}catch(e){}xhr[i].open("GET",settings.url_dl+"?r="+Math.random()+"&ckSize="+settings.garbagePhp_chunkSize,!0),xhr[i].send()}}.bind(this),1+delay)}.bind(this),i=0;it)){var speed=totLoaded/(t/1e3);dlStatus=(8*speed*settings.overheadCompensationFactor/1048576).toFixed(2),(t/1e3>settings.time_dl||failed)&&((failed||isNaN(dlStatus))&&(dlStatus="Fail"),clearRequests(),clearInterval(interval),done())}}.bind(this),200)}}function ulTest(done){if(!ulCalled){ulCalled=!0;var totLoaded=0,startT=(new Date).getTime(),failed=!1;xhr=[];for(var testStream=function(i,delay){setTimeout(function(){if(3===testStatus){var prevLoaded=0,x=new XMLHttpRequest;xhr[i]=x;var ie11workaround;try{xhr[i].upload.onprogress,ie11workaround=!1}catch(e){ie11workaround=!0}ie11workaround?(xhr[i].onload=function(){totLoaded+=262144,testStream(i,0)},xhr[i].onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)},xhr[i].open("POST",settings.url_ul+"?r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(reqsmall)):(xhr[i].upload.onprogress=function(event){if(3!==testStatus)try{x.abort()}catch(e){}var loadDiff=event.loaded<=0?0:event.loaded-prevLoaded;isNaN(loadDiff)||!isFinite(loadDiff)||0>loadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].upload.onload=function(){testStream(i,0)}.bind(this),xhr[i].upload.onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this),xhr[i].open("POST",settings.url_ul+"?r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(req))}}.bind(this),1)}.bind(this),i=0;it)){var speed=totLoaded/(t/1e3);ulStatus=(8*speed*settings.overheadCompensationFactor/1048576).toFixed(2),(t/1e3>settings.time_ul||failed)&&((failed||isNaN(ulStatus))&&(ulStatus="Fail"),clearRequests(),clearInterval(interval),done())}}.bind(this),200)}}function pingTest(done){if(!ptCalled){ptCalled=!0;var prevT=null,ping=0,jitter=0,i=0,prevInstspd=0;xhr=[];var doPing=function(){prevT=(new Date).getTime(),xhr[0]=new XMLHttpRequest,xhr[0].onload=function(){if(0===i)prevT=(new Date).getTime();else{var instspd=(new Date).getTime()-prevT,instjitter=Math.abs(instspd-prevInstspd);1===i?ping=instspd:(ping=.9*ping+.1*instspd,jitter=instjitter>jitter?.2*jitter+.8*instjitter:.9*jitter+.1*instjitter),prevInstspd=instspd}pingStatus=ping.toFixed(2),jitterStatus=jitter.toFixed(2),i++,ii;i++)req.push(r);req=new Blob(req),r=new ArrayBuffer(262144);try{r=new Float32Array(r);for(var i=0;i Date: Fri, 16 Jun 2017 06:59:11 +0200 Subject: [PATCH 05/18] Added customizable grace time at beginning of tests; GET parameters can now be used in the URLs in the settings; Minor changes --- .gitignore | 1 + doc.md | 47 ++++++++++++++++------- example6.html | 4 +- speedtest_worker.js | 83 ++++++++++++++++++++++++++++------------- speedtest_worker.min.js | 2 +- 5 files changed, 95 insertions(+), 42 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..52b8dffa3 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +ugly.bat diff --git a/doc.md b/doc.md index e90aa4634..89f344716 100644 --- a/doc.md +++ b/doc.md @@ -1,7 +1,7 @@ # HTML5 Speedtest > by Federico Dossena -> Version 4.2.4, June 15 2017 +> Version 4.2.5, June 16 2017 > [https://github.com/adolfintel/speedtest/](https://github.com/adolfintel/speedtest/) @@ -95,32 +95,32 @@ format: * `5` = Test aborted * __dlStatus__ is either * Empty string (not started or aborted) - * Download speed in Megabit/s as a number with 2 digits + * Download speed in Megabit/s as a number with 2 decimals * The string "Fail" (test failed) * __ulStatus__ is either * Empty string (not started or aborted) - * Upload speed in Megabit/s as a number with 2 digits + * Upload speed in Megabit/s as a number with 2 decimals * The string "Fail" (test failed) * __pingStatus__ is either * Empty string (not started or aborted) - * Estimated ping in milliseconds as a number with 2 digits + * Estimated ping in milliseconds as a number with 2 decimals * The string "Fail" (test failed) * __clientIp__ is either * Empty string (not fetched yet or failed) * The client's IP address as a string * __jitterStatus__ is either * Empty string (not started or aborted) - * Estimated jitter in milliseconds as a number with 2 digits (lower = stable connection) + * Estimated jitter in milliseconds as a number with 2 decimals (lower = stable connection) * The string "Fail" (test failed) ### Starting the test -To start the test, send the start command to the worker: +To start the test with the default settings, which is usually the best choice, send the start command to the worker: ```js w.postMessage('start') ``` -This starts the test with the default settings, which is usually the best choice. If you want, you can change these settings and pass them to the worker as JSON with like this: +If you want, you can change these settings and pass them to the worker as JSON when you start it, like this: ```js w.postMessage('start {"param1": "value1", "param2": "value2", ...}') @@ -139,7 +139,7 @@ w.postMessage('start {"param1": "value1", "param2": "value2", ...}') * __url_dl__: path to garbage.php or a large file to use for the download test * Default: `garbage.php` * __Important:__ path is relative to js file -* __url_ul__: path to ab empty file or empty.php to use for the upload test +* __url_ul__: path to an empty file or empty.php to use for the upload test * Default: `empty.php` * __Important:__ path is relative to js file * __url_ping__: path to an empty file or empty.php to use for the ping test @@ -148,7 +148,9 @@ w.postMessage('start {"param1": "value1", "param2": "value2", ...}') * __url_getIp__: path to getIP.php or replacement * Default: `getIP.php` * __Important:__ path is relative to js file -* __enable_quirks__: enables browser-specific optimizations. These optimizations override some of the default settings below. They do not override settings that are explicitly set. + +#### Advanced test parameters +* __enable_quirks__: enables browser-specific optimizations. These optimizations override some of the default settings. They do not override settings that are explicitly set. * Default: `true` * __garbagePhp_chunkSize__: size of chunks sent by garbage.php in megabytes * Default: `20` @@ -170,6 +172,12 @@ w.postMessage('start {"param1": "value1", "param2": "value2", ...}') * `2`: Ignore all errors * Default: `1` * Recommended: `1` +* __time_dlGraceTime__: How long to wait (in seconds) before actually measuring the download speed. This is a good idea because we want to wait for the TCP window to be at its maximum (or close to it) + * Default: `1.5` + * Recommended: `>=0` +* __time_ulGraceTime__: How long to wait (in seconds) before actually measuring the upload speed. This is a good idea because we want to wait for the buffers to be full (avoids the peak at the beginning of the test) + * Default: `3` + * Recommended: `>=1` * __allow_fetchAPI__: allow the use of Fetch API for the download test instead of regular XHR. Experimental, not recommended. * Default: `false` * __force_fetchAPI__: forces the use of Fetch API on all browsers that support it @@ -180,8 +188,11 @@ Fetch API are used if the following conditions are met: OR force_fetchAPI is true and the browser supports Fetch API * __overheadCompensationFactor__: compensation for HTTP and network overhead. Default value assumes typical MTUs used over the Internet. You might want to change this if you're using this in your internal network with different MTUs, or if you're using IPv6 instead of IPv4. * Default: `1.13359567567567567568` (1048576/925000) assumes HTTP+TCP+IPv4+ETH with typical MTUs used over the Internet - * `1460 / 1514`: TCP+IPv4+ETH, ignoring HTTP overhead (this measures the speed at which you actually download/upload files instead of estimating the speed of the connection) - * `1440 / 1514`: TCP+IPv6+ETH, ignoring HTTP overhead (this measures the speed at which you actually download/upload files instead of estimating the speed of the connection) + * `1.0513`: HTTP+TCP+IPv6+ETH, over the Internet (empirically tested, not calculated) + * `1.0369`: Alternative value for HTTP+TCP+IPv4+ETH, over the Internet (empirically tested, not calculated) + * `1460 / 1514`: TCP+IPv4+ETH, ignoring HTTP overhead + * `1440 / 1514`: TCP+IPv6+ETH, ignoring HTTP overhead + * `1`: ignore overheads. This measures the speed at which you actually download and upload files ### Aborting the test prematurely The test can be aborted at any time by sending an abort command to the worker: @@ -194,6 +205,15 @@ This will terminate all network activity and stop the worker. __Important:__ do not simply kill the worker while it's running, as it may leave pending XHR requests! +## Troubleshooting +These are the most common issues reported by users, and how to fix them + +#### Download test gives very low result +Are garbage.php and empty.php (or your replacements) reachable? +Press F12, select network and start the test. Do you see errors? (cancelled requests are not errors) + +#### Upload test is inaccurate, and I see lag spikes +Check your server's maximum POST size, make sure it's at least 20Mbytes, possibly more ## Using the test without PHP If your server does not support PHP, or you're using something newer like Node.js, you can still use this test by replacing `garbage.php`, `empty.php` and `getIP.php` with equivalents. @@ -215,7 +235,7 @@ Your replacement must simply respond with a HTTP code 200 and send nothing else. #### Replacement for `getIP.php` Your replacement must simply respond with the client's IP as plaintext. Nothing fancy. -### JS +#### JS You need to start the test with your replacements like this: ```js @@ -228,6 +248,7 @@ w.postMessage('start {"url_dl": "newGarbageURL", "url_ul": "newEmptyURL", "url_p * __Chrome:__ high CPU usage from XHR requests with very fast connections (like gigabit). For this reason, the test may report inaccurate results if your CPU is too slow. (Does not affect most computers) * __IE11:__ the upload test is not precise on very fast connections +* __IE11:__ the upload test may not work over HTTPS * __Safari:__ works, but needs more testing and tweaking for very fast connections ## Making changes @@ -241,7 +262,7 @@ To create the minified version, use UglifyJS like this: uglifyjs -c --screw-ie8 speedtest_worker.js > speedtest_worker.min.js ``` -Pull requests are much appreciated. If you don't use github (or git), simply contact me. +Pull requests are much appreciated. If you don't use github (or git), simply contact me at dosse91@paranoici.org. __Important:__ please add your name to modified versions to distinguish them from the main project. diff --git a/example6.html b/example6.html index 2d7a0fae0..3a420d504 100644 --- a/example6.html +++ b/example6.html @@ -88,8 +88,8 @@

Jitter

// speedtest cancelled, clear output data data = [] } - download.textContent = data[1] - upload.textContent = data[2] + download.textContent = (status==1&&data[1]==0)?"Starting":data[1] + upload.textContent = (status==3&&data[2]==0)?"Starting":data[2] ping.textContent = data[3] ip.textContent = data[4] jitter.textContent = data[5] diff --git a/speedtest_worker.js b/speedtest_worker.js index dade6c8ff..d99a1ab46 100644 --- a/speedtest_worker.js +++ b/speedtest_worker.js @@ -1,5 +1,5 @@ /* - HTML5 Speedtest v4.2.4 + HTML5 Speedtest v4.2.5 by Federico Dossena https://github.com/adolfintel/speedtest/ GNU LGPLv3 License @@ -17,6 +17,8 @@ var clientIp = '' // client's IP address as reported by getIP.php var settings = { time_ul: 15, // duration of upload test in seconds time_dl: 15, // duration of download test in seconds + time_ulGraceTime: 3, //time to wait in seconds before actually measuring ul speed (wait for buffers to fill) + time_dlGraceTime: 1.5, //time to wait in seconds before actually measuring dl speed (wait for TCP window to increase) count_ping: 35, // number of pings to perform in ping test url_dl: 'garbage.php', // path to a large file or garbage.php, used for download test. must be relative to this js file url_ul: 'empty.php', // path to an empty file, used for upload test. must be relative to this js file @@ -44,6 +46,11 @@ var interval = null // timer used in tests */ var useFetchAPI = false +/* + this function is used on URLs passed in the settings to determine whether we need a ? or an & as a separator +*/ +function url_sep (url) { return url.match(/\?/) ? '&' : '?'; } + /* listener for commands from main thread to this worker. commands: @@ -100,6 +107,8 @@ this.addEventListener('message', function (e) { if (typeof s.xhr_dlUseBlob !== 'undefined') settings.xhr_dlUseBlob = s.xhr_dlUseBlob // use blob for download test if (typeof s.garbagePhp_chunkSize !== 'undefined') settings.garbagePhp_chunkSize = s.garbagePhp_chunkSize // size of garbage.php chunks if (typeof s.force_fetchAPI !== 'undefined') settings.force_fetchAPI = s.force_fetchAPI // use fetch api on all browsers that support it if enabled + if (typeof s.time_dlGraceTime !== 'undefined') settings.time_dlGraceTime = s.time_dlGraceTime // dl test grace time before measuring + if (typeof s.time_ulGraceTime !== 'undefined') settings.time_ulGraceTime = s.time_ulGraceTime // ul test grace time before measuring if (typeof s.overheadCompensationFactor !== 'undefined') settings.overheadCompensationFactor = s.overheadCompensationFactor //custom overhead compensation factor (default assumes HTTP+TCP+IP+ETH with typical MTUs) if (settings.allow_fetchAPI && settings.force_fetchAPI && (!!self.fetch)) useFetchAPI = true } catch (e) { } @@ -137,7 +146,7 @@ function getIp (done) { xhr.onerror = function () { done() } - xhr.open('GET', settings.url_getIp + '?r=' + Math.random(), true) + xhr.open('GET', settings.url_getIp + url_sep(settings.url_getIp) + 'r=' + Math.random(), true) xhr.send() } // download test, calls done function when it's over @@ -146,6 +155,7 @@ function dlTest (done) { if (dlCalled) return; else dlCalled = true // dlTest already called? var totLoaded = 0.0, // total number of loaded bytes startT = new Date().getTime(), // timestamp when test was started + graceTimeDone = false, //set to true after the grace time is past failed = false // set to true if a stream fails xhr = [] // function to create a download stream. streams are slightly delayed so that they will not end at the same time @@ -153,7 +163,7 @@ function dlTest (done) { setTimeout(function () { if (testStatus !== 1) return // delayed stream ended up starting after the end of the download test if (useFetchAPI) { - xhr[i] = fetch(settings.url_dl + '?r=' + Math.random() + '&ckSize=' + settings.garbagePhp_chunkSize).then(function (response) { + xhr[i] = fetch(settings.url_dl + url_sep(settings.url_dl) + 'r=' + Math.random() + '&ckSize=' + settings.garbagePhp_chunkSize).then(function (response) { var reader = response.body.getReader() var consume = function () { return reader.read().then(function (result) { @@ -192,7 +202,7 @@ function dlTest (done) { }.bind(this) // send xhr try { if (settings.xhr_dlUseBlob) xhr[i].responseType = 'blob'; else xhr[i].responseType = 'arraybuffer' } catch (e) { } - xhr[i].open('GET', settings.url_dl + '?r=' + Math.random() + '&ckSize=' + settings.garbagePhp_chunkSize, true) // random string to prevent caching + xhr[i].open('GET', settings.url_dl + url_sep(settings.url_dl) + 'r=' + Math.random() + '&ckSize=' + settings.garbagePhp_chunkSize, true) // random string to prevent caching xhr[i].send() } }.bind(this), 1 + delay) @@ -205,13 +215,23 @@ function dlTest (done) { interval = setInterval(function () { var t = new Date().getTime() - startT if (t < 200) return - var speed = totLoaded / (t / 1000.0) - dlStatus = ((speed * 8 * settings.overheadCompensationFactor)/1048576).toFixed(2) // speed is multiplied by 8 to go from bytes to bits, overhead compensation is applied, then everything is divided by 1048576 to go to megabits/s - if ((t / 1000.0) > settings.time_dl || failed) { // test is over, stop streams and timer - if (failed || isNaN(dlStatus)) dlStatus = 'Fail' - clearRequests() - clearInterval(interval) - done() + if (!graceTimeDone){ + if (t > 1000 * settings.time_dlGraceTime){ + if (totLoaded > 0){ // if the connection is so slow that we didn't get a single chunk yet, do not reset + startT = new Date().getTime() + totLoaded = 0.0; + } + graceTimeDone = true; + } + }else{ + var speed = totLoaded / (t / 1000.0) + dlStatus = ((speed * 8 * settings.overheadCompensationFactor)/1048576).toFixed(2) // speed is multiplied by 8 to go from bytes to bits, overhead compensation is applied, then everything is divided by 1048576 to go to megabits/s + if ((t / 1000.0) > settings.time_dl || failed) { // test is over, stop streams and timer + if (failed || isNaN(dlStatus)) dlStatus = 'Fail' + clearRequests() + clearInterval(interval) + done() + } } }.bind(this), 200) } @@ -230,9 +250,10 @@ reqsmall = new Blob(reqsmall) var ulCalled = false // used to prevent multiple accidental calls to ulTest function ulTest (done) { if (ulCalled) return; else ulCalled = true // ulTest already called? - var totLoaded = 0.0 // total number of transmitted bytes - var startT = new Date().getTime() // timestamp when test was started - var failed = false // set to true if a stream fails + var totLoaded = 0.0, // total number of transmitted bytes + startT = new Date().getTime(), // timestamp when test was started + graceTimeDone = false, //set to true after the grace time is past + failed = false // set to true if a stream fails xhr = [] // function to create an upload stream. streams are slightly delayed so that they will not end at the same time var testStream = function (i, delay) { @@ -261,7 +282,7 @@ function ulTest (done) { delete (xhr[i]) if (settings.xhr_ignoreErrors === 1) testStream(i,100); //restart stream after 100ms } - xhr[i].open('POST', settings.url_ul + '?r=' + Math.random(), true) // random string to prevent caching + xhr[i].open('POST', settings.url_ul + url_sep(settings.url_ul) + 'r=' + Math.random(), true) // random string to prevent caching xhr[i].setRequestHeader('Content-Encoding', 'identity') // disable compression (some browsers may refuse it, but data is incompressible anyway) xhr[i].send(reqsmall) } else { @@ -285,7 +306,7 @@ function ulTest (done) { if (settings.xhr_ignoreErrors === 1) testStream(i, 100) //restart stream after 100ms }.bind(this) // send xhr - xhr[i].open('POST', settings.url_ul + '?r=' + Math.random(), true) // random string to prevent caching + xhr[i].open('POST', settings.url_ul + url_sep(settings.url_ul) + 'r=' + Math.random(), true) // random string to prevent caching xhr[i].setRequestHeader('Content-Encoding', 'identity') // disable compression (some browsers may refuse it, but data is incompressible anyway) xhr[i].send(req) } @@ -299,13 +320,23 @@ function ulTest (done) { interval = setInterval(function () { var t = new Date().getTime() - startT if (t < 200) return - var speed = totLoaded / (t / 1000.0) - ulStatus = ((speed * 8 * settings.overheadCompensationFactor)/1048576).toFixed(2) // speed is multiplied by 8 to go from bytes to bits, overhead compensation is applied, then everything is divided by 1048576 to go to megabits/s - if ((t / 1000.0) > settings.time_ul || failed) { // test is over, stop streams and timer - if (failed || isNaN(ulStatus)) ulStatus = 'Fail' - clearRequests() - clearInterval(interval) - done() + if (!graceTimeDone){ + if (t > 1000 * settings.time_ulGraceTime){ + if (totLoaded > 0){ // if the connection is so slow that we didn't get a single chunk yet, do not reset + startT = new Date().getTime() + totLoaded = 0.0; + } + graceTimeDone = true; + } + }else{ + var speed = totLoaded / (t / 1000.0) + ulStatus = ((speed * 8 * settings.overheadCompensationFactor)/1048576).toFixed(2) // speed is multiplied by 8 to go from bytes to bits, overhead compensation is applied, then everything is divided by 1048576 to go to megabits/s + if ((t / 1000.0) > settings.time_ul || failed) { // test is over, stop streams and timer + if (failed || isNaN(ulStatus)) ulStatus = 'Fail' + clearRequests() + clearInterval(interval) + done() + } } }.bind(this), 200) } @@ -330,7 +361,7 @@ function pingTest (done) { } else { var instspd = (new Date().getTime() - prevT) var instjitter = Math.abs(instspd - prevInstspd) - if (i === 1) ping = instspd; /* first ping, can't tell jiutter yet*/ else { + if (i === 1) ping = instspd; /* first ping, can't tell jitter yet*/ else { ping = ping * 0.9 + instspd * 0.1 // ping, weighted average jitter = instjitter > jitter ? (jitter * 0.2 + instjitter * 0.8) : (jitter * 0.9 + instjitter * 0.1) // update jitter, weighted average. spikes in ping values are given more weight. } @@ -351,13 +382,13 @@ function pingTest (done) { } if (settings.xhr_ignoreErrors === 1) doPing() //retry ping - if(settings.xhr_ignoreErrors === 2){ //ignore failed ping + if (settings.xhr_ignoreErrors === 2){ //ignore failed ping i++ if (i < settings.count_ping) doPing(); else done() // more pings to do? } }.bind(this) // sent xhr - xhr[0].open('GET', settings.url_ping + '?r=' + Math.random(), true) // random string to prevent caching + xhr[0].open('GET', settings.url_ping + url_sep(settings.url_ping) + 'r=' + Math.random(), true) // random string to prevent caching xhr[0].send() }.bind(this) doPing() // start first ping diff --git a/speedtest_worker.min.js b/speedtest_worker.min.js index 4387e1eaa..895390a63 100644 --- a/speedtest_worker.min.js +++ b/speedtest_worker.min.js @@ -1 +1 @@ -function clearRequests(){if(xhr){for(var i=0;iloadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].onload=function(){try{xhr[i].abort()}catch(e){}testStream(i,0)}.bind(this),xhr[i].onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this);try{settings.xhr_dlUseBlob?xhr[i].responseType="blob":xhr[i].responseType="arraybuffer"}catch(e){}xhr[i].open("GET",settings.url_dl+"?r="+Math.random()+"&ckSize="+settings.garbagePhp_chunkSize,!0),xhr[i].send()}}.bind(this),1+delay)}.bind(this),i=0;it)){var speed=totLoaded/(t/1e3);dlStatus=(8*speed*settings.overheadCompensationFactor/1048576).toFixed(2),(t/1e3>settings.time_dl||failed)&&((failed||isNaN(dlStatus))&&(dlStatus="Fail"),clearRequests(),clearInterval(interval),done())}}.bind(this),200)}}function ulTest(done){if(!ulCalled){ulCalled=!0;var totLoaded=0,startT=(new Date).getTime(),failed=!1;xhr=[];for(var testStream=function(i,delay){setTimeout(function(){if(3===testStatus){var prevLoaded=0,x=new XMLHttpRequest;xhr[i]=x;var ie11workaround;try{xhr[i].upload.onprogress,ie11workaround=!1}catch(e){ie11workaround=!0}ie11workaround?(xhr[i].onload=function(){totLoaded+=262144,testStream(i,0)},xhr[i].onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)},xhr[i].open("POST",settings.url_ul+"?r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(reqsmall)):(xhr[i].upload.onprogress=function(event){if(3!==testStatus)try{x.abort()}catch(e){}var loadDiff=event.loaded<=0?0:event.loaded-prevLoaded;isNaN(loadDiff)||!isFinite(loadDiff)||0>loadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].upload.onload=function(){testStream(i,0)}.bind(this),xhr[i].upload.onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this),xhr[i].open("POST",settings.url_ul+"?r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(req))}}.bind(this),1)}.bind(this),i=0;it)){var speed=totLoaded/(t/1e3);ulStatus=(8*speed*settings.overheadCompensationFactor/1048576).toFixed(2),(t/1e3>settings.time_ul||failed)&&((failed||isNaN(ulStatus))&&(ulStatus="Fail"),clearRequests(),clearInterval(interval),done())}}.bind(this),200)}}function pingTest(done){if(!ptCalled){ptCalled=!0;var prevT=null,ping=0,jitter=0,i=0,prevInstspd=0;xhr=[];var doPing=function(){prevT=(new Date).getTime(),xhr[0]=new XMLHttpRequest,xhr[0].onload=function(){if(0===i)prevT=(new Date).getTime();else{var instspd=(new Date).getTime()-prevT,instjitter=Math.abs(instspd-prevInstspd);1===i?ping=instspd:(ping=.9*ping+.1*instspd,jitter=instjitter>jitter?.2*jitter+.8*instjitter:.9*jitter+.1*instjitter),prevInstspd=instspd}pingStatus=ping.toFixed(2),jitterStatus=jitter.toFixed(2),i++,ii;i++)req.push(r);req=new Blob(req),r=new ArrayBuffer(262144);try{r=new Float32Array(r);for(var i=0;iloadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].onload=function(){try{xhr[i].abort()}catch(e){}testStream(i,0)}.bind(this),xhr[i].onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this);try{settings.xhr_dlUseBlob?xhr[i].responseType="blob":xhr[i].responseType="arraybuffer"}catch(e){}xhr[i].open("GET",settings.url_dl+url_sep(settings.url_dl)+"r="+Math.random()+"&ckSize="+settings.garbagePhp_chunkSize,!0),xhr[i].send()}}.bind(this),1+delay)}.bind(this),i=0;it))if(graceTimeDone){var speed=totLoaded/(t/1e3);dlStatus=(8*speed*settings.overheadCompensationFactor/1048576).toFixed(2),(t/1e3>settings.time_dl||failed)&&((failed||isNaN(dlStatus))&&(dlStatus="Fail"),clearRequests(),clearInterval(interval),done())}else t>1e3*settings.time_dlGraceTime&&(totLoaded>0&&(startT=(new Date).getTime(),totLoaded=0),graceTimeDone=!0)}.bind(this),200)}}function ulTest(done){if(!ulCalled){ulCalled=!0;var totLoaded=0,startT=(new Date).getTime(),graceTimeDone=!1,failed=!1;xhr=[];for(var testStream=function(i,delay){setTimeout(function(){if(3===testStatus){var prevLoaded=0,x=new XMLHttpRequest;xhr[i]=x;var ie11workaround;try{xhr[i].upload.onprogress,ie11workaround=!1}catch(e){ie11workaround=!0}ie11workaround?(xhr[i].onload=function(){totLoaded+=262144,testStream(i,0)},xhr[i].onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)},xhr[i].open("POST",settings.url_ul+url_sep(settings.url_ul)+"r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(reqsmall)):(xhr[i].upload.onprogress=function(event){if(3!==testStatus)try{x.abort()}catch(e){}var loadDiff=event.loaded<=0?0:event.loaded-prevLoaded;isNaN(loadDiff)||!isFinite(loadDiff)||0>loadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].upload.onload=function(){testStream(i,0)}.bind(this),xhr[i].upload.onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this),xhr[i].open("POST",settings.url_ul+url_sep(settings.url_ul)+"r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(req))}}.bind(this),1)}.bind(this),i=0;it))if(graceTimeDone){var speed=totLoaded/(t/1e3);ulStatus=(8*speed*settings.overheadCompensationFactor/1048576).toFixed(2),(t/1e3>settings.time_ul||failed)&&((failed||isNaN(ulStatus))&&(ulStatus="Fail"),clearRequests(),clearInterval(interval),done())}else t>1e3*settings.time_ulGraceTime&&(totLoaded>0&&(startT=(new Date).getTime(),totLoaded=0),graceTimeDone=!0)}.bind(this),200)}}function pingTest(done){if(!ptCalled){ptCalled=!0;var prevT=null,ping=0,jitter=0,i=0,prevInstspd=0;xhr=[];var doPing=function(){prevT=(new Date).getTime(),xhr[0]=new XMLHttpRequest,xhr[0].onload=function(){if(0===i)prevT=(new Date).getTime();else{var instspd=(new Date).getTime()-prevT,instjitter=Math.abs(instspd-prevInstspd);1===i?ping=instspd:(ping=.9*ping+.1*instspd,jitter=instjitter>jitter?.2*jitter+.8*instjitter:.9*jitter+.1*instjitter),prevInstspd=instspd}pingStatus=ping.toFixed(2),jitterStatus=jitter.toFixed(2),i++,ii;i++)req.push(r);req=new Blob(req),r=new ArrayBuffer(262144);try{r=new Float32Array(r);for(var i=0;i Date: Sat, 17 Jun 2017 14:45:03 +0200 Subject: [PATCH 06/18] Added warning if parsing of custom settings fails --- speedtest_worker.js | 2 +- speedtest_worker.min.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/speedtest_worker.js b/speedtest_worker.js index d99a1ab46..965001d56 100644 --- a/speedtest_worker.js +++ b/speedtest_worker.js @@ -111,7 +111,7 @@ this.addEventListener('message', function (e) { if (typeof s.time_ulGraceTime !== 'undefined') settings.time_ulGraceTime = s.time_ulGraceTime // ul test grace time before measuring if (typeof s.overheadCompensationFactor !== 'undefined') settings.overheadCompensationFactor = s.overheadCompensationFactor //custom overhead compensation factor (default assumes HTTP+TCP+IP+ETH with typical MTUs) if (settings.allow_fetchAPI && settings.force_fetchAPI && (!!self.fetch)) useFetchAPI = true - } catch (e) { } + } catch (e) { console.warn("Possible error in custom test settings. Some settings may not be applied. Exception: "+e) } // run the tests console.log(settings) console.log('Fetch API: ' + useFetchAPI) diff --git a/speedtest_worker.min.js b/speedtest_worker.min.js index 895390a63..df72f24dd 100644 --- a/speedtest_worker.min.js +++ b/speedtest_worker.min.js @@ -1 +1 @@ -function url_sep(url){return url.match(/\?/)?"&":"?"}function clearRequests(){if(xhr){for(var i=0;iloadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].onload=function(){try{xhr[i].abort()}catch(e){}testStream(i,0)}.bind(this),xhr[i].onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this);try{settings.xhr_dlUseBlob?xhr[i].responseType="blob":xhr[i].responseType="arraybuffer"}catch(e){}xhr[i].open("GET",settings.url_dl+url_sep(settings.url_dl)+"r="+Math.random()+"&ckSize="+settings.garbagePhp_chunkSize,!0),xhr[i].send()}}.bind(this),1+delay)}.bind(this),i=0;it))if(graceTimeDone){var speed=totLoaded/(t/1e3);dlStatus=(8*speed*settings.overheadCompensationFactor/1048576).toFixed(2),(t/1e3>settings.time_dl||failed)&&((failed||isNaN(dlStatus))&&(dlStatus="Fail"),clearRequests(),clearInterval(interval),done())}else t>1e3*settings.time_dlGraceTime&&(totLoaded>0&&(startT=(new Date).getTime(),totLoaded=0),graceTimeDone=!0)}.bind(this),200)}}function ulTest(done){if(!ulCalled){ulCalled=!0;var totLoaded=0,startT=(new Date).getTime(),graceTimeDone=!1,failed=!1;xhr=[];for(var testStream=function(i,delay){setTimeout(function(){if(3===testStatus){var prevLoaded=0,x=new XMLHttpRequest;xhr[i]=x;var ie11workaround;try{xhr[i].upload.onprogress,ie11workaround=!1}catch(e){ie11workaround=!0}ie11workaround?(xhr[i].onload=function(){totLoaded+=262144,testStream(i,0)},xhr[i].onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)},xhr[i].open("POST",settings.url_ul+url_sep(settings.url_ul)+"r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(reqsmall)):(xhr[i].upload.onprogress=function(event){if(3!==testStatus)try{x.abort()}catch(e){}var loadDiff=event.loaded<=0?0:event.loaded-prevLoaded;isNaN(loadDiff)||!isFinite(loadDiff)||0>loadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].upload.onload=function(){testStream(i,0)}.bind(this),xhr[i].upload.onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this),xhr[i].open("POST",settings.url_ul+url_sep(settings.url_ul)+"r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(req))}}.bind(this),1)}.bind(this),i=0;it))if(graceTimeDone){var speed=totLoaded/(t/1e3);ulStatus=(8*speed*settings.overheadCompensationFactor/1048576).toFixed(2),(t/1e3>settings.time_ul||failed)&&((failed||isNaN(ulStatus))&&(ulStatus="Fail"),clearRequests(),clearInterval(interval),done())}else t>1e3*settings.time_ulGraceTime&&(totLoaded>0&&(startT=(new Date).getTime(),totLoaded=0),graceTimeDone=!0)}.bind(this),200)}}function pingTest(done){if(!ptCalled){ptCalled=!0;var prevT=null,ping=0,jitter=0,i=0,prevInstspd=0;xhr=[];var doPing=function(){prevT=(new Date).getTime(),xhr[0]=new XMLHttpRequest,xhr[0].onload=function(){if(0===i)prevT=(new Date).getTime();else{var instspd=(new Date).getTime()-prevT,instjitter=Math.abs(instspd-prevInstspd);1===i?ping=instspd:(ping=.9*ping+.1*instspd,jitter=instjitter>jitter?.2*jitter+.8*instjitter:.9*jitter+.1*instjitter),prevInstspd=instspd}pingStatus=ping.toFixed(2),jitterStatus=jitter.toFixed(2),i++,ii;i++)req.push(r);req=new Blob(req),r=new ArrayBuffer(262144);try{r=new Float32Array(r);for(var i=0;iloadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].onload=function(){try{xhr[i].abort()}catch(e){}testStream(i,0)}.bind(this),xhr[i].onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this);try{settings.xhr_dlUseBlob?xhr[i].responseType="blob":xhr[i].responseType="arraybuffer"}catch(e){}xhr[i].open("GET",settings.url_dl+url_sep(settings.url_dl)+"r="+Math.random()+"&ckSize="+settings.garbagePhp_chunkSize,!0),xhr[i].send()}}.bind(this),1+delay)}.bind(this),i=0;it))if(graceTimeDone){var speed=totLoaded/(t/1e3);dlStatus=(8*speed*settings.overheadCompensationFactor/1048576).toFixed(2),(t/1e3>settings.time_dl||failed)&&((failed||isNaN(dlStatus))&&(dlStatus="Fail"),clearRequests(),clearInterval(interval),done())}else t>1e3*settings.time_dlGraceTime&&(totLoaded>0&&(startT=(new Date).getTime(),totLoaded=0),graceTimeDone=!0)}.bind(this),200)}}function ulTest(done){if(!ulCalled){ulCalled=!0;var totLoaded=0,startT=(new Date).getTime(),graceTimeDone=!1,failed=!1;xhr=[];for(var testStream=function(i,delay){setTimeout(function(){if(3===testStatus){var prevLoaded=0,x=new XMLHttpRequest;xhr[i]=x;var ie11workaround;try{xhr[i].upload.onprogress,ie11workaround=!1}catch(e){ie11workaround=!0}ie11workaround?(xhr[i].onload=function(){totLoaded+=262144,testStream(i,0)},xhr[i].onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)},xhr[i].open("POST",settings.url_ul+url_sep(settings.url_ul)+"r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(reqsmall)):(xhr[i].upload.onprogress=function(event){if(3!==testStatus)try{x.abort()}catch(e){}var loadDiff=event.loaded<=0?0:event.loaded-prevLoaded;isNaN(loadDiff)||!isFinite(loadDiff)||0>loadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].upload.onload=function(){testStream(i,0)}.bind(this),xhr[i].upload.onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this),xhr[i].open("POST",settings.url_ul+url_sep(settings.url_ul)+"r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(req))}}.bind(this),1)}.bind(this),i=0;it))if(graceTimeDone){var speed=totLoaded/(t/1e3);ulStatus=(8*speed*settings.overheadCompensationFactor/1048576).toFixed(2),(t/1e3>settings.time_ul||failed)&&((failed||isNaN(ulStatus))&&(ulStatus="Fail"),clearRequests(),clearInterval(interval),done())}else t>1e3*settings.time_ulGraceTime&&(totLoaded>0&&(startT=(new Date).getTime(),totLoaded=0),graceTimeDone=!0)}.bind(this),200)}}function pingTest(done){if(!ptCalled){ptCalled=!0;var prevT=null,ping=0,jitter=0,i=0,prevInstspd=0;xhr=[];var doPing=function(){prevT=(new Date).getTime(),xhr[0]=new XMLHttpRequest,xhr[0].onload=function(){if(0===i)prevT=(new Date).getTime();else{var instspd=(new Date).getTime()-prevT,instjitter=Math.abs(instspd-prevInstspd);1===i?ping=instspd:(ping=.9*ping+.1*instspd,jitter=instjitter>jitter?.2*jitter+.8*instjitter:.9*jitter+.1*instjitter),prevInstspd=instspd}pingStatus=ping.toFixed(2),jitterStatus=jitter.toFixed(2),i++,ii;i++)req.push(r);req=new Blob(req),r=new ArrayBuffer(262144);try{r=new Float32Array(r);for(var i=0;i Date: Sun, 18 Jun 2017 12:10:59 +0200 Subject: [PATCH 07/18] Added more common issues in troubleshooting section of doc.md --- doc.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc.md b/doc.md index 89f344716..8ab513a7c 100644 --- a/doc.md +++ b/doc.md @@ -215,6 +215,12 @@ Press F12, select network and start the test. Do you see errors? (cancelled requ #### Upload test is inaccurate, and I see lag spikes Check your server's maximum POST size, make sure it's at least 20Mbytes, possibly more +#### All tests are wrong, give extremely high results, browser lags/crashes, ... +You're running the test on localhost, therefore it is trying to measure the speed of your loopback interface. The test is meant to be run over an Internet connection, from a different machine. + +#### Ping test shows double the actual ping +Make sure your server is sending the ```Connection:keep-alive``` header + ## Using the test without PHP If your server does not support PHP, or you're using something newer like Node.js, you can still use this test by replacing `garbage.php`, `empty.php` and `getIP.php` with equivalents. From b0172cc1089d12572304f3253ff29f5c5e3d449a Mon Sep 17 00:00:00 2001 From: dosse91 Date: Mon, 19 Jun 2017 17:19:58 +0200 Subject: [PATCH 08/18] Improved custom settings parsing --- speedtest_worker.js | 6 +++++- speedtest_worker.min.js | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/speedtest_worker.js b/speedtest_worker.js index 965001d56..5183caf46 100644 --- a/speedtest_worker.js +++ b/speedtest_worker.js @@ -68,7 +68,11 @@ this.addEventListener('message', function (e) { testStatus = 1 try { // parse settings, if present - var s = JSON.parse(e.data.substring(5)) + var s = {} + try{ + var ss = e.data.substring(5); + if (ss) s = JSON.parse(ss); + }catch(e){ console.warn("Error parsing custom settings JSON. Please check your syntax"); } if (typeof s.url_dl !== 'undefined') settings.url_dl = s.url_dl // download url if (typeof s.url_ul !== 'undefined') settings.url_ul = s.url_ul // upload url if (typeof s.url_ping !== 'undefined') settings.url_ping = s.url_ping // ping url diff --git a/speedtest_worker.min.js b/speedtest_worker.min.js index df72f24dd..5fccd3d86 100644 --- a/speedtest_worker.min.js +++ b/speedtest_worker.min.js @@ -1 +1 @@ -function url_sep(url){return url.match(/\?/)?"&":"?"}function clearRequests(){if(xhr){for(var i=0;iloadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].onload=function(){try{xhr[i].abort()}catch(e){}testStream(i,0)}.bind(this),xhr[i].onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this);try{settings.xhr_dlUseBlob?xhr[i].responseType="blob":xhr[i].responseType="arraybuffer"}catch(e){}xhr[i].open("GET",settings.url_dl+url_sep(settings.url_dl)+"r="+Math.random()+"&ckSize="+settings.garbagePhp_chunkSize,!0),xhr[i].send()}}.bind(this),1+delay)}.bind(this),i=0;it))if(graceTimeDone){var speed=totLoaded/(t/1e3);dlStatus=(8*speed*settings.overheadCompensationFactor/1048576).toFixed(2),(t/1e3>settings.time_dl||failed)&&((failed||isNaN(dlStatus))&&(dlStatus="Fail"),clearRequests(),clearInterval(interval),done())}else t>1e3*settings.time_dlGraceTime&&(totLoaded>0&&(startT=(new Date).getTime(),totLoaded=0),graceTimeDone=!0)}.bind(this),200)}}function ulTest(done){if(!ulCalled){ulCalled=!0;var totLoaded=0,startT=(new Date).getTime(),graceTimeDone=!1,failed=!1;xhr=[];for(var testStream=function(i,delay){setTimeout(function(){if(3===testStatus){var prevLoaded=0,x=new XMLHttpRequest;xhr[i]=x;var ie11workaround;try{xhr[i].upload.onprogress,ie11workaround=!1}catch(e){ie11workaround=!0}ie11workaround?(xhr[i].onload=function(){totLoaded+=262144,testStream(i,0)},xhr[i].onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)},xhr[i].open("POST",settings.url_ul+url_sep(settings.url_ul)+"r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(reqsmall)):(xhr[i].upload.onprogress=function(event){if(3!==testStatus)try{x.abort()}catch(e){}var loadDiff=event.loaded<=0?0:event.loaded-prevLoaded;isNaN(loadDiff)||!isFinite(loadDiff)||0>loadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].upload.onload=function(){testStream(i,0)}.bind(this),xhr[i].upload.onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this),xhr[i].open("POST",settings.url_ul+url_sep(settings.url_ul)+"r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(req))}}.bind(this),1)}.bind(this),i=0;it))if(graceTimeDone){var speed=totLoaded/(t/1e3);ulStatus=(8*speed*settings.overheadCompensationFactor/1048576).toFixed(2),(t/1e3>settings.time_ul||failed)&&((failed||isNaN(ulStatus))&&(ulStatus="Fail"),clearRequests(),clearInterval(interval),done())}else t>1e3*settings.time_ulGraceTime&&(totLoaded>0&&(startT=(new Date).getTime(),totLoaded=0),graceTimeDone=!0)}.bind(this),200)}}function pingTest(done){if(!ptCalled){ptCalled=!0;var prevT=null,ping=0,jitter=0,i=0,prevInstspd=0;xhr=[];var doPing=function(){prevT=(new Date).getTime(),xhr[0]=new XMLHttpRequest,xhr[0].onload=function(){if(0===i)prevT=(new Date).getTime();else{var instspd=(new Date).getTime()-prevT,instjitter=Math.abs(instspd-prevInstspd);1===i?ping=instspd:(ping=.9*ping+.1*instspd,jitter=instjitter>jitter?.2*jitter+.8*instjitter:.9*jitter+.1*instjitter),prevInstspd=instspd}pingStatus=ping.toFixed(2),jitterStatus=jitter.toFixed(2),i++,ii;i++)req.push(r);req=new Blob(req),r=new ArrayBuffer(262144);try{r=new Float32Array(r);for(var i=0;iloadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].onload=function(){try{xhr[i].abort()}catch(e){}testStream(i,0)}.bind(this),xhr[i].onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this);try{settings.xhr_dlUseBlob?xhr[i].responseType="blob":xhr[i].responseType="arraybuffer"}catch(e){}xhr[i].open("GET",settings.url_dl+url_sep(settings.url_dl)+"r="+Math.random()+"&ckSize="+settings.garbagePhp_chunkSize,!0),xhr[i].send()}}.bind(this),1+delay)}.bind(this),i=0;it))if(graceTimeDone){var speed=totLoaded/(t/1e3);dlStatus=(8*speed*settings.overheadCompensationFactor/1048576).toFixed(2),(t/1e3>settings.time_dl||failed)&&((failed||isNaN(dlStatus))&&(dlStatus="Fail"),clearRequests(),clearInterval(interval),done())}else t>1e3*settings.time_dlGraceTime&&(totLoaded>0&&(startT=(new Date).getTime(),totLoaded=0),graceTimeDone=!0)}.bind(this),200)}}function ulTest(done){if(!ulCalled){ulCalled=!0;var totLoaded=0,startT=(new Date).getTime(),graceTimeDone=!1,failed=!1;xhr=[];for(var testStream=function(i,delay){setTimeout(function(){if(3===testStatus){var prevLoaded=0,x=new XMLHttpRequest;xhr[i]=x;var ie11workaround;try{xhr[i].upload.onprogress,ie11workaround=!1}catch(e){ie11workaround=!0}ie11workaround?(xhr[i].onload=function(){totLoaded+=262144,testStream(i,0)},xhr[i].onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)},xhr[i].open("POST",settings.url_ul+url_sep(settings.url_ul)+"r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(reqsmall)):(xhr[i].upload.onprogress=function(event){if(3!==testStatus)try{x.abort()}catch(e){}var loadDiff=event.loaded<=0?0:event.loaded-prevLoaded;isNaN(loadDiff)||!isFinite(loadDiff)||0>loadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].upload.onload=function(){testStream(i,0)}.bind(this),xhr[i].upload.onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this),xhr[i].open("POST",settings.url_ul+url_sep(settings.url_ul)+"r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(req))}}.bind(this),1)}.bind(this),i=0;it))if(graceTimeDone){var speed=totLoaded/(t/1e3);ulStatus=(8*speed*settings.overheadCompensationFactor/1048576).toFixed(2),(t/1e3>settings.time_ul||failed)&&((failed||isNaN(ulStatus))&&(ulStatus="Fail"),clearRequests(),clearInterval(interval),done())}else t>1e3*settings.time_ulGraceTime&&(totLoaded>0&&(startT=(new Date).getTime(),totLoaded=0),graceTimeDone=!0)}.bind(this),200)}}function pingTest(done){if(!ptCalled){ptCalled=!0;var prevT=null,ping=0,jitter=0,i=0,prevInstspd=0;xhr=[];var doPing=function(){prevT=(new Date).getTime(),xhr[0]=new XMLHttpRequest,xhr[0].onload=function(){if(0===i)prevT=(new Date).getTime();else{var instspd=(new Date).getTime()-prevT,instjitter=Math.abs(instspd-prevInstspd);1===i?ping=instspd:(ping=.9*ping+.1*instspd,jitter=instjitter>jitter?.2*jitter+.8*instjitter:.9*jitter+.1*instjitter),prevInstspd=instspd}pingStatus=ping.toFixed(2),jitterStatus=jitter.toFixed(2),i++,ii;i++)req.push(r);req=new Blob(req),r=new ArrayBuffer(262144);try{r=new Float32Array(r);for(var i=0;i Date: Mon, 19 Jun 2017 17:27:52 +0200 Subject: [PATCH 09/18] Added quickstart videos --- README.md | 6 +++++- doc.md | 5 ++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index adba922bb..b4fd00c37 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,11 @@ Only modern browsers are supported (IE11, latest Edge, latest Chrome, latest Fir - Your server must accept large POST requests (up to 20 Megabytes), otherwise the upload test will fail - It's also better if your server does not use compression, but it's not mandatory -## How to use +## Quick installation videos +* [Debian 9.0 with Apache](https://fdossena.com/?p=speedtest/quickstart_deb.frag) +* [Windows Server 2016 with IIS](https://fdossena.com/?p=speedtest/quickstart_win.frag) + +## How to use in your site See the examples or doc.md ## License diff --git a/doc.md b/doc.md index 8ab513a7c..0ea708f48 100644 --- a/doc.md +++ b/doc.md @@ -1,7 +1,7 @@ # HTML5 Speedtest > by Federico Dossena -> Version 4.2.5, June 16 2017 +> Version 4.2.6, June 19 2017 > [https://github.com/adolfintel/speedtest/](https://github.com/adolfintel/speedtest/) @@ -24,6 +24,9 @@ First of all, the requirements to run this test: If this looks good, let's proceed and see how to use the test. +## Quick installation videos +* [Debian 9.0 with Apache](https://fdossena.com/?p=speedtest/quickstart_deb.frag) +* [Windows Server 2016 with IIS](https://fdossena.com/?p=speedtest/quickstart_win.frag) ## Installation To install the test on your server, upload the following files: From a7011b2e990e0fc9cf4641ee0abcbf298a38757c Mon Sep 17 00:00:00 2001 From: dosse91 Date: Thu, 22 Jun 2017 08:33:39 +0200 Subject: [PATCH 10/18] Removed unused Fetch API support (may come back in future); Removed quirk for Safari, now treated as chromium-based browser --- doc.md | 11 +----- speedtest_worker.js | 85 ++++++++++++++--------------------------- speedtest_worker.min.js | 2 +- 3 files changed, 30 insertions(+), 68 deletions(-) diff --git a/doc.md b/doc.md index 0ea708f48..7f8de9ece 100644 --- a/doc.md +++ b/doc.md @@ -1,7 +1,7 @@ # HTML5 Speedtest > by Federico Dossena -> Version 4.2.6, June 19 2017 +> Version 4.2.7, June 22 2017 > [https://github.com/adolfintel/speedtest/](https://github.com/adolfintel/speedtest/) @@ -168,7 +168,6 @@ w.postMessage('start {"param1": "value1", "param2": "value2", ...}') * Default: `3` * Recommended: `>=1` * Default override: 1 on Firefox if enable_quirks is true - * Default override: 10 on Safari if enable_quirks is true * __xhr_ignoreErrors__: how to react to errors in download/upload streams and the ping test * `0`: Fail test on error (behaviour of previous versions of this test) * `1`: Restart a stream/ping when it fails @@ -181,14 +180,6 @@ w.postMessage('start {"param1": "value1", "param2": "value2", ...}') * __time_ulGraceTime__: How long to wait (in seconds) before actually measuring the upload speed. This is a good idea because we want to wait for the buffers to be full (avoids the peak at the beginning of the test) * Default: `3` * Recommended: `>=1` -* __allow_fetchAPI__: allow the use of Fetch API for the download test instead of regular XHR. Experimental, not recommended. - * Default: `false` -* __force_fetchAPI__: forces the use of Fetch API on all browsers that support it - * Default: `false` -Fetch API are used if the following conditions are met: - * allow_fetchAPI is true - * Chromium-based browser with support for Fetch API and enable_quirks is true -OR force_fetchAPI is true and the browser supports Fetch API * __overheadCompensationFactor__: compensation for HTTP and network overhead. Default value assumes typical MTUs used over the Internet. You might want to change this if you're using this in your internal network with different MTUs, or if you're using IPv6 instead of IPv4. * Default: `1.13359567567567567568` (1048576/925000) assumes HTTP+TCP+IPv4+ETH with typical MTUs used over the Internet * `1.0513`: HTTP+TCP+IPv6+ETH, over the Internet (empirically tested, not calculated) diff --git a/speedtest_worker.js b/speedtest_worker.js index 5183caf46..86bb043bb 100644 --- a/speedtest_worker.js +++ b/speedtest_worker.js @@ -1,5 +1,5 @@ /* - HTML5 Speedtest v4.2.5 + HTML5 Speedtest v4.2.7 by Federico Dossena https://github.com/adolfintel/speedtest/ GNU LGPLv3 License @@ -30,8 +30,6 @@ var settings = { xhr_dlUseBlob: false, // if set to true, it reduces ram usage but uses the hard drive (useful with large garbagePhp_chunkSize and/or high xhr_dlMultistream) garbagePhp_chunkSize: 20, // size of chunks sent by garbage.php (can be different if enable_quirks is active) enable_quirks: true, // enable quirks for specific browsers. currently it overrides settings to optimize for specific browsers, unless they are already being overridden with the start command - allow_fetchAPI: false, // enables Fetch API. currently disabled because it leaks memory like no tomorrow - force_fetchAPI: false, // when Fetch API is enabled, it will force usage on every browser that supports it overheadCompensationFactor: 1048576/925000 //compensation for HTTP+TCP+IP+ETH overhead. 925000 is how much data is actually carried over 1048576 (1mb) bytes downloaded/uploaded. This default value assumes HTTP+TCP+IPv4+ETH with typical MTUs over the Internet. You may want to change this if you're going through your local network with a different MTU or if you're going over IPv6 (see doc.md for some other values) } @@ -80,7 +78,6 @@ this.addEventListener('message', function (e) { if (typeof s.time_dl !== 'undefined') settings.time_dl = s.time_dl // duration of download test if (typeof s.time_ul !== 'undefined') settings.time_ul = s.time_ul // duration of upload test if (typeof s.enable_quirks !== 'undefined') settings.enable_quirks = s.enable_quirks // enable quirks or not - if (typeof s.allow_fetchAPI !== 'undefined') settings.allow_fetchAPI = s.allow_fetchAPI // allows fetch api to be used if supported // quirks for specific browsers. more may be added in future releases if (settings.enable_quirks) { var ua = navigator.userAgent @@ -92,14 +89,7 @@ this.addEventListener('message', function (e) { // edge more precise with 3 download streams settings.xhr_dlMultistream = 3 } - if ((/Safari.(\d+)/i.test(ua)) && !(/Chrome.(\d+)/i.test(ua))) { - // safari more precise with 10 upload streams and 5mb chunks for download test - settings.xhr_ulMultistream = 10 - settings.garbagePhp_chunkSize = 5 - } if (/Chrome.(\d+)/i.test(ua) && (!!self.fetch)) { - // chrome can't handle large xhr very well, use fetch api if available and allowed - if (settings.allow_fetchAPI) useFetchAPI = true // chrome more precise with 5 streams settings.xhr_dlMultistream = 5 } @@ -110,15 +100,12 @@ this.addEventListener('message', function (e) { if (typeof s.xhr_ignoreErrors !== 'undefined') settings.xhr_ignoreErrors = s.xhr_ignoreErrors // what to do in case of errors during the test if (typeof s.xhr_dlUseBlob !== 'undefined') settings.xhr_dlUseBlob = s.xhr_dlUseBlob // use blob for download test if (typeof s.garbagePhp_chunkSize !== 'undefined') settings.garbagePhp_chunkSize = s.garbagePhp_chunkSize // size of garbage.php chunks - if (typeof s.force_fetchAPI !== 'undefined') settings.force_fetchAPI = s.force_fetchAPI // use fetch api on all browsers that support it if enabled if (typeof s.time_dlGraceTime !== 'undefined') settings.time_dlGraceTime = s.time_dlGraceTime // dl test grace time before measuring if (typeof s.time_ulGraceTime !== 'undefined') settings.time_ulGraceTime = s.time_ulGraceTime // ul test grace time before measuring if (typeof s.overheadCompensationFactor !== 'undefined') settings.overheadCompensationFactor = s.overheadCompensationFactor //custom overhead compensation factor (default assumes HTTP+TCP+IP+ETH with typical MTUs) - if (settings.allow_fetchAPI && settings.force_fetchAPI && (!!self.fetch)) useFetchAPI = true } catch (e) { console.warn("Possible error in custom test settings. Some settings may not be applied. Exception: "+e) } // run the tests console.log(settings) - console.log('Fetch API: ' + useFetchAPI) getIp(function () { dlTest(function () { testStatus = 2; pingTest(function () { testStatus = 3; ulTest(function () { testStatus = 4 }) }) }) }) } if (params[0] === 'abort') { // abort command @@ -166,49 +153,33 @@ function dlTest (done) { var testStream = function (i, delay) { setTimeout(function () { if (testStatus !== 1) return // delayed stream ended up starting after the end of the download test - if (useFetchAPI) { - xhr[i] = fetch(settings.url_dl + url_sep(settings.url_dl) + 'r=' + Math.random() + '&ckSize=' + settings.garbagePhp_chunkSize).then(function (response) { - var reader = response.body.getReader() - var consume = function () { - return reader.read().then(function (result) { - if (result.done) testStream(i); else { - totLoaded += result.value.length - if (xhr[i].cancelRequested) reader.cancel() - } - return consume() - }.bind(this)) - }.bind(this) - return consume() - }.bind(this)) - } else { - var prevLoaded = 0 // number of bytes loaded last time onprogress was called - var x = new XMLHttpRequest() - xhr[i] = x - xhr[i].onprogress = function (event) { - if (testStatus !== 1) { try { x.abort() } catch (e) { } } // just in case this XHR is still running after the download test - // progress event, add number of new loaded bytes to totLoaded - var loadDiff = event.loaded <= 0 ? 0 : (event.loaded - prevLoaded) - if (isNaN(loadDiff) || !isFinite(loadDiff) || loadDiff < 0) return // just in case - totLoaded += loadDiff - prevLoaded = event.loaded - }.bind(this) - xhr[i].onload = function () { - // the large file has been loaded entirely, start again - try { xhr[i].abort() } catch (e) { } // reset the stream data to empty ram - testStream(i, 0) - }.bind(this) - xhr[i].onerror = function () { - // error - if (settings.xhr_ignoreErrors === 0) failed=true //abort - try { xhr[i].abort() } catch (e) { } - delete (xhr[i]) - if (settings.xhr_ignoreErrors === 1) testStream(i, 100) //restart stream after 100ms - }.bind(this) - // send xhr - try { if (settings.xhr_dlUseBlob) xhr[i].responseType = 'blob'; else xhr[i].responseType = 'arraybuffer' } catch (e) { } - xhr[i].open('GET', settings.url_dl + url_sep(settings.url_dl) + 'r=' + Math.random() + '&ckSize=' + settings.garbagePhp_chunkSize, true) // random string to prevent caching - xhr[i].send() - } + var prevLoaded = 0 // number of bytes loaded last time onprogress was called + var x = new XMLHttpRequest() + xhr[i] = x + xhr[i].onprogress = function (event) { + if (testStatus !== 1) { try { x.abort() } catch (e) { } } // just in case this XHR is still running after the download test + // progress event, add number of new loaded bytes to totLoaded + var loadDiff = event.loaded <= 0 ? 0 : (event.loaded - prevLoaded) + if (isNaN(loadDiff) || !isFinite(loadDiff) || loadDiff < 0) return // just in case + totLoaded += loadDiff + prevLoaded = event.loaded + }.bind(this) + xhr[i].onload = function () { + // the large file has been loaded entirely, start again + try { xhr[i].abort() } catch (e) { } // reset the stream data to empty ram + testStream(i, 0) + }.bind(this) + xhr[i].onerror = function () { + // error + if (settings.xhr_ignoreErrors === 0) failed=true //abort + try { xhr[i].abort() } catch (e) { } + delete (xhr[i]) + if (settings.xhr_ignoreErrors === 1) testStream(i, 100) //restart stream after 100ms + }.bind(this) + // send xhr + try { if (settings.xhr_dlUseBlob) xhr[i].responseType = 'blob'; else xhr[i].responseType = 'arraybuffer' } catch (e) { } + xhr[i].open('GET', settings.url_dl + url_sep(settings.url_dl) + 'r=' + Math.random() + '&ckSize=' + settings.garbagePhp_chunkSize, true) // random string to prevent caching + xhr[i].send() }.bind(this), 1 + delay) }.bind(this) // open streams diff --git a/speedtest_worker.min.js b/speedtest_worker.min.js index 5fccd3d86..7223949bf 100644 --- a/speedtest_worker.min.js +++ b/speedtest_worker.min.js @@ -1 +1 @@ -function url_sep(url){return url.match(/\?/)?"&":"?"}function clearRequests(){if(xhr){for(var i=0;iloadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].onload=function(){try{xhr[i].abort()}catch(e){}testStream(i,0)}.bind(this),xhr[i].onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this);try{settings.xhr_dlUseBlob?xhr[i].responseType="blob":xhr[i].responseType="arraybuffer"}catch(e){}xhr[i].open("GET",settings.url_dl+url_sep(settings.url_dl)+"r="+Math.random()+"&ckSize="+settings.garbagePhp_chunkSize,!0),xhr[i].send()}}.bind(this),1+delay)}.bind(this),i=0;it))if(graceTimeDone){var speed=totLoaded/(t/1e3);dlStatus=(8*speed*settings.overheadCompensationFactor/1048576).toFixed(2),(t/1e3>settings.time_dl||failed)&&((failed||isNaN(dlStatus))&&(dlStatus="Fail"),clearRequests(),clearInterval(interval),done())}else t>1e3*settings.time_dlGraceTime&&(totLoaded>0&&(startT=(new Date).getTime(),totLoaded=0),graceTimeDone=!0)}.bind(this),200)}}function ulTest(done){if(!ulCalled){ulCalled=!0;var totLoaded=0,startT=(new Date).getTime(),graceTimeDone=!1,failed=!1;xhr=[];for(var testStream=function(i,delay){setTimeout(function(){if(3===testStatus){var prevLoaded=0,x=new XMLHttpRequest;xhr[i]=x;var ie11workaround;try{xhr[i].upload.onprogress,ie11workaround=!1}catch(e){ie11workaround=!0}ie11workaround?(xhr[i].onload=function(){totLoaded+=262144,testStream(i,0)},xhr[i].onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)},xhr[i].open("POST",settings.url_ul+url_sep(settings.url_ul)+"r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(reqsmall)):(xhr[i].upload.onprogress=function(event){if(3!==testStatus)try{x.abort()}catch(e){}var loadDiff=event.loaded<=0?0:event.loaded-prevLoaded;isNaN(loadDiff)||!isFinite(loadDiff)||0>loadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].upload.onload=function(){testStream(i,0)}.bind(this),xhr[i].upload.onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this),xhr[i].open("POST",settings.url_ul+url_sep(settings.url_ul)+"r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(req))}}.bind(this),1)}.bind(this),i=0;it))if(graceTimeDone){var speed=totLoaded/(t/1e3);ulStatus=(8*speed*settings.overheadCompensationFactor/1048576).toFixed(2),(t/1e3>settings.time_ul||failed)&&((failed||isNaN(ulStatus))&&(ulStatus="Fail"),clearRequests(),clearInterval(interval),done())}else t>1e3*settings.time_ulGraceTime&&(totLoaded>0&&(startT=(new Date).getTime(),totLoaded=0),graceTimeDone=!0)}.bind(this),200)}}function pingTest(done){if(!ptCalled){ptCalled=!0;var prevT=null,ping=0,jitter=0,i=0,prevInstspd=0;xhr=[];var doPing=function(){prevT=(new Date).getTime(),xhr[0]=new XMLHttpRequest,xhr[0].onload=function(){if(0===i)prevT=(new Date).getTime();else{var instspd=(new Date).getTime()-prevT,instjitter=Math.abs(instspd-prevInstspd);1===i?ping=instspd:(ping=.9*ping+.1*instspd,jitter=instjitter>jitter?.2*jitter+.8*instjitter:.9*jitter+.1*instjitter),prevInstspd=instspd}pingStatus=ping.toFixed(2),jitterStatus=jitter.toFixed(2),i++,ii;i++)req.push(r);req=new Blob(req),r=new ArrayBuffer(262144);try{r=new Float32Array(r);for(var i=0;iloadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].onload=function(){try{xhr[i].abort()}catch(e){}testStream(i,0)}.bind(this),xhr[i].onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this);try{settings.xhr_dlUseBlob?xhr[i].responseType="blob":xhr[i].responseType="arraybuffer"}catch(e){}xhr[i].open("GET",settings.url_dl+url_sep(settings.url_dl)+"r="+Math.random()+"&ckSize="+settings.garbagePhp_chunkSize,!0),xhr[i].send()}}.bind(this),1+delay)}.bind(this),i=0;it))if(graceTimeDone){var speed=totLoaded/(t/1e3);dlStatus=(8*speed*settings.overheadCompensationFactor/1048576).toFixed(2),(t/1e3>settings.time_dl||failed)&&((failed||isNaN(dlStatus))&&(dlStatus="Fail"),clearRequests(),clearInterval(interval),done())}else t>1e3*settings.time_dlGraceTime&&(totLoaded>0&&(startT=(new Date).getTime(),totLoaded=0),graceTimeDone=!0)}.bind(this),200)}}function ulTest(done){if(!ulCalled){ulCalled=!0;var totLoaded=0,startT=(new Date).getTime(),graceTimeDone=!1,failed=!1;xhr=[];for(var testStream=function(i,delay){setTimeout(function(){if(3===testStatus){var prevLoaded=0,x=new XMLHttpRequest;xhr[i]=x;var ie11workaround;try{xhr[i].upload.onprogress,ie11workaround=!1}catch(e){ie11workaround=!0}ie11workaround?(xhr[i].onload=function(){totLoaded+=262144,testStream(i,0)},xhr[i].onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)},xhr[i].open("POST",settings.url_ul+url_sep(settings.url_ul)+"r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(reqsmall)):(xhr[i].upload.onprogress=function(event){if(3!==testStatus)try{x.abort()}catch(e){}var loadDiff=event.loaded<=0?0:event.loaded-prevLoaded;isNaN(loadDiff)||!isFinite(loadDiff)||0>loadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].upload.onload=function(){testStream(i,0)}.bind(this),xhr[i].upload.onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this),xhr[i].open("POST",settings.url_ul+url_sep(settings.url_ul)+"r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(req))}}.bind(this),1)}.bind(this),i=0;it))if(graceTimeDone){var speed=totLoaded/(t/1e3);ulStatus=(8*speed*settings.overheadCompensationFactor/1048576).toFixed(2),(t/1e3>settings.time_ul||failed)&&((failed||isNaN(ulStatus))&&(ulStatus="Fail"),clearRequests(),clearInterval(interval),done())}else t>1e3*settings.time_ulGraceTime&&(totLoaded>0&&(startT=(new Date).getTime(),totLoaded=0),graceTimeDone=!0)}.bind(this),200)}}function pingTest(done){if(!ptCalled){ptCalled=!0;var prevT=null,ping=0,jitter=0,i=0,prevInstspd=0;xhr=[];var doPing=function(){prevT=(new Date).getTime(),xhr[0]=new XMLHttpRequest,xhr[0].onload=function(){if(0===i)prevT=(new Date).getTime();else{var instspd=(new Date).getTime()-prevT,instjitter=Math.abs(instspd-prevInstspd);1===i?ping=instspd:(ping=.9*ping+.1*instspd,jitter=instjitter>jitter?.2*jitter+.8*instjitter:.9*jitter+.1*instjitter),prevInstspd=instspd}pingStatus=ping.toFixed(2),jitterStatus=jitter.toFixed(2),i++,ii;i++)req.push(r);req=new Blob(req),r=new ArrayBuffer(262144);try{r=new Float32Array(r);for(var i=0;i Date: Sat, 24 Jun 2017 10:38:37 +0200 Subject: [PATCH 11/18] Updated README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index b4fd00c37..5fe525ee9 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,9 @@ Only modern browsers are supported (IE11, latest Edge, latest Chrome, latest Fir ## How to use in your site See the examples or doc.md +## Docker +Please see the ```docker``` branch + ## License Copyright (C) 2016-2017 Federico Dossena From 976412e802babf1cf0b26e16b8050a1c54fd1b85 Mon Sep 17 00:00:00 2001 From: dosse91 Date: Mon, 26 Jun 2017 13:18:57 +0200 Subject: [PATCH 12/18] Added donation info --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 5fe525ee9..eac1b3676 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,9 @@ See the examples or doc.md ## Docker Please see the ```docker``` branch +## Donate +If you want to support this project, you can [send a donation via PayPal](https://www.paypal.me/sineisochronic). + ## License Copyright (C) 2016-2017 Federico Dossena From f432f84e3ac260ff6664cb9e082f2e0f4e289f46 Mon Sep 17 00:00:00 2001 From: dosse91 Date: Tue, 27 Jun 2017 16:04:19 +0200 Subject: [PATCH 13/18] Added more troubleshooting --- doc.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc.md b/doc.md index 7f8de9ece..d3ce4f7c8 100644 --- a/doc.md +++ b/doc.md @@ -203,8 +203,9 @@ __Important:__ do not simply kill the worker while it's running, as it may leave These are the most common issues reported by users, and how to fix them #### Download test gives very low result -Are garbage.php and empty.php (or your replacements) reachable? -Press F12, select network and start the test. Do you see errors? (cancelled requests are not errors) +Are garbage.php and empty.php (or your replacements) reachable? +Press F12, select network and start the test. Do you see errors? (cancelled requests are not errors) +If a small download starts, open it in a text editor. Does it say it's missing openssl_random_pseudo_bytes()? In this case, install OpenSSL (this is usually included when you install Apache and PHP on most distros). #### Upload test is inaccurate, and I see lag spikes Check your server's maximum POST size, make sure it's at least 20Mbytes, possibly more From 4e04794ff048221e63bffcb4bad573027ef87355 Mon Sep 17 00:00:00 2001 From: dosse91 Date: Wed, 5 Jul 2017 12:30:59 +0200 Subject: [PATCH 14/18] Updated known issues --- doc.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc.md b/doc.md index d3ce4f7c8..831d7543f 100644 --- a/doc.md +++ b/doc.md @@ -251,6 +251,7 @@ w.postMessage('start {"url_dl": "newGarbageURL", "url_ul": "newEmptyURL", "url_p * __IE11:__ the upload test is not precise on very fast connections * __IE11:__ the upload test may not work over HTTPS * __Safari:__ works, but needs more testing and tweaking for very fast connections +* __Firefox:__ on some Linux systems with hardware acceleration turned off, the page rendering makes the browser lag, reducing the accuracy of the ping/jitter test ## Making changes Since this is an open source project, you can modify it. From 739d92d69a9adbfa3e4f5e6d3b32ba87961e8bd2 Mon Sep 17 00:00:00 2001 From: dosse91 Date: Thu, 13 Jul 2017 16:22:01 +0200 Subject: [PATCH 15/18] Added link to #50 --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index eac1b3676..0eea20e14 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,8 @@ Only modern browsers are supported (IE11, latest Edge, latest Chrome, latest Fir * [Debian 9.0 with Apache](https://fdossena.com/?p=speedtest/quickstart_deb.frag) * [Windows Server 2016 with IIS](https://fdossena.com/?p=speedtest/quickstart_win.frag) +Also, here's an [example config on Ubuntu 16 LTS](https://github.com/adolfintel/speedtest/issues/50) + ## How to use in your site See the examples or doc.md From b20c13728fbdc1391179775d92105e40114e35f5 Mon Sep 17 00:00:00 2001 From: dosse91 Date: Sun, 16 Jul 2017 08:22:08 +0200 Subject: [PATCH 16/18] The time limits are now ignored if the speed is still 0.00 when the limit is reached --- doc.md | 6 +++--- speedtest_worker.js | 6 +++--- speedtest_worker.min.js | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/doc.md b/doc.md index 831d7543f..160f29a3e 100644 --- a/doc.md +++ b/doc.md @@ -1,7 +1,7 @@ # HTML5 Speedtest > by Federico Dossena -> Version 4.2.7, June 22 2017 +> Version 4.2.8, July 16 2017 > [https://github.com/adolfintel/speedtest/](https://github.com/adolfintel/speedtest/) @@ -130,10 +130,10 @@ w.postMessage('start {"param1": "value1", "param2": "value2", ...}') ``` #### Test parameters -* __time_dl__: How long the download test should be in seconds +* __time_dl__: How long the download test should be in seconds. The test will continue regardless of this limit if the speed is still 0.00 when the limit is reached. * Default: `15` * Recommended: `>=5` -* __time_ul__: How long the upload test should be in seconds +* __time_ul__: How long the upload test should be in seconds. The test will continue regardless of this limit if the speed is still 0.00 when the limit is reached. * Default: `15` * Recommended: `>=10` * __count_ping__: How many pings to perform in the ping test diff --git a/speedtest_worker.js b/speedtest_worker.js index 86bb043bb..78bc6416d 100644 --- a/speedtest_worker.js +++ b/speedtest_worker.js @@ -1,5 +1,5 @@ /* - HTML5 Speedtest v4.2.7 + HTML5 Speedtest v4.2.8 by Federico Dossena https://github.com/adolfintel/speedtest/ GNU LGPLv3 License @@ -201,7 +201,7 @@ function dlTest (done) { }else{ var speed = totLoaded / (t / 1000.0) dlStatus = ((speed * 8 * settings.overheadCompensationFactor)/1048576).toFixed(2) // speed is multiplied by 8 to go from bytes to bits, overhead compensation is applied, then everything is divided by 1048576 to go to megabits/s - if ((t / 1000.0) > settings.time_dl || failed) { // test is over, stop streams and timer + if (((t / 1000.0) > settings.time_dl && dlStatus > 0) || failed) { // test is over, stop streams and timer if (failed || isNaN(dlStatus)) dlStatus = 'Fail' clearRequests() clearInterval(interval) @@ -306,7 +306,7 @@ function ulTest (done) { }else{ var speed = totLoaded / (t / 1000.0) ulStatus = ((speed * 8 * settings.overheadCompensationFactor)/1048576).toFixed(2) // speed is multiplied by 8 to go from bytes to bits, overhead compensation is applied, then everything is divided by 1048576 to go to megabits/s - if ((t / 1000.0) > settings.time_ul || failed) { // test is over, stop streams and timer + if (((t / 1000.0) > settings.time_ul && ulStatus > 0) || failed) { // test is over, stop streams and timer if (failed || isNaN(ulStatus)) ulStatus = 'Fail' clearRequests() clearInterval(interval) diff --git a/speedtest_worker.min.js b/speedtest_worker.min.js index 7223949bf..933bb391e 100644 --- a/speedtest_worker.min.js +++ b/speedtest_worker.min.js @@ -1 +1 @@ -function url_sep(url){return url.match(/\?/)?"&":"?"}function clearRequests(){if(xhr){for(var i=0;iloadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].onload=function(){try{xhr[i].abort()}catch(e){}testStream(i,0)}.bind(this),xhr[i].onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this);try{settings.xhr_dlUseBlob?xhr[i].responseType="blob":xhr[i].responseType="arraybuffer"}catch(e){}xhr[i].open("GET",settings.url_dl+url_sep(settings.url_dl)+"r="+Math.random()+"&ckSize="+settings.garbagePhp_chunkSize,!0),xhr[i].send()}}.bind(this),1+delay)}.bind(this),i=0;it))if(graceTimeDone){var speed=totLoaded/(t/1e3);dlStatus=(8*speed*settings.overheadCompensationFactor/1048576).toFixed(2),(t/1e3>settings.time_dl||failed)&&((failed||isNaN(dlStatus))&&(dlStatus="Fail"),clearRequests(),clearInterval(interval),done())}else t>1e3*settings.time_dlGraceTime&&(totLoaded>0&&(startT=(new Date).getTime(),totLoaded=0),graceTimeDone=!0)}.bind(this),200)}}function ulTest(done){if(!ulCalled){ulCalled=!0;var totLoaded=0,startT=(new Date).getTime(),graceTimeDone=!1,failed=!1;xhr=[];for(var testStream=function(i,delay){setTimeout(function(){if(3===testStatus){var prevLoaded=0,x=new XMLHttpRequest;xhr[i]=x;var ie11workaround;try{xhr[i].upload.onprogress,ie11workaround=!1}catch(e){ie11workaround=!0}ie11workaround?(xhr[i].onload=function(){totLoaded+=262144,testStream(i,0)},xhr[i].onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)},xhr[i].open("POST",settings.url_ul+url_sep(settings.url_ul)+"r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(reqsmall)):(xhr[i].upload.onprogress=function(event){if(3!==testStatus)try{x.abort()}catch(e){}var loadDiff=event.loaded<=0?0:event.loaded-prevLoaded;isNaN(loadDiff)||!isFinite(loadDiff)||0>loadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].upload.onload=function(){testStream(i,0)}.bind(this),xhr[i].upload.onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this),xhr[i].open("POST",settings.url_ul+url_sep(settings.url_ul)+"r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(req))}}.bind(this),1)}.bind(this),i=0;it))if(graceTimeDone){var speed=totLoaded/(t/1e3);ulStatus=(8*speed*settings.overheadCompensationFactor/1048576).toFixed(2),(t/1e3>settings.time_ul||failed)&&((failed||isNaN(ulStatus))&&(ulStatus="Fail"),clearRequests(),clearInterval(interval),done())}else t>1e3*settings.time_ulGraceTime&&(totLoaded>0&&(startT=(new Date).getTime(),totLoaded=0),graceTimeDone=!0)}.bind(this),200)}}function pingTest(done){if(!ptCalled){ptCalled=!0;var prevT=null,ping=0,jitter=0,i=0,prevInstspd=0;xhr=[];var doPing=function(){prevT=(new Date).getTime(),xhr[0]=new XMLHttpRequest,xhr[0].onload=function(){if(0===i)prevT=(new Date).getTime();else{var instspd=(new Date).getTime()-prevT,instjitter=Math.abs(instspd-prevInstspd);1===i?ping=instspd:(ping=.9*ping+.1*instspd,jitter=instjitter>jitter?.2*jitter+.8*instjitter:.9*jitter+.1*instjitter),prevInstspd=instspd}pingStatus=ping.toFixed(2),jitterStatus=jitter.toFixed(2),i++,ii;i++)req.push(r);req=new Blob(req),r=new ArrayBuffer(262144);try{r=new Float32Array(r);for(var i=0;iloadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].onload=function(){try{xhr[i].abort()}catch(e){}testStream(i,0)}.bind(this),xhr[i].onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this);try{settings.xhr_dlUseBlob?xhr[i].responseType="blob":xhr[i].responseType="arraybuffer"}catch(e){}xhr[i].open("GET",settings.url_dl+url_sep(settings.url_dl)+"r="+Math.random()+"&ckSize="+settings.garbagePhp_chunkSize,!0),xhr[i].send()}}.bind(this),1+delay)}.bind(this),i=0;it))if(graceTimeDone){var speed=totLoaded/(t/1e3);dlStatus=(8*speed*settings.overheadCompensationFactor/1048576).toFixed(2),(t/1e3>settings.time_dl&&dlStatus>0||failed)&&((failed||isNaN(dlStatus))&&(dlStatus="Fail"),clearRequests(),clearInterval(interval),done())}else t>1e3*settings.time_dlGraceTime&&(totLoaded>0&&(startT=(new Date).getTime(),totLoaded=0),graceTimeDone=!0)}.bind(this),200)}}function ulTest(done){if(!ulCalled){ulCalled=!0;var totLoaded=0,startT=(new Date).getTime(),graceTimeDone=!1,failed=!1;xhr=[];for(var testStream=function(i,delay){setTimeout(function(){if(3===testStatus){var prevLoaded=0,x=new XMLHttpRequest;xhr[i]=x;var ie11workaround;try{xhr[i].upload.onprogress,ie11workaround=!1}catch(e){ie11workaround=!0}ie11workaround?(xhr[i].onload=function(){totLoaded+=262144,testStream(i,0)},xhr[i].onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)},xhr[i].open("POST",settings.url_ul+url_sep(settings.url_ul)+"r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(reqsmall)):(xhr[i].upload.onprogress=function(event){if(3!==testStatus)try{x.abort()}catch(e){}var loadDiff=event.loaded<=0?0:event.loaded-prevLoaded;isNaN(loadDiff)||!isFinite(loadDiff)||0>loadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].upload.onload=function(){testStream(i,0)}.bind(this),xhr[i].upload.onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this),xhr[i].open("POST",settings.url_ul+url_sep(settings.url_ul)+"r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(req))}}.bind(this),1)}.bind(this),i=0;it))if(graceTimeDone){var speed=totLoaded/(t/1e3);ulStatus=(8*speed*settings.overheadCompensationFactor/1048576).toFixed(2),(t/1e3>settings.time_ul&&ulStatus>0||failed)&&((failed||isNaN(ulStatus))&&(ulStatus="Fail"),clearRequests(),clearInterval(interval),done())}else t>1e3*settings.time_ulGraceTime&&(totLoaded>0&&(startT=(new Date).getTime(),totLoaded=0),graceTimeDone=!0)}.bind(this),200)}}function pingTest(done){if(!ptCalled){ptCalled=!0;var prevT=null,ping=0,jitter=0,i=0,prevInstspd=0;xhr=[];var doPing=function(){prevT=(new Date).getTime(),xhr[0]=new XMLHttpRequest,xhr[0].onload=function(){if(0===i)prevT=(new Date).getTime();else{var instspd=(new Date).getTime()-prevT,instjitter=Math.abs(instspd-prevInstspd);1===i?ping=instspd:(ping=.9*ping+.1*instspd,jitter=instjitter>jitter?.2*jitter+.8*instjitter:.9*jitter+.1*instjitter),prevInstspd=instspd}pingStatus=ping.toFixed(2),jitterStatus=jitter.toFixed(2),i++,ii;i++)req.push(r);req=new Blob(req),r=new ArrayBuffer(262144);try{r=new Float32Array(r);for(var i=0;i Date: Wed, 19 Jul 2017 16:04:37 +1000 Subject: [PATCH 17/18] add support to disable specific tests when pass "-1" (#52) * add support to disable specific tests when pass "-1" * add checkbox on example7.html & remove unnecessary codes in js --- doc.md | 2 + example7.html | 119 ++++++++++++++++++++++++++++++++++++++++ speedtest_worker.js | 5 ++ speedtest_worker.min.js | 2 +- 4 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 example7.html diff --git a/doc.md b/doc.md index 160f29a3e..de6fe6e9f 100644 --- a/doc.md +++ b/doc.md @@ -129,6 +129,8 @@ If you want, you can change these settings and pass them to the worker as JSON w w.postMessage('start {"param1": "value1", "param2": "value2", ...}') ``` +Pass "-1" to disable specific tests + #### Test parameters * __time_dl__: How long the download test should be in seconds. The test will continue regardless of this limit if the speed is still 0.00 when the limit is reached. * Default: `15` diff --git a/example7.html b/example7.html new file mode 100644 index 000000000..f0ed2da88 --- /dev/null +++ b/example7.html @@ -0,0 +1,119 @@ + + + + + + + Speedtest + + + + + + +
+
+
+

+ Speedtest + + +

+

+ Your IP: + +

+
+
+

Download

+

+

Mbit/s

+

+
+
+

Upload

+

+

Mbit/s

+

+
+
+

Ping

+

+

ms

+

+
+
+

Jitter

+

+

ms

+
+
+
+ + + + diff --git a/speedtest_worker.js b/speedtest_worker.js index 78bc6416d..5c0c70995 100644 --- a/speedtest_worker.js +++ b/speedtest_worker.js @@ -94,6 +94,7 @@ this.addEventListener('message', function (e) { settings.xhr_dlMultistream = 5 } } + if (typeof s.count_ping !== 'undefined') settings.count_ping = s.count_ping // number of pings for ping test if (typeof s.xhr_dlMultistream !== 'undefined') settings.xhr_dlMultistream = s.xhr_dlMultistream // number of download streams if (typeof s.xhr_ulMultistream !== 'undefined') settings.xhr_ulMultistream = s.xhr_ulMultistream // number of upload streams @@ -129,6 +130,7 @@ function clearRequests () { } // gets client's IP using url_getIp, then calls the done function function getIp (done) { + if (settings.url_getIp == "-1") {done(); return} xhr = new XMLHttpRequest() xhr.onload = function () { clientIp = xhr.responseText @@ -144,6 +146,7 @@ function getIp (done) { var dlCalled = false // used to prevent multiple accidental calls to dlTest function dlTest (done) { if (dlCalled) return; else dlCalled = true // dlTest already called? + if (settings.url_dl == "-1") {done(); return} var totLoaded = 0.0, // total number of loaded bytes startT = new Date().getTime(), // timestamp when test was started graceTimeDone = false, //set to true after the grace time is past @@ -225,6 +228,7 @@ reqsmall = new Blob(reqsmall) var ulCalled = false // used to prevent multiple accidental calls to ulTest function ulTest (done) { if (ulCalled) return; else ulCalled = true // ulTest already called? + if (settings.url_ul == "-1") {done(); return} var totLoaded = 0.0, // total number of transmitted bytes startT = new Date().getTime(), // timestamp when test was started graceTimeDone = false, //set to true after the grace time is past @@ -319,6 +323,7 @@ function ulTest (done) { var ptCalled = false // used to prevent multiple accidental calls to pingTest function pingTest (done) { if (ptCalled) return; else ptCalled = true // pingTest already called? + if (settings.url_ping == "-1") {done(); return} var prevT = null // last time a pong was received var ping = 0.0 // current ping value var jitter = 0.0 // current jitter value diff --git a/speedtest_worker.min.js b/speedtest_worker.min.js index 933bb391e..ae33a00eb 100644 --- a/speedtest_worker.min.js +++ b/speedtest_worker.min.js @@ -1 +1 @@ -function url_sep(url){return url.match(/\?/)?"&":"?"}function clearRequests(){if(xhr){for(var i=0;iloadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].onload=function(){try{xhr[i].abort()}catch(e){}testStream(i,0)}.bind(this),xhr[i].onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this);try{settings.xhr_dlUseBlob?xhr[i].responseType="blob":xhr[i].responseType="arraybuffer"}catch(e){}xhr[i].open("GET",settings.url_dl+url_sep(settings.url_dl)+"r="+Math.random()+"&ckSize="+settings.garbagePhp_chunkSize,!0),xhr[i].send()}}.bind(this),1+delay)}.bind(this),i=0;it))if(graceTimeDone){var speed=totLoaded/(t/1e3);dlStatus=(8*speed*settings.overheadCompensationFactor/1048576).toFixed(2),(t/1e3>settings.time_dl&&dlStatus>0||failed)&&((failed||isNaN(dlStatus))&&(dlStatus="Fail"),clearRequests(),clearInterval(interval),done())}else t>1e3*settings.time_dlGraceTime&&(totLoaded>0&&(startT=(new Date).getTime(),totLoaded=0),graceTimeDone=!0)}.bind(this),200)}}function ulTest(done){if(!ulCalled){ulCalled=!0;var totLoaded=0,startT=(new Date).getTime(),graceTimeDone=!1,failed=!1;xhr=[];for(var testStream=function(i,delay){setTimeout(function(){if(3===testStatus){var prevLoaded=0,x=new XMLHttpRequest;xhr[i]=x;var ie11workaround;try{xhr[i].upload.onprogress,ie11workaround=!1}catch(e){ie11workaround=!0}ie11workaround?(xhr[i].onload=function(){totLoaded+=262144,testStream(i,0)},xhr[i].onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)},xhr[i].open("POST",settings.url_ul+url_sep(settings.url_ul)+"r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(reqsmall)):(xhr[i].upload.onprogress=function(event){if(3!==testStatus)try{x.abort()}catch(e){}var loadDiff=event.loaded<=0?0:event.loaded-prevLoaded;isNaN(loadDiff)||!isFinite(loadDiff)||0>loadDiff||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].upload.onload=function(){testStream(i,0)}.bind(this),xhr[i].upload.onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this),xhr[i].open("POST",settings.url_ul+url_sep(settings.url_ul)+"r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(req))}}.bind(this),1)}.bind(this),i=0;it))if(graceTimeDone){var speed=totLoaded/(t/1e3);ulStatus=(8*speed*settings.overheadCompensationFactor/1048576).toFixed(2),(t/1e3>settings.time_ul&&ulStatus>0||failed)&&((failed||isNaN(ulStatus))&&(ulStatus="Fail"),clearRequests(),clearInterval(interval),done())}else t>1e3*settings.time_ulGraceTime&&(totLoaded>0&&(startT=(new Date).getTime(),totLoaded=0),graceTimeDone=!0)}.bind(this),200)}}function pingTest(done){if(!ptCalled){ptCalled=!0;var prevT=null,ping=0,jitter=0,i=0,prevInstspd=0;xhr=[];var doPing=function(){prevT=(new Date).getTime(),xhr[0]=new XMLHttpRequest,xhr[0].onload=function(){if(0===i)prevT=(new Date).getTime();else{var instspd=(new Date).getTime()-prevT,instjitter=Math.abs(instspd-prevInstspd);1===i?ping=instspd:(ping=.9*ping+.1*instspd,jitter=instjitter>jitter?.2*jitter+.8*instjitter:.9*jitter+.1*instjitter),prevInstspd=instspd}pingStatus=ping.toFixed(2),jitterStatus=jitter.toFixed(2),i++,ii;i++)req.push(r);req=new Blob(req),r=new ArrayBuffer(262144);try{r=new Float32Array(r);for(var i=0;isettings.time_dl&&dlStatus>0||failed)&&((failed||isNaN(dlStatus))&&(dlStatus="Fail"),clearRequests(),clearInterval(interval),done())):t>1e3*settings.time_dlGraceTime&&(totLoaded>0&&(startT=(new Date).getTime(),totLoaded=0),graceTimeDone=!0))}.bind(this),200)}else done()}function ulTest(done){if(!ulCalled)if(ulCalled=!0,"-1"!=settings.url_ul){var totLoaded=0,startT=(new Date).getTime(),graceTimeDone=!1,failed=!1;xhr=[];for(var testStream=function(i,delay){setTimeout(function(){if(3===testStatus){var prevLoaded=0,x=new XMLHttpRequest;xhr[i]=x;var ie11workaround;try{xhr[i].upload.onprogress,ie11workaround=!1}catch(e){ie11workaround=!0}ie11workaround?(xhr[i].onload=function(){totLoaded+=262144,testStream(i,0)},xhr[i].onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)},xhr[i].open("POST",settings.url_ul+url_sep(settings.url_ul)+"r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(reqsmall)):(xhr[i].upload.onprogress=function(event){if(3!==testStatus)try{x.abort()}catch(e){}var loadDiff=event.loaded<=0?0:event.loaded-prevLoaded;isNaN(loadDiff)||!isFinite(loadDiff)||loadDiff<0||(totLoaded+=loadDiff,prevLoaded=event.loaded)}.bind(this),xhr[i].upload.onload=function(){testStream(i,0)}.bind(this),xhr[i].upload.onerror=function(){0===settings.xhr_ignoreErrors&&(failed=!0);try{xhr[i].abort()}catch(e){}delete xhr[i],1===settings.xhr_ignoreErrors&&testStream(i,100)}.bind(this),xhr[i].open("POST",settings.url_ul+url_sep(settings.url_ul)+"r="+Math.random(),!0),xhr[i].setRequestHeader("Content-Encoding","identity"),xhr[i].send(req))}}.bind(this),1)}.bind(this),i=0;isettings.time_ul&&ulStatus>0||failed)&&((failed||isNaN(ulStatus))&&(ulStatus="Fail"),clearRequests(),clearInterval(interval),done())):t>1e3*settings.time_ulGraceTime&&(totLoaded>0&&(startT=(new Date).getTime(),totLoaded=0),graceTimeDone=!0))}.bind(this),200)}else done()}function pingTest(done){if(!ptCalled)if(ptCalled=!0,"-1"!=settings.url_ping){var prevT=null,ping=0,jitter=0,i=0,prevInstspd=0;xhr=[];var doPing=function(){prevT=(new Date).getTime(),xhr[0]=new XMLHttpRequest,xhr[0].onload=function(){if(0===i)prevT=(new Date).getTime();else{var instspd=(new Date).getTime()-prevT,instjitter=Math.abs(instspd-prevInstspd);1===i?ping=instspd:(ping=.9*ping+.1*instspd,jitter=instjitter>jitter?.2*jitter+.8*instjitter:.9*jitter+.1*instjitter),prevInstspd=instspd}pingStatus=ping.toFixed(2),jitterStatus=jitter.toFixed(2),++i Date: Wed, 19 Jul 2017 08:10:10 +0200 Subject: [PATCH 18/18] Updated doc.md --- doc.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/doc.md b/doc.md index de6fe6e9f..6b8863b03 100644 --- a/doc.md +++ b/doc.md @@ -1,7 +1,7 @@ # HTML5 Speedtest > by Federico Dossena -> Version 4.2.8, July 16 2017 +> Version 4.2.9, July 19 2017 > [https://github.com/adolfintel/speedtest/](https://github.com/adolfintel/speedtest/) @@ -129,8 +129,6 @@ If you want, you can change these settings and pass them to the worker as JSON w w.postMessage('start {"param1": "value1", "param2": "value2", ...}') ``` -Pass "-1" to disable specific tests - #### Test parameters * __time_dl__: How long the download test should be in seconds. The test will continue regardless of this limit if the speed is still 0.00 when the limit is reached. * Default: `15` @@ -141,17 +139,21 @@ Pass "-1" to disable specific tests * __count_ping__: How many pings to perform in the ping test * Default: `35` * Recommended: `>=20` -* __url_dl__: path to garbage.php or a large file to use for the download test +* __url_dl__: path to garbage.php or a large file to use for the download test. * Default: `garbage.php` + * The string "-1" disables the test * __Important:__ path is relative to js file * __url_ul__: path to an empty file or empty.php to use for the upload test * Default: `empty.php` + * The string "-1" disables the test * __Important:__ path is relative to js file * __url_ping__: path to an empty file or empty.php to use for the ping test * Default: `empty.php` + * The string "-1" disables the test * __Important:__ path is relative to js file * __url_getIp__: path to getIP.php or replacement * Default: `getIP.php` + * The string "-1" disables the test * __Important:__ path is relative to js file #### Advanced test parameters