Skip to content

Commit

Permalink
Speedtest worker now outputs status as JSON for easier parsing; Remov…
Browse files Browse the repository at this point in the history
…ed partial CSV implementation; Updated examples and documentation
  • Loading branch information
adolfintel committed Aug 8, 2018
1 parent 0ce73a7 commit 0954c6a
Show file tree
Hide file tree
Showing 14 changed files with 112 additions and 147 deletions.
62 changes: 21 additions & 41 deletions doc.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# HTML5 Speedtest

> by Federico Dossena
> Version 4.6, August 7, 2018
> Version 4.6.1, August 8, 2018
> [https://github.com/adolfintel/speedtest/](https://github.com/adolfintel/speedtest/)

Expand Down Expand Up @@ -107,54 +107,41 @@ we'll see the details of the format of the response.

```js
w.onmessage = function (event) {
var data = event.data.split(';')
var testState = data[0]
var dlStatus = data[1]
var ulStatus = data[2]
var pingStatus = data[3]
var jitterStatus = data[5]
var clientIp = data[4]
var dlProgress = data[6]
var ulProgress = data[7]
var pingProgress = data[8]
var testId = data[9]
if (testState >= 4) {
var data = JSON.parse(event.data);
if (data.testState >= 4) {
clearInterval(timer) // test is finished or aborted
}
// .. update your page here ..
}
```

#### Response format
The response from the worker is composed of values separated by `;` (semicolon) in this
format:
The response from the worker is a JSON string containing these entries:

`testState;dlStatus;ulStatus;pingStatus;clientIp;jitterStatus;dlProgress;ulProgress;pingProgress`

* __testState__ is an integer between -1 and 5
* __testState__: an integer between -1 and 5
* `-1` = Test not started yet
* `0` = Test starting
* `1` = Download test in progress
* `2` = Ping + Jitter test in progress
* `3` = Upload test in progress
* `4` = Test finished
* `5` = Test aborted
* __dlStatus__ is either
* __dlStatus__: either
* Empty string (not started or aborted)
* Download speed in Megabit/s as a number with 2 decimals
* The string "Fail" (test failed)
* __ulStatus__ is either
* __ulStatus__: either
* Empty string (not started or aborted)
* Upload speed in Megabit/s as a number with 2 decimals
* The string "Fail" (test failed)
* __pingStatus__ is either
* __pingStatus__: either
* Empty string (not started or aborted)
* Estimated ping in milliseconds as a number with 2 decimals
* The string "Fail" (test failed)
* __clientIp__ is either
* __clientIp__: either
* Empty string (not fetched yet or failed)
* The client's IP address as a string
* __jitterStatus__ is either
* The client's IP address as a string (with ISP info if enabled)
* __jitterStatus__: either
* Empty string (not started or aborted)
* Estimated jitter in milliseconds as a number with 2 decimals (lower = stable connection)
* The string "Fail" (test failed)
Expand Down Expand Up @@ -278,7 +265,11 @@ w.postMessage('start '+JSON.stringify(params))
* `1514 / 1460`: TCP+IPv4+ETH, ignoring HTTP overhead
* `1514 / 1440`: TCP+IPv6+ETH, ignoring HTTP overhead
* `1`: ignore overheads. This measures the speed at which you actually download and upload files rather than the raw connection speed
* __telemetry_extra__: Extra data that you want to be passed to the telemetry. This is a string field, if you want to pass an object, make sure you use ``JSON.stringify``.
* __telemetry_level__: The type of telemetry to use. See the telemetry section for more info about this
* Default: `none`
* `basic`: send results only
* `full`: send results and debug info
* __telemetry_extra__: Extra data that you want to be passed to the telemetry. This is a string field, if you want to pass an object, make sure you use ``JSON.stringify``. This string will be added to the database entry for this test.

### Aborting the test prematurely
The test can be aborted at any time by sending an abort command to the worker:
Expand Down Expand Up @@ -327,10 +318,10 @@ You need to start the test with your replacements like this:
w.postMessage('start {"url_dl": "newGarbageURL", "url_ul": "newEmptyURL", "url_ping": "newEmptyURL", "url_getIp": "newIpURL"}')
```
## Telemetry
Telemetry currently requires PHP and either MySQL, PostgreSQL or SQLite. Alternatively, it is possible to save to a CSV file.
Telemetry currently requires PHP and either MySQL, PostgreSQL or SQLite.
To set up the telemetry, we need to do 4 things:
* copy the `telemetry` folder
* edit `telemetry_settings.php` to add your database or CSV settings
* edit `telemetry_settings.php` to add your database settings
* create the database
* enable telemetry

Expand All @@ -341,7 +332,7 @@ If you see a table called `speedtest_users`, empty, you did it right.

### Configuring `telemetry.php`
Open `telemetry_settings.php` with notepad or a similar text editor.
Set your preferred database, ``$db_type="mysql";``, ``$db_type="sqlite";``, ``$db_type="postgresql";`` or ``$db_type="csv";``
Set your preferred database, ``$db_type="mysql";``, ``$db_type="sqlite";`` or ``$db_type="postgresql";``
If you choose to use Sqlite3, you must set the path to your database file:
```php
$Sqlite_db_file = "../telemetry.sql";
Expand All @@ -363,26 +354,19 @@ $PostgreSql_hostname="DB_HOSTNAME"; //database address, usually localhost
$PostgreSql_databasename="DB_NAME"; //the name of the database where you loaded telemetry_postgresql.sql
```

If you choose to use a CSV file, you must set the Csv_File and timezone variables.
```php
$Csv_File="myReportFile.csv";
$timezone='Europe/Paris';
```
__Note__: CSV currently only supports basic telemetry, the log will not be saved

### Enabling telemetry
Edit your test page; where you start the worker, you need to specify the `telemetry_level`.
There are 3 levels:
* `none`: telemetry is disabled (default)
* `basic`: telemetry collects IP, ISP info, User Agent, Preferred language, Test results
* `full`: same as above, but also collects a log (10-150 Kb each, not recommended)
* `full`: same as above, but also collects a debug log (10-150 Kb each, not recommended unless you're developing the speedtest)

Example:
```js
w.postMessage('start {"telemetry_level":"basic"}')
```

Also, see example-telemetry.html
You can use example-telemetryEnabled.html and example-telemetry-resultSharing.html as starting points.

### Results sharing
This feature generates an image that can be share by the user containing the download, upload, ping, jitter and ISP (if enabled).
Expand All @@ -391,17 +375,13 @@ To use this feature, copy the `results` folder. You can customize the style of t

This feature requires Telemetry to be enabled, and FreeType2 must be installed in PHP (if not already be installed by your distro).

__Note:__ CSV doesn't currently support this.

__Important:__ This feature relies on PHP functions `imagefttext` and `imageftbbox` that are well known for being problematic. The most common problem is that they can't find the font files and therefore nothing is drawn. This problem is metioned [here](http://php.net/manual/en/function.imagefttext.php) and was experienced by a lot of users.

### Seeing the results
A basic front-end for visualizing and searching tests by ID is available in `telemetry/stats.php`.

A login is required to access the interface. __Important__: change the default password in `telemetry_settings.php`.

__Note:__ CSV doesn't currently support this.

## Troubleshooting
These are the most common issues reported by users, and how to fix them. If you still need help, contact me at [info@fdossena.com](mailto:info@fdossena.com).

Expand Down
10 changes: 5 additions & 5 deletions example-basic.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ <h4>Latency</h4>
var w = new Worker('speedtest_worker.min.js') // create new worker
setInterval(function () { w.postMessage('status') }, 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').textContent = data[1] + ' Mbit/s'
document.getElementById('upload').textContent = data[2] + ' Mbit/s'
document.getElementById('ping').textContent = data[3] + ' ms, ' + data[5] + ' ms jitter'
document.getElementById('ip').textContent = data[4]
var data = JSON.parse(event.data); //fetch speedtest worker output
document.getElementById('download').textContent = data.dlStatus + ' Mbit/s'
document.getElementById('upload').textContent = data.ulStatus + ' Mbit/s'
document.getElementById('ping').textContent = data.pingStatus + ' ms, ' + data.jitterStatus + ' ms jitter'
document.getElementById('ip').textContent = data.clientIp
}
w.postMessage('start') // start the speedtest (default params. keep garbage.php and empty.dat in the same directory as the js file)
</script>
Expand Down
20 changes: 10 additions & 10 deletions example-chart.html
Original file line number Diff line number Diff line change
Expand Up @@ -200,8 +200,8 @@
w = new Worker('speedtest_worker.min.js')
var interval = setInterval(function () { w.postMessage('status') }, 100)
w.onmessage = function (event) {
var data = event.data.split(';')
var status = Number(data[0])
var data = JSON.parse(event.data)
var status = data.testStatus
if (status >= 4) {
clearInterval(interval)
document.getElementById('abortBtn').style.display = 'none'
Expand All @@ -211,24 +211,24 @@
if (status === 5) {
document.getElementById('testArea').style.display = 'none'
}
if (status === 1 && Number(data[1]) > 0) {
chart1.data.datasets[0].data[~~(20*Number(data[6]))]=(Number(data[1]))
if (status === 1 && Number(data.dlStatus) > 0) {
chart1.data.datasets[0].data[~~(20*Number(data.dlProgress))]=(Number(data.dlStatus))
chart1.data.labels[chart1.data.datasets[0].data.length - 1] = ''
chart1.update()
}
if (status === 3 && Number(data[2]) > 0) {
chart1.data.datasets[1].data[~~(20*Number(data[7]))]=(Number(data[2]))
if (status === 3 && Number(data.ulStatus) > 0) {
chart1.data.datasets[1].data[~~(20*Number(data.ulProgress))]=(Number(data.ulStatus))
chart1.data.labels[chart1.data.datasets[1].data.length - 1] = ''
chart1.update()
}
if (status === 2 && Number(data[3]) > 0) {
chart2.data.datasets[0].data.push(Number(data[3]))
chart2.data.datasets[1].data.push(Number(data[5]))
if (status === 2 && Number(data.pingStatus) > 0) {
chart2.data.datasets[0].data.push(Number(data.pingStatus))
chart2.data.datasets[1].data.push(Number(data.jitterStatus))
chart2.data.labels[chart2.data.datasets[0].data.length - 1] = ''
chart2.data.labels[chart2.data.datasets[1].data.length - 1] = ''
chart2.update()
}
ip.textContent = data[4]
ip.textContent = data.clientIp
}
w.postMessage('start')
}
Expand Down
14 changes: 7 additions & 7 deletions example-customSettings.html
Original file line number Diff line number Diff line change
Expand Up @@ -131,18 +131,18 @@
w.postMessage('start '+JSON.stringify(parameters)); //run the test with custom parameters
I("startStopBtn").className="running";
w.onmessage=function(e){
var data=e.data.split(';');
var status=Number(data[0]);
var data=JSON.parse(e.data);
var status=data.testStatus;
if(status>=4){
//test completed
I("startStopBtn").className="";
w=null;
}
I("ip").textContent=data[4];
I("dlText").textContent=(status==1&&data[1]==0)?"...":data[1];
I("ulText").textContent=(status==3&&data[2]==0)?"...":data[2];
I("pingText").textContent=data[3];
I("jitText").textContent=data[5];
I("ip").textContent=data.clientIp;
I("dlText").textContent=(status==1&&data.dlStatus==0)?"...":data.dlStatus;
I("ulText").textContent=(status==3&&data.ulStatus==0)?"...":data.ulStatus;
I("pingText").textContent=data.pingStatus;
I("jitText").textContent=data.jitterStatus;
};
}
}
Expand Down
8 changes: 4 additions & 4 deletions example-customSettings2.html
Original file line number Diff line number Diff line change
Expand Up @@ -125,15 +125,15 @@
w.postMessage('start {"test_order":"D_U"}'); //run only download and upload tests, with a 1s pause in between
I("startStopBtn").className="running";
w.onmessage=function(e){
var data=e.data.split(';');
var status=Number(data[0]);
var data=JSON.parse(e.data);
var status=data.testStatus;
if(status>=4){
//test completed
I("startStopBtn").className="";
w=null;
}
I("dlText").textContent=(status==1&&data[1]==0)?"...":data[1];
I("ulText").textContent=(status==3&&data[2]==0)?"...":data[2];
I("dlText").textContent=(status==1&&data.dlStatus==0)?"...":data.dlStatus;
I("ulText").textContent=(status==3&&data.ulStatus==0)?"...":data.ulStatus;
};
}
}
Expand Down
24 changes: 12 additions & 12 deletions example-gauges.html
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,8 @@
w.postMessage('start'); //Add optional parameters as a JSON object to this command
I("startStopBtn").className="running";
w.onmessage=function(e){
data=e.data.split(';');
var status=Number(data[0]);
data=JSON.parse(e.data);
var status=data.testStatus;
if(status>=4){
//test completed
I("startStopBtn").className="";
Expand All @@ -177,16 +177,16 @@
//this function reads the data sent back by the worker and updates the UI
function updateUI(forced){
if(!forced&&(!data||!w)) return;
var status=Number(data[0]);
I("ip").textContent=data[4];
I("dlText").textContent=(status==1&&data[1]==0)?"...":data[1];
drawMeter(I("dlMeter"),mbpsToAmount(Number(data[1]*(status==1?oscillate():1))),meterBk,dlColor,Number(data[6]),progColor);
I("ulText").textContent=(status==3&&data[2]==0)?"...":data[2];
drawMeter(I("ulMeter"),mbpsToAmount(Number(data[2]*(status==3?oscillate():1))),meterBk,ulColor,Number(data[7]),progColor);
I("pingText").textContent=data[3];
drawMeter(I("pingMeter"),msToAmount(Number(data[3]*(status==2?oscillate():1))),meterBk,pingColor,Number(data[8]),progColor);
I("jitText").textContent=data[5];
drawMeter(I("jitMeter"),msToAmount(Number(data[5]*(status==2?oscillate():1))),meterBk,jitColor,Number(data[8]),progColor);
var status=data.testStatus;
I("ip").textContent=data.clientIp;
I("dlText").textContent=(status==1&&data.dlStatus==0)?"...":data.dlStatus;
drawMeter(I("dlMeter"),mbpsToAmount(Number(data.dlStatus*(status==1?oscillate():1))),meterBk,dlColor,Number(data.dlProgress),progColor);
I("ulText").textContent=(status==3&&data.ulStatus==0)?"...":data.ulStatus;
drawMeter(I("ulMeter"),mbpsToAmount(Number(data.ulStatus*(status==3?oscillate():1))),meterBk,ulColor,Number(data.ulProgress),progColor);
I("pingText").textContent=data.pingStatus;
drawMeter(I("pingMeter"),msToAmount(Number(data.pingStatus*(status==2?oscillate():1))),meterBk,pingColor,Number(data.pingProgress),progColor);
I("jitText").textContent=data.jitterStatus;
drawMeter(I("jitMeter"),msToAmount(Number(data.jitterStatus*(status==2?oscillate():1))),meterBk,jitColor,Number(data.pingProgress),progColor);
}
function oscillate(){
return 1+0.02*Math.sin(Date.now()/100);
Expand Down
14 changes: 7 additions & 7 deletions example-pretty.html
Original file line number Diff line number Diff line change
Expand Up @@ -125,18 +125,18 @@
w.postMessage('start'); //Add optional parameters as a JSON object to this command
I("startStopBtn").className="running";
w.onmessage=function(e){
var data=e.data.split(';');
var status=Number(data[0]);
var data=JSON.parse(e.data);
var status=data.testStatus;
if(status>=4){
//test completed
I("startStopBtn").className="";
w=null;
}
I("ip").textContent=data[4];
I("dlText").textContent=(status==1&&data[1]==0)?"...":data[1];
I("ulText").textContent=(status==3&&data[2]==0)?"...":data[2];
I("pingText").textContent=data[3];
I("jitText").textContent=data[5];
I("ip").textContent=data.clientIp;
I("dlText").textContent=(status==1&&data.dlStatus==0)?"...":data.dlStatus;
I("ulText").textContent=(status==3&&data.ulStatus==0)?"...":data.ulStatus;
I("pingText").textContent=data.pingStatus;
I("jitText").textContent=data.jitterStatus;
};
}
}
Expand Down
16 changes: 8 additions & 8 deletions example-progressBar.html
Original file line number Diff line number Diff line change
Expand Up @@ -143,19 +143,19 @@
w.postMessage('start'); //Add optional parameters as a JSON object to this command
I("startStopBtn").className="running";
w.onmessage=function(e){
var data=e.data.split(';');
var status=Number(data[0]);
var data=JSON.parse(e.data);
var status=data.testStatus;
if(status>=4){
//test completed
I("startStopBtn").className="";
w=null;
}
I("ip").textContent=data[4];
I("dlText").textContent=(status==1&&data[1]==0)?"...":data[1];
I("ulText").textContent=(status==3&&data[2]==0)?"...":data[2];
I("pingText").textContent=data[3];
I("jitText").textContent=data[5];
var prog=(Number(data[6])*2+Number(data[7])*2+Number(data[8]))/5;
I("ip").textContent=data.clientIp;
I("dlText").textContent=(status==1&&data.dlStatus==0)?"...":data.dlStatus;
I("ulText").textContent=(status==3&&data.ulStatus==0)?"...":data.ulStatus;
I("pingText").textContent=data.pingStatus;
I("jitText").textContent=data.jitterStatus;
var prog=(Number(data.dlProgress)*2+Number(data.ulProgress)*2+Number(data.pingProgress))/5;
I("progress").style.width=(100*prog)+"%";
};
}
Expand Down
Loading

0 comments on commit 0954c6a

Please sign in to comment.