Compare commits

...

58 commits

Author SHA1 Message Date
Federico Dossena
dd1ce2cb88 Fixed vulnerability in json.php caused my missing Content-Type header 2024-04-27 15:12:32 +02:00
sstidl
748f49c3b6
build docker images on schedule
to ensure docker image updates, we build the image once a week
2024-04-15 11:49:40 +02:00
sstidl
4640c64412
revert .github/workflows/snyk-security.yml
was an accident. sorry. but we got a freshdocker image without php8 bugs this way ;-)
2024-04-15 11:29:24 +02:00
sstidl
5b17b397c5
Create snyk-security.yml 2024-04-15 08:06:48 +02:00
sstidl
a51b59aa1a
Merge pull request #540 from JoKneeMo/master
Add dual mode to enable standalone operation with other test servers
2024-02-07 17:32:25 +01:00
sstidl
3f3faeb6a1
Merge pull request #597 from thgoebel/mention-cli
Mention CLI client in Readme
2024-02-02 16:03:49 +01:00
sstidl
c1974d5a53
fix #598 2024-02-02 15:56:48 +01:00
Thore Goebel
a6ae760279 Mention CLI client in Readme 2024-01-31 22:46:46 +01:00
sstidl
022c2a976f
Merge pull request #590 from librespeed/dev
Docker images for ARM64
2023-12-02 12:31:15 +01:00
Stefan Stidl
c32bf52bca correct version string 2023-12-02 12:25:21 +01:00
sstidl
a6c0818b66
Update docker-publish.yml: add arm64 2023-12-02 11:58:20 +01:00
Michael Erdely
6eeda75765
Update speedtest.js (#589)
Bump version
2023-11-29 05:40:24 +01:00
sstidl
e5f54fbd78
Release 5.3.0 (#587)
* Update README.md

Added simple manual instructions

* Link to docker documentation

* Add basic docker compose example

* add descriptive alt-text to GIF

* Fix some typos and formatting

* Updated Docker to use php8.2-apache as the upstream image.

* Clean up apt lists after installation in Dockerfile

* Update Dockerfile

Co-authored-by: Quentame <polletquentin74@me.com>

* fix typo

* doc: sqlite db persistance explained

* Create docker-publish.yml

* Update docker-publish.yml

* Update docker-publish.yml

* fix action

* switch docker image location

* without image signing

* remove signing

* switch units to Mbit/s

* move examples to folder

* fix ipinfo parsing

* fix regression on getIpinfo

* removed trailing whitespaces

* integrate ios favicon

closes #400

* set single-server-full as index

---------

Co-authored-by: Les W <30345058+leswaters@users.noreply.github.com>
Co-authored-by: bt90 <btom1990@googlemail.com>
Co-authored-by: An | Anton Röhm <18481195+AnTheMaker@users.noreply.github.com>
Co-authored-by: 0kyn <0kyn.dev@gmail.com>
Co-authored-by: Marc Zampetti <zampettim@users.noreply.github.com>
Co-authored-by: Peter Dave Hello <hsu@peterdavehello.org>
Co-authored-by: Quentame <polletquentin74@me.com>
Co-authored-by: Stefan STIDL <stefan.stidl@ffg.at>
2023-11-26 12:46:27 +01:00
sstidl
05261e608c set single-server-full as index 2023-11-18 14:15:42 +00:00
JoKneeMo
11f698b376 Revert "Set content-type before sending image data for a valid response"
This reverts commit 82d64bff96.
2023-11-08 11:05:47 -05:00
sstidl
c4c87bc144 integrate ios favicon
closes #400
2023-11-08 12:21:59 +00:00
sstidl
2662491132 removed trailing whitespaces 2023-11-06 10:43:06 +01:00
sstidl
21fb3c53aa
Merge pull request #583 from sstidl/dev
Fix ipinfo

Close #511
2023-11-05 02:43:57 +01:00
Stefan Stidl
fee5799a11 fix regression on getIpinfo 2023-11-05 02:27:49 +01:00
Stefan Stidl
9b8faee10d fix ipinfo parsing 2023-11-05 02:08:11 +01:00
sstidl
58e2b413f2
Merge pull request #582 from sstidl/dev
move examples to folder
2023-11-05 01:16:26 +01:00
Stefan Stidl
338b2c27ef move examples to folder 2023-11-05 01:14:02 +01:00
sstidl
74eab590eb
Merge pull request #581 from sstidl/dev
switch units to Mbit/s
2023-11-05 01:08:30 +01:00
Stefan Stidl
63583cb3f4 switch units to Mbit/s 2023-11-05 01:05:50 +01:00
sstidl
32f6e65a2e
Merge pull request #580 from sstidl/switch-docker-repo
Switch docker repo from docker-hub to github packages
2023-11-05 00:43:23 +01:00
Stefan Stidl
b1aa63259c remove signing 2023-11-05 00:35:46 +01:00
Stefan Stidl
e468c3d068 without image signing 2023-11-05 00:20:39 +01:00
Stefan Stidl
7c05ba084e switch docker image location 2023-11-05 00:14:48 +01:00
Stefan Stidl
60ea879bc3 fix action 2023-11-04 23:52:59 +01:00
sstidl
6c92ed8091
Update docker-publish.yml 2023-11-04 23:44:07 +01:00
sstidl
cecf591d0f
Update docker-publish.yml 2023-11-04 23:14:27 +01:00
sstidl
06803a85fa
Merge pull request #512 from AnTheMaker/patch-1
Add descriptive alt-text to GIF in Readme
2023-11-04 15:14:10 +01:00
sstidl
364e6304b0
Merge pull request #1 from sstidl/sstidl-patch-1
Create docker-publish.yml
2023-11-04 02:44:48 +01:00
sstidl
992e24bab6
Create docker-publish.yml 2023-11-04 02:32:32 +01:00
sstidl
bce9ced9ff
Merge pull request #500 from awsles/patch-1
Update README.md
2023-11-04 01:26:54 +01:00
sstidl
197750eef9
Merge pull request #514 from 0kyn/fix-typos
Fix some typos and formatting
2023-11-04 01:24:38 +01:00
sstidl
8fafd70b46
Merge pull request #501 from bt90/patch-2
Link to docker documentation
2023-11-04 01:19:40 +01:00
Stefan STIDL
e0fa5148fe doc: sqlite db persistance explained 2023-11-04 00:35:21 +01:00
Stefan STIDL
013e7d56e2 fix typo 2023-11-04 00:34:33 +01:00
sstidl
34dd410d14
Merge pull request #573 from PeterDaveHelloKitchen/Dockerfile
Clean up apt lists after installation in Dockerfile
2023-11-03 23:41:59 +01:00
sstidl
871ce1a664
Merge pull request #502 from bt90/patch-3
Add basic docker compose example
2023-11-03 23:17:55 +01:00
sstidl
ae07bf2bdc
Merge pull request #570 from zampettim/upgrade-docker-php-82
Updated Docker to use php8.2-apache as the upstream image.
2023-11-03 22:48:39 +01:00
sstidl
bb1506c39a
Update Dockerfile
Co-authored-by: Quentame <polletquentin74@me.com>
2023-11-03 22:48:12 +01:00
Peter Dave Hello
abc85eb8c5 Clean up apt lists after installation in Dockerfile 2023-09-26 00:22:04 +08:00
Federico Dossena
6a4a05d883 Replaced speedtest with speed test 2023-08-28 22:12:08 +02:00
Marc Zampetti
427edfc416 Updated Docker to use php8.2-apache as the upstream image. 2023-08-09 16:36:48 -04:00
Tracy-B
5956e07a7a
Microsoft SQL Server support and Sanity Check page (#569)
* Create telemetry_mssql.sql

SQL to create the speedtest_users table on Microsoft SQL server.

This SQL is based on the original templates, and continues to uses nvarchar for all fields as the templates do – even though some of the fields should be int.

* Added support for Microsoft SQL Server

* New sanitycheck.php page plus required updates to telemetry_db.php

Created a sanity check page to verify that the required PHP extensions are installed, and that it is possible to connect to the database.

* Update README.md - MS SQL support

Added mention of Microsoft SQL Server as a supported DB engine.

* Added missing <tr> to sanitycheck.php
2023-07-31 08:10:56 +02:00
JoKneeMo
20f1f46d1f Docker support for a server that behaves as a standalone frontend AND a backend for another server. 2022-12-11 23:08:46 -05:00
JoKneeMo
82d64bff96 Set content-type before sending image data for a valid response 2022-12-11 22:57:06 -05:00
0kyn
71e3b47130 Fix some typos and formatting 2022-10-03 21:16:26 +02:00
An | Anton Röhm
dd8c5f74f6
add descriptive alt-text to GIF 2022-10-01 13:45:28 +02:00
bt90
16e0fb9bf4
Add basic docker compose example 2022-07-14 17:08:12 +02:00
bt90
d1ca8c2630
Link to docker documentation 2022-07-14 16:45:28 +02:00
Les W
79afc769ea
Update README.md
Added simple manual instructions
2022-07-09 16:13:26 +01:00
rizlas
97e0390680
Postgres and mysql dependencies in docker file (#498) 2022-07-08 16:36:02 +00:00
Pavel Djundik
fa0ac8cd23
Use random_bytes when available (#487) 2022-06-24 06:53:21 +00:00
dosse91
2f19331378 Fixed a bug that caused the frontend-only mode to not work properly with telemetry enabled on the docker version 2022-03-08 13:57:30 +01:00
Jeff Bierschbach
afa84968c7
Save correct IP address to database (#457) 2022-01-08 12:16:05 +00:00
32 changed files with 751 additions and 211 deletions

View file

@ -24,5 +24,5 @@ Browser, OS, type of connection, unusual software, ...
What should have happened
## Screenshots
If necessary, add screenshots of the test.
If necessary, add screenshots of the test.
F12 > Network screenshots can be particularly useful

81
.github/workflows/docker-publish.yml vendored Normal file
View file

@ -0,0 +1,81 @@
name: Docker
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
on:
schedule:
- cron: '30 20 * * Sun'
push:
branches: ["*"]
# Publish semver tags as releases.
tags: ["v*.*.*"]
pull_request:
branches: ["{{is_default_branch}}"]
env:
# Use docker.io for Docker Hub if empty
REGISTRY: ghcr.io
# github.repository as <account>/<repo>
IMAGE_NAME: ${{ github.repository }}
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
# This is used to complete the identity challenge
# with sigstore/fulcio when running outside of PRs.
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v3
# Set up BuildKit Docker container builder to be able to build
# multi-platform images and export cache
# https://github.com/docker/setup-buildx-action
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
# Login against a Docker registry except on PR
# https://github.com/docker/login-action
- name: Log into registry ${{ env.REGISTRY }}
if: github.event_name != 'pull_request'
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# Extract metadata (tags, labels) for Docker
# https://github.com/docker/metadata-action
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
# set latest tag for default branch
type=raw,value=latest,enable={{is_default_branch}}
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
# Build and push Docker image with Buildx (don't push on PR)
# https://github.com/docker/build-push-action
- name: Build and push Docker image
id: build-and-push
uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0
with:
context: .
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

1
.gitignore vendored
View file

@ -1,2 +1,3 @@
results/idObfuscation_salt.php
backend/getIP_serverLocation.php
db-dir/

View file

@ -1,20 +1,21 @@
FROM php:7.4-apache
FROM php:8-apache
# Install extensions
RUN apt-get update && apt-get install -y \
libfreetype6-dev \
libjpeg62-turbo-dev \
libpng-dev \
libfreetype6-dev \
libjpeg62-turbo-dev \
libpng-dev \
libpq-dev \
&& docker-php-ext-install -j$(nproc) iconv \
&& docker-php-ext-configure gd --with-freetype=/usr/include/ --with-jpeg=/usr/include/ \
&& docker-php-ext-install -j$(nproc) gd
&& docker-php-ext-configure pgsql -with-pgsql=/usr/local/pgsql \
&& docker-php-ext-install -j$(nproc) gd pdo pdo_mysql pdo_pgsql pgsql \
&& rm -rf /var/lib/apt/lists/*
# Prepare files and folders
RUN mkdir -p /speedtest/
# Copy sources
COPY backend/ /speedtest/backend
COPY results/*.php /speedtest/results/
@ -28,8 +29,7 @@ COPY docker/servers.json /servers.json
COPY docker/*.php /speedtest/
COPY docker/entrypoint.sh /
# Prepare environment variabiles defaults
# Prepare default environment variables
ENV TITLE=LibreSpeed
ENV MODE=standalone
ENV PASSWORD=password
@ -39,6 +39,5 @@ ENV REDACT_IP_ADDRESSES=false
ENV WEBPORT=80
# Final touches
EXPOSE 80
CMD ["bash", "/entrypoint.sh"]

View file

@ -4,13 +4,13 @@
No Flash, No Java, No Websocket, No Bullshit.
This is a very lightweight Speedtest implemented in Javascript, using XMLHttpRequest and Web Workers.
This is a very lightweight speed test implemented in Javascript, using XMLHttpRequest and Web Workers.
## Try it
[Take a Speedtest](https://librespeed.org)
[Take a speed test](https://librespeed.org)
## Compatibility
All modern browsers are supported: IE11, latest Edge, latest Chrome, latest Firefox, latest Safari.
All modern browsers are supported: IE11, latest Edge, latest Chrome, latest Firefox, latest Safari.
Works with mobile versions too.
## Features
@ -23,23 +23,39 @@ Works with mobile versions too.
* Results sharing (optional)
* Multiple Points of Test (optional)
![Screenshot](https://speedtest.fdossena.com/mpot_v6.gif)
![Screenrecording of a running Speedtest](https://speedtest.fdossena.com/mpot_v6.gif)
## Server requirements
* A reasonably fast web server with Apache 2 (nginx, IIS also supported)
* PHP 5.4 (other backends also available)
* MySQL database to store test results (optional, PostgreSQL and SQLite also supported)
* PHP 5.4 or newer (other backends also available)
* MySQL database to store test results (optional, Microsoft SQL Server, PostgreSQL and SQLite also supported)
* A fast! internet connection
## Installation videos
## Installation
Assuming you have PHP installed, the installation steps are quite simple.
I set this up on a QNAP.
For this example, I am using a folder called **speedtest** in my web share area.
1. Choose one of the example-xxx.html files in `examples` folder as your index.html if the default index.html does not fit.
2. Add: speedtest.js, speedtest_worker.js, and favicon.ico to your speedtest folder.
3. Download all of the backend folder into speedtest/backend.
4. Download all of the results folder into speedtest/results.
5. Be sure your permissions allow execute (755).
6. Visit YOURSITE/speedtest/index.html and voila!
### Installation Video
There is a more in-depth installation video here:
* [Quick start installation guide for Ubuntu Server 19.04](https://fdossena.com/?p=speedtest/quickstart_v5_ubuntu.frag)
## Android app
A template to build an Android client for your LibreSpeed installation is available [here](https://github.com/librespeed/speedtest-android).
## CLI client
A command line client is available [here](https://github.com/librespeed/speedtest-cli).
## Docker
A docker image is available on the [Docker Hub](https://registry.hub.docker.com/r/adolfintel/speedtest), see `doc_docker.md` for more info about it
A docker image is available on [GitHub](https://github.com/librespeed/speedtest/pkgs/container/speedtest), check our [docker documentation](doc_docker.md) for more info about it.
## Go backend
A Go implementation is available in the [`speedtest-go`](https://github.com/librespeed/speedtest-go) repo, maintained by [Maddie Zhan](https://github.com/maddie).
@ -48,8 +64,8 @@ A Go implementation is available in the [`speedtest-go`](https://github.com/libr
A partial Node.js implementation is available in the `node` branch, developed by [dunklesToast](https://github.com/dunklesToast). It's not recommended to use at the moment.
## Donate
[![Donate with Liberapay](https://liberapay.com/assets/widgets/donate.svg)](https://liberapay.com/fdossena/donate)
[Donate with PayPal](https://www.paypal.me/sineisochronic)
[![Donate with Liberapay](https://liberapay.com/assets/widgets/donate.svg)](https://liberapay.com/fdossena/donate)
[Donate with PayPal](https://www.paypal.me/sineisochronic)
## License
Copyright (C) 2016-2022 Federico Dossena

View file

@ -53,7 +53,11 @@ function sendHeaders()
$chunks = getChunkCount();
// Generate data
$data = openssl_random_pseudo_bytes(1048576);
if (function_exists('random_bytes')) {
$data = random_bytes(1048576);
} else {
$data = openssl_random_pseudo_bytes(1048576);
}
// Deliver chunks of 1048576 bytes
sendHeaders();

View file

@ -2,7 +2,7 @@
/*
* This script detects the client's IP address and fetches ISP info from ipinfo.io/
* Output from this script is a JSON string composed of 2 objects: a string called processedString which contains the combined IP, ISP, Contry and distance as it can be presented to the user; and an object called rawIspInfo which contains the raw data from ipinfo.io (will be empty if isp detection is disabled).
* Output from this script is a JSON string composed of 2 objects: a string called processedString which contains the combined IP, ISP, Country and distance as it can be presented to the user; and an object called rawIspInfo which contains the raw data from ipinfo.io (will be empty if isp detection is disabled).
* Client side, the output of this script can be treated as JSON or as regular text. If the output is regular text, it will be shown to the user as is.
*/
@ -11,24 +11,7 @@ error_reporting(0);
define('API_KEY_FILE', 'getIP_ipInfo_apikey.php');
define('SERVER_LOCATION_CACHE_FILE', 'getIP_serverLocation.php');
/**
* @return string
*/
function getClientIp()
{
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_REAL_IP'])) {
$ip = $_SERVER['HTTP_X_REAL_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
$ip = preg_replace('/,.*/', '', $ip); # hosts are comma-separated, client is first
} else {
$ip = $_SERVER['REMOTE_ADDR'];
}
return preg_replace('/^::ffff:/', '', $ip);
}
require_once 'getIP_util.php';
/**
* @param string $ip
@ -93,7 +76,7 @@ function getIpInfoTokenString()
return '';
}
return '?token='.$IPINFO_APIKEY;
return '?token=' . $IPINFO_APIKEY;
}
/**
@ -103,7 +86,7 @@ function getIpInfoTokenString()
*/
function getIspInfo($ip)
{
$json = file_get_contents('https://ipinfo.io/'.$ip.'/json'.getIpInfoTokenString());
$json = file_get_contents('https://ipinfo.io/' . $ip . '/json' . getIpInfoTokenString());
if (!is_string($json)) {
return null;
}
@ -123,17 +106,91 @@ function getIspInfo($ip)
*/
function getIsp($rawIspInfo)
{
if (
!is_array($rawIspInfo)
|| !array_key_exists('org', $rawIspInfo)
|| !is_string($rawIspInfo['org'])
|| empty($rawIspInfo['org'])
) {
return 'Unknown ISP';
if (is_array($rawIspInfo)) {
/* variant with no token
has json like:
{
"ip": "xxx.xxx.xxx.xxx",
"hostname": "example.com",
"city": "Vienna",
"region": "Vienna",
"country": "AT",
"loc": "48.2085,16.3721",
"org": "ASxxxx T-Mobile Austria GmbH",
"postal": "nnnn",
"timezone": "Europe/Vienna",
"readme": "https://ipinfo.io/missingauth"
}
*/
if (
array_key_exists('org', $rawIspInfo)
&& is_string($rawIspInfo['org'])
&& !empty($rawIspInfo['org'])
) {
// Remove AS##### from ISP name, if present
return preg_replace('/AS\\d+\\s/', '', $rawIspInfo['org']);
}
/*
variant with valid token has json:
{
"ip": "xxx.xxx.xxx.xxx",
"hostname": "example.com",
"city": "Vienna",
"region": "Vienna",
"country": "AT",
"loc": "48.2085,16.3721",
"postal": "1010",
"timezone": "Europe/Vienna",
"asn": {
"asn": "ASxxxx",
"name": "T-Mobile Austria GmbH",
"domain": "t-mobile.at",
"route": "xxx.xxx.xxx.xxx/xx",
"type": "isp"
},
"company": {
"name": "XX",
"domain": "example.com",
"type": "isp"
},
"privacy": {
"vpn": true,
"proxy": false,
"tor": false,
"relay": false,
"hosting": false,
"service": ""
},
"abuse": {
"address": "...",
"country": "AT",
"email": "abuse@example.com",
"name": "XXX",
"network": "xxx.xxx.xxx.xxx-xxx.xxx.xxx.xxx",
"phone": ""
},
"domains": {
"total": 0,
"domains": [
]
}
}
*/
if (
array_key_exists('asn', $rawIspInfo)
&& is_array($rawIspInfo['asn'])
&& !empty($rawIspInfo['asn'])
&& array_key_exists('name', $rawIspInfo['asn'])
&& is_string($rawIspInfo['asn']['name'])
) {
// Remove AS##### from ISP name, if present
return $rawIspInfo['asn']['name'];
}
}
// Remove AS##### from ISP name, if present
return preg_replace('/AS\\d+\\s/', '', $rawIspInfo['org']);
return 'Unknown ISP';
}
/**
@ -152,7 +209,7 @@ function getServerLocation()
return $serverLoc;
}
$json = file_get_contents('https://ipinfo.io/json'.getIpInfoTokenString());
$json = file_get_contents('https://ipinfo.io/json' . getIpInfoTokenString());
if (!is_string($json)) {
return null;
}
@ -168,7 +225,7 @@ function getServerLocation()
}
$serverLoc = $details['loc'];
$cacheData = "<?php\n\n\$serverLoc = '".addslashes($serverLoc)."';\n";
$cacheData = "<?php\n\n\$serverLoc = '" . addslashes($serverLoc) . "';\n";
file_put_contents(SERVER_LOCATION_CACHE_FILE, $cacheData);
return $serverLoc;
@ -257,7 +314,7 @@ function calculateDistance($clientLocation, $serverLocation, $unit)
$dist = '<15';
}
return $dist.' mi';
return $dist . ' mi';
}
if ('km' === $unit) {
@ -266,7 +323,7 @@ function calculateDistance($clientLocation, $serverLocation, $unit)
$dist = '<20';
}
return $dist.' km';
return $dist . ' km';
}
return null;
@ -305,17 +362,17 @@ function sendResponse(
) {
$processedString = $ip;
if (is_string($ipInfo)) {
$processedString .= ' - '.$ipInfo;
$processedString .= ' - ' . $ipInfo;
}
if (
is_array($rawIspInfo)
&& array_key_exists('country', $rawIspInfo)
) {
$processedString .= ', '.$rawIspInfo['country'];
$processedString .= ', ' . $rawIspInfo['country'];
}
if (is_string($distance)) {
$processedString .= ' ('.$distance.')';
$processedString .= ' (' . $distance . ')';
}
sendHeaders();

20
backend/getIP_util.php Normal file
View file

@ -0,0 +1,20 @@
<?php
/**
* @return string
*/
function getClientIp() {
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_REAL_IP'])) {
$ip = $_SERVER['HTTP_X_REAL_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
$ip = preg_replace('/,.*/', '', $ip); # hosts are comma-separated, client is first
} else {
$ip = $_SERVER['REMOTE_ADDR'];
}
return preg_replace('/^::ffff:/', '', $ip);
}

127
doc.md
View file

@ -1,11 +1,11 @@
# LibreSpeed
> by Federico Dossena
> by Federico Dossena
> Version 5.2.4
> [https://github.com/librespeed/speedtest/](https://github.com/librespeed/speedtest/)
## Introduction
LibreSpeed is a Free and Open Source speedtest that you can host on your server(s), and users can run in their browser.
LibreSpeed is a Free and Open Source speed test that you can host on your server(s), and users can run in their browser.
__Features:__
* Download test
@ -30,7 +30,7 @@ The following browsers are officially supported:
Client side, the test can use up to 500MB of RAM on very fast connections.
## Quick start guides
These guides cover a simple single server installation of the Speedtest.
These guides cover a simple single server installation of the speed test.
* [Quick start installation guide for Ubuntu Server 19.04](https://fdossena.com/?p=speedtest/quickstart_v5_ubuntu.frag)
@ -50,14 +50,14 @@ Server side, you'll need:
* If you want to enable results sharing:
- FreeType 2 and its PHP module (this is usually installed automatically by most distros)
Let's install the speedtest.
Let's install the speed test.
Put all files on your web server via FTP or by copying them directly. You can install it in the root, or in a subdirectory.
__Important:__ The speedtest needs write permissions in the installation folder!
__Important:__ The speed test needs write permissions in the installation folder!
#### ipinfo.io
The speedtest uses [ipinfo.io](https://ipinfo.io) to detect ISP and distance from server. This is completely optional and can be disabled if you want (see Speedtest settings), but it is enabled by default, and if you expect more than ~500 tests per day, you will need to sign up to [ipinfo.io](https://ipinfo.io) and edit `backend/getIP_ipInfo_apikey.php` to set your access token.
The speed test uses [ipinfo.io](https://ipinfo.io) to detect ISP and distance from server. This is completely optional and can be disabled if you want (see speed test settings), but it is enabled by default, and if you expect more than ~500 tests per day, you will need to sign up to [ipinfo.io](https://ipinfo.io) and edit `backend/getIP_ipInfo_apikey.php` to set your access token.
IpInfo.io has kindly offered free access to their APIs for users of this project; if you're interested, contact me at [info@fdossena.com](mailto:info@fdossena.com) and provide a description of what you intend to do with the project, and you'll get the API key.
@ -109,22 +109,21 @@ A basic front-end for visualizing and searching tests by ID is available in `res
A login is required to access the interface. __Important__: change the default password in `results/telemetry_settings.php`.
#### The end
Now that the test is installed, rename one of the examples to `index.html` and delete the other examples.
The best starting point for most people is `example-singleServer-gauges.html`. If you want to use telemetry and results sharing, use `example-singleServer-full.html` instead.
Now that the test is installed, the default page uses telemetry and results sharing. If you want another index page, rename one of the examples to `index.html` and delete the other examples. The best starting point for most people is `example-singleServer-gauges.html`.
If you're not using telemetry and results sharing, you can delete the `results` folder too.
Details about the examples and how to make custom UIs will be discussed later. If you don't want to make a custom UI, feel free to modify the example and replace "LibreSpeed Example" with the name of your test.
#### Privacy
Telemetry contains personal information (according to GDPR defintion), therefore it is important to treat this data respectfully of national and international laws, especially if you plan to offer the service in the European Union.
Telemetry contains personal information (according to GDPR definition), therefore it is important to treat this data respectfully of national and international laws, especially if you plan to offer the service in the European Union.
`example-singleServer-full.html` and `example-multipleServers-full.html` both contain a privacy policy for the service: you MUST read it, change it if necessary, and add your email address for data deletion requests. __Failure to comply with GDPR regulations can get you in serious trouble.__
Default `index.html` and `example-multipleServers-full.html` both contain a privacy policy for the service: you MUST read it, change it if necessary, and add your email address for data deletion requests. __Failure to comply with GDPR regulations can get you in serious trouble.__
### Multiple servers, PHP
The speedtest can automatically choose between multiple test points and use the one with the lowest ping in a list.
The speed test can automatically choose between multiple test points and use the one with the lowest ping in a list.
Note that this is an advanced use case and it is recommended that you already know how to use the speedtest with a single server.
Note that this is an advanced use case and it is recommended that you already know how to use the speed test with a single server.
We must distinguish 2 types of servers:
* __Frontend server__: hosts the UI, the JS files, and optionally telemetry and results sharing stuff. You only need 1 of these, and this is the server that your clients will first connect to.
@ -143,20 +142,20 @@ Requirements:
* If you want to enable results sharing:
- FreeType 2 and its PHP module (this is usually installed automatically by most distros)
To install the speedtest frontend, copy the following files to your web server:
To install the speed test frontend, copy the following files to your web server:
* `speedtest.js`
* `speedtest_worker.js`
* Optionally, the `results` folder
* One of the `multipleServers` examples (the best starting points are `example-multipleServers-pretty.html` if you don't want to use telemetry and results sharing, `example-multipleServers-full.html` if you want to use them). Rename the example you choose to `index.html`
__Important:__ The speedtest needs write permissions in the installation folder!
__Important:__ The speed test needs write permissions in the installation folder!
##### Server list
Edit `index.html`, you will see a list of servers:
```js
var SPEEDTEST_SERVERS=[
{
"name":"Speedtest Demo Server 1", //user friendly name for the server
"name":"Speed test Demo Server 1", //user friendly name for the server
"server":"//mpotdemo.fdossena.com/", //URL to the server. // at the beginning will be replaced with http:// or https:// automatically
"dlURL":"garbage.php", //path to download test on this server (garbage.php or replacement)
"ulURL":"empty.php", //path to upload test on this server (empty.php or replacement)
@ -164,7 +163,7 @@ var SPEEDTEST_SERVERS=[
"getIpURL":"getIP.php" //path to getIP on this server (getIP.php or replacement)
},
{
"name":"Speedtest Demo Server 2",
"name":"Speed test Demo Server 2",
"server":"//mpotdemo2.fdossena.com/",
"dlURL":"garbage.php",
"ulURL":"empty.php",
@ -221,10 +220,10 @@ Requirements:
To install a backend, simply copy all the files in the `backend` folder to your backend server.
__Important:__ The speedtest needs write permissions in the installation folder!
__Important:__ The speed test needs write permissions in the installation folder!
#### ipinfo.io
The speedtest uses [ipinfo.io](https://ipinfo.io) to detect ISP and distance from server. This is completely optional and can be disabled if you want (see Speedtest settings), but it is enabled by default, and if you expect more than ~500 tests per day, you will need to sign up to [ipinfo.io](https://ipinfo.io) and edit `getIP_ipInfo_apikey.php` to set your access token.
The speed test uses [ipinfo.io](https://ipinfo.io) to detect ISP and distance from server. This is completely optional and can be disabled if you want (see speed test settings), but it is enabled by default, and if you expect more than ~500 tests per day, you will need to sign up to [ipinfo.io](https://ipinfo.io) and edit `getIP_ipInfo_apikey.php` to set your access token.
IpInfo.io has kindly offered free access to their APIs for users of this project; if you're interested, contact me at [info@fdossena.com](mailto:info@fdossena.com) and provide a description of what you intend to do with the project, and you'll get the API key.
@ -240,14 +239,14 @@ __Single server:__
* `example-singleServer-customSettings.html`: Same as `example-singleServer-pretty.html` but configures the test so that it only performs download and upload tests, and with a fixed length instead of automatic
* `example-singleServer-gauges.html`: The most sophisticated example, with the same functionality as `example-singleServer-pretty.html` but adds gauges. This is also a good starting point, but the gauges may slow down underpowered devices
* `example-singleServer-chart.html`: Shows how to use the test with the Chart.js library
* `example-singleServer-full.html`: The most complete example. Based on `example-singleServer-gauges.html`, also enables telemetry and results sharing
* default `index.html`: The most complete example. Based on `example-singleServer-gauges.html`, also enables telemetry and results sharing
__Multiple servers:__
* `example-multipleServers-pretty.html`: Same as `example-singleServer-pretty.html` but with multiple test points. Server selection is fully automatic
* `example-multipleServers-full.html`: Same as `example-singleServer-full.html` but with multiple test points. Server selection is automatic but the server can be changed afterwards by the user
* `example-multipleServers-full.html`: Same as default `index.html` but with multiple test points. Server selection is automatic but the server can be changed afterwards by the user
### Initialization
To use the speedtest in your page, first you need to load it:
To use the speed test in your page, first you need to load it:
```xml
<script type="text/javascript" src="speedtest.js"></script>
```
@ -271,7 +270,7 @@ s.onend=function(aborted){
}
```
The `onupdate` event handler will be called periodically by the test with data coming from the speedtest worker thread. The `data` argument is an object containing the following:
The `onupdate` event handler will be called periodically by the test with data coming from the speed test worker thread. The `data` argument is an object containing the following:
* __testState__: an integer between -1 and 5
* `-1` = Test not started yet
* `0` = Test starting
@ -307,7 +306,7 @@ The `onupdate` event handler will be called periodically by the test with data c
The `onend` event handler will be called at the end of the test (`onupdate` will be called first), with a boolean telling you if the test was aborted (either manually or because of an error) or if it ended normally.
### Test parameters
Before starting the test, you can change some of the settings from their default values. You might want to do this to better adapt the speedtest to a specific scenario, such as a satellite connection. To change a setting, use
Before starting the test, you can change some of the settings from their default values. You might want to do this to better adapt the speed test to a specific scenario, such as a satellite connection. To change a setting, use
```js
s.setParameter("parameter_name",value);
```
@ -362,7 +361,7 @@ __Main parameters:__
* __Important:__ On Firefox, it is better to run the upload test last
* __getIp_ispInfo__: if true, the server will try to get ISP info and pass it along with the IP address. This will add `isp=true` to the request to `url_getIp`. getIP.php accomplishes this using ipinfo.io
* Default: `true`
* __getIp_ispInfo_distance__: if true, the server will try to get an estimate of the distance from the client to the speedtest server. This will add a `distance` argument to the request to `url_getIp`. `__getIp_ispInfo__` must be enabled in order for this to work. getIP.php accomplishes this using ipinfo.io
* __getIp_ispInfo_distance__: if true, the server will try to get an estimate of the distance from the client to the speed test server. This will add a `distance` argument to the request to `url_getIp`. `__getIp_ispInfo__` must be enabled in order for this to work. getIP.php accomplishes this using ipinfo.io
* `km`: estimate distance in kilometers
* `mi`: estimate distance in miles
* not set: do not measure distance
@ -421,7 +420,7 @@ __Advanced parameters:__ (Seriously, don't change these unless you know what you
### Multiple Points of Test
If you want to use more than one test server, this is the time to add all your test points and select the best one. Skip this part if you don't want to use this feature.
The best way to do this is to declare an array with all your servers, and give it to the speedtest:
The best way to do this is to declare an array with all your servers, and give it to the speed test:
```js
var SPEEDTEST_SERVERS=[
server1,
@ -459,7 +458,7 @@ s.selectServer(function(server){
//do something
})
```
The `selectServer` function is asynchronous in order to avoid freeing the UI, and it will run a callback function when it is done choosing the server with the lowest ping.
The `selectServer` function is asynchronous in order to avoid freeing the UI, and it will run a callback function when it is done choosing the server with the lowest ping.
The `server` argument is the selected server, and you can display it in the UI if you want. __You cannot start the test until the selection is done!__
You can also set the test point manually (for instance, from a combobox in the UI):
@ -483,15 +482,15 @@ s.abort();
When the test is finished, you can run it again if you want, or you can just destroy `s`.
## Implementation details
The purpose of this section is to help developers who want to make changes to the inner workings of the speedtest.
The purpose of this section is to help developers who want to make changes to the inner workings of the speed test.
It will be divided into 4 sections: `speedtest.js`, `speedtest_worker.js`, the `backend` files and the `results` files.
### `speedtest.js`
This is the main interface between your webpage and the speedtest.
It hides the speedtest web worker to the page, and provides many convenient functions to control the test.
This is the main interface between your webpage and the speed test.
It hides the speed test web worker to the page, and provides many convenient functions to control the test.
You can think of this as a finite state machine. These are the states (use getState() to see them):
* __0__: here you can change the speedtest settings (such as test duration) with the `setParameter("parameter",value)` function. From here you can either start the test using `start()` (goes to state 3) or you can add multiple test points using `addTestPoint(server)` or `addTestPoints(serverList)` (goes to state 1). Additionally, this is the perfect moment to set up callbacks for the `onupdate(data)` and `onend(aborted)` events.
* __0__: here you can change the speed test settings (such as test duration) with the `setParameter("parameter",value)` function. From here you can either start the test using `start()` (goes to state 3) or you can add multiple test points using `addTestPoint(server)` or `addTestPoints(serverList)` (goes to state 1). Additionally, this is the perfect moment to set up callbacks for the `onupdate(data)` and `onend(aborted)` events.
* __1__: here you can add test points. You only need to do this if you want to use multiple test points.
A server is defined as an object like this:
```
@ -506,16 +505,16 @@ You can think of this as a finite state machine. These are the states (use getSt
```
While in state 1, you can only add test points, you cannot change the test settings. When you're done, use selectServer(callback) to select the test point with the lowest ping. This is asynchronous, when it's done, it will call your callback function and move to state 2. Calling setSelectedServer(server) will manually select a server and move to state 2.
* __2__: test point selected, ready to start the test. Use `start()` to begin, this will move to state 3
* __3__: test running. Here, your `onupdate` event calback will be called periodically, with data coming from the worker about speed and progress. A data object will be passed to your `onupdate` function, with the following items:
- `dlStatus`: download speed in mbps
- `ulStatus`: upload speed in mbps
* __3__: test running. Here, your `onupdate` event callback will be called periodically, with data coming from the worker about speed and progress. A data object will be passed to your `onupdate` function, with the following items:
- `dlStatus`: download speed in Mbit/s
- `ulStatus`: upload speed in Mbit/s
- `pingStatus`: ping in ms
- `jitterStatus`: jitter in ms
- `dlProgress`: progress of the download test as a float 0-1
- `ulProgress`: progress of the upload test as a float 0-1
- `pingProgress`: progress of the ping/jitter test as a float 0-1
- `testState`: state of the test (-1=not started, 0=starting, 1=download test, 2=ping+jitter test, 3=upload test, 4=finished, 5=aborted)
- `clientIp`: IP address of the client performing the test (and optionally ISP and distance)
- `clientIp`: IP address of the client performing the test (and optionally ISP and distance)
At the end of the test, the `onend` function will be called, with a boolean specifying whether the test was aborted or if it ended normally.
The test can be aborted at any time with `abort()`.
At the end of the test, it will move to state 4
@ -531,7 +530,7 @@ Change one of the test settings from their defaults.
- parameter: string with the name of the parameter that you want to set
- value: new value for the parameter
Invalid values or nonexistant parameters will be ignored by the speedtest worker.
Invalid values or nonexistant parameters will be ignored by the speed test worker.
##### addTestPoint(server)
Add a test point (multiple points of test)
@ -547,7 +546,7 @@ Add a test point (multiple points of test)
}
```
Note that this will add `mpot`:`true` to the parameters sent to the speedtest worker.
Note that this will add `mpot`:`true` to the parameters sent to the speed test worker.
##### addTestPoints(list)
Same as addTestPoint, but you can pass an array of servers
@ -575,14 +574,14 @@ Starts the test.
Note (multiple points of test): the selected server will be added to the `telemetry_extra` string. If this string was already set, then `telemetry_extra` will be a JSON string containing both the server and the original string
During the test, the `onupdate(data)` callback function will be called periodically with data from the worker.
During the test, the `onupdate(data)` callback function will be called periodically with data from the worker.
At the end of the test, the `onend(aborted)` function will be called with a boolean telling you if the test was aborted or if it ended normally.
##### abort()
Aborts the test while it's running.
### `speedtest_worker.js`
This is where the actual speedtest code is. It receives the settings from the main thread, runs the test, and reports back the results.
This is where the actual speed test code is. It receives the settings from the main thread, runs the test, and reports back the results.
The worker accepts 3 commands:
* `start`: starts the test. Optionally, test settings can be passed as a JSON string after the word start and a space
@ -664,7 +663,7 @@ Access-Control-Allow-Headers: Content-Encoding, Content-Type
This file stores telemetry information into the database.
Data is passed as POST parameters:
* `ispinfo`: ISP info (if enabled, empty strng otherwise)
* `ispinfo`: ISP info (if enabled, empty string otherwise)
* `extra`: the `telemetry_extra` string passed to the worker (if set, empty string otherwise)
* `dl`: download speed
* `ul`: upload speed
@ -689,11 +688,11 @@ See the code for the implementation details, it's basically a bunch of bitwise o
Simple UI to display and search test results. Not required to run the test.
## Alternative backends
If for some reason you can't or don't want to use PHP, the speedtest can run with other backends, or even no backend (with limited functionality).
If for some reason you can't or don't want to use PHP, the speed test can run with other backends, or even no backend (with limited functionality).
You will need replacements for `backend/garbage.php` and `backend/empty.php` and optionally `backend/getIP.php`, and the test needs to know where to find them:
```js
//Speedtest initialization
//Speed test initialization
var s=new Speedtest();
...
//Custom backend
@ -725,7 +724,7 @@ Your replacement can simply respond with the client's IP as plaintext or do some
If you want to make your own backend, see the section on the implementation details of `getIP.php`.
### No backend
The speedtest can run, albeit with limited functionality, using only a web server as backend, with no PHP or other server-side scripting.
The speed test can run, albeit with limited functionality, using only a web server as backend, with no PHP or other server-side scripting.
You will be able to run the download and upload test, but no IP, ISP and distance detection, no telemetry and results sharing, and only a single point of test.
@ -741,14 +740,14 @@ s.setParameter("url_ping","backend/empty.dat");
s.setParameter("test_order","P_D_U");
```
This will point to our static files and set the test to only do ping/jitter, download and uplod tests.
This will point to our static files and set the test to only do ping/jitter, download and upload tests.
## 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).
#### 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/or I see lag spikes
@ -764,15 +763,15 @@ You're running the test on localhost, therefore it is trying to measure the spee
Make sure your server is sending the `Connection:keep-alive` header
#### The server is behind a load balancer, proxy, etc. and I get the wrong IP address
Edit getIP.php and replace lines 14-23 with what is more appropriate in your scenario.
Edit getIP.php and replace lines 14-23 with what is more appropriate in your scenario.
Example: `$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];`
#### The results sharing just generates a blank image
If the image doesn't display and the browser displays a broken image icon, FreeType2 is not installed or configured properly.
If the image doesn't display and the browser displays a broken image icon, FreeType2 is not installed or configured properly.
If the image is blank, this usually happens because PHP can't find the font files inside the `results` folder. You can fix your PHP config or edit `results/index.php` and use absolute paths for the fonts. This is a [known issue with PHP](http://php.net/manual/en/function.imagefttext.php) and no real solution is known.
#### My server is behind Cloudflare and I can't reach full speed on some of the tests
This is not a speedtest related issue, as it can be replicated in virtually any HTTP file upload/download.
This is not a speed test related issue, as it can be replicated in virtually any HTTP file upload/download.
Go to your domain's DNS settings and change "DNS and HTTP proxy (CDN)" to "DNS only", and wait for the settings to be applied (can take a few minutes).
#### On Windows Server, using IIS, the upload test doesn't work, CORS errors are visible in the console
@ -781,36 +780,36 @@ This is a configuration issue. Make a file called web.config in wwwroot and adap
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<cors enabled="true" failUnlistedOrigins="false">
<add origin="*">
<cors enabled="true" failUnlistedOrigins="false">
<add origin="*">
<allowHeaders allowAllRequestedHeaders="true" />
<allowMethods>
<add method="GET" />
<add method="POST" />
<add method="PUT" />
<add method="DELETE" />
<add method="OPTIONS" />
</allowMethods>
<allowMethods>
<add method="GET" />
<add method="POST" />
<add method="PUT" />
<add method="DELETE" />
<add method="OPTIONS" />
</allowMethods>
<exposeHeaders>
</exposeHeaders>
</exposeHeaders>
</add>
</cors>
</system.webServer>
</cors>
</system.webServer>
</configuration>
```
#### ID obfuscation doesn't work (incorrect output, blank results image)
ID obfuscation only works on 64-bit PHP (requires PHP_INT_SIZE to be 8).
ID obfuscation only works on 64-bit PHP (requires PHP_INT_SIZE to be 8).
Note that older versions of PHP 5 on Windows use PHP_INT_SIZE of 4, even if they're 64 bit. If you're in this situation, update your PHP install.
Also, make sure that the web server has write permission on the `results` folder.
## Known bugs and limitations
### General
* 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. Different browsers may also show different results, especially on very fast connections on slow devices.
* The ping/jitter test is measured by seeing how long it takes for an empty XHR to complete. It is not an actual ICMP ping. Different browsers may also show different results, especially on very fast connections on slow devices.
### IE specific
* The upload test is not precise on very fast connections with high latency (will probably be fixed by Edge 17)
* On IE11, a same origin policy error is erroneously triggered under unknown conditions. Seems to be related to running the test from unusual URLs like a top level domain (for instance http://abc/speedtest). These are bugs in IE11's implementation of the same origin policy, not in the speedtest itself.
* On IE11, a same origin policy error is erroneously triggered under unknown conditions. Seems to be related to running the test from unusual URLs like a top level domain (for instance http://abc/speedtest). These are bugs in IE11's implementation of the same origin policy, not in the speed test itself.
* On IE11, under unknown circumstances, on some systems the test can only be run once, after which speedtest_worker.js will not be loaded by IE until the browser is restarted. This is a rare bug in IE11.
### Firefox specific
* On some Linux systems with hardware acceleration turned off, the page rendering makes the browser lag, reducing the accuracy of the ping/jitter test, and potentially even the download and upload tests on very fast connections.
@ -818,7 +817,7 @@ Also, make sure that the web server has write permission on the `results` folder
## Contributing
Since this is an open source project, you can modify it.
If you made some changes that you think should make it into the main project, send a Pull Request on GitHub, or contact me at [info@fdossena.com](mailto:info@fdossena.com).
If you made some changes that you think should make it into the main project, send a Pull Request on GitHub, or contact me at [info@fdossena.com](mailto:info@fdossena.com).
We don't require you to use a specific coding convention, write the code however you want and we'll change the formatting if necessary.
Donations are also appreciated: you can donate with [PayPal](https://www.paypal.me/sineisochronic) or [Liberapay](https://liberapay.com/fdossena/donate).

View file

@ -1,13 +1,41 @@
A docker version of LibreSpeed is available on docker hub: [https://hub.docker.com/r/adolfintel/speedtest/](https://hub.docker.com/r/adolfintel/speedtest/)
A docker version of LibreSpeed is available here: [GitHub Packages](https://github.com/librespeed/speedtest/pkgs/container/speedtest)
## Downloading from Docker hub
To download LibreSpeed from the docker hub, use this command:
## Downloading docker image
To download LibreSpeed from the docker repo, use this command:
```
docker pull adolfintel/speedtest
docker pull ghcr.io/librespeed/speedtest
```
You will now have a new docker image called `adolfintel/speedtest`.
You will now have a new docker image called `librespeed/speedtest`.
## Docker Compose
To start the container using [docker compose](https://docs.docker.com/compose/) the following configuration can be used:
```yml
version: '3.7'
services:
speedtest:
container_name: speedtest
image: ghcr.io/librespeed/speedtest:latest
restart: always
environment:
MODE: standalone
#TITLE: "LibreSpeed"
#TELEMETRY: "false"
#ENABLE_ID_OBFUSCATION: "false"
#REDACT_IP_ADDRESSES: "false"
#PASSWORD:
#EMAIL:
#DISABLE_IPINFO: "false"
#DISTANCE: "km"
#WEBPORT: 80
ports:
- "80:80" # webport mapping (host:container)
```
Please adjust the environment variables according to the intended operating mode.
## Standalone mode
If you want to install LibreSpeed on a single server, you need to configure it in standalone mode. To do this, set the `MODE` environment variable to `standalone`.
@ -15,8 +43,8 @@ If you want to install LibreSpeed on a single server, you need to configure it i
The test can be accessed on port 80.
Here's a list of additional environment variables available in this mode:
* __`TITLE`__: Title of your speedtest. Default value: `LibreSpeed`
* __`TELEMETRY`__: Whether to enable telemetry or not. Default value: `false`
* __`TITLE`__: Title of your speed test. Default value: `LibreSpeed`
* __`TELEMETRY`__: Whether to enable telemetry or not. If enabled, you maybe want your data to be persisted. See below. Default value: `false`
* __`ENABLE_ID_OBFUSCATION`__: When set to true with telemetry enabled, test IDs are obfuscated, to avoid exposing the database internal sequential IDs. Default value: `false`
* __`REDACT_IP_ADDRESSES`__: When set to true with telemetry enabled, IP addresses and hostnames are redacted from the collected telemetry, for better privacy. Default value: `false`
* __`PASSWORD`__: Password to access the stats page. If not set, stats page will not allow accesses.
@ -28,17 +56,24 @@ Here's a list of additional environment variables available in this mode:
If telemetry is enabled, a stats page will be available at `http://your.server/results/stats.php`, but a password must be specified.
### Persist sqlite database
Default DB driver is sqlite. The DB file is written to `/database/db.sql`.
So if you want your data to be persisted over image updates, you have to mount a volume with `-v $PWD/db-dir:/database`.
###### Example
This command starts LibreSpeed in standalone mode, with the default settings, on port 80:
```
docker run -e MODE=standalone -p 80:80 -it adolfintel/speedtest
docker run -e MODE=standalone -p 80:80 -it ghcr.io/librespeed/speedtest
```
This command starts LibreSpeed in standalone mode, with telemetry, ID obfuscation and a stats password, on port 86:
```
docker run -e MODE=standalone -e TELEMETRY=true -e ENABLE_ID_OBFUSCATION=true -e PASSWORD="yourPasswordHere" -e WEBPORT=86 -p 86:86 -it adolfintel/speedtest
docker run -e MODE=standalone -e TELEMETRY=true -e ENABLE_ID_OBFUSCATION=true -e PASSWORD="yourPasswordHere" -e WEBPORT=86 -p 86:86 -v $PWD/db-dir/:/database -it ghcr.io/librespeed/speedtest
```
## Multiple Points of Test
@ -55,7 +90,7 @@ Here's a list of additional environment variables available in this mode:
###### Example:
This command starts LibreSpeed in backend mode, with the default settings, on port 80:
```
docker run -e MODE=backend -p 80:80 -it adolfintel/speedtest
docker run -e MODE=backend -p 80:80 -it ghcr.io/librespeed/speedtest
```
### Frontend mode
@ -85,7 +120,7 @@ In frontend mode, LibreSpeed serves clients the Web UI and a list of servers. To
```
Note: if a server only supports HTTP or HTTPS, specify the protocol in the server field. If it supports both, just use `//`.
* Mount this file to `/servers.json` in the container (example at the end of this file)
The test can be accessed on port 80.
Here's a list of additional environment variables available in this mode:
@ -102,5 +137,12 @@ Here's a list of additional environment variables available in this mode:
###### Example
This command starts LibreSpeed in frontend mode, with a given `servers.json` file, and with telemetry, ID obfuscation, and a stats password:
```
docker run -e MODE=frontend -e TELEMETRY=true -e ENABLE_ID_OBFUSCATION=true -e PASSWORD="yourPasswordHere" -v $(pwd)/servers.json:/servers.json -p 80:80 -it adolfintel/speedtest
docker run -e MODE=frontend -e TELEMETRY=true -e ENABLE_ID_OBFUSCATION=true -e PASSWORD="yourPasswordHere" -v $(pwd)/servers.json:/servers.json -p 80:80 -it ghcr.io/librespeed/speedtest
```
### Dual mode
In dual mode, LibreSpeed operates as a standalone server that can also connect to other test points.
To do this:
* Set the `MODE` environment variable to `dual`
* Follow the `servers.json` instructions for the frontend mode
* The first server entry should be the local server, using the server endpoint address that a client can access.

View file

@ -13,7 +13,7 @@ cp /speedtest/*.js /var/www/html/
cp /speedtest/favicon.ico /var/www/html/
# Set up backend side for standlone modes
if [ "$MODE" == "standalone" ]; then
if [[ "$MODE" == "standalone" || "$MODE" == "dual" ]]; then
cp -r /speedtest/backend/ /var/www/html/backend
if [ ! -z "$IPINFO_APIKEY" ]; then
sed -i s/\$IPINFO_APIKEY\ =\ \'\'/\$IPINFO_APIKEY\ =\ \'$IPINFO_APIKEY\'/g /var/www/html/backend/getIP_ipInfo_apikey.php
@ -28,16 +28,21 @@ if [ "$MODE" == "backend" ]; then
fi
# Set up index.php for frontend-only or standalone modes
if [ "$MODE" == "frontend" ]; then
if [[ "$MODE" == "frontend" || "$MODE" == "dual" ]]; then
cp /speedtest/frontend.php /var/www/html/index.php
elif [ "$MODE" == "standalone" ]; then
cp /speedtest/standalone.php /var/www/html/index.php
fi
# Apply Telemetry settings when running in standalone or frontend mode and telemetry is enabled
if [[ "$TELEMETRY" == "true" && ( "$MODE" == "frontend" || "$MODE" == "standalone" ) ]]; then
if [[ "$TELEMETRY" == "true" && ( "$MODE" == "frontend" || "$MODE" == "standalone" || "$MODE" == "dual" ) ]]; then
cp -r /speedtest/results /var/www/html/results
if [ "$MODE" == "frontend" ]; then
mkdir /var/www/html/backend
cp /speedtest/backend/getIP_util.php /var/www/html/backend
fi
if [ "$DB_TYPE" == "mysql" ]; then
sed -i 's/$db_type = '\''.*'\''/$db_type = '\'$DB_TYPE\''/g' /var/www/html/results/telemetry_settings.php
sed -i 's/$MySql_username = '\''.*'\''/$MySql_username = '\'$DB_USERNAME\''/g' /var/www/html/results/telemetry_settings.php
@ -77,7 +82,7 @@ chown -R www-data /var/www/html/*
# Allow selection of Apache port for network_mode: host
if [ "$WEBPORT" != "80" ]; then
sed -i "s/^Listen 80\$/Listen $WEBPORT/g" /etc/apache2/ports.conf
sed -i "s/*:80>/*:$WEBPORT>/g" /etc/apache2/sites-available/000-default.conf
sed -i "s/*:80>/*:$WEBPORT>/g" /etc/apache2/sites-available/000-default.conf
fi
echo "Done, Starting APACHE"

View file

@ -3,6 +3,8 @@
<head>
<link rel="shortcut icon" href="favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, user-scalable=no" />
<link rel="apple-touch-icon" href="favicon.ico">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta charset="UTF-8" />
<script type="text/javascript" src="speedtest.js"></script>
<script type="text/javascript">
@ -11,8 +13,8 @@ function I(i){return document.getElementById(i);}
//LIST OF TEST SERVERS. See documentation for details if needed
var SPEEDTEST_SERVERS= <?= file_get_contents('/servers.json') ?: '[]' ?>;
//INITIALIZE SPEEDTEST
var s=new Speedtest(); //create speedtest object
//INITIALIZE SPEED TEST
var s=new Speedtest(); //create speed test object
<?php if(getenv("TELEMETRY")=="true"){ ?>
s.setParameter("telemetry_level","basic");
<?php } ?>
@ -112,7 +114,7 @@ function format(d){
var uiData=null;
function startStop(){
if(s.getState()==3){
//speedtest is running, abort
//speed test is running, abort
s.abort();
data=null;
I("startStopBtn").className="";
@ -409,13 +411,13 @@ function initUI(){
<div class="testName">Download</div>
<canvas id="dlMeter" class="meter"></canvas>
<div id="dlText" class="meterText"></div>
<div class="unit">Mbps</div>
<div class="unit">Mbit/s</div>
</div>
<div class="testArea">
<div class="testName">Upload</div>
<canvas id="ulMeter" class="meter"></canvas>
<div id="ulText" class="meterText"></div>
<div class="unit">Mbps</div>
<div class="unit">Mbit/s</div>
</div>
</div>
<div id="ipArea">
@ -432,7 +434,7 @@ function initUI(){
</div>
<div id="privacyPolicy" style="display:none">
<h2>Privacy Policy</h2>
<p>This HTML5 Speedtest server is configured with telemetry enabled.</p>
<p>This HTML5 speed test server is configured with telemetry enabled.</p>
<h4>What data we collect</h4>
<p>
At the end of the test, the following data is collected and stored:

View file

@ -2,13 +2,15 @@
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, user-scalable=no" />
<link rel="apple-touch-icon" href="favicon.ico">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta charset="UTF-8" />
<link rel="shortcut icon" href="favicon.ico">
<script type="text/javascript" src="speedtest.js"></script>
<script type="text/javascript">
function I(i){return document.getElementById(i);}
//INITIALIZE SPEEDTEST
var s=new Speedtest(); //create speedtest object
//INITIALIZE SPEED TEST
var s=new Speedtest(); //create speed test object
<?php if(getenv("TELEMETRY")=="true"){ ?>
s.setParameter("telemetry_level","basic");
<?php } ?>
@ -65,7 +67,7 @@ function format(d){
var uiData=null;
function startStop(){
if(s.getState()==3){
//speedtest is running, abort
//speed test is running, abort
s.abort();
data=null;
I("startStopBtn").className="";
@ -301,13 +303,13 @@ function initUI(){
<div class="testName">Download</div>
<canvas id="dlMeter" class="meter"></canvas>
<div id="dlText" class="meterText"></div>
<div class="unit">Mbps</div>
<div class="unit">Mbit/s</div>
</div>
<div class="testArea">
<div class="testName">Upload</div>
<canvas id="ulMeter" class="meter"></canvas>
<div id="ulText" class="meterText"></div>
<div class="unit">Mbps</div>
<div class="unit">Mbit/s</div>
</div>
</div>
<div id="ipArea">
@ -324,7 +326,7 @@ function initUI(){
</div>
<div id="privacyPolicy" style="display:none">
<h2>Privacy Policy</h2>
<p>This HTML5 Speedtest server is configured with telemetry enabled.</p>
<p>This HTML5 speed test server is configured with telemetry enabled.</p>
<h4>What data we collect</h4>
<p>
At the end of the test, the following data is collected and stored:

View file

@ -30,7 +30,7 @@ var SPEEDTEST_SERVERS=[
];
//INITIALIZE SPEEDTEST
var s=new Speedtest(); //create speedtest object
var s=new Speedtest(); //create speed test object
s.setParameter("telemetry_level","basic"); //enable telemetry
//SERVER AUTO SELECTION
@ -122,7 +122,7 @@ function format(d){
var uiData=null;
function startStop(){
if(s.getState()==3){
//speedtest is running, abort
//speed test is running, abort
s.abort();
data=null;
I("startStopBtn").className="";
@ -424,13 +424,13 @@ function initUI(){
<div class="testName">Download</div>
<canvas id="dlMeter" class="meter"></canvas>
<div id="dlText" class="meterText"></div>
<div class="unit">Mbps</div>
<div class="unit">Mbit/s</div>
</div>
<div class="testArea">
<div class="testName">Upload</div>
<canvas id="ulMeter" class="meter"></canvas>
<div id="ulText" class="meterText"></div>
<div class="unit">Mbps</div>
<div class="unit">Mbit/s</div>
</div>
</div>
<div id="ipArea">
@ -447,7 +447,7 @@ function initUI(){
</div>
<div id="privacyPolicy" style="display:none">
<h2>Privacy Policy</h2>
<p>This HTML5 Speedtest server is configured with telemetry enabled.</p>
<p>This HTML5 speed test server is configured with telemetry enabled.</p>
<h4>What data we collect</h4>
<p>
At the end of the test, the following data is collected and stored:

View file

@ -70,7 +70,7 @@ function loadServers(){ //called when the page is fully loaded
s.addTestPoints(SPEEDTEST_SERVERS);
selectServer();
}
}
@ -212,12 +212,12 @@ function I(id){return document.getElementById(id);}
<div class="testArea">
<div class="testName">Download</div>
<div id="dlText" class="meterText"></div>
<div class="unit">Mbps</div>
<div class="unit">Mbit/s</div>
</div>
<div class="testArea">
<div class="testName">Upload</div>
<div id="ulText" class="meterText"></div>
<div class="unit">Mbps</div>
<div class="unit">Mbit/s</div>
</div>
</div>
<div class="testGroup">

View file

@ -29,7 +29,7 @@
document.getElementById('ping').textContent = data.pingStatus + ' ms, ' + data.jitterStatus + ' ms jitter'
document.getElementById('ip').textContent = data.clientIp
}
s.start(); // start the speedtest with default settings
s.start(); // start the speed test with default settings
</script>
<a href="https://github.com/librespeed/speedtest">Source code</a>

View file

@ -250,7 +250,7 @@
<br/>
<a href="javascript:abortTest()" id="abortBtn">Abort</a>
</div>
<a href="javascript:runTest()" id="startBtn">Run speedtest</a>
<a href="javascript:runTest()" id="startBtn">Run speed test</a>
<br/><br/> Charts by <a href="http://www.chartjs.org/">Chart.js</a><br/><br/><a href="https://github.com/librespeed/speedtest">Source code</a>
</body>
</html>

View file

@ -157,12 +157,12 @@ function I(id){return document.getElementById(id);}
<div class="testArea">
<div class="testName">Download</div>
<div id="dlText" class="meterText"></div>
<div class="unit">Mbps</div>
<div class="unit">Mbit/s</div>
</div>
<div class="testArea">
<div class="testName">Upload</div>
<div id="ulText" class="meterText"></div>
<div class="unit">Mbps</div>
<div class="unit">Mbit/s</div>
</div>
</div>
</div>

View file

@ -242,13 +242,13 @@ function initUI(){
<div class="testName">Download</div>
<canvas id="dlMeter" class="meter"></canvas>
<div id="dlText" class="meterText"></div>
<div class="unit">Mbps</div>
<div class="unit">Mbit/s</div>
</div>
<div class="testArea">
<div class="testName">Upload</div>
<canvas id="ulMeter" class="meter"></canvas>
<div id="ulText" class="meterText"></div>
<div class="unit">Mbps</div>
<div class="unit">Mbit/s</div>
</div>
</div>
<div id="ipArea">

View file

@ -160,12 +160,12 @@ function I(id){return document.getElementById(id);}
<div class="testArea">
<div class="testName">Download</div>
<div id="dlText" class="meterText"></div>
<div class="unit">Mbps</div>
<div class="unit">Mbit/s</div>
</div>
<div class="testArea">
<div class="testName">Upload</div>
<div id="ulText" class="meterText"></div>
<div class="unit">Mbps</div>
<div class="unit">Mbit/s</div>
</div>
</div>
<div class="testGroup">

View file

@ -180,12 +180,12 @@ function I(id){return document.getElementById(id);}
<div class="testArea">
<div class="testName">Download</div>
<div id="dlText" class="meterText"></div>
<div class="unit">Mbps</div>
<div class="unit">Mbit/s</div>
</div>
<div class="testArea">
<div class="testName">Upload</div>
<div id="ulText" class="meterText"></div>
<div class="unit">Mbps</div>
<div class="unit">Mbit/s</div>
</div>
</div>
<div class="testGroup">

View file

@ -298,13 +298,13 @@ function initUI(){
<div class="testName">Download</div>
<canvas id="dlMeter" class="meter"></canvas>
<div id="dlText" class="meterText"></div>
<div class="unit">Mbps</div>
<div class="unit">Mbit/s</div>
</div>
<div class="testArea">
<div class="testName">Upload</div>
<canvas id="ulMeter" class="meter"></canvas>
<div id="ulText" class="meterText"></div>
<div class="unit">Mbps</div>
<div class="unit">Mbit/s</div>
</div>
</div>
<div id="ipArea">
@ -321,7 +321,7 @@ function initUI(){
</div>
<div id="privacyPolicy" style="display:none">
<h2>Privacy Policy</h2>
<p>This HTML5 Speedtest server is configured with telemetry enabled.</p>
<p>This HTML5 speed test server is configured with telemetry enabled.</p>
<h4>What data we collect</h4>
<p>
At the end of the test, the following data is collected and stored:

View file

@ -162,7 +162,7 @@ function drawImage($speedtest)
$POSITION_Y_WATERMARK = 223 * $SCALE;
// configure labels
$MBPS_TEXT = 'Mbps';
$MBPS_TEXT = 'Mbit/s';
$MS_TEXT = 'ms';
$PING_TEXT = 'Ping';
$JIT_TEXT = 'Jitter';

View file

@ -1,5 +1,8 @@
<?php
error_reporting(0);
header('Content-Type: application/json; charset=utf-8');
require_once 'telemetry_db.php';
/**
@ -53,7 +56,7 @@ function formatSpeedtestData($speedtest)
$speedtest = getSpeedtestUserById($_GET['id']);
if (!is_array($speedtest)) {
echo '{}';
} else {
$speedtest = formatSpeedtestData($speedtest);
echo json_encode(array('timestamp'=>$speedtest['timestamp'],'download'=>$speedtest['dl'],'upload'=>$speedtest['ul'],'ping'=>$speedtest['ping'],'jitter'=>$speedtest['jitter'],'ispinfo'=>$speedtest['ispinfo']));
}
$speedtest = formatSpeedtestData($speedtest);
echo json_encode(array('timestamp'=>$speedtest['timestamp'],'download'=>$speedtest['dl'],'upload'=>$speedtest['ul'],'ping'=>$speedtest['ping'],'jitter'=>$speedtest['jitter'],'ispinfo'=>$speedtest['ispinfo']));

189
results/sanitycheck.php Normal file
View file

@ -0,0 +1,189 @@
<?php
require_once 'telemetry_settings.php';
require_once 'telemetry_db.php';
require_once '../backend/getIP_util.php';
error_reporting(E_ALL);
$pass="<span class='Pass'>Pass</span>";
$failed="<span class='Failed'>failed</span>";
$na="<span class='na'>N/A</span>";
?>
<html>
<head>
<title>Speed Test installation sanity check</title>
<style>
table,th,td { border: 1px solid;}
.Pass { color:green;}
.Failed { color:red;}
.na { color:orange;}
.SectionHeading { font-style: italic;}
</style>
</head>
<body>
<table><tr><th>Item</th><th>Status</th><th>Comment</th></tr>
<tr><td colspan="3" class='SectionHeading'>PHP extensions</td></tr>
<tr><td>gd</td><td>
<?php
if(extension_loaded('gd')){
echo $pass;
} else {
echo $failed;
}
?>
</td><td>Used to render result images.</td></tr>
<tr><td>openssl</td><td>
<?php
if(extension_loaded('openssl')){
echo $pass;
} else {
echo $failed;
}
?>
</td><td></td></tr>
<tr><td>pdo_sqlsrv</td><td>
<?php
if(!isset($db_type) || $db_type != 'mssql'){
echo $na;
}elseif(extension_loaded('pdo_sqlsrv')){
echo $pass;
} else {
echo $failed;
}
?>
</td><td>Only required if using MS SQL.</td></tr>
<tr><td>pdo_mysql</td><td>
<?php
if(!isset($db_type) || $db_type != 'mysql'){
echo $na;
}elseif(extension_loaded('pdo_mysql')){
echo $pass;
} else {
echo $failed;
}
?>
</td><td>Only required if using mysql.</td></tr>
<tr><td>pdo_sqlite</td><td>
<?php
if(!isset($db_type) || $db_type != 'sqlite'){
echo $na;
}elseif(extension_loaded('pdo_sqlite')){
echo $pass;
} else {
echo $failed;
}
?>
</td><td>Only required if using sqlite.</td></tr>
<tr><td>pdo_pgsql</td><td>
<?php
if(!isset($db_type) || $db_type != 'postgresql'){
echo $na;
}elseif(extension_loaded('pdo_pgsql')){
echo $pass;
} else {
echo $failed;
}
?>
</td><td>Only required if using postgresql.</td></tr>
<tr><td colspan="3" class='SectionHeading'>Database check</td></tr>
<tr><td>Connecting to DB</td><td>
<?php
$pdo = getPdo(true);
if (($pdo instanceof PDO)) {
echo $pass;
echo "</td><td></td>";
} else {
echo $failed;
echo "</td><td>". htmlspecialchars($pdo) . "</td>";
}
?>
</tr>
<tr><td>Insert into DB</td><td>
<?php
$ip = getClientIp();
$ispinfo="";
$extra='{"DBTest":"This is a simple test of the database. No speed test was done."}';
$ua = $_SERVER['HTTP_USER_AGENT'];
$lang = '';
if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
$lang = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
}
$dl=$ul=$ping=$jitter="";
$log="";
$insertResult = insertSpeedtestUser($ip, $ispinfo, $extra, $ua, $lang, $dl, $ul, $ping, $jitter, $log, true);
if(($insertResult instanceof Exception)){
echo $failed;
echo "</td><td>";
echo htmlspecialchars($insertResult->getMessage()) . "</td>";
} else {
echo $pass;
echo "</td><td></td>";
}
?>
</tr>
<tr><td>Read from DB</td><td>
<?php
if(!($insertResult instanceof Exception)){
$QueryResult = getSpeedtestUserById($insertResult,true);
if(($QueryResult instanceof Exception)){
echo $failed;
echo "</td><td>";
echo htmlspecialchars($insertResult->getMessage()) . "</td>";
} elseif(!is_array($QueryResult)) {
echo $failed;
echo "</td><td>Test result not retrieved from database.</td>";
} else {
echo $pass;
echo "</td><td></td>";
}
} else {
echo "</td><td>Insert failed so can't test reading inserted data</td>";
}
?>
</tr>
</table>
</body>
</html>
<?php
/*
$speedtests = getLatestSpeedtestUsers();
print_r ($speedtests);
echo ' ';
print_r($pdo);
if(!isset($pdo)){
echo 'got nothing';
}
if($pdo == false){
echo 'got a false';
}
if (!($pdo instanceof PDO)) {
echo 'not a PDO';
}
if (($pdo instanceof PDO)) {
echo 'is PDO';
}
$speedtest = getSpeedtestUserById(1);
print_r ($speedtest);
*/
?>

View file

@ -2,8 +2,9 @@
require 'telemetry_settings.php';
require_once 'telemetry_db.php';
require_once '../backend/getIP_util.php';
$ip = $_SERVER['REMOTE_ADDR'];
$ip = getClientIp();
$ispinfo = $_POST['ispinfo'];
$extra = $_POST['extra'];
$ua = $_SERVER['HTTP_USER_AGENT'];

View file

@ -7,18 +7,24 @@ define('TELEMETRY_SETTINGS_FILE', 'telemetry_settings.php');
/**
* @return PDO|false
*/
function getPdo()
function getPdo($returnErrorMessage = false)
{
if (
!file_exists(TELEMETRY_SETTINGS_FILE)
|| !is_readable(TELEMETRY_SETTINGS_FILE)
) {
if($returnErrorMessage){
return 'missing TELEMETRY_SETTINGS_FILE';
}
return false;
}
require TELEMETRY_SETTINGS_FILE;
if (!isset($db_type)) {
if($returnErrorMessage){
return "db_type not set in '" . TELEMETRY_SETTINGS_FILE . "'";
}
return false;
}
@ -27,6 +33,47 @@ function getPdo()
];
try {
if ('mssql' === $db_type) {
if (!isset(
$MsSql_server,
$MsSql_databasename,
$MsSql_WindowsAuthentication
)) {
if($returnErrorMessage){
return "Required MSSQL database settings missing in '" . TELEMETRY_SETTINGS_FILE . "'";
}
return false;
}
if (!$MsSql_WindowsAuthentication and
!isset(
$MsSql_username,
$MsSql_password,
)
) {
if($returnErrorMessage){
return "Required MSSQL database settings missing in '" . TELEMETRY_SETTINGS_FILE . "'";
}
return false;
}
$dsn = 'sqlsrv:'
.'server='.$MsSql_server
.';Database='.$MsSql_databasename;
if($MsSql_TrustServerCertificate === true){
$dsn = $dsn . ';TrustServerCertificate=1';
}
if($MsSql_TrustServerCertificate === false){
$dsn = $dsn . ';TrustServerCertificate=0';
}
if($MsSql_WindowsAuthentication){
return new PDO($dsn, "", "", $pdoOptions);
} else {
return new PDO($dsn, $MySql_username, $MySql_password, $pdoOptions);
}
}
if ('mysql' === $db_type) {
if (!isset(
$MySql_hostname,
@ -35,7 +82,10 @@ function getPdo()
$MySql_username,
$MySql_password
)) {
return false;
if($returnErrorMessage){
return "Required mysql database settings missing in '" . TELEMETRY_SETTINGS_FILE . "'";
}
return false;
}
$dsn = 'mysql:'
@ -48,6 +98,9 @@ function getPdo()
if ('sqlite' === $db_type) {
if (!isset($Sqlite_db_file)) {
if($returnErrorMessage){
return "Required sqlite database settings missing in '" . TELEMETRY_SETTINGS_FILE . "'";
}
return false;
}
@ -80,7 +133,10 @@ function getPdo()
$PostgreSql_username,
$PostgreSql_password
)) {
return false;
if($returnErrorMessage){
return "Required postgresql database settings missing in '" . TELEMETRY_SETTINGS_FILE . "'";
}
return false;
}
$dsn = 'pgsql:'
@ -90,9 +146,15 @@ function getPdo()
return new PDO($dsn, $PostgreSql_username, $PostgreSql_password, $pdoOptions);
}
} catch (Exception $e) {
if($returnErrorMessage){
return $e->getMessage();
}
return false;
}
if($returnErrorMessage){
return "db_type '" . $db_type . "' not supported";
}
return false;
}
@ -109,12 +171,15 @@ function isObfuscationEnabled()
}
/**
* @return string|false returns the id of the inserted column or false on error
* @return string|false returns the id of the inserted column or false on error if returnErrorMessage is false or a error message if returnErrorMessage is true
*/
function insertSpeedtestUser($ip, $ispinfo, $extra, $ua, $lang, $dl, $ul, $ping, $jitter, $log)
function insertSpeedtestUser($ip, $ispinfo, $extra, $ua, $lang, $dl, $ul, $ping, $jitter, $log, $returnExceptionOnError = false)
{
$pdo = getPdo();
if (!($pdo instanceof PDO)) {
if($returnExceptionOnError){
return new Exception("Failed to get database connection object");
}
return false;
}
@ -129,6 +194,9 @@ function insertSpeedtestUser($ip, $ispinfo, $extra, $ua, $lang, $dl, $ul, $ping,
]);
$id = $pdo->lastInsertId();
} catch (Exception $e) {
if($returnExceptionOnError){
return $e;
}
return false;
}
@ -142,16 +210,19 @@ function insertSpeedtestUser($ip, $ispinfo, $extra, $ua, $lang, $dl, $ul, $ping,
/**
* @param int|string $id
*
* @return array|null|false returns the speedtest data as array, null
* @return array|null|false|exception returns the speedtest data as array, null
* if no data is found for the given id or
* false if there was an error
* false or an exception if there was an error (based on returnExceptionOnError)
*
* @throws RuntimeException
*/
function getSpeedtestUserById($id)
function getSpeedtestUserById($id,$returnExceptionOnError = false)
{
$pdo = getPdo();
if (!($pdo instanceof PDO)) {
if($returnExceptionOnError){
return new Exception("Failed to get database connection object");
}
return false;
}
@ -170,6 +241,9 @@ function getSpeedtestUserById($id)
$stmt->execute();
$row = $stmt->fetch(PDO::FETCH_ASSOC);
} catch (Exception $e) {
if($returnExceptionOnError){
return $e;
}
return false;
}
@ -195,14 +269,20 @@ function getLatestSpeedtestUsers()
return false;
}
require TELEMETRY_SETTINGS_FILE;
try {
$stmt = $pdo->query(
'SELECT
id, timestamp, ip, ispinfo, ua, lang, dl, ul, ping, jitter, log, extra
$sql = 'SELECT ';
if('mssql' === $db_type) {$sql .= ' TOP(100) ';}
$sql .= ' id, timestamp, ip, ispinfo, ua, lang, dl, ul, ping, jitter, log, extra
FROM speedtest_users
ORDER BY timestamp DESC
LIMIT 100'
);
ORDER BY timestamp DESC ';
if('mssql' !== $db_type) {$sql .= ' LIMIT 100 ';}
$stmt = $pdo->query($sql);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

View file

@ -0,0 +1,30 @@
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[speedtest_users](
[id] [bigint] IDENTITY(120,1) NOT NULL,
[timestamp] [datetime] NOT NULL,
[ip] [nvarchar](max) NOT NULL,
[ispinfo] [nvarchar](max) NULL,
[extra] [nvarchar](max) NULL,
[ua] [nvarchar](max) NOT NULL,
[lang] [nvarchar](max) NOT NULL,
[dl] [nvarchar](max) NULL,
[ul] [nvarchar](max) NULL,
[ping] [nvarchar](max) NULL,
[jitter] [nvarchar](max) NULL,
[log] [nvarchar](max) NULL,
CONSTRAINT [PK_speedtest_users] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[speedtest_users] ADD CONSTRAINT [DF_speedtest_users_timestamp] DEFAULT (getdate()) FOR [timestamp]
GO

View file

@ -15,14 +15,14 @@ SET client_min_messages = warning;
SET row_security = off;
--
-- Name: plpgsql; Type: EXTENSION; Schema: -; Owner:
-- Name: plpgsql; Type: EXTENSION; Schema: -; Owner:
--
CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog;
--
-- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner:
-- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner:
--
COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language';
@ -68,7 +68,7 @@ CREATE SEQUENCE speedtest_users_id_seq
CACHE 1;
-- Commented out the following line because it assumes the user of the speedtest server, @bplower
-- ALTER TABLE speedtest_users_id_seq OWNER TO speedtest;
-- ALTER TABLE speedtest_users_id_seq OWNER TO speedtest;
--
-- Name: speedtest_users_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: speedtest

View file

@ -1,6 +1,6 @@
<?php
// Type of db: "mysql", "sqlite" or "postgresql"
// Type of db: "mssql", "mysql", "sqlite" or "postgresql"
$db_type = 'mysql';
// Password to login to stats.php. Change this!!!
$stats_password = 'PASSWORD';
@ -12,6 +12,15 @@ $redact_ip_addresses = false;
// Sqlite3 settings
$Sqlite_db_file = '../../speedtest_telemetry.sql';
// mssql settings
$MsSql_server = 'DB_HOSTNAME';
$MsSql_databasename = 'DB_NAME';
$MsSql_WindowsAuthentication = true; #true or false
$MsSql_username = 'USERNAME'; #not used if MsSql_WindowsAuthentication is true
$MsSql_password = 'PASSWORD'; #not used if MsSql_WindowsAuthentication is true
$MsSql_TrustServerCertificate = true; #true, false or comment out for driver default
#Download driver from https://docs.microsoft.com/en-us/sql/connect/php/download-drivers-php-sql-server?view=sql-server-ver16
// Mysql settings
$MySql_username = 'USERNAME';
$MySql_password = 'PASSWORD';

View file

@ -6,15 +6,15 @@
*/
/*
This is the main interface between your webpage and the speedtest.
It hides the speedtest web worker to the page, and provides many convenient functions to control the test.
This is the main interface between your webpage and the speed test.
It hides the speed test web worker to the page, and provides many convenient functions to control the test.
The best way to learn how to use this is to look at the basic example, but here's some documentation.
To initialize the test, create a new Speedtest object:
var s=new Speedtest();
Now you can think of this as a finite state machine. These are the states (use getState() to see them):
- 0: here you can change the speedtest settings (such as test duration) with the setParameter("parameter",value) method. From here you can either start the test using start() (goes to state 3) or you can add multiple test points using addTestPoint(server) or addTestPoints(serverList) (goes to state 1). Additionally, this is the perfect moment to set up callbacks for the onupdate(data) and onend(aborted) events.
- 0: here you can change the speed test settings (such as test duration) with the setParameter("parameter",value) method. From here you can either start the test using start() (goes to state 3) or you can add multiple test points using addTestPoint(server) or addTestPoints(serverList) (goes to state 1). Additionally, this is the perfect moment to set up callbacks for the onupdate(data) and onend(aborted) events.
- 1: here you can add test points. You only need to do this if you want to use multiple test points.
A server is defined as an object like this:
{
@ -27,16 +27,16 @@
}
While in state 1, you can only add test points, you cannot change the test settings. When you're done, use selectServer(callback) to select the test point with the lowest ping. This is asynchronous, when it's done, it will call your callback function and move to state 2. Calling setSelectedServer(server) will manually select a server and move to state 2.
- 2: test point selected, ready to start the test. Use start() to begin, this will move to state 3
- 3: test running. Here, your onupdate event calback will be called periodically, with data coming from the worker about speed and progress. A data object will be passed to your onupdate function, with the following items:
- dlStatus: download speed in mbps
- ulStatus: upload speed in mbps
- 3: test running. Here, your onupdate event callback will be called periodically, with data coming from the worker about speed and progress. A data object will be passed to your onupdate function, with the following items:
- dlStatus: download speed in Mbit/s
- ulStatus: upload speed in Mbit/s
- pingStatus: ping in ms
- jitterStatus: jitter in ms
- dlProgress: progress of the download test as a float 0-1
- ulProgress: progress of the upload test as a float 0-1
- pingProgress: progress of the ping/jitter test as a float 0-1
- testState: state of the test (-1=not started, 0=starting, 1=download test, 2=ping+jitter test, 3=upload test, 4=finished, 5=aborted)
- clientIp: IP address of the client performing the test (and optionally ISP and distance)
- clientIp: IP address of the client performing the test (and optionally ISP and distance)
At the end of the test, the onend function will be called, with a boolean specifying whether the test was aborted or if it ended normally.
The test can be aborted at any time with abort().
At the end of the test, it will move to state 4
@ -46,10 +46,10 @@
function Speedtest() {
this._serverList = []; //when using multiple points of test, this is a list of test points
this._selectedServer = null; //when using multiple points of test, this is the selected server
this._settings = {}; //settings for the speedtest worker
this._settings = {}; //settings for the speed test worker
this._state = 0; //0=adding settings, 1=adding servers, 2=server selection done, 3=test running, 4=done
console.log(
"LibreSpeed by Federico Dossena v5.2.5 - https://github.com/librespeed/speedtest"
"LibreSpeed by Federico Dossena v5.3.1 - https://github.com/librespeed/speedtest"
);
}
@ -66,7 +66,7 @@ Speedtest.prototype = {
* - parameter: string with the name of the parameter that you want to set
* - value: new value for the parameter
*
* Invalid values or nonexistant parameters will be ignored by the speedtest worker.
* Invalid values or nonexistant parameters will be ignored by the speed test worker.
*/
setParameter: function(parameter, value) {
if (this._state == 3)

View file

@ -60,12 +60,12 @@ var settings = {
garbagePhp_chunkSize: 100, // 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
ping_allowPerformanceApi: true, // if enabled, the ping test will attempt to calculate the ping more precisely using the Performance API. Currently works perfectly in Chrome, badly in Edge, and not at all in Firefox. If Performance API is not supported or the result is obviously wrong, a fallback is provided.
overheadCompensationFactor: 1.06, //can be changed to compensatie for transport overhead. (see doc.md for some other values)
overheadCompensationFactor: 1.06, //can be changed to compensate for transport overhead. (see doc.md for some other values)
useMebibits: false, //if set to true, speed will be reported in mebibits/s instead of megabits/s
telemetry_level: 0, // 0=disabled, 1=basic (results only), 2=full (results and timing) 3=debug (results+log)
url_telemetry: "results/telemetry.php", // path to the script that adds telemetry data to the database
telemetry_extra: "", //extra data that can be passed to the telemetry through the settings
forceIE11Workaround: false //when set to true, it will foce the IE11 upload test on all browsers. Debug only
forceIE11Workaround: false //when set to true, it will force the IE11 upload test on all browsers. Debug only
};
var xhr = null; // array of currently active xhr requests
@ -423,7 +423,7 @@ function dlTest(done) {
200
);
}
// upload test, calls done function whent it's over
// upload test, calls done function when it's over
var ulCalled = false; // used to prevent multiple accidental calls to ulTest
function ulTest(done) {
tverb("ulTest");
@ -474,7 +474,7 @@ function ulTest(done) {
}
}
if (ie11workaround) {
// IE11 workarond: xhr.upload does not work properly, therefore we send a bunch of small 256k requests and use the onload event as progress. This is not precise, especially on fast connections
// IE11 workaround: xhr.upload does not work properly, therefore we send a bunch of small 256k requests and use the onload event as progress. This is not precise, especially on fast connections
xhr[i].onload = xhr[i].onerror = function() {
tverb("ul stream progress event (ie11wa)");
totLoaded += reqsmall.size;