Jelajahi Sumber

Rewritten to use Web Workers; Easier to use; Added more examples

dosse91 9 tahun lalu
induk
melakukan
3f99127b69
7 mengubah file dengan 301 tambahan dan 174 penghapusan
  1. 4 4
      README.md
  2. 0 47
      example.html
  3. 26 0
      example1.html
  4. 70 0
      example2.html
  5. 97 0
      example3.html
  6. 0 123
      speedtest.js
  7. 104 0
      speedtest_worker.js

+ 4 - 4
README.md

@@ -2,23 +2,23 @@
 
 No Flash, No Java, No Websocket, No Bullshit.
 
-This is a very small (4k) Speedtest implemented in Javascript, that relies only on XMLHttpRequest.
+This is a very small (4k) Speedtest implemented in Javascript, using XMLHttpRequest and Web Workers.
 
 ## Try it
 [Take a Speedtest](http://speedtest.adolfintel.com)
 
 ## Compatibility
-Microsoft Edge, Firefox 10+, Chrome 10+, Opera 15+, Safari 7 (not tested)
+Only modern browsers are supported (Edge 12+)
 
 ## Requirements
- - A reasonably fast web serve
+ - A reasonably fast web server
  - Some way to generate garbage data using either the included PHP script, a [big file of random data](http://downloads.adolfintel.com/geth.php?r=speedtest-bigfile), or a symlink to /dev/urandom
  - Your server must not compress the data it sends
  - Your server must accept large POST requests (up to 10 Megabytes), otherwise the upload test will fail
  - Client side, there must not be any type of buffering (such as a proxy), or you may get incorrect results
 
 ## How to use
-See example.html, it's not rocket science.
+See the examples, it's really simple.
 
 ## License
 Copyright (C) 2016 Federico Dossena

+ 0 - 47
example.html

@@ -1,47 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<title>Speedtest</title>
-<script type="text/javascript" src="speedtest.js"></script>
-</head>
-<body>
-<h1>Speedtest</h1>
-<h4>Download</h4>
-<div id="download">Wait...</div>
-<h4>Upload</h4>
-<div id="upload">Wait...</div>
-<h4>Latency</h4>
-<div id="ping">Wait...</div>
-<script type="text/javascript">
-	var tester=new DownloadTester("garbage.php",function(){
-		//when the download test is done, start the upload test
-		tester=new UploadTester("upload-test", function(){
-			//when the upload test is done, start the latency test
-			tester=new PingTester("empty.dat",null //when the test ends, do nothing
-			,function(){
-				//called periodically to update latency
-				document.getElementById("ping").innerHTML=tester.getValue()+" ms";
-			},function(){
-				//when the latency test fails
-				document.getElementById("ping").innerHTML="Failed";
-			}
-			);
-		},function(){
-			//called periodically to update upload speed
-			document.getElementById("upload").innerHTML=tester.getValue()+" Megabit/s";
-		},function(){
-			//when the upload test fails
-			document.getElementById("upload").innerHTML="Failed";
-			tester.onDone();
-		});
-	},function(){
-		//called periodically to update download speed
-		document.getElementById("download").innerHTML=tester.getValue()+" Megabit/s";
-	},function(){
-		//when the download test fails
-		document.getElementById("download").innerHTML="Failed";
-		tester.onDone();
-	});
-</script>
-</body>
-</html>

+ 26 - 0
example1.html

@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Speedtest</title>
+</head>
+<body>
+<h1>Speedtest</h1>
+<h4>Download</h4>
+<div id="download"></div>
+<h4>Upload</h4>
+<div id="upload"></div>
+<h4>Latency</h4>
+<div id="ping"></div>
+<script type="text/javascript">
+	var w=new Worker("speedtest_worker.js"); //create new worker
+	setInterval(function(){w.postMessage("status");}.bind(this),100); //ask for status every 100ms
+	w.onmessage=function(event){ //when status is received, split the string and put the values in the appropriate fields
+		var data=event.data.split(";"); //string format: status;download;upload;ping (speeds are in mbit/s) (status: 0=not started, 1=downloading, 2=uploading, 3=ping, 4=done, 5=aborted)
+		document.getElementById("download").innerHTML=data[1]+" Mbit/s";
+		document.getElementById("upload").innerHTML=data[2]+" Mbit/s";
+		document.getElementById("ping").innerHTML=data[3]+" ms";
+	}
+	w.postMessage("start"); //start the speedtest (default params: garbage.php empty.dat empty.dat)
+</script>
+</body>
+</html>

+ 70 - 0
example2.html

@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Speedtest</title>
+<style type="text/css">
+html,body{
+	margin:0;
+	padding:0;
+	border:none;
+	text-align:center;
+}
+div.test{
+	display:inline-block;
+	width:30vw;
+	text-align:center;
+}
+div.testName,div.meterUnit{
+	font-size:3vw;
+}
+div.meter{
+	font-size:6vw;
+	line-height:1.5em;
+	height:1.5em !important;
+}
+.flash{
+	animation:flash 0.6s linear infinite;
+}
+@keyframes flash{
+	0%{opacity:0.6;}
+	50%{opacity:1;}
+}
+
+</style>
+</head>
+<body>
+<h1>Speedtest</h1>
+<div class="test">
+	<div class="testName">Download</div>
+	<div class="meter">&nbsp;<span id="download"></span>&nbsp;</div>
+	<div class="meterUnit">Mbit/s</div>
+</div>
+<div class="test">
+	<div class="testName">Upload</div>
+	<div class="meter">&nbsp;<span id="upload"></span>&nbsp;</div>
+	<div class="meterUnit">Mbit/s</div>
+</div>
+<div class="test">
+	<div class="testName">Latency</div>
+	<div class="meter">&nbsp;<span id="ping"></span>&nbsp;</div>
+	<div class="meterUnit">ms</div>
+</div>
+<script type="text/javascript">
+	var w=new Worker("speedtest_worker.js");
+	var interval=setInterval(function(){w.postMessage("status");}.bind(this),100);
+	w.onmessage=function(event){
+		var data=event.data.split(";");
+		var status=Number(data[0]);
+		var dl=document.getElementById("download"),ul=document.getElementById("upload"),ping=document.getElementById("ping");
+		dl.className=status==1?"flash":"";ul.className=status==2?"flash":"";ping.className=status==3?"flash":"";
+		if(status>=4){
+			clearInterval(interval);
+		}
+		dl.innerHTML=data[1];
+		ul.innerHTML=data[2];
+		ping.innerHTML=data[3];
+	}.bind(this);
+	w.postMessage("start garbage.php empty.dat empty.dat");
+</script>
+</body>
+</html>

+ 97 - 0
example3.html

@@ -0,0 +1,97 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Speedtest</title>
+<style type="text/css">
+html,body{
+	margin:0;
+	padding:0;
+	border:none;
+	text-align:center;
+}
+div.test{
+	display:inline-block;
+	width:30vw;
+	text-align:center;
+}
+div.testName,div.meterUnit{
+	font-size:3vw;
+}
+div.meter{
+	font-size:6vw;
+	line-height:1.5em;
+	height:1.5em !important;
+}
+.flash{
+	animation:flash 0.6s linear infinite;
+}
+@keyframes flash{
+	0%{opacity:0.6;}
+	50%{opacity:1;}
+}
+a{
+	display:inline-block;
+	border:0.15em solid #000000;
+	padding:0.3em 0.5em;
+	margin:0.6em;
+	color:#000000;
+	text-decoration:none;
+}
+</style>
+<script type="text/javascript">
+var w=null;
+function runTest(){
+	document.getElementById("startBtn").style.display="none";
+	document.getElementById("testArea").style.display="";
+	document.getElementById("abortBtn").style.display="";
+	w=new Worker("speedtest_worker.js");
+	var interval=setInterval(function(){w.postMessage("status");}.bind(this),100);
+	w.onmessage=function(event){
+		var data=event.data.split(";");
+		var status=Number(data[0]);
+		var dl=document.getElementById("download"),ul=document.getElementById("upload"),ping=document.getElementById("ping");
+		dl.className=status==1?"flash":"";ul.className=status==2?"flash":"";ping.className=status==3?"flash":"";
+		if(status>=4){
+			clearInterval(interval);
+			document.getElementById("abortBtn").style.display="none";
+			document.getElementById("startBtn").style.display="";
+			w=null;
+		}
+		if(status==5){
+			document.getElementById("testArea").style.display="none";
+		}
+		dl.innerHTML=data[1];
+		ul.innerHTML=data[2];
+		ping.innerHTML=data[3];
+	}.bind(this);
+	w.postMessage("start garbage.php empty.dat empty.dat");
+}
+function abortTest(){
+	if(w)w.postMessage("abort");
+}
+</script>
+</head>
+<body>
+<h1>Speedtest</h1>
+<div id="testArea" style="display:none">
+<div class="test">
+	<div class="testName">Download</div>
+	<div class="meter">&nbsp;<span id="download"></span>&nbsp;</div>
+	<div class="meterUnit">Mbit/s</div>
+</div>
+<div class="test">
+	<div class="testName">Upload</div>
+	<div class="meter">&nbsp;<span id="upload"></span>&nbsp;</div>
+	<div class="meterUnit">Mbit/s</div>
+</div>
+<div class="test">
+	<div class="testName">Latency</div>
+	<div class="meter">&nbsp;<span id="ping"></span>&nbsp;</div>
+	<div class="meterUnit">ms</div>
+</div>
+<br/>
+<a href="javascript:abortTest()" id="abortBtn">Abort</a>
+</div>
+<a href="javascript:runTest()" id="startBtn">Run speedtest</a>
+</body>
+</html>

+ 0 - 123
speedtest.js

@@ -1,123 +0,0 @@
-function DownloadTester(serverURL,done,update,err){
-	this.xhr=new XMLHttpRequest();
-	this.firstTick=true;
-	this.prevLoaded=0;
-	this.startT=new Date().getTime();
-	this.prevT=new Date().getTime();
-	this.speed=0.0;
-	if(done)this.onDone=done; if(update)this.onUpdate=update; if(err)this.onFail=err;
-	this.xhr.onprogress=function(event){
-		var instspd=(event.loaded-this.prevLoaded)/((new Date().getTime()-this.prevT)/1000.0);
-		if(isNaN(instspd)||!isFinite(instspd)) return;
-		if(this.firstTick){
-			this.speed=instspd;
-			this.firstTick=false;
-		}else{
-			this.speed=this.speed*0.9+instspd*0.1;
-		}
-		this.prevLoaded=event.loaded;
-		this.prevT=new Date().getTime();
-		this.onUpdate();
-		if(((this.prevT-this.startT)/1000.0)>15){try{this.xhr.abort();}catch(e){} this.onDone();}
-	}.bind(this);
-	this.xhr.onload=function(){
-		this.onUpdate();
-		this.onDone();
-	}.bind(this);
-	this.xhr.onerror=function(){
-		this.onUpdate();
-		this.onFail();
-	}.bind(this);
-	this.xhr.open("GET", serverURL+"?random="+Math.random(),true);
-	this.xhr.send();
-}
-DownloadTester.prototype={
-	constructor:DownloadTester,
-	onDone:function(){},
-	onFail:function(){},
-	onUpdate:function(){},
-	getValue:function(){return ((this.speed*8)/1048576.0).toFixed(2);},
-	cancel:function(){try{this.xhr.abort();}catch(e){}}
-}
-
-function UploadTester(serverURL,done,update,err){
-	this.xhr=new XMLHttpRequest();
-	this.firstTick=true;
-	this.prevLoaded=0;
-	this.startT=new Date().getTime();
-	this.prevT=new Date().getTime();
-	this.speed=0.0;
-	if(done)this.onDone=done; if(update)this.onUpdate=update; if(err)this.onFail=err;
-	this.xhr.upload.onprogress=function(event){
-		var instspd=(event.loaded-this.prevLoaded)/((new Date().getTime()-this.prevT)/1000.0);
-		if(isNaN(instspd)||!isFinite(instspd)) return;
-		if(this.firstTick){
-			this.firstTick=false;
-		}else{
-			this.speed=this.speed*0.7+instspd*0.3;
-		}
-		this.prevLoaded=event.loaded;
-		this.prevT=new Date().getTime();
-		this.onUpdate();
-		if(((this.prevT-this.startT)/1000.0)>15){try{this.xhr.abort();}catch(e){} this.onDone();}
-	}.bind(this);
-	this.xhr.onload=function(){
-		this.onUpdate();
-		this.onDone();
-	}.bind(this);
-	this.xhr.onerror=function(){
-		this.onUpdate();
-		this.onFail();
-	}.bind(this);
-	this.xhr.open("POST", serverURL+"?random="+Math.random(),true);
-	this.xhr.send(new ArrayBuffer(10485760));
-}
-UploadTester.prototype={
-	constructor:UploadTester,
-	onDone:function(){},
-	onFail:function(){},
-	onUpdate:function(){},
-	getValue:function(){return ((this.speed*8)/1048576.0).toFixed(2);},
-	cancel:function(){try{this.xhr.abort();}catch(e){}}
-}
-
-function PingTester(serverURL,done,update,err){
-	this.xhr=null;
-	this.prevT=null;
-	this.ping=0.0;
-	this.i=0;
-	this.pingURL=serverURL;
-	if(done)this.onDone=done;
-	if(update)this.onUpdate=update;
-	if(err)this.onFail=err;
-	this.doPing=function(){
-		this.prevT=new Date().getTime();
-		this.xhr=new XMLHttpRequest();
-		this.xhr.onload=function(){
-			if(this.i==0){
-				this.prevT=new Date().getTime();
-			}else{
-				var instspd=new Date().getTime()-this.prevT;
-				if(this.i==1) this.ping=instspd; else this.ping=this.ping*0.9+instspd*0.1;
-			}
-			this.onUpdate();
-			this.i++;
-			if(this.i<50) this.doPing(); else this.onDone();
-		}.bind(this);
-		this.xhr.onerror=function(){
-			this.onUpdate();
-			this.onFail();
-		}.bind(this);
-		this.xhr.open("GET", this.pingURL+"?random="+Math.random(),true);
-		this.xhr.send();
-	}.bind(this);
-	this.doPing();
-}
-PingTester.prototype={
-	constructor:PingTester,
-	onDone:function(){},
-	onFail:function(){},
-	onUpdate:function(){},
-	getValue:function(){return this.ping.toFixed(2);},
-	cancel:function(){this.i=9999; if(this.xhr) try{xhr.abort();}catch(e){}}
-}

+ 104 - 0
speedtest_worker.js

@@ -0,0 +1,104 @@
+var testStatus=0,dlStatus="",ulStatus="",pingStatus="";
+var xhr=null;
+this.addEventListener('message', function(e){
+	var params=e.data.split(" ");
+	if(params[0]=="status"){
+		postMessage(testStatus+";"+dlStatus+";"+ulStatus+";"+pingStatus);
+	}
+	if(params[0]=="start"){
+		if(testStatus==0){
+			testStatus=1;
+			var dlUrl=params[1]?params[1]:"garbage.php", ulUrl=params[2]?params[2]:"empty.dat", pingUrl=params[3]?params[3]:"empty.dat";
+			dlTest(dlUrl,function(){testStatus=2;ulTest(ulUrl,function(){testStatus=3;pingTest(pingUrl,function(){testStatus=4;});});});
+		}
+	}
+	if(params[0]=="abort"){
+		try{if(xhr)xhr.abort();}catch(e){}
+		testStatus=5;dlStatus="";ulStatus="";pingStatus="";
+	}
+});
+
+function dlTest(serverURL,done){
+	var firstTick=true,startT=new Date().getTime(), prevT=new Date().getTime(),prevLoaded=0,speed=0.0;
+	xhr=new XMLHttpRequest();
+	xhr.onprogress=function(event){
+		var instspd=(event.loaded-prevLoaded)/((new Date().getTime()-prevT)/1000.0);
+		if(isNaN(instspd)||!isFinite(instspd)) return;
+		if(firstTick){
+			speed=instspd;
+			firstTick=false;
+		}else{
+			speed=speed*0.9+instspd*0.1;
+		}
+		prevLoaded=event.loaded;
+		prevT=new Date().getTime();
+		dlStatus=((speed*8)/1048576.0).toFixed(2);
+		if(((prevT-startT)/1000.0)>15){try{xhr.abort();}catch(e){} xhr=null; done();}
+	}.bind(this);
+	xhr.onload=function(){
+		dlStatus=((speed*8)/1048576.0).toFixed(2);
+		xhr=null;
+		done();
+	}.bind(this);
+	xhr.onerror=function(){
+		dlStatus="Fail";
+		xhr=null;
+		done();
+	}.bind(this);
+	xhr.open("GET", serverURL+"?random="+Math.random(),true);
+	xhr.send();
+}
+
+function ulTest(serverURL,done){
+	var firstTick=true,startT=new Date().getTime(), prevT=new Date().getTime(),prevLoaded=0,speed=0.0;
+	xhr=new XMLHttpRequest();
+	xhr.upload.onprogress=function(event){
+		var instspd=(event.loaded-prevLoaded)/((new Date().getTime()-prevT)/1000.0);
+		if(isNaN(instspd)||!isFinite(instspd)) return;
+		if(firstTick){
+			firstTick=false;
+		}else{
+			speed=speed*0.7+instspd*0.3;
+		}
+		prevLoaded=event.loaded;
+		prevT=new Date().getTime();
+		ulStatus=((speed*8)/1048576.0).toFixed(2);
+		if(((prevT-startT)/1000.0)>15){try{xhr.abort();}catch(e){} xhr=null; done();}
+	}.bind(this);
+	xhr.onload=function(){
+		ulStatus=((speed*8)/1048576.0).toFixed(2);
+		done();
+	}.bind(this);
+	xhr.onerror=function(){
+		ulStatus="Fail";
+		done();
+	}.bind(this);
+	xhr.open("POST", serverURL+"?random="+Math.random(),true);
+	xhr.send(new ArrayBuffer(10485760));
+}
+
+function pingTest(pingUrl,done){
+	var prevT=null,ping=0.0,i=0;
+	var doPing=function(){
+		prevT=new Date().getTime();
+		xhr=new XMLHttpRequest();
+		xhr.onload=function(){
+			if(i==0){
+				prevT=new Date().getTime();
+			}else{
+				var instspd=new Date().getTime()-prevT;
+				if(i==1) ping=instspd; else ping=ping*0.9+instspd*0.1;
+			}
+			pingStatus=ping.toFixed(2);
+			i++;
+			if(i<50) doPing(); else done();
+		}.bind(this);
+		xhr.onerror=function(){
+			pingStatus="Fail";
+			done();
+		}.bind(this);
+		xhr.open("GET", pingUrl+"?random="+Math.random(),true);
+		xhr.send();
+	}.bind(this);
+	doPing();
+}