From 710887f4007150d26a717dc3b296cd6755ae1868 Mon Sep 17 00:00:00 2001 From: dosse91 Date: Tue, 13 Jun 2017 14:20:05 +0200 Subject: [PATCH] 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 c4ad91d..f94e52e 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 19bc8aa..777c0bf 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 fab73a0..2591c25 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