Compare commits
83 commits
Author | SHA1 | Date | |
---|---|---|---|
|
dd1ce2cb88 | ||
|
748f49c3b6 | ||
|
4640c64412 | ||
|
5b17b397c5 | ||
|
a51b59aa1a | ||
|
3f3faeb6a1 | ||
|
c1974d5a53 | ||
|
a6ae760279 | ||
|
022c2a976f | ||
|
c32bf52bca | ||
|
a6c0818b66 | ||
|
6eeda75765 | ||
|
e5f54fbd78 | ||
|
05261e608c | ||
|
11f698b376 | ||
|
c4c87bc144 | ||
|
2662491132 | ||
|
21fb3c53aa | ||
|
fee5799a11 | ||
|
9b8faee10d | ||
|
58e2b413f2 | ||
|
338b2c27ef | ||
|
74eab590eb | ||
|
63583cb3f4 | ||
|
32f6e65a2e | ||
|
b1aa63259c | ||
|
e468c3d068 | ||
|
7c05ba084e | ||
|
60ea879bc3 | ||
|
6c92ed8091 | ||
|
cecf591d0f | ||
|
06803a85fa | ||
|
364e6304b0 | ||
|
992e24bab6 | ||
|
bce9ced9ff | ||
|
197750eef9 | ||
|
8fafd70b46 | ||
|
e0fa5148fe | ||
|
013e7d56e2 | ||
|
34dd410d14 | ||
|
871ce1a664 | ||
|
ae07bf2bdc | ||
|
bb1506c39a | ||
|
abc85eb8c5 | ||
|
6a4a05d883 | ||
|
427edfc416 | ||
|
5956e07a7a | ||
|
20f1f46d1f | ||
|
82d64bff96 | ||
|
71e3b47130 | ||
|
dd8c5f74f6 | ||
|
16e0fb9bf4 | ||
|
d1ca8c2630 | ||
|
79afc769ea | ||
|
97e0390680 | ||
|
fa0ac8cd23 | ||
|
2f19331378 | ||
|
afa84968c7 | ||
|
5bddaac12a | ||
|
a85f2c086f | ||
|
4234f5fad3 | ||
|
eca7f06e21 | ||
|
3937b940e8 | ||
|
8be8aba5ed | ||
|
084646d00e | ||
|
2ea9b4f4a3 | ||
|
624de7778b | ||
|
a3b20ba316 | ||
|
53fb62e096 | ||
|
011f8843fc | ||
|
3a35cd9cf2 | ||
|
a8af6ba700 | ||
|
fb7575b67b | ||
|
c6a36e68c4 | ||
|
72cccd0619 | ||
|
41638deab9 | ||
|
4f9e1a9342 | ||
|
a69a70f5f4 | ||
|
d6877577d5 | ||
|
e36de9a6ce | ||
|
5cef820749 | ||
|
3ccdd3d017 | ||
|
1d6568a9d3 |
0
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file → Executable file
0
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file → Executable file
81
.github/workflows/docker-publish.yml
vendored
Normal 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
Normal file → Executable file
|
@ -1,2 +1,3 @@
|
||||||
results/idObfuscation_salt.php
|
results/idObfuscation_salt.php
|
||||||
backend/getIP_serverLocation.php
|
backend/getIP_serverLocation.php
|
||||||
|
db-dir/
|
||||||
|
|
0
.logo/LibreSpeed.ai
Normal file → Executable file
0
.logo/LibreSpeed.svg
Normal file → Executable file
Before Width: | Height: | Size: 247 KiB After Width: | Height: | Size: 247 KiB |
0
.logo/icon_huge.png
Normal file → Executable file
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 60 KiB |
0
.logo/logo2.png
Normal file → Executable file
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 108 KiB |
0
.logo/logo3.png
Normal file → Executable file
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 108 KiB |
43
Dockerfile
Executable file
|
@ -0,0 +1,43 @@
|
||||||
|
FROM php:8-apache
|
||||||
|
|
||||||
|
# Install extensions
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
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-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/
|
||||||
|
COPY results/*.ttf /speedtest/results/
|
||||||
|
|
||||||
|
COPY *.js /speedtest/
|
||||||
|
COPY favicon.ico /speedtest/
|
||||||
|
|
||||||
|
COPY docker/servers.json /servers.json
|
||||||
|
|
||||||
|
COPY docker/*.php /speedtest/
|
||||||
|
COPY docker/entrypoint.sh /
|
||||||
|
|
||||||
|
# Prepare default environment variables
|
||||||
|
ENV TITLE=LibreSpeed
|
||||||
|
ENV MODE=standalone
|
||||||
|
ENV PASSWORD=password
|
||||||
|
ENV TELEMETRY=false
|
||||||
|
ENV ENABLE_ID_OBFUSCATION=false
|
||||||
|
ENV REDACT_IP_ADDRESSES=false
|
||||||
|
ENV WEBPORT=80
|
||||||
|
|
||||||
|
# Final touches
|
||||||
|
EXPOSE 80
|
||||||
|
CMD ["bash", "/entrypoint.sh"]
|
34
README.md
Normal file → Executable file
|
@ -4,10 +4,10 @@
|
||||||
|
|
||||||
No Flash, No Java, No Websocket, No Bullshit.
|
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
|
## Try it
|
||||||
[Take a Speedtest](https://librespeed.org)
|
[Take a speed test](https://librespeed.org)
|
||||||
|
|
||||||
## Compatibility
|
## 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.
|
||||||
|
@ -23,26 +23,42 @@ Works with mobile versions too.
|
||||||
* Results sharing (optional)
|
* Results sharing (optional)
|
||||||
* Multiple Points of Test (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
|
## Server requirements
|
||||||
* A reasonably fast web server with Apache 2 (nginx, IIS also supported)
|
* A reasonably fast web server with Apache 2 (nginx, IIS also supported)
|
||||||
* PHP 5.4 (other backends also available)
|
* PHP 5.4 or newer (other backends also available)
|
||||||
* MySQL database to store test results (optional, PostgreSQL and SQLite also supported)
|
* MySQL database to store test results (optional, Microsoft SQL Server, PostgreSQL and SQLite also supported)
|
||||||
* A fast! internet connection
|
* 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)
|
* [Quick start installation guide for Ubuntu Server 19.04](https://fdossena.com/?p=speedtest/quickstart_v5_ubuntu.frag)
|
||||||
|
|
||||||
## Android app
|
## Android app
|
||||||
A template to build an Android client for your LibreSpeed installation is available [here](https://github.com/librespeed/speedtest-android).
|
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
|
## Docker
|
||||||
Please see the `docker` branch
|
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
|
## Go backend
|
||||||
A Go implementation is available in the `go` branch, maintained by [Maddie Zhan](https://github.com/maddie).
|
A Go implementation is available in the [`speedtest-go`](https://github.com/librespeed/speedtest-go) repo, maintained by [Maddie Zhan](https://github.com/maddie).
|
||||||
|
|
||||||
## Node.js backend
|
## Node.js backend
|
||||||
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.
|
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.
|
||||||
|
@ -52,7 +68,7 @@ A partial Node.js implementation is available in the `node` branch, developed by
|
||||||
[Donate with PayPal](https://www.paypal.me/sineisochronic)
|
[Donate with PayPal](https://www.paypal.me/sineisochronic)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
Copyright (C) 2016-2020 Federico Dossena
|
Copyright (C) 2016-2022 Federico Dossena
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU Lesser General Public License as published by
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
|
16
backend/empty.php
Normal file → Executable file
|
@ -1,12 +1,14 @@
|
||||||
<?php
|
<?php
|
||||||
header( "HTTP/1.1 200 OK" );
|
|
||||||
if(isset($_GET["cors"])){
|
header('HTTP/1.1 200 OK');
|
||||||
|
|
||||||
|
if (isset($_GET['cors'])) {
|
||||||
header('Access-Control-Allow-Origin: *');
|
header('Access-Control-Allow-Origin: *');
|
||||||
header('Access-Control-Allow-Methods: GET, POST');
|
header('Access-Control-Allow-Methods: GET, POST');
|
||||||
header('Access-Control-Allow-Headers: Content-Encoding, Content-Type');
|
header('Access-Control-Allow-Headers: Content-Encoding, Content-Type');
|
||||||
}
|
}
|
||||||
header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0, s-maxage=0");
|
|
||||||
header("Cache-Control: post-check=0, pre-check=0", false);
|
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0, s-maxage=0');
|
||||||
header("Pragma: no-cache");
|
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||||
header("Connection: keep-alive");
|
header('Pragma: no-cache');
|
||||||
?>
|
header('Connection: keep-alive');
|
||||||
|
|
52
backend/garbage.php
Normal file → Executable file
|
@ -1,31 +1,67 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
// Disable Compression
|
// Disable Compression
|
||||||
@ini_set('zlib.output_compression', 'Off');
|
@ini_set('zlib.output_compression', 'Off');
|
||||||
@ini_set('output_buffering', 'Off');
|
@ini_set('output_buffering', 'Off');
|
||||||
@ini_set('output_handler', '');
|
@ini_set('output_handler', '');
|
||||||
// Headers
|
|
||||||
|
/**
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
function getChunkCount()
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
!array_key_exists('ckSize', $_GET)
|
||||||
|
|| !ctype_digit($_GET['ckSize'])
|
||||||
|
|| (int) $_GET['ckSize'] <= 0
|
||||||
|
) {
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((int) $_GET['ckSize'] > 1024) {
|
||||||
|
return 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int) $_GET['ckSize'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function sendHeaders()
|
||||||
|
{
|
||||||
header('HTTP/1.1 200 OK');
|
header('HTTP/1.1 200 OK');
|
||||||
if(isset($_GET["cors"])){
|
|
||||||
|
if (isset($_GET['cors'])) {
|
||||||
header('Access-Control-Allow-Origin: *');
|
header('Access-Control-Allow-Origin: *');
|
||||||
header('Access-Control-Allow-Methods: GET, POST');
|
header('Access-Control-Allow-Methods: GET, POST');
|
||||||
}
|
}
|
||||||
// Download follows...
|
|
||||||
|
// Indicate a file download
|
||||||
header('Content-Description: File Transfer');
|
header('Content-Description: File Transfer');
|
||||||
header('Content-Type: application/octet-stream');
|
header('Content-Type: application/octet-stream');
|
||||||
header('Content-Disposition: attachment; filename=random.dat');
|
header('Content-Disposition: attachment; filename=random.dat');
|
||||||
header('Content-Transfer-Encoding: binary');
|
header('Content-Transfer-Encoding: binary');
|
||||||
// Never cache me
|
|
||||||
|
// Cache settings: never cache this request
|
||||||
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0, s-maxage=0');
|
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0, s-maxage=0');
|
||||||
header('Cache-Control: post-check=0, pre-check=0', false);
|
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||||
header('Pragma: no-cache');
|
header('Pragma: no-cache');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine how much data we should send
|
||||||
|
$chunks = getChunkCount();
|
||||||
|
|
||||||
// Generate data
|
// Generate data
|
||||||
|
if (function_exists('random_bytes')) {
|
||||||
|
$data = random_bytes(1048576);
|
||||||
|
} else {
|
||||||
$data = openssl_random_pseudo_bytes(1048576);
|
$data = openssl_random_pseudo_bytes(1048576);
|
||||||
|
}
|
||||||
|
|
||||||
// Deliver chunks of 1048576 bytes
|
// Deliver chunks of 1048576 bytes
|
||||||
$chunks=isset($_GET['ckSize']) ? intval($_GET['ckSize']) : 4;
|
sendHeaders();
|
||||||
if(empty($chunks)){$chunks = 4;}
|
|
||||||
if($chunks>1024){$chunks = 1024;}
|
|
||||||
for ($i = 0; $i < $chunks; $i++) {
|
for ($i = 0; $i < $chunks; $i++) {
|
||||||
echo $data;
|
echo $data;
|
||||||
flush();
|
flush();
|
||||||
}
|
}
|
||||||
?>
|
|
||||||
|
|
473
backend/getIP.php
Normal file → Executable file
|
@ -1,59 +1,234 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This script detects the client's IP address and fetches ISP info from ipinfo.io/
|
* 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.
|
* 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.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
error_reporting(0);
|
error_reporting(0);
|
||||||
$ip = "";
|
|
||||||
header('Content-Type: application/json; charset=utf-8');
|
define('API_KEY_FILE', 'getIP_ipInfo_apikey.php');
|
||||||
if(isset($_GET["cors"])){
|
define('SERVER_LOCATION_CACHE_FILE', 'getIP_serverLocation.php');
|
||||||
header('Access-Control-Allow-Origin: *');
|
|
||||||
header('Access-Control-Allow-Methods: GET, POST');
|
require_once 'getIP_util.php';
|
||||||
}
|
|
||||||
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0, s-maxage=0');
|
/**
|
||||||
header('Cache-Control: post-check=0, pre-check=0', false);
|
* @param string $ip
|
||||||
header('Pragma: no-cache');
|
*
|
||||||
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
|
* @return string|null
|
||||||
$ip = $_SERVER['HTTP_CLIENT_IP'];
|
*/
|
||||||
} elseif (!empty($_SERVER['X-Real-IP'])) {
|
function getLocalOrPrivateIpInfo($ip)
|
||||||
$ip = $_SERVER['X-Real-IP'];
|
{
|
||||||
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
// ::1/128 is the only localhost ipv6 address. there are no others, no need to strpos this
|
||||||
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
|
if ('::1' === $ip) {
|
||||||
$ip = preg_replace("/,.*/", "", $ip); # hosts are comma-separated, client is first
|
return 'localhost IPv6 access';
|
||||||
} else {
|
|
||||||
$ip = $_SERVER['REMOTE_ADDR'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$ip = preg_replace("/^::ffff:/", "", $ip);
|
// simplified IPv6 link-local address (should match fe80::/10)
|
||||||
|
if (stripos($ip, 'fe80:') === 0) {
|
||||||
|
return 'link-local IPv6 access';
|
||||||
|
}
|
||||||
|
|
||||||
if ($ip == "::1") { // ::1/128 is the only localhost ipv6 address. there are no others, no need to strpos this
|
// anything within the 127/8 range is localhost ipv4, the ip must start with 127.0
|
||||||
echo json_encode(['processedString' => $ip . " - localhost IPv6 access", 'rawIspInfo' => ""]);
|
if (strpos($ip, '127.') === 0) {
|
||||||
die();
|
return 'localhost IPv4 access';
|
||||||
}
|
}
|
||||||
if (stripos($ip, 'fe80:') === 0) { // simplified IPv6 link-local address (should match fe80::/10)
|
|
||||||
echo json_encode(['processedString' => $ip . " - link-local IPv6 access", 'rawIspInfo' => ""]);
|
// 10/8 private IPv4
|
||||||
die();
|
if (strpos($ip, '10.') === 0) {
|
||||||
|
return 'private IPv4 access';
|
||||||
}
|
}
|
||||||
if (strpos($ip, '127.') === 0) { //anything within the 127/8 range is localhost ipv4, the ip must start with 127.0
|
|
||||||
echo json_encode(['processedString' => $ip . " - localhost IPv4 access", 'rawIspInfo' => ""]);
|
// 172.16/12 private IPv4
|
||||||
die();
|
if (preg_match('/^172\.(1[6-9]|2\d|3[01])\./', $ip) === 1) {
|
||||||
|
return 'private IPv4 access';
|
||||||
}
|
}
|
||||||
if (strpos($ip, '10.') === 0) { // 10/8 private IPv4
|
|
||||||
echo json_encode(['processedString' => $ip . " - private IPv4 access", 'rawIspInfo' => ""]);
|
// 192.168/16 private IPv4
|
||||||
die();
|
if (strpos($ip, '192.168.') === 0) {
|
||||||
|
return 'private IPv4 access';
|
||||||
}
|
}
|
||||||
if (preg_match('/^172\.(1[6-9]|2\d|3[01])\./', $ip) === 1) { // 172.16/12 private IPv4
|
|
||||||
echo json_encode(['processedString' => $ip . " - private IPv4 access", 'rawIspInfo' => ""]);
|
// IPv4 link-local
|
||||||
die();
|
if (strpos($ip, '169.254.') === 0) {
|
||||||
|
return 'link-local IPv4 access';
|
||||||
}
|
}
|
||||||
if (strpos($ip, '192.168.') === 0) { // 192.168/16 private IPv4
|
|
||||||
echo json_encode(['processedString' => $ip . " - private IPv4 access", 'rawIspInfo' => ""]);
|
return null;
|
||||||
die();
|
|
||||||
}
|
}
|
||||||
if (strpos($ip, '169.254.') === 0) { // IPv4 link-local
|
|
||||||
echo json_encode(['processedString' => $ip . " - link-local IPv4 access", 'rawIspInfo' => ""]);
|
/**
|
||||||
die();
|
* @return string
|
||||||
|
*/
|
||||||
|
function getIpInfoTokenString()
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
!file_exists(API_KEY_FILE)
|
||||||
|
|| !is_readable(API_KEY_FILE)
|
||||||
|
) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
require API_KEY_FILE;
|
||||||
|
|
||||||
|
if (empty($IPINFO_APIKEY)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '?token=' . $IPINFO_APIKEY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $ip
|
||||||
|
*
|
||||||
|
* @return array|null
|
||||||
|
*/
|
||||||
|
function getIspInfo($ip)
|
||||||
|
{
|
||||||
|
$json = file_get_contents('https://ipinfo.io/' . $ip . '/json' . getIpInfoTokenString());
|
||||||
|
if (!is_string($json)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = json_decode($json, true);
|
||||||
|
if (!is_array($data)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array|null $rawIspInfo
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function getIsp($rawIspInfo)
|
||||||
|
{
|
||||||
|
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'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'Unknown ISP';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
function getServerLocation()
|
||||||
|
{
|
||||||
|
$serverLoc = null;
|
||||||
|
if (
|
||||||
|
file_exists(SERVER_LOCATION_CACHE_FILE)
|
||||||
|
&& is_readable(SERVER_LOCATION_CACHE_FILE)
|
||||||
|
) {
|
||||||
|
require SERVER_LOCATION_CACHE_FILE;
|
||||||
|
}
|
||||||
|
if (is_string($serverLoc) && !empty($serverLoc)) {
|
||||||
|
return $serverLoc;
|
||||||
|
}
|
||||||
|
|
||||||
|
$json = file_get_contents('https://ipinfo.io/json' . getIpInfoTokenString());
|
||||||
|
if (!is_string($json)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$details = json_decode($json, true);
|
||||||
|
if (
|
||||||
|
!is_array($details)
|
||||||
|
|| !array_key_exists('loc', $details)
|
||||||
|
|| !is_string($details['loc'])
|
||||||
|
|| empty($details['loc'])
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$serverLoc = $details['loc'];
|
||||||
|
$cacheData = "<?php\n\n\$serverLoc = '" . addslashes($serverLoc) . "';\n";
|
||||||
|
file_put_contents(SERVER_LOCATION_CACHE_FILE, $cacheData);
|
||||||
|
|
||||||
|
return $serverLoc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -66,89 +241,163 @@ if (strpos($ip, '169.254.') === 0) { // IPv4 link-local
|
||||||
*
|
*
|
||||||
* @return float [km]
|
* @return float [km]
|
||||||
*/
|
*/
|
||||||
function distance($latitudeFrom, $longitudeFrom, $latitudeTo, $longitudeTo) {
|
function distance(
|
||||||
|
$latitudeFrom,
|
||||||
|
$longitudeFrom,
|
||||||
|
$latitudeTo,
|
||||||
|
$longitudeTo
|
||||||
|
) {
|
||||||
$rad = M_PI / 180;
|
$rad = M_PI / 180;
|
||||||
$theta = $longitudeFrom - $longitudeTo;
|
$theta = $longitudeFrom - $longitudeTo;
|
||||||
$dist = sin($latitudeFrom * $rad) * sin($latitudeTo * $rad) + cos($latitudeFrom * $rad) * cos($latitudeTo * $rad) * cos($theta * $rad);
|
$dist = sin($latitudeFrom * $rad)
|
||||||
|
* sin($latitudeTo * $rad)
|
||||||
|
+ cos($latitudeFrom * $rad)
|
||||||
|
* cos($latitudeTo * $rad)
|
||||||
|
* cos($theta * $rad);
|
||||||
|
|
||||||
return acos($dist) / $rad * 60 * 1.853;
|
return acos($dist) / $rad * 60 * 1.853;
|
||||||
}
|
}
|
||||||
function getIpInfoTokenString(){
|
|
||||||
$apikeyFile="getIP_ipInfo_apikey.php";
|
/**
|
||||||
if(!file_exists($apikeyFile)) return "";
|
* @param array|null $rawIspInfo
|
||||||
require $apikeyFile;
|
*
|
||||||
if(empty($IPINFO_APIKEY)) return "";
|
* @return string|null
|
||||||
return "?token=".$IPINFO_APIKEY;
|
*/
|
||||||
|
function getDistance($rawIspInfo)
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
!is_array($rawIspInfo)
|
||||||
|
|| !array_key_exists('loc', $rawIspInfo)
|
||||||
|
|| !isset($_GET['distance'])
|
||||||
|
|| !in_array($_GET['distance'], ['mi', 'km'], true)
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
if (isset($_GET["isp"])) {
|
|
||||||
$isp = "";
|
$unit = $_GET['distance'];
|
||||||
$rawIspInfo=null;
|
$clientLocation = $rawIspInfo['loc'];
|
||||||
try {
|
$serverLocation = getServerLocation();
|
||||||
$json = file_get_contents("https://ipinfo.io/" . $ip . "/json".getIpInfoTokenString());
|
|
||||||
$details = json_decode($json, true);
|
if (!is_string($serverLocation)) {
|
||||||
$rawIspInfo=$details;
|
return null;
|
||||||
if (array_key_exists("org", $details)){
|
|
||||||
$isp .= $details["org"];
|
|
||||||
$isp=preg_replace("/AS\d{1,}\s/","",$isp); //Remove AS##### from ISP name, if present
|
|
||||||
}else{
|
|
||||||
$isp .= "Unknown ISP";
|
|
||||||
}
|
}
|
||||||
if (array_key_exists("country", $details)){
|
|
||||||
$isp .= ", " . $details["country"];
|
return calculateDistance(
|
||||||
|
$serverLocation,
|
||||||
|
$clientLocation,
|
||||||
|
$unit
|
||||||
|
);
|
||||||
}
|
}
|
||||||
$clientLoc = NULL;
|
|
||||||
$serverLoc = NULL;
|
/**
|
||||||
if (array_key_exists("loc", $details)){
|
* @param string $clientLocation
|
||||||
$clientLoc = $details["loc"];
|
* @param string $serverLocation
|
||||||
}
|
* @param string $unit
|
||||||
if (isset($_GET["distance"])) {
|
*
|
||||||
if ($clientLoc) {
|
* @return string
|
||||||
$locFile="getIP_serverLocation.php";
|
*/
|
||||||
$serverLoc=null;
|
function calculateDistance($clientLocation, $serverLocation, $unit)
|
||||||
if(file_exists($locFile)){
|
{
|
||||||
require $locFile;
|
list($clientLatitude, $clientLongitude) = explode(',', $clientLocation);
|
||||||
}else{
|
list($serverLatitude, $serverLongitude) = explode(',', $serverLocation);
|
||||||
$json = file_get_contents("https://ipinfo.io/json".getIpInfoTokenString());
|
$dist = distance(
|
||||||
$details = json_decode($json, true);
|
$clientLatitude,
|
||||||
if (array_key_exists("loc", $details)){
|
$clientLongitude,
|
||||||
$serverLoc = $details["loc"];
|
$serverLatitude,
|
||||||
}
|
$serverLongitude
|
||||||
if($serverLoc){
|
);
|
||||||
$lf=fopen($locFile,"w");
|
|
||||||
fwrite($lf,chr(60)."?php\n");
|
if ('mi' === $unit) {
|
||||||
fwrite($lf,'$serverLoc="'.addslashes($serverLoc).'";');
|
|
||||||
fwrite($lf,"\n");
|
|
||||||
fwrite($lf,"?".chr(62));
|
|
||||||
fclose($lf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($serverLoc) {
|
|
||||||
try {
|
|
||||||
$clientLoc = explode(",", $clientLoc);
|
|
||||||
$serverLoc = explode(",", $serverLoc);
|
|
||||||
$dist = distance($clientLoc[0], $clientLoc[1], $serverLoc[0], $serverLoc[1]);
|
|
||||||
if ($_GET["distance"] == "mi") {
|
|
||||||
$dist /= 1.609344;
|
$dist /= 1.609344;
|
||||||
$dist = round($dist, -1);
|
$dist = round($dist, -1);
|
||||||
if ($dist < 15)
|
if ($dist < 15) {
|
||||||
$dist = "<15";
|
$dist = '<15';
|
||||||
$isp .= " (" . $dist . " mi)";
|
|
||||||
}else if ($_GET["distance"] == "km") {
|
|
||||||
$dist = round($dist, -1);
|
|
||||||
if ($dist < 20)
|
|
||||||
$dist = "<20";
|
|
||||||
$isp .= " (" . $dist . " km)";
|
|
||||||
}
|
}
|
||||||
} catch (Exception $e) {
|
|
||||||
|
|
||||||
|
return $dist . ' mi';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ('km' === $unit) {
|
||||||
|
$dist = round($dist, -1);
|
||||||
|
if ($dist < 20) {
|
||||||
|
$dist = '<20';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $dist . ' km';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
} catch (Exception $ex) {
|
|
||||||
$isp = "Unknown ISP";
|
/**
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function sendHeaders()
|
||||||
|
{
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
|
||||||
|
if (isset($_GET['cors'])) {
|
||||||
|
header('Access-Control-Allow-Origin: *');
|
||||||
|
header('Access-Control-Allow-Methods: GET, POST');
|
||||||
}
|
}
|
||||||
echo json_encode(['processedString' => $ip . " - " . $isp, 'rawIspInfo' => $rawIspInfo]);
|
|
||||||
} else {
|
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0, s-maxage=0');
|
||||||
echo json_encode(['processedString' => $ip, 'rawIspInfo' => ""]);
|
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||||
|
header('Pragma: no-cache');
|
||||||
}
|
}
|
||||||
?>
|
|
||||||
|
/**
|
||||||
|
* @param string $ip
|
||||||
|
* @param string|null $ipInfo
|
||||||
|
* @param string|null $distance
|
||||||
|
* @param array|null $rawIspInfo
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function sendResponse(
|
||||||
|
$ip,
|
||||||
|
$ipInfo = null,
|
||||||
|
$distance = null,
|
||||||
|
$rawIspInfo = null
|
||||||
|
) {
|
||||||
|
$processedString = $ip;
|
||||||
|
if (is_string($ipInfo)) {
|
||||||
|
$processedString .= ' - ' . $ipInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
is_array($rawIspInfo)
|
||||||
|
&& array_key_exists('country', $rawIspInfo)
|
||||||
|
) {
|
||||||
|
$processedString .= ', ' . $rawIspInfo['country'];
|
||||||
|
}
|
||||||
|
if (is_string($distance)) {
|
||||||
|
$processedString .= ' (' . $distance . ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
sendHeaders();
|
||||||
|
echo json_encode([
|
||||||
|
'processedString' => $processedString,
|
||||||
|
'rawIspInfo' => $rawIspInfo ?: '',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$ip = getClientIp();
|
||||||
|
|
||||||
|
$localIpInfo = getLocalOrPrivateIpInfo($ip);
|
||||||
|
// local ip, no need to fetch further information
|
||||||
|
if (is_string($localIpInfo)) {
|
||||||
|
sendResponse($ip, $localIpInfo);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($_GET['isp'])) {
|
||||||
|
sendResponse($ip);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$rawIspInfo = getIspInfo($ip);
|
||||||
|
$isp = getIsp($rawIspInfo);
|
||||||
|
$distance = getDistance($rawIspInfo);
|
||||||
|
|
||||||
|
sendResponse($ip, $isp, $distance, $rawIspInfo);
|
||||||
|
|
5
backend/getIP_ipInfo_apikey.php
Normal file → Executable file
|
@ -1,3 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
$IPINFO_APIKEY=""; //put your token between the quotes if you have one
|
|
||||||
?>
|
// put your token between the quotes if you have one
|
||||||
|
$IPINFO_APIKEY = '';
|
||||||
|
|
20
backend/getIP_util.php
Normal 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);
|
||||||
|
}
|
||||||
|
|
35
doc.md
Normal file → Executable file
|
@ -1,7 +1,7 @@
|
||||||
# LibreSpeed
|
# LibreSpeed
|
||||||
|
|
||||||
> by Federico Dossena
|
> by Federico Dossena
|
||||||
> Version 5.2
|
> Version 5.2.4
|
||||||
> [https://github.com/librespeed/speedtest/](https://github.com/librespeed/speedtest/)
|
> [https://github.com/librespeed/speedtest/](https://github.com/librespeed/speedtest/)
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
@ -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.
|
Client side, the test can use up to 500MB of RAM on very fast connections.
|
||||||
|
|
||||||
## Quick start guides
|
## 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)
|
* [Quick start installation guide for Ubuntu Server 19.04](https://fdossena.com/?p=speedtest/quickstart_v5_ubuntu.frag)
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ Server side, you'll need:
|
||||||
* PHP 5.4 or newer, a 64-bit version is strongly recommended
|
* PHP 5.4 or newer, a 64-bit version is strongly recommended
|
||||||
* OpenSSL and its PHP module (this is usually installed automatically by most distros)
|
* OpenSSL and its PHP module (this is usually installed automatically by most distros)
|
||||||
* If you want to store test results (telemetry), one of the following:
|
* If you want to store test results (telemetry), one of the following:
|
||||||
- MySQL/MariaDB and the mysqli PHP module
|
- MySQL/MariaDB and its PHP PDO module
|
||||||
- PostgreSQL and its PHP PDO module
|
- PostgreSQL and its PHP PDO module
|
||||||
- SQLite 3 and its PHP PDO module
|
- SQLite 3 and its PHP PDO module
|
||||||
* If you want to enable results sharing:
|
* If you want to enable results sharing:
|
||||||
|
@ -57,7 +57,7 @@ Put all files on your web server via FTP or by copying them directly. You can in
|
||||||
__Important:__ The speed test needs write permissions in the installation folder!
|
__Important:__ The speed test needs write permissions in the installation folder!
|
||||||
|
|
||||||
#### ipinfo.io
|
#### 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.
|
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,17 +109,16 @@ 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`.
|
A login is required to access the interface. __Important__: change the default password in `results/telemetry_settings.php`.
|
||||||
|
|
||||||
#### The end
|
#### The end
|
||||||
Now that the test is installed, rename one of the examples to `index.html` and delete the other examples.
|
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`.
|
||||||
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.
|
|
||||||
|
|
||||||
If you're not using telemetry and results sharing, you can delete the `results` folder too.
|
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.
|
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
|
#### 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
|
### Multiple servers, PHP
|
||||||
The speed test 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.
|
||||||
|
@ -137,7 +136,7 @@ Requirements:
|
||||||
* Apache 2 (nginx and IIS also supported). A fast connection is not mandatory, but is still recommended
|
* Apache 2 (nginx and IIS also supported). A fast connection is not mandatory, but is still recommended
|
||||||
* PHP 5.4 or newer
|
* PHP 5.4 or newer
|
||||||
* If you want to store test results (telemetry), one of the following:
|
* If you want to store test results (telemetry), one of the following:
|
||||||
- MySQL/MariaDB and the mysqli PHP module
|
- MySQL/MariaDB and its PHP PDO module
|
||||||
- PostgreSQL and its PHP PDO module
|
- PostgreSQL and its PHP PDO module
|
||||||
- SQLite 3 and its PHP PDO module
|
- SQLite 3 and its PHP PDO module
|
||||||
* If you want to enable results sharing:
|
* If you want to enable results sharing:
|
||||||
|
@ -224,7 +223,7 @@ To install a backend, simply copy all the files in the `backend` folder to your
|
||||||
__Important:__ The speed test needs write permissions in the installation folder!
|
__Important:__ The speed test needs write permissions in the installation folder!
|
||||||
|
|
||||||
#### ipinfo.io
|
#### 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.
|
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,11 +239,11 @@ __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-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-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-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:__
|
__Multiple servers:__
|
||||||
* `example-multipleServers-pretty.html`: Same as `example-singleServer-pretty.html` but with multiple test points. Server selection is fully automatic
|
* `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
|
### Initialization
|
||||||
To use the speed test in your page, first you need to load it:
|
To use the speed test in your page, first you need to load it:
|
||||||
|
@ -506,9 +505,9 @@ 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.
|
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
|
* __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:
|
* __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 mbps
|
- `dlStatus`: download speed in Mbit/s
|
||||||
- `ulStatus`: upload speed in mbps
|
- `ulStatus`: upload speed in Mbit/s
|
||||||
- `pingStatus`: ping in ms
|
- `pingStatus`: ping in ms
|
||||||
- `jitterStatus`: jitter in ms
|
- `jitterStatus`: jitter in ms
|
||||||
- `dlProgress`: progress of the download test as a float 0-1
|
- `dlProgress`: progress of the download test as a float 0-1
|
||||||
|
@ -664,7 +663,7 @@ Access-Control-Allow-Headers: Content-Encoding, Content-Type
|
||||||
This file stores telemetry information into the database.
|
This file stores telemetry information into the database.
|
||||||
|
|
||||||
Data is passed as POST parameters:
|
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)
|
* `extra`: the `telemetry_extra` string passed to the worker (if set, empty string otherwise)
|
||||||
* `dl`: download speed
|
* `dl`: download speed
|
||||||
* `ul`: upload speed
|
* `ul`: upload speed
|
||||||
|
@ -741,7 +740,7 @@ s.setParameter("url_ping","backend/empty.dat");
|
||||||
s.setParameter("test_order","P_D_U");
|
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
|
## 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).
|
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).
|
||||||
|
@ -807,7 +806,7 @@ Also, make sure that the web server has write permission on the `results` folder
|
||||||
|
|
||||||
## Known bugs and limitations
|
## Known bugs and limitations
|
||||||
### General
|
### 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
|
### IE specific
|
||||||
* The upload test is not precise on very fast connections with high latency (will probably be fixed by Edge 17)
|
* 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 speed test 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.
|
||||||
|
|
148
doc_docker.md
Executable file
|
@ -0,0 +1,148 @@
|
||||||
|
A docker version of LibreSpeed is available here: [GitHub Packages](https://github.com/librespeed/speedtest/pkgs/container/speedtest)
|
||||||
|
|
||||||
|
## Downloading docker image
|
||||||
|
To download LibreSpeed from the docker repo, use this command:
|
||||||
|
|
||||||
|
```
|
||||||
|
docker pull ghcr.io/librespeed/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`.
|
||||||
|
|
||||||
|
The test can be accessed on port 80.
|
||||||
|
|
||||||
|
Here's a list of additional environment variables available in this mode:
|
||||||
|
* __`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.
|
||||||
|
* __`EMAIL`__: Email address for GDPR requests. Must be specified when telemetry is enabled.
|
||||||
|
* __`IPINFO_APIKEY`__: API key for ipinfo.io. Optional, but required if you expect to serve a large number of tests
|
||||||
|
* __`DISABLE_IPINFO`__: If set to true, ISP info and distance will not be fetched from ipinfo.io. Default: value: `false`
|
||||||
|
* __`DISTANCE`__: When `DISABLE_IPINFO` is set to false, this specifies how the distance from the server is measured. Can be either `km` for kilometers, `mi` for miles, or an empty string to disable distance measurement. Default value: `km`
|
||||||
|
* __`WEBPORT`__: Allows choosing a custom port for the included web server. Default value: `80`. Note that you will have to expose it through docker with the -p argument
|
||||||
|
|
||||||
|
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 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 -v $PWD/db-dir/:/database -it ghcr.io/librespeed/speedtest
|
||||||
|
```
|
||||||
|
|
||||||
|
## Multiple Points of Test
|
||||||
|
For multiple servers, you need to set up 1+ LibreSpeed backends, and 1 LibreSpeed frontend.
|
||||||
|
|
||||||
|
### Backend mode
|
||||||
|
In backend mode, LibreSpeed provides only a test point with no UI. To do this, set the `MODE` environment variable to `backend`.
|
||||||
|
|
||||||
|
The following backend files can be accessed on port 80: `garbage.php`, `empty.php`, `getIP.php`
|
||||||
|
|
||||||
|
Here's a list of additional environment variables available in this mode:
|
||||||
|
* __`IPINFO_APIKEY`__: API key for ipinfo.io. Optional, but required if you expect to serve a large number of tests
|
||||||
|
|
||||||
|
###### Example:
|
||||||
|
This command starts LibreSpeed in backend mode, with the default settings, on port 80:
|
||||||
|
```
|
||||||
|
docker run -e MODE=backend -p 80:80 -it ghcr.io/librespeed/speedtest
|
||||||
|
```
|
||||||
|
|
||||||
|
### Frontend mode
|
||||||
|
In frontend mode, LibreSpeed serves clients the Web UI and a list of servers. To do this:
|
||||||
|
* Set the `MODE` environment variable to `frontend`
|
||||||
|
* Create a servers.json file with your test points. The syntax is the following:
|
||||||
|
```
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "Friendly name for Server 1",
|
||||||
|
"server" :"//server1.mydomain.com/",
|
||||||
|
"dlURL" :"garbage.php",
|
||||||
|
"ulURL" :"empty.php",
|
||||||
|
"pingURL" :"empty.php",
|
||||||
|
"getIpURL" :"getIP.php"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Friendly name for Server 2",
|
||||||
|
"server" :"https://server2.mydomain.com/",
|
||||||
|
"dlURL" :"garbage.php",
|
||||||
|
"ulURL" :"empty.php",
|
||||||
|
"pingURL" :"empty.php",
|
||||||
|
"getIpURL" :"getIP.php"
|
||||||
|
},
|
||||||
|
...more servers...
|
||||||
|
]
|
||||||
|
```
|
||||||
|
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:
|
||||||
|
* __`TITLE`__: Title of your speedtest. Default value: `LibreSpeed`
|
||||||
|
* __`TELEMETRY`__: Whether to enable telemetry or not. 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.
|
||||||
|
* __`EMAIL`__: Email address for GDPR requests. Must be specified when telemetry is enabled.
|
||||||
|
* __`DISABLE_IPINFO`__: If set to true, ISP info and distance will not be fetched from ipinfo.io. Default: value: `false`
|
||||||
|
* __`DISTANCE`__: When `DISABLE_IPINFO` is set to false, this specifies how the distance from the server is measured. Can be either `km` for kilometers, `mi` for miles, or an empty string to disable distance measurement. Default value: `km`
|
||||||
|
* __`WEBPORT`__: Allows choosing a custom port for the included web server. Default value: `80`
|
||||||
|
|
||||||
|
###### 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 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.
|
91
docker/entrypoint.sh
Executable file
|
@ -0,0 +1,91 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
rm -rf /var/www/html/*
|
||||||
|
|
||||||
|
# Copy frontend files
|
||||||
|
cp /speedtest/*.js /var/www/html/
|
||||||
|
|
||||||
|
# Copy favicon
|
||||||
|
cp /speedtest/favicon.ico /var/www/html/
|
||||||
|
|
||||||
|
# Set up backend side for standlone modes
|
||||||
|
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
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$MODE" == "backend" ]; then
|
||||||
|
cp -r /speedtest/backend/* /var/www/html
|
||||||
|
if [ ! -z "$IPINFO_APIKEY" ]; then
|
||||||
|
sed -i s/\$IPINFO_APIKEY\ =\ \'\'/\$IPINFO_APIKEY\ =\ \'$IPINFO_APIKEY\'/g /var/www/html/getIP_ipInfo_apikey.php
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set up index.php for frontend-only or standalone modes
|
||||||
|
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" || "$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
|
||||||
|
sed -i 's/$MySql_password = '\''.*'\''/$MySql_password = '\'$DB_PASSWORD\''/g' /var/www/html/results/telemetry_settings.php
|
||||||
|
sed -i 's/$MySql_hostname = '\''.*'\''/$MySql_hostname = '\'$DB_HOSTNAME\''/g' /var/www/html/results/telemetry_settings.php
|
||||||
|
sed -i 's/$MySql_databasename = '\''.*'\''/$MySql_databasename = '\'$DB_NAME\''/g' /var/www/html/results/telemetry_settings.php
|
||||||
|
if [ "$DB_PORT" != "" ]; then
|
||||||
|
sed -i 's/$MySql_port = '\''.*'\''/$MySql_port = '\'$DB_PORT\''/g' /var/www/html/results/telemetry_settings.php
|
||||||
|
fi
|
||||||
|
elif [ "$DB_TYPE" == "postgresql" ]; then
|
||||||
|
sed -i 's/$db_type = '\''.*'\''/$db_type = '\'$DB_TYPE\''/g' /var/www/html/results/telemetry_settings.php
|
||||||
|
sed -i 's/$PostgreSql_username = '\''.*'\''/$PostgreSql_username = '\'$DB_USERNAME\''/g' /var/www/html/results/telemetry_settings.php
|
||||||
|
sed -i 's/$PostgreSql_password = '\''.*'\''/$PostgreSql_password = '\'$DB_PASSWORD\''/g' /var/www/html/results/telemetry_settings.php
|
||||||
|
sed -i 's/$PostgreSql_hostname = '\''.*'\''/$PostgreSql_hostname = '\'$DB_HOSTNAME\''/g' /var/www/html/results/telemetry_settings.php
|
||||||
|
sed -i 's/$PostgreSql_databasename = '\''.*'\''/$PostgreSql_databasename = '\'$DB_NAME\''/g' /var/www/html/results/telemetry_settings.php
|
||||||
|
else
|
||||||
|
sed -i s/\$db_type\ =\ \'.*\'/\$db_type\ =\ \'sqlite\'\/g /var/www/html/results/telemetry_settings.php
|
||||||
|
fi
|
||||||
|
|
||||||
|
sed -i s/\$Sqlite_db_file\ =\ \'.*\'/\$Sqlite_db_file=\'\\\/database\\\/db.sql\'/g /var/www/html/results/telemetry_settings.php
|
||||||
|
sed -i s/\$stats_password\ =\ \'.*\'/\$stats_password\ =\ \'$PASSWORD\'/g /var/www/html/results/telemetry_settings.php
|
||||||
|
|
||||||
|
if [ "$ENABLE_ID_OBFUSCATION" == "true" ]; then
|
||||||
|
sed -i s/\$enable_id_obfuscation\ =\ .*\;/\$enable_id_obfuscation\ =\ true\;/g /var/www/html/results/telemetry_settings.php
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$REDACT_IP_ADDRESSES" == "true" ]; then
|
||||||
|
sed -i s/\$redact_ip_addresses\ =\ .*\;/\$redact_ip_addresses\ =\ true\;/g /var/www/html/results/telemetry_settings.php
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p /database/
|
||||||
|
chown www-data /database/
|
||||||
|
fi
|
||||||
|
|
||||||
|
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
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Done, Starting APACHE"
|
||||||
|
|
||||||
|
# This runs apache
|
||||||
|
apache2-foreground
|
46
example-multipleServers-full.html → docker/frontend.php
Normal file → Executable file
|
@ -3,35 +3,27 @@
|
||||||
<head>
|
<head>
|
||||||
<link rel="shortcut icon" href="favicon.ico">
|
<link rel="shortcut icon" href="favicon.ico">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, user-scalable=no" />
|
<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" />
|
<meta charset="UTF-8" />
|
||||||
<script type="text/javascript" src="speedtest.js"></script>
|
<script type="text/javascript" src="speedtest.js"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
function I(i){return document.getElementById(i);}
|
function I(i){return document.getElementById(i);}
|
||||||
|
|
||||||
//LIST OF TEST SERVERS. See documentation for details if needed
|
//LIST OF TEST SERVERS. See documentation for details if needed
|
||||||
var SPEEDTEST_SERVERS=[
|
var SPEEDTEST_SERVERS= <?= file_get_contents('/servers.json') ?: '[]' ?>;
|
||||||
{ //this is my demo server, remove it
|
|
||||||
name:"Speedtest Demo Server (Helsinki)", //user friendly name for the server
|
|
||||||
server:"//fi.openspeed.org/", //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)
|
|
||||||
pingURL:"empty.php", //path to ping/jitter test on this server (empty.php or replacement)
|
|
||||||
getIpURL:"getIP.php" //path to getIP on this server (getIP.php or replacement)
|
|
||||||
},
|
|
||||||
{ //this is my demo server, remove it
|
|
||||||
name:"Old Speedtest Demo Server",
|
|
||||||
server:"//mpotdemo.fdossena.com/",
|
|
||||||
dlURL:"garbage.php",
|
|
||||||
ulURL:"empty.php",
|
|
||||||
pingURL:"empty.php",
|
|
||||||
getIpURL:"getIP.php"
|
|
||||||
}
|
|
||||||
//add other servers here, comma separated
|
|
||||||
];
|
|
||||||
|
|
||||||
//INITIALIZE SPEED TEST
|
//INITIALIZE SPEED TEST
|
||||||
var s=new Speedtest(); //create speed test object
|
var s=new Speedtest(); //create speed test object
|
||||||
s.setParameter("telemetry_level","basic"); //enable telemetry
|
<?php if(getenv("TELEMETRY")=="true"){ ?>
|
||||||
|
s.setParameter("telemetry_level","basic");
|
||||||
|
<?php } ?>
|
||||||
|
<?php if(getenv("DISABLE_IPINFO")=="true"){ ?>
|
||||||
|
s.setParameter("getIp_ispInfo",false);
|
||||||
|
<?php } ?>
|
||||||
|
<?php if(getenv("DISTANCE")){ ?>
|
||||||
|
s.setParameter("getIp_ispInfo_distance","<?=getenv("DISTANCE") ?>");
|
||||||
|
<?php } ?>
|
||||||
|
|
||||||
//SERVER AUTO SELECTION
|
//SERVER AUTO SELECTION
|
||||||
function initServers(){
|
function initServers(){
|
||||||
|
@ -386,16 +378,18 @@ function initUI(){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<title>LibreSpeed Example</title>
|
<title><?= getenv('TITLE') ?: 'LibreSpeed Example' ?></title>
|
||||||
</head>
|
</head>
|
||||||
<body onload="initServers()">
|
<body onload="initServers()">
|
||||||
<h1>LibreSpeed Example</h1>
|
<h1><?= getenv('TITLE') ?: 'LibreSpeed Example' ?></h1>
|
||||||
<div id="loading" class="visible">
|
<div id="loading" class="visible">
|
||||||
<p id="message"><span class="loadCircle"></span>Selecting a server...</p>
|
<p id="message"><span class="loadCircle"></span>Selecting a server...</p>
|
||||||
</div>
|
</div>
|
||||||
<div id="testWrapper" class="hidden">
|
<div id="testWrapper" class="hidden">
|
||||||
<div id="startStopBtn" onclick="startStop()"></div>
|
<div id="startStopBtn" onclick="startStop()"></div>
|
||||||
|
<?php if(getenv("TELEMETRY")=="true"){ ?>
|
||||||
<a class="privacy" href="#" onclick="I('privacyPolicy').style.display=''">Privacy</a>
|
<a class="privacy" href="#" onclick="I('privacyPolicy').style.display=''">Privacy</a>
|
||||||
|
<?php } ?>
|
||||||
<div id="serverArea">
|
<div id="serverArea">
|
||||||
Server: <select id="server" onchange="s.setSelectedServer(SPEEDTEST_SERVERS[this.value])"></select>
|
Server: <select id="server" onchange="s.setSelectedServer(SPEEDTEST_SERVERS[this.value])"></select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -417,13 +411,13 @@ function initUI(){
|
||||||
<div class="testName">Download</div>
|
<div class="testName">Download</div>
|
||||||
<canvas id="dlMeter" class="meter"></canvas>
|
<canvas id="dlMeter" class="meter"></canvas>
|
||||||
<div id="dlText" class="meterText"></div>
|
<div id="dlText" class="meterText"></div>
|
||||||
<div class="unit">Mbps</div>
|
<div class="unit">Mbit/s</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="testArea">
|
<div class="testArea">
|
||||||
<div class="testName">Upload</div>
|
<div class="testName">Upload</div>
|
||||||
<canvas id="ulMeter" class="meter"></canvas>
|
<canvas id="ulMeter" class="meter"></canvas>
|
||||||
<div id="ulText" class="meterText"></div>
|
<div id="ulText" class="meterText"></div>
|
||||||
<div class="unit">Mbps</div>
|
<div class="unit">Mbit/s</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="ipArea">
|
<div id="ipArea">
|
||||||
|
@ -440,7 +434,7 @@ function initUI(){
|
||||||
</div>
|
</div>
|
||||||
<div id="privacyPolicy" style="display:none">
|
<div id="privacyPolicy" style="display:none">
|
||||||
<h2>Privacy Policy</h2>
|
<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>
|
<h4>What data we collect</h4>
|
||||||
<p>
|
<p>
|
||||||
At the end of the test, the following data is collected and stored:
|
At the end of the test, the following data is collected and stored:
|
||||||
|
@ -471,7 +465,7 @@ function initUI(){
|
||||||
<h4>Data removal</h4>
|
<h4>Data removal</h4>
|
||||||
<p>
|
<p>
|
||||||
If you want to have your information deleted, you need to provide either the ID of the test or your IP address. This is the only way to identify your data, without this information we won't be able to comply with your request.<br/><br/>
|
If you want to have your information deleted, you need to provide either the ID of the test or your IP address. This is the only way to identify your data, without this information we won't be able to comply with your request.<br/><br/>
|
||||||
Contact this email address for all deletion requests: <a href="mailto:PUT@YOUR_EMAIL.HERE">TO BE FILLED BY DEVELOPER</a>.
|
Contact this email address for all deletion requests: <a href="mailto:<?=getenv("EMAIL") ?>"><?=getenv("EMAIL") ?></a>.
|
||||||
</p>
|
</p>
|
||||||
<br/><br/>
|
<br/><br/>
|
||||||
<a class="privacy" href="#" onclick="I('privacyPolicy').style.display='none'">Close</a><br/>
|
<a class="privacy" href="#" onclick="I('privacyPolicy').style.display='none'">Close</a><br/>
|
19
docker/servers.json
Executable file
|
@ -0,0 +1,19 @@
|
||||||
|
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "Example server 1 (HTTP and HTTPS)",
|
||||||
|
"server" :"//example1.mydomain.com/",
|
||||||
|
"dlURL" :"garbage.php",
|
||||||
|
"ulURL" :"empty.php",
|
||||||
|
"pingURL" :"empty.php",
|
||||||
|
"getIpURL" :"getIP.php"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Example server 2 (HTTPS only)",
|
||||||
|
"server" :"https://example2.mydomain.com/",
|
||||||
|
"dlURL" :"garbage.php",
|
||||||
|
"ulURL" :"empty.php",
|
||||||
|
"pingURL" :"empty.php",
|
||||||
|
"getIpURL" :"getIP.php"
|
||||||
|
}
|
||||||
|
]
|
367
docker/standalone.php
Executable file
|
@ -0,0 +1,367 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<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 SPEED TEST
|
||||||
|
var s=new Speedtest(); //create speed test object
|
||||||
|
<?php if(getenv("TELEMETRY")=="true"){ ?>
|
||||||
|
s.setParameter("telemetry_level","basic");
|
||||||
|
<?php } ?>
|
||||||
|
<?php if(getenv("DISABLE_IPINFO")=="true"){ ?>
|
||||||
|
s.setParameter("getIp_ispInfo",false);
|
||||||
|
<?php } ?>
|
||||||
|
<?php if(getenv("DISTANCE")){ ?>
|
||||||
|
s.setParameter("getIp_ispInfo_distance","<?=getenv("DISTANCE") ?>");
|
||||||
|
<?php } ?>
|
||||||
|
|
||||||
|
var meterBk=/Trident.*rv:(\d+\.\d+)/i.test(navigator.userAgent)?"#EAEAEA":"#80808040";
|
||||||
|
var dlColor="#6060AA",
|
||||||
|
ulColor="#616161";
|
||||||
|
var progColor=meterBk;
|
||||||
|
|
||||||
|
//CODE FOR GAUGES
|
||||||
|
function drawMeter(c,amount,bk,fg,progress,prog){
|
||||||
|
var ctx=c.getContext("2d");
|
||||||
|
var dp=window.devicePixelRatio||1;
|
||||||
|
var cw=c.clientWidth*dp, ch=c.clientHeight*dp;
|
||||||
|
var sizScale=ch*0.0055;
|
||||||
|
if(c.width==cw&&c.height==ch){
|
||||||
|
ctx.clearRect(0,0,cw,ch);
|
||||||
|
}else{
|
||||||
|
c.width=cw;
|
||||||
|
c.height=ch;
|
||||||
|
}
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.strokeStyle=bk;
|
||||||
|
ctx.lineWidth=12*sizScale;
|
||||||
|
ctx.arc(c.width/2,c.height-58*sizScale,c.height/1.8-ctx.lineWidth,-Math.PI*1.1,Math.PI*0.1);
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.strokeStyle=fg;
|
||||||
|
ctx.lineWidth=12*sizScale;
|
||||||
|
ctx.arc(c.width/2,c.height-58*sizScale,c.height/1.8-ctx.lineWidth,-Math.PI*1.1,amount*Math.PI*1.2-Math.PI*1.1);
|
||||||
|
ctx.stroke();
|
||||||
|
if(typeof progress !== "undefined"){
|
||||||
|
ctx.fillStyle=prog;
|
||||||
|
ctx.fillRect(c.width*0.3,c.height-16*sizScale,c.width*0.4*progress,4*sizScale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function mbpsToAmount(s){
|
||||||
|
return 1-(1/(Math.pow(1.3,Math.sqrt(s))));
|
||||||
|
}
|
||||||
|
function format(d){
|
||||||
|
d=Number(d);
|
||||||
|
if(d<10) return d.toFixed(2);
|
||||||
|
if(d<100) return d.toFixed(1);
|
||||||
|
return d.toFixed(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//UI CODE
|
||||||
|
var uiData=null;
|
||||||
|
function startStop(){
|
||||||
|
if(s.getState()==3){
|
||||||
|
//speed test is running, abort
|
||||||
|
s.abort();
|
||||||
|
data=null;
|
||||||
|
I("startStopBtn").className="";
|
||||||
|
initUI();
|
||||||
|
}else{
|
||||||
|
//test is not running, begin
|
||||||
|
I("startStopBtn").className="running";
|
||||||
|
I("shareArea").style.display="none";
|
||||||
|
s.onupdate=function(data){
|
||||||
|
uiData=data;
|
||||||
|
};
|
||||||
|
s.onend=function(aborted){
|
||||||
|
I("startStopBtn").className="";
|
||||||
|
updateUI(true);
|
||||||
|
if(!aborted){
|
||||||
|
//if testId is present, show sharing panel, otherwise do nothing
|
||||||
|
try{
|
||||||
|
var testId=uiData.testId;
|
||||||
|
if(testId!=null){
|
||||||
|
var shareURL=window.location.href.substring(0,window.location.href.lastIndexOf("/"))+"/results/?id="+testId;
|
||||||
|
I("resultsImg").src=shareURL;
|
||||||
|
I("resultsURL").value=shareURL;
|
||||||
|
I("testId").innerHTML=testId;
|
||||||
|
I("shareArea").style.display="";
|
||||||
|
}
|
||||||
|
}catch(e){}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
s.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//this function reads the data sent back by the test and updates the UI
|
||||||
|
function updateUI(forced){
|
||||||
|
if(!forced&&s.getState()!=3) return;
|
||||||
|
if(uiData==null) return;
|
||||||
|
var status=uiData.testState;
|
||||||
|
I("ip").textContent=uiData.clientIp;
|
||||||
|
I("dlText").textContent=(status==1&&uiData.dlStatus==0)?"...":format(uiData.dlStatus);
|
||||||
|
drawMeter(I("dlMeter"),mbpsToAmount(Number(uiData.dlStatus*(status==1?oscillate():1))),meterBk,dlColor,Number(uiData.dlProgress),progColor);
|
||||||
|
I("ulText").textContent=(status==3&&uiData.ulStatus==0)?"...":format(uiData.ulStatus);
|
||||||
|
drawMeter(I("ulMeter"),mbpsToAmount(Number(uiData.ulStatus*(status==3?oscillate():1))),meterBk,ulColor,Number(uiData.ulProgress),progColor);
|
||||||
|
I("pingText").textContent=format(uiData.pingStatus);
|
||||||
|
I("jitText").textContent=format(uiData.jitterStatus);
|
||||||
|
}
|
||||||
|
function oscillate(){
|
||||||
|
return 1+0.02*Math.sin(Date.now()/100);
|
||||||
|
}
|
||||||
|
//update the UI every frame
|
||||||
|
window.requestAnimationFrame=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.msRequestAnimationFrame||(function(callback,element){setTimeout(callback,1000/60);});
|
||||||
|
function frame(){
|
||||||
|
requestAnimationFrame(frame);
|
||||||
|
updateUI();
|
||||||
|
}
|
||||||
|
frame(); //start frame loop
|
||||||
|
//function to (re)initialize UI
|
||||||
|
function initUI(){
|
||||||
|
drawMeter(I("dlMeter"),0,meterBk,dlColor,0);
|
||||||
|
drawMeter(I("ulMeter"),0,meterBk,ulColor,0);
|
||||||
|
I("dlText").textContent="";
|
||||||
|
I("ulText").textContent="";
|
||||||
|
I("pingText").textContent="";
|
||||||
|
I("jitText").textContent="";
|
||||||
|
I("ip").textContent="";
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style type="text/css">
|
||||||
|
html,body{
|
||||||
|
border:none; padding:0; margin:0;
|
||||||
|
background:#FFFFFF;
|
||||||
|
color:#202020;
|
||||||
|
}
|
||||||
|
body{
|
||||||
|
text-align:center;
|
||||||
|
font-family:"Roboto",sans-serif;
|
||||||
|
}
|
||||||
|
h1{
|
||||||
|
color:#404040;
|
||||||
|
}
|
||||||
|
#startStopBtn{
|
||||||
|
display:inline-block;
|
||||||
|
margin:0 auto;
|
||||||
|
color:#6060AA;
|
||||||
|
background-color:rgba(0,0,0,0);
|
||||||
|
border:0.15em solid #6060FF;
|
||||||
|
border-radius:0.3em;
|
||||||
|
transition:all 0.3s;
|
||||||
|
box-sizing:border-box;
|
||||||
|
width:8em; height:3em;
|
||||||
|
line-height:2.7em;
|
||||||
|
cursor:pointer;
|
||||||
|
box-shadow: 0 0 0 rgba(0,0,0,0.1), inset 0 0 0 rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
#startStopBtn:hover{
|
||||||
|
box-shadow: 0 0 2em rgba(0,0,0,0.1), inset 0 0 1em rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
#startStopBtn.running{
|
||||||
|
background-color:#FF3030;
|
||||||
|
border-color:#FF6060;
|
||||||
|
color:#FFFFFF;
|
||||||
|
}
|
||||||
|
#startStopBtn:before{
|
||||||
|
content:"Start";
|
||||||
|
}
|
||||||
|
#startStopBtn.running:before{
|
||||||
|
content:"Abort";
|
||||||
|
}
|
||||||
|
#test{
|
||||||
|
margin-top:2em;
|
||||||
|
margin-bottom:12em;
|
||||||
|
}
|
||||||
|
div.testArea{
|
||||||
|
display:inline-block;
|
||||||
|
width:16em;
|
||||||
|
height:12.5em;
|
||||||
|
position:relative;
|
||||||
|
box-sizing:border-box;
|
||||||
|
}
|
||||||
|
div.testArea2{
|
||||||
|
display:inline-block;
|
||||||
|
width:14em;
|
||||||
|
height:7em;
|
||||||
|
position:relative;
|
||||||
|
box-sizing:border-box;
|
||||||
|
text-align:center;
|
||||||
|
}
|
||||||
|
div.testArea div.testName{
|
||||||
|
position:absolute;
|
||||||
|
top:0.1em; left:0;
|
||||||
|
width:100%;
|
||||||
|
font-size:1.4em;
|
||||||
|
z-index:9;
|
||||||
|
}
|
||||||
|
div.testArea2 div.testName{
|
||||||
|
display:block;
|
||||||
|
text-align:center;
|
||||||
|
font-size:1.4em;
|
||||||
|
}
|
||||||
|
div.testArea div.meterText{
|
||||||
|
position:absolute;
|
||||||
|
bottom:1.55em; left:0;
|
||||||
|
width:100%;
|
||||||
|
font-size:2.5em;
|
||||||
|
z-index:9;
|
||||||
|
}
|
||||||
|
div.testArea2 div.meterText{
|
||||||
|
display:inline-block;
|
||||||
|
font-size:2.5em;
|
||||||
|
}
|
||||||
|
div.meterText:empty:before{
|
||||||
|
content:"0.00";
|
||||||
|
}
|
||||||
|
div.testArea div.unit{
|
||||||
|
position:absolute;
|
||||||
|
bottom:2em; left:0;
|
||||||
|
width:100%;
|
||||||
|
z-index:9;
|
||||||
|
}
|
||||||
|
div.testArea2 div.unit{
|
||||||
|
display:inline-block;
|
||||||
|
}
|
||||||
|
div.testArea canvas{
|
||||||
|
position:absolute;
|
||||||
|
top:0; left:0; width:100%; height:100%;
|
||||||
|
z-index:1;
|
||||||
|
}
|
||||||
|
div.testGroup{
|
||||||
|
display:block;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
#shareArea{
|
||||||
|
width:95%;
|
||||||
|
max-width:40em;
|
||||||
|
margin:0 auto;
|
||||||
|
margin-top:2em;
|
||||||
|
}
|
||||||
|
#shareArea > *{
|
||||||
|
display:block;
|
||||||
|
width:100%;
|
||||||
|
height:auto;
|
||||||
|
margin: 0.25em 0;
|
||||||
|
}
|
||||||
|
#privacyPolicy{
|
||||||
|
position:fixed;
|
||||||
|
top:2em;
|
||||||
|
bottom:2em;
|
||||||
|
left:2em;
|
||||||
|
right:2em;
|
||||||
|
overflow-y:auto;
|
||||||
|
width:auto;
|
||||||
|
height:auto;
|
||||||
|
box-shadow:0 0 3em 1em #000000;
|
||||||
|
z-index:999999;
|
||||||
|
text-align:left;
|
||||||
|
background-color:#FFFFFF;
|
||||||
|
padding:1em;
|
||||||
|
}
|
||||||
|
a.privacy{
|
||||||
|
text-align:center;
|
||||||
|
font-size:0.8em;
|
||||||
|
color:#808080;
|
||||||
|
display:block;
|
||||||
|
}
|
||||||
|
@media all and (max-width:40em){
|
||||||
|
body{
|
||||||
|
font-size:0.8em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<title><?= getenv('TITLE') ?: 'LibreSpeed Example' ?></title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1><?= getenv('TITLE') ?: 'LibreSpeed Example' ?></h1>
|
||||||
|
<div id="testWrapper">
|
||||||
|
<div id="startStopBtn" onclick="startStop()"></div><br/>
|
||||||
|
<?php if(getenv("TELEMETRY")=="true"){ ?>
|
||||||
|
<a class="privacy" href="#" onclick="I('privacyPolicy').style.display=''">Privacy</a>
|
||||||
|
<?php } ?>
|
||||||
|
<div id="test">
|
||||||
|
<div class="testGroup">
|
||||||
|
<div class="testArea2">
|
||||||
|
<div class="testName">Ping</div>
|
||||||
|
<div id="pingText" class="meterText" style="color:#AA6060"></div>
|
||||||
|
<div class="unit">ms</div>
|
||||||
|
</div>
|
||||||
|
<div class="testArea2">
|
||||||
|
<div class="testName">Jitter</div>
|
||||||
|
<div id="jitText" class="meterText" style="color:#AA6060"></div>
|
||||||
|
<div class="unit">ms</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="testGroup">
|
||||||
|
<div class="testArea">
|
||||||
|
<div class="testName">Download</div>
|
||||||
|
<canvas id="dlMeter" class="meter"></canvas>
|
||||||
|
<div id="dlText" class="meterText"></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">Mbit/s</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="ipArea">
|
||||||
|
<span id="ip"></span>
|
||||||
|
</div>
|
||||||
|
<div id="shareArea" style="display:none">
|
||||||
|
<h3>Share results</h3>
|
||||||
|
<p>Test ID: <span id="testId"></span></p>
|
||||||
|
<input type="text" value="" id="resultsURL" readonly="readonly" onclick="this.select();this.focus();this.select();document.execCommand('copy');alert('Link copied')"/>
|
||||||
|
<img src="" id="resultsImg" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a href="https://github.com/librespeed/speedtest">Source code</a>
|
||||||
|
</div>
|
||||||
|
<div id="privacyPolicy" style="display:none">
|
||||||
|
<h2>Privacy Policy</h2>
|
||||||
|
<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:
|
||||||
|
<ul>
|
||||||
|
<li>Test ID</li>
|
||||||
|
<li>Time of testing</li>
|
||||||
|
<li>Test results (download and upload speed, ping and jitter)</li>
|
||||||
|
<li>IP address</li>
|
||||||
|
<li>ISP information</li>
|
||||||
|
<li>Approximate location (inferred from IP address, not GPS)</li>
|
||||||
|
<li>User agent and browser locale</li>
|
||||||
|
<li>Test log (contains no personal information)</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
<h4>How we use the data</h4>
|
||||||
|
<p>
|
||||||
|
Data collected through this service is used to:
|
||||||
|
<ul>
|
||||||
|
<li>Allow sharing of test results (sharable image for forums, etc.)</li>
|
||||||
|
<li>To improve the service offered to you (for instance, to detect problems on our side)</li>
|
||||||
|
</ul>
|
||||||
|
No personal information is disclosed to third parties.
|
||||||
|
</p>
|
||||||
|
<h4>Your consent</h4>
|
||||||
|
<p>
|
||||||
|
By starting the test, you consent to the terms of this privacy policy.
|
||||||
|
</p>
|
||||||
|
<h4>Data removal</h4>
|
||||||
|
<p>
|
||||||
|
If you want to have your information deleted, you need to provide either the ID of the test or your IP address. This is the only way to identify your data, without this information we won't be able to comply with your request.<br/><br/>
|
||||||
|
Contact this email address for all deletion requests: <a href="mailto:<?=getenv("EMAIL") ?>"><?=getenv("EMAIL") ?></a>.
|
||||||
|
</p>
|
||||||
|
<br/><br/>
|
||||||
|
<a class="privacy" href="#" onclick="I('privacyPolicy').style.display='none'">Close</a><br/>
|
||||||
|
</div>
|
||||||
|
<script type="text/javascript">setTimeout(function(){initUI()},100);</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
490
examples/example-multipleServers-full.html
Executable file
|
@ -0,0 +1,490 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<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" />
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<script type="text/javascript" src="speedtest.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
function I(i){return document.getElementById(i);}
|
||||||
|
|
||||||
|
//LIST OF TEST SERVERS. See documentation for details if needed
|
||||||
|
var SPEEDTEST_SERVERS=[
|
||||||
|
{ //this server doesn't actually exist, remove it
|
||||||
|
name:"Example Server 1", //user friendly name for the server
|
||||||
|
server:"//test1.mydomain.com/", //URL to the server. // at the beginning will be replaced with http:// or https:// automatically
|
||||||
|
dlURL:"backend/garbage.php", //path to download test on this server (garbage.php or replacement)
|
||||||
|
ulURL:"backend/empty.php", //path to upload test on this server (empty.php or replacement)
|
||||||
|
pingURL:"backend/empty.php", //path to ping/jitter test on this server (empty.php or replacement)
|
||||||
|
getIpURL:"backend/getIP.php" //path to getIP on this server (getIP.php or replacement)
|
||||||
|
},
|
||||||
|
{ //this server doesn't actually exist, remove it
|
||||||
|
name:"Example Server 2", //user friendly name for the server
|
||||||
|
server:"//test2.example.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)
|
||||||
|
pingURL:"empty.php", //path to ping/jitter test on this server (empty.php or replacement)
|
||||||
|
getIpURL:"getIP.php" //path to getIP on this server (getIP.php or replacement)
|
||||||
|
}
|
||||||
|
//add other servers here, comma separated
|
||||||
|
];
|
||||||
|
|
||||||
|
//INITIALIZE SPEEDTEST
|
||||||
|
var s=new Speedtest(); //create speed test object
|
||||||
|
s.setParameter("telemetry_level","basic"); //enable telemetry
|
||||||
|
|
||||||
|
//SERVER AUTO SELECTION
|
||||||
|
function initServers(){
|
||||||
|
var noServersAvailable=function(){
|
||||||
|
I("message").innerHTML="No servers available";
|
||||||
|
}
|
||||||
|
var runServerSelect=function(){
|
||||||
|
s.selectServer(function(server){
|
||||||
|
if(server!=null){ //at least 1 server is available
|
||||||
|
I("loading").className="hidden"; //hide loading message
|
||||||
|
//populate server list for manual selection
|
||||||
|
for(var i=0;i<SPEEDTEST_SERVERS.length;i++){
|
||||||
|
if(SPEEDTEST_SERVERS[i].pingT==-1) continue;
|
||||||
|
var option=document.createElement("option");
|
||||||
|
option.value=i;
|
||||||
|
option.textContent=SPEEDTEST_SERVERS[i].name;
|
||||||
|
if(SPEEDTEST_SERVERS[i]===server) option.selected=true;
|
||||||
|
I("server").appendChild(option);
|
||||||
|
}
|
||||||
|
//show test UI
|
||||||
|
I("testWrapper").className="visible";
|
||||||
|
initUI();
|
||||||
|
}else{ //no servers are available, the test cannot proceed
|
||||||
|
noServersAvailable();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if(typeof SPEEDTEST_SERVERS === "string"){
|
||||||
|
//need to fetch list of servers from specified URL
|
||||||
|
s.loadServerList(SPEEDTEST_SERVERS,function(servers){
|
||||||
|
if(servers==null){ //failed to load server list
|
||||||
|
noServersAvailable();
|
||||||
|
}else{ //server list loaded
|
||||||
|
SPEEDTEST_SERVERS=servers;
|
||||||
|
runServerSelect();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
//hardcoded server list
|
||||||
|
s.addTestPoints(SPEEDTEST_SERVERS);
|
||||||
|
runServerSelect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var meterBk=/Trident.*rv:(\d+\.\d+)/i.test(navigator.userAgent)?"#EAEAEA":"#80808040";
|
||||||
|
var dlColor="#6060AA",
|
||||||
|
ulColor="#616161";
|
||||||
|
var progColor=meterBk;
|
||||||
|
|
||||||
|
//CODE FOR GAUGES
|
||||||
|
function drawMeter(c,amount,bk,fg,progress,prog){
|
||||||
|
var ctx=c.getContext("2d");
|
||||||
|
var dp=window.devicePixelRatio||1;
|
||||||
|
var cw=c.clientWidth*dp, ch=c.clientHeight*dp;
|
||||||
|
var sizScale=ch*0.0055;
|
||||||
|
if(c.width==cw&&c.height==ch){
|
||||||
|
ctx.clearRect(0,0,cw,ch);
|
||||||
|
}else{
|
||||||
|
c.width=cw;
|
||||||
|
c.height=ch;
|
||||||
|
}
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.strokeStyle=bk;
|
||||||
|
ctx.lineWidth=12*sizScale;
|
||||||
|
ctx.arc(c.width/2,c.height-58*sizScale,c.height/1.8-ctx.lineWidth,-Math.PI*1.1,Math.PI*0.1);
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.strokeStyle=fg;
|
||||||
|
ctx.lineWidth=12*sizScale;
|
||||||
|
ctx.arc(c.width/2,c.height-58*sizScale,c.height/1.8-ctx.lineWidth,-Math.PI*1.1,amount*Math.PI*1.2-Math.PI*1.1);
|
||||||
|
ctx.stroke();
|
||||||
|
if(typeof progress !== "undefined"){
|
||||||
|
ctx.fillStyle=prog;
|
||||||
|
ctx.fillRect(c.width*0.3,c.height-16*sizScale,c.width*0.4*progress,4*sizScale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function mbpsToAmount(s){
|
||||||
|
return 1-(1/(Math.pow(1.3,Math.sqrt(s))));
|
||||||
|
}
|
||||||
|
function format(d){
|
||||||
|
d=Number(d);
|
||||||
|
if(d<10) return d.toFixed(2);
|
||||||
|
if(d<100) return d.toFixed(1);
|
||||||
|
return d.toFixed(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//UI CODE
|
||||||
|
var uiData=null;
|
||||||
|
function startStop(){
|
||||||
|
if(s.getState()==3){
|
||||||
|
//speed test is running, abort
|
||||||
|
s.abort();
|
||||||
|
data=null;
|
||||||
|
I("startStopBtn").className="";
|
||||||
|
I("server").disabled=false;
|
||||||
|
initUI();
|
||||||
|
}else{
|
||||||
|
//test is not running, begin
|
||||||
|
I("startStopBtn").className="running";
|
||||||
|
I("shareArea").style.display="none";
|
||||||
|
I("server").disabled=true;
|
||||||
|
s.onupdate=function(data){
|
||||||
|
uiData=data;
|
||||||
|
};
|
||||||
|
s.onend=function(aborted){
|
||||||
|
I("startStopBtn").className="";
|
||||||
|
I("server").disabled=false;
|
||||||
|
updateUI(true);
|
||||||
|
if(!aborted){
|
||||||
|
//if testId is present, show sharing panel, otherwise do nothing
|
||||||
|
try{
|
||||||
|
var testId=uiData.testId;
|
||||||
|
if(testId!=null){
|
||||||
|
var shareURL=window.location.href.substring(0,window.location.href.lastIndexOf("/"))+"/results/?id="+testId;
|
||||||
|
I("resultsImg").src=shareURL;
|
||||||
|
I("resultsURL").value=shareURL;
|
||||||
|
I("testId").innerHTML=testId;
|
||||||
|
I("shareArea").style.display="";
|
||||||
|
}
|
||||||
|
}catch(e){}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
s.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//this function reads the data sent back by the test and updates the UI
|
||||||
|
function updateUI(forced){
|
||||||
|
if(!forced&&s.getState()!=3) return;
|
||||||
|
if(uiData==null) return;
|
||||||
|
var status=uiData.testState;
|
||||||
|
I("ip").textContent=uiData.clientIp;
|
||||||
|
I("dlText").textContent=(status==1&&uiData.dlStatus==0)?"...":format(uiData.dlStatus);
|
||||||
|
drawMeter(I("dlMeter"),mbpsToAmount(Number(uiData.dlStatus*(status==1?oscillate():1))),meterBk,dlColor,Number(uiData.dlProgress),progColor);
|
||||||
|
I("ulText").textContent=(status==3&&uiData.ulStatus==0)?"...":format(uiData.ulStatus);
|
||||||
|
drawMeter(I("ulMeter"),mbpsToAmount(Number(uiData.ulStatus*(status==3?oscillate():1))),meterBk,ulColor,Number(uiData.ulProgress),progColor);
|
||||||
|
I("pingText").textContent=format(uiData.pingStatus);
|
||||||
|
I("jitText").textContent=format(uiData.jitterStatus);
|
||||||
|
}
|
||||||
|
function oscillate(){
|
||||||
|
return 1+0.02*Math.sin(Date.now()/100);
|
||||||
|
}
|
||||||
|
//update the UI every frame
|
||||||
|
window.requestAnimationFrame=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.msRequestAnimationFrame||(function(callback,element){setTimeout(callback,1000/60);});
|
||||||
|
function frame(){
|
||||||
|
requestAnimationFrame(frame);
|
||||||
|
updateUI();
|
||||||
|
}
|
||||||
|
frame(); //start frame loop
|
||||||
|
//function to (re)initialize UI
|
||||||
|
function initUI(){
|
||||||
|
drawMeter(I("dlMeter"),0,meterBk,dlColor,0);
|
||||||
|
drawMeter(I("ulMeter"),0,meterBk,ulColor,0);
|
||||||
|
I("dlText").textContent="";
|
||||||
|
I("ulText").textContent="";
|
||||||
|
I("pingText").textContent="";
|
||||||
|
I("jitText").textContent="";
|
||||||
|
I("ip").textContent="";
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style type="text/css">
|
||||||
|
html,body{
|
||||||
|
border:none; padding:0; margin:0;
|
||||||
|
background:#FFFFFF;
|
||||||
|
color:#202020;
|
||||||
|
}
|
||||||
|
body{
|
||||||
|
text-align:center;
|
||||||
|
font-family:"Roboto",sans-serif;
|
||||||
|
}
|
||||||
|
h1{
|
||||||
|
color:#404040;
|
||||||
|
}
|
||||||
|
#loading{
|
||||||
|
background-color:#FFFFFF;
|
||||||
|
color:#404040;
|
||||||
|
text-align:center;
|
||||||
|
}
|
||||||
|
span.loadCircle{
|
||||||
|
display:inline-block;
|
||||||
|
width:2em;
|
||||||
|
height:2em;
|
||||||
|
vertical-align:middle;
|
||||||
|
background:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAAAP1BMVEUAAAB2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZyFzwnAAAAFHRSTlMAEvRFvX406baecwbf0casimhSHyiwmqgAAADpSURBVHja7dbJbQMxAENRahnN5lkc//5rDRAkDeRgHszXgACJoKiIiIiIiIiIiIiIiIiIiIj4HHspsrpAVhdVVguzrA4OWc10WcEqpwKbnBo0OU1Q5NSpsoJFTgOecrrdEag85DRgktNqfoEdTjnd7hrEHMEJvmRUYJbTYk5Agy6nau6Abp5Cm7mDBtRdPi9gyKdU7w4p1fsLvyqs8hl4z9/w3n/Hmr9WoQ65lAU4d7lMYOz//QboRR5jBZibLMZdAR6O/Vfa1PlxNr3XdS3HzK/HVPRu/KnLs8iAOh993VpRRERERMT/fAN60wwWaVyWwAAAAABJRU5ErkJggg==');
|
||||||
|
background-size:2em 2em;
|
||||||
|
margin-right:0.5em;
|
||||||
|
animation: spin 0.6s linear infinite;
|
||||||
|
}
|
||||||
|
@keyframes spin{
|
||||||
|
0%{transform:rotate(0deg);}
|
||||||
|
100%{transform:rotate(359deg);}
|
||||||
|
}
|
||||||
|
#startStopBtn{
|
||||||
|
display:inline-block;
|
||||||
|
margin:0 auto;
|
||||||
|
color:#6060AA;
|
||||||
|
background-color:rgba(0,0,0,0);
|
||||||
|
border:0.15em solid #6060FF;
|
||||||
|
border-radius:0.3em;
|
||||||
|
transition:all 0.3s;
|
||||||
|
box-sizing:border-box;
|
||||||
|
width:8em; height:3em;
|
||||||
|
line-height:2.7em;
|
||||||
|
cursor:pointer;
|
||||||
|
box-shadow: 0 0 0 rgba(0,0,0,0.1), inset 0 0 0 rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
#startStopBtn:hover{
|
||||||
|
box-shadow: 0 0 2em rgba(0,0,0,0.1), inset 0 0 1em rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
#startStopBtn.running{
|
||||||
|
background-color:#FF3030;
|
||||||
|
border-color:#FF6060;
|
||||||
|
color:#FFFFFF;
|
||||||
|
}
|
||||||
|
#startStopBtn:before{
|
||||||
|
content:"Start";
|
||||||
|
}
|
||||||
|
#startStopBtn.running:before{
|
||||||
|
content:"Abort";
|
||||||
|
}
|
||||||
|
#serverArea{
|
||||||
|
margin-top:1em;
|
||||||
|
}
|
||||||
|
#server{
|
||||||
|
font-size:1em;
|
||||||
|
padding:0.2em;
|
||||||
|
}
|
||||||
|
#test{
|
||||||
|
margin-top:2em;
|
||||||
|
margin-bottom:12em;
|
||||||
|
}
|
||||||
|
div.testArea{
|
||||||
|
display:inline-block;
|
||||||
|
width:16em;
|
||||||
|
height:12.5em;
|
||||||
|
position:relative;
|
||||||
|
box-sizing:border-box;
|
||||||
|
}
|
||||||
|
div.testArea2{
|
||||||
|
display:inline-block;
|
||||||
|
width:14em;
|
||||||
|
height:7em;
|
||||||
|
position:relative;
|
||||||
|
box-sizing:border-box;
|
||||||
|
text-align:center;
|
||||||
|
}
|
||||||
|
div.testArea div.testName{
|
||||||
|
position:absolute;
|
||||||
|
top:0.1em; left:0;
|
||||||
|
width:100%;
|
||||||
|
font-size:1.4em;
|
||||||
|
z-index:9;
|
||||||
|
}
|
||||||
|
div.testArea2 div.testName{
|
||||||
|
display:block;
|
||||||
|
text-align:center;
|
||||||
|
font-size:1.4em;
|
||||||
|
}
|
||||||
|
div.testArea div.meterText{
|
||||||
|
position:absolute;
|
||||||
|
bottom:1.55em; left:0;
|
||||||
|
width:100%;
|
||||||
|
font-size:2.5em;
|
||||||
|
z-index:9;
|
||||||
|
}
|
||||||
|
div.testArea2 div.meterText{
|
||||||
|
display:inline-block;
|
||||||
|
font-size:2.5em;
|
||||||
|
}
|
||||||
|
div.meterText:empty:before{
|
||||||
|
content:"0.00";
|
||||||
|
}
|
||||||
|
div.testArea div.unit{
|
||||||
|
position:absolute;
|
||||||
|
bottom:2em; left:0;
|
||||||
|
width:100%;
|
||||||
|
z-index:9;
|
||||||
|
}
|
||||||
|
div.testArea2 div.unit{
|
||||||
|
display:inline-block;
|
||||||
|
}
|
||||||
|
div.testArea canvas{
|
||||||
|
position:absolute;
|
||||||
|
top:0; left:0; width:100%; height:100%;
|
||||||
|
z-index:1;
|
||||||
|
}
|
||||||
|
div.testGroup{
|
||||||
|
display:block;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
#shareArea{
|
||||||
|
width:95%;
|
||||||
|
max-width:40em;
|
||||||
|
margin:0 auto;
|
||||||
|
margin-top:2em;
|
||||||
|
}
|
||||||
|
#shareArea > *{
|
||||||
|
display:block;
|
||||||
|
width:100%;
|
||||||
|
height:auto;
|
||||||
|
margin: 0.25em 0;
|
||||||
|
}
|
||||||
|
#privacyPolicy{
|
||||||
|
position:fixed;
|
||||||
|
top:2em;
|
||||||
|
bottom:2em;
|
||||||
|
left:2em;
|
||||||
|
right:2em;
|
||||||
|
overflow-y:auto;
|
||||||
|
width:auto;
|
||||||
|
height:auto;
|
||||||
|
box-shadow:0 0 3em 1em #000000;
|
||||||
|
z-index:999999;
|
||||||
|
text-align:left;
|
||||||
|
background-color:#FFFFFF;
|
||||||
|
padding:1em;
|
||||||
|
}
|
||||||
|
a.privacy{
|
||||||
|
text-align:center;
|
||||||
|
font-size:0.8em;
|
||||||
|
color:#808080;
|
||||||
|
padding: 0 3em;
|
||||||
|
}
|
||||||
|
div.closePrivacyPolicy {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
div.closePrivacyPolicy a.privacy {
|
||||||
|
padding: 1em 3em;
|
||||||
|
}
|
||||||
|
@media all and (max-width:40em){
|
||||||
|
body{
|
||||||
|
font-size:0.8em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
div.visible{
|
||||||
|
animation: fadeIn 0.4s;
|
||||||
|
display:block;
|
||||||
|
}
|
||||||
|
div.hidden{
|
||||||
|
animation: fadeOut 0.4s;
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
@keyframes fadeIn{
|
||||||
|
0%{
|
||||||
|
opacity:0;
|
||||||
|
}
|
||||||
|
100%{
|
||||||
|
opacity:1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes fadeOut{
|
||||||
|
0%{
|
||||||
|
display:block;
|
||||||
|
opacity:1;
|
||||||
|
}
|
||||||
|
100%{
|
||||||
|
display:block;
|
||||||
|
opacity:0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<title>LibreSpeed Example</title>
|
||||||
|
</head>
|
||||||
|
<body onload="initServers()">
|
||||||
|
<h1>LibreSpeed Example</h1>
|
||||||
|
<div id="loading" class="visible">
|
||||||
|
<p id="message"><span class="loadCircle"></span>Selecting a server...</p>
|
||||||
|
</div>
|
||||||
|
<div id="testWrapper" class="hidden">
|
||||||
|
<div id="startStopBtn" onclick="startStop()"></div><br/>
|
||||||
|
<a class="privacy" href="#" onclick="I('privacyPolicy').style.display=''">Privacy</a>
|
||||||
|
<div id="serverArea">
|
||||||
|
Server: <select id="server" onchange="s.setSelectedServer(SPEEDTEST_SERVERS[this.value])"></select>
|
||||||
|
</div>
|
||||||
|
<div id="test">
|
||||||
|
<div class="testGroup">
|
||||||
|
<div class="testArea2">
|
||||||
|
<div class="testName">Ping</div>
|
||||||
|
<div id="pingText" class="meterText" style="color:#AA6060"></div>
|
||||||
|
<div class="unit">ms</div>
|
||||||
|
</div>
|
||||||
|
<div class="testArea2">
|
||||||
|
<div class="testName">Jitter</div>
|
||||||
|
<div id="jitText" class="meterText" style="color:#AA6060"></div>
|
||||||
|
<div class="unit">ms</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="testGroup">
|
||||||
|
<div class="testArea">
|
||||||
|
<div class="testName">Download</div>
|
||||||
|
<canvas id="dlMeter" class="meter"></canvas>
|
||||||
|
<div id="dlText" class="meterText"></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">Mbit/s</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="ipArea">
|
||||||
|
<span id="ip"></span>
|
||||||
|
</div>
|
||||||
|
<div id="shareArea" style="display:none">
|
||||||
|
<h3>Share results</h3>
|
||||||
|
<p>Test ID: <span id="testId"></span></p>
|
||||||
|
<input type="text" value="" id="resultsURL" readonly="readonly" onclick="this.select();this.focus();this.select();document.execCommand('copy');alert('Link copied')"/>
|
||||||
|
<img src="" id="resultsImg" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a href="https://github.com/librespeed/speedtest">Source code</a>
|
||||||
|
</div>
|
||||||
|
<div id="privacyPolicy" style="display:none">
|
||||||
|
<h2>Privacy Policy</h2>
|
||||||
|
<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:
|
||||||
|
<ul>
|
||||||
|
<li>Test ID</li>
|
||||||
|
<li>Time of testing</li>
|
||||||
|
<li>Test results (download and upload speed, ping and jitter)</li>
|
||||||
|
<li>IP address</li>
|
||||||
|
<li>ISP information</li>
|
||||||
|
<li>Approximate location (inferred from IP address, not GPS)</li>
|
||||||
|
<li>User agent and browser locale</li>
|
||||||
|
<li>Test log (contains no personal information)</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
<h4>How we use the data</h4>
|
||||||
|
<p>
|
||||||
|
Data collected through this service is used to:
|
||||||
|
<ul>
|
||||||
|
<li>Allow sharing of test results (sharable image for forums, etc.)</li>
|
||||||
|
<li>To improve the service offered to you (for instance, to detect problems on our side)</li>
|
||||||
|
</ul>
|
||||||
|
No personal information is disclosed to third parties.
|
||||||
|
</p>
|
||||||
|
<h4>Your consent</h4>
|
||||||
|
<p>
|
||||||
|
By starting the test, you consent to the terms of this privacy policy.
|
||||||
|
</p>
|
||||||
|
<h4>Data removal</h4>
|
||||||
|
<p>
|
||||||
|
If you want to have your information deleted, you need to provide either the ID of the test or your IP address. This is the only way to identify your data, without this information we won't be able to comply with your request.<br/><br/>
|
||||||
|
Contact this email address for all deletion requests: <a href="mailto:PUT@YOUR_EMAIL.HERE">TO BE FILLED BY DEVELOPER</a>.
|
||||||
|
</p>
|
||||||
|
<br/><br/>
|
||||||
|
<div class="closePrivacyPolicy">
|
||||||
|
<a class="privacy" href="#" onclick="I('privacyPolicy').style.display='none'">Close</a>
|
||||||
|
</div>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
30
example-multipleServers-pretty.html → examples/example-multipleServers-pretty.html
Normal file → Executable file
|
@ -10,21 +10,21 @@
|
||||||
|
|
||||||
//LIST OF TEST SERVERS. See documentation for details if needed
|
//LIST OF TEST SERVERS. See documentation for details if needed
|
||||||
var SPEEDTEST_SERVERS=[
|
var SPEEDTEST_SERVERS=[
|
||||||
{ //this is my demo server, remove it
|
{ //this server doesn't actually exist, remove it
|
||||||
name:"Speedtest Demo Server (Helsinki)", //user friendly name for the server
|
name:"Example Server 1", //user friendly name for the server
|
||||||
server:"//fi.openspeed.org/", //URL to the server. // at the beginning will be replaced with http:// or https:// automatically
|
server:"//test1.mydomain.com/", //URL to the server. // at the beginning will be replaced with http:// or https:// automatically
|
||||||
|
dlURL:"backend/garbage.php", //path to download test on this server (garbage.php or replacement)
|
||||||
|
ulURL:"backend/empty.php", //path to upload test on this server (empty.php or replacement)
|
||||||
|
pingURL:"backend/empty.php", //path to ping/jitter test on this server (empty.php or replacement)
|
||||||
|
getIpURL:"backend/getIP.php" //path to getIP on this server (getIP.php or replacement)
|
||||||
|
},
|
||||||
|
{ //this server doesn't actually exist, remove it
|
||||||
|
name:"Example Server 2", //user friendly name for the server
|
||||||
|
server:"//test2.example.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)
|
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)
|
ulURL:"empty.php", //path to upload test on this server (empty.php or replacement)
|
||||||
pingURL:"empty.php", //path to ping/jitter test on this server (empty.php or replacement)
|
pingURL:"empty.php", //path to ping/jitter test on this server (empty.php or replacement)
|
||||||
getIpURL:"getIP.php" //path to getIP on this server (getIP.php or replacement)
|
getIpURL:"getIP.php" //path to getIP on this server (getIP.php or replacement)
|
||||||
},
|
|
||||||
{ //this is my demo server, remove it
|
|
||||||
name:"Old Speedtest Demo Server",
|
|
||||||
server:"//mpotdemo.fdossena.com/",
|
|
||||||
dlURL:"garbage.php",
|
|
||||||
ulURL:"empty.php",
|
|
||||||
pingURL:"empty.php",
|
|
||||||
getIpURL:"getIP.php"
|
|
||||||
}
|
}
|
||||||
//add other servers here, comma separated
|
//add other servers here, comma separated
|
||||||
];
|
];
|
||||||
|
@ -48,8 +48,12 @@ s.onend=function(aborted){ //callback for test ended/aborted
|
||||||
}
|
}
|
||||||
function selectServer(){ //called after loading server list
|
function selectServer(){ //called after loading server list
|
||||||
s.selectServer(function(server){ //run server selection. When the server has been selected, display it in the UI
|
s.selectServer(function(server){ //run server selection. When the server has been selected, display it in the UI
|
||||||
|
if(server==null){
|
||||||
|
I("serverId").textContent="No servers available";
|
||||||
|
}else{
|
||||||
I("startStopBtn").style.display=""; //show start/stop button again
|
I("startStopBtn").style.display=""; //show start/stop button again
|
||||||
I("serverId").textContent=server.name; //show name of test server
|
I("serverId").textContent=server.name; //show name of test server
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function loadServers(){ //called when the page is fully loaded
|
function loadServers(){ //called when the page is fully loaded
|
||||||
|
@ -208,12 +212,12 @@ function I(id){return document.getElementById(id);}
|
||||||
<div class="testArea">
|
<div class="testArea">
|
||||||
<div class="testName">Download</div>
|
<div class="testName">Download</div>
|
||||||
<div id="dlText" class="meterText"></div>
|
<div id="dlText" class="meterText"></div>
|
||||||
<div class="unit">Mbps</div>
|
<div class="unit">Mbit/s</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="testArea">
|
<div class="testArea">
|
||||||
<div class="testName">Upload</div>
|
<div class="testName">Upload</div>
|
||||||
<div id="ulText" class="meterText"></div>
|
<div id="ulText" class="meterText"></div>
|
||||||
<div class="unit">Mbps</div>
|
<div class="unit">Mbit/s</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="testGroup">
|
<div class="testGroup">
|
0
example-singleServer-basic.html → examples/example-singleServer-basic.html
Normal file → Executable file
0
example-singleServer-chart.html → examples/example-singleServer-chart.html
Normal file → Executable file
4
example-singleServer-customSettings.html → examples/example-singleServer-customSettings.html
Normal file → Executable file
|
@ -157,12 +157,12 @@ function I(id){return document.getElementById(id);}
|
||||||
<div class="testArea">
|
<div class="testArea">
|
||||||
<div class="testName">Download</div>
|
<div class="testName">Download</div>
|
||||||
<div id="dlText" class="meterText"></div>
|
<div id="dlText" class="meterText"></div>
|
||||||
<div class="unit">Mbps</div>
|
<div class="unit">Mbit/s</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="testArea">
|
<div class="testArea">
|
||||||
<div class="testName">Upload</div>
|
<div class="testName">Upload</div>
|
||||||
<div id="ulText" class="meterText"></div>
|
<div id="ulText" class="meterText"></div>
|
||||||
<div class="unit">Mbps</div>
|
<div class="unit">Mbit/s</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
4
example-singleServer-gauges.html → examples/example-singleServer-gauges.html
Normal file → Executable file
|
@ -242,13 +242,13 @@ function initUI(){
|
||||||
<div class="testName">Download</div>
|
<div class="testName">Download</div>
|
||||||
<canvas id="dlMeter" class="meter"></canvas>
|
<canvas id="dlMeter" class="meter"></canvas>
|
||||||
<div id="dlText" class="meterText"></div>
|
<div id="dlText" class="meterText"></div>
|
||||||
<div class="unit">Mbps</div>
|
<div class="unit">Mbit/s</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="testArea">
|
<div class="testArea">
|
||||||
<div class="testName">Upload</div>
|
<div class="testName">Upload</div>
|
||||||
<canvas id="ulMeter" class="meter"></canvas>
|
<canvas id="ulMeter" class="meter"></canvas>
|
||||||
<div id="ulText" class="meterText"></div>
|
<div id="ulText" class="meterText"></div>
|
||||||
<div class="unit">Mbps</div>
|
<div class="unit">Mbit/s</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="ipArea">
|
<div id="ipArea">
|
4
example-singleServer-pretty.html → examples/example-singleServer-pretty.html
Normal file → Executable file
|
@ -160,12 +160,12 @@ function I(id){return document.getElementById(id);}
|
||||||
<div class="testArea">
|
<div class="testArea">
|
||||||
<div class="testName">Download</div>
|
<div class="testName">Download</div>
|
||||||
<div id="dlText" class="meterText"></div>
|
<div id="dlText" class="meterText"></div>
|
||||||
<div class="unit">Mbps</div>
|
<div class="unit">Mbit/s</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="testArea">
|
<div class="testArea">
|
||||||
<div class="testName">Upload</div>
|
<div class="testName">Upload</div>
|
||||||
<div id="ulText" class="meterText"></div>
|
<div id="ulText" class="meterText"></div>
|
||||||
<div class="unit">Mbps</div>
|
<div class="unit">Mbit/s</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="testGroup">
|
<div class="testGroup">
|
4
example-singleServer-progressBar.html → examples/example-singleServer-progressBar.html
Normal file → Executable file
|
@ -180,12 +180,12 @@ function I(id){return document.getElementById(id);}
|
||||||
<div class="testArea">
|
<div class="testArea">
|
||||||
<div class="testName">Download</div>
|
<div class="testName">Download</div>
|
||||||
<div id="dlText" class="meterText"></div>
|
<div id="dlText" class="meterText"></div>
|
||||||
<div class="unit">Mbps</div>
|
<div class="unit">Mbit/s</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="testArea">
|
<div class="testArea">
|
||||||
<div class="testName">Upload</div>
|
<div class="testName">Upload</div>
|
||||||
<div id="ulText" class="meterText"></div>
|
<div id="ulText" class="meterText"></div>
|
||||||
<div class="unit">Mbps</div>
|
<div class="unit">Mbit/s</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="testGroup">
|
<div class="testGroup">
|
0
favicon.ico
Normal file → Executable file
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
20
example-singleServer-full.html → index.html
Normal file → Executable file
|
@ -258,7 +258,14 @@ function initUI(){
|
||||||
text-align:center;
|
text-align:center;
|
||||||
font-size:0.8em;
|
font-size:0.8em;
|
||||||
color:#808080;
|
color:#808080;
|
||||||
display:block;
|
padding: 0 3em;
|
||||||
|
}
|
||||||
|
div.closePrivacyPolicy {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
div.closePrivacyPolicy a.privacy {
|
||||||
|
padding: 1em 3em;
|
||||||
}
|
}
|
||||||
@media all and (max-width:40em){
|
@media all and (max-width:40em){
|
||||||
body{
|
body{
|
||||||
|
@ -291,13 +298,13 @@ function initUI(){
|
||||||
<div class="testName">Download</div>
|
<div class="testName">Download</div>
|
||||||
<canvas id="dlMeter" class="meter"></canvas>
|
<canvas id="dlMeter" class="meter"></canvas>
|
||||||
<div id="dlText" class="meterText"></div>
|
<div id="dlText" class="meterText"></div>
|
||||||
<div class="unit">Mbps</div>
|
<div class="unit">Mbit/s</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="testArea">
|
<div class="testArea">
|
||||||
<div class="testName">Upload</div>
|
<div class="testName">Upload</div>
|
||||||
<canvas id="ulMeter" class="meter"></canvas>
|
<canvas id="ulMeter" class="meter"></canvas>
|
||||||
<div id="ulText" class="meterText"></div>
|
<div id="ulText" class="meterText"></div>
|
||||||
<div class="unit">Mbps</div>
|
<div class="unit">Mbit/s</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="ipArea">
|
<div id="ipArea">
|
||||||
|
@ -314,7 +321,7 @@ function initUI(){
|
||||||
</div>
|
</div>
|
||||||
<div id="privacyPolicy" style="display:none">
|
<div id="privacyPolicy" style="display:none">
|
||||||
<h2>Privacy Policy</h2>
|
<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>
|
<h4>What data we collect</h4>
|
||||||
<p>
|
<p>
|
||||||
At the end of the test, the following data is collected and stored:
|
At the end of the test, the following data is collected and stored:
|
||||||
|
@ -348,7 +355,10 @@ function initUI(){
|
||||||
Contact this email address for all deletion requests: <a href="mailto:PUT@YOUR_EMAIL.HERE">TO BE FILLED BY DEVELOPER</a>.
|
Contact this email address for all deletion requests: <a href="mailto:PUT@YOUR_EMAIL.HERE">TO BE FILLED BY DEVELOPER</a>.
|
||||||
</p>
|
</p>
|
||||||
<br/><br/>
|
<br/><br/>
|
||||||
<a class="privacy" href="#" onclick="I('privacyPolicy').style.display='none'">Close</a><br/>
|
<div class="closePrivacyPolicy">
|
||||||
|
<a class="privacy" href="#" onclick="I('privacyPolicy').style.display='none'">Close</a>
|
||||||
|
</div>
|
||||||
|
<br/>
|
||||||
</div>
|
</div>
|
||||||
<script type="text/javascript">setTimeout(function(){initUI()},100);</script>
|
<script type="text/javascript">setTimeout(function(){initUI()},100);</script>
|
||||||
</body>
|
</body>
|
0
results/OpenSans-Light.ttf
Normal file → Executable file
0
results/OpenSans-Semibold.ttf
Normal file → Executable file
107
results/idObfuscation.php
Normal file → Executable file
|
@ -1,43 +1,72 @@
|
||||||
<?php
|
<?php
|
||||||
function getObfuscationSalt(){
|
|
||||||
$saltFile=dirname(__FILE__)."/idObfuscation_salt.php";
|
define('ID_OBFUSCATION_SALT_FILE', __DIR__.'/idObfuscation_salt.php');
|
||||||
if(file_exists($saltFile)){
|
|
||||||
require $saltFile;
|
/**
|
||||||
}else{
|
* @return string|int
|
||||||
$bytes=openssl_random_pseudo_bytes(4);
|
|
||||||
$sf=fopen($saltFile,"w");
|
|
||||||
fwrite($sf,chr(60)."?php\n");
|
|
||||||
fwrite($sf,'$OBFUSCATION_SALT=0x'.bin2hex($bytes).";\n");
|
|
||||||
fwrite($sf,"?".chr(62));
|
|
||||||
fclose($sf);
|
|
||||||
require $saltFile;
|
|
||||||
}
|
|
||||||
return isset($OBFUSCATION_SALT)?$OBFUSCATION_SALT:0;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
This is a simple reversible hash function I made for encoding and decoding test IDs.
|
|
||||||
It is not cryptographically secure, don't use it to hash passwords or something!
|
|
||||||
*/
|
*/
|
||||||
function obfdeobf($id,$dec){
|
function getObfuscationSalt()
|
||||||
$salt=getObfuscationSalt()&0xFFFFFFFF;
|
{
|
||||||
$id=$id&0xFFFFFFFF;
|
if (!file_exists(ID_OBFUSCATION_SALT_FILE)) {
|
||||||
if($dec){
|
$bytes = openssl_random_pseudo_bytes(4);
|
||||||
$id=$id^$salt;
|
|
||||||
$id=(($id&0xAAAAAAAA)>>1)|($id&0x55555555)<<1;
|
$saltData = "<?php\n\n\$OBFUSCATION_SALT = 0x".bin2hex($bytes).";\n";
|
||||||
$id=(($id&0x0000FFFF)<<16)|(($id&0xFFFF0000)>>16);
|
file_put_contents(ID_OBFUSCATION_SALT_FILE, $saltData);
|
||||||
return $id;
|
|
||||||
}else{
|
|
||||||
$id=(($id&0x0000FFFF)<<16)|(($id&0xFFFF0000)>>16);
|
|
||||||
$id=(($id&0xAAAAAAAA)>>1)|($id&0x55555555)<<1;
|
|
||||||
return $id^$salt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function obfuscateId($id){
|
|
||||||
return str_pad(base_convert(obfdeobf($id+1,false),10,36),7,0,STR_PAD_LEFT);
|
|
||||||
}
|
|
||||||
function deobfuscateId($id){
|
|
||||||
return obfdeobf(base_convert($id,36,10),true)-1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//IMPORTANT: DO NOT ADD ANYTHING BELOW THE PHP CLOSING TAG, NOT EVEN EMPTY LINES!
|
if (
|
||||||
?>
|
file_exists(ID_OBFUSCATION_SALT_FILE)
|
||||||
|
&& is_readable(ID_OBFUSCATION_SALT_FILE)
|
||||||
|
) {
|
||||||
|
require ID_OBFUSCATION_SALT_FILE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isset($OBFUSCATION_SALT) ? $OBFUSCATION_SALT : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a simple reversible hash function I made for encoding and decoding test IDs.
|
||||||
|
* It is not cryptographically secure, don't use it to hash passwords or something!
|
||||||
|
*
|
||||||
|
* @param int|string $id
|
||||||
|
* @param bool $dec
|
||||||
|
*
|
||||||
|
* @return int|string
|
||||||
|
*/
|
||||||
|
function obfdeobf($id, $dec)
|
||||||
|
{
|
||||||
|
$salt = getObfuscationSalt() & 0xFFFFFFFF;
|
||||||
|
$id &= 0xFFFFFFFF;
|
||||||
|
if ($dec) {
|
||||||
|
$id ^= $salt;
|
||||||
|
$id = (($id & 0xAAAAAAAA) >> 1) | ($id & 0x55555555) << 1;
|
||||||
|
$id = (($id & 0x0000FFFF) << 16) | (($id & 0xFFFF0000) >> 16);
|
||||||
|
|
||||||
|
return $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = (($id & 0x0000FFFF) << 16) | (($id & 0xFFFF0000) >> 16);
|
||||||
|
$id = (($id & 0xAAAAAAAA) >> 1) | ($id & 0x55555555) << 1;
|
||||||
|
|
||||||
|
return $id ^ $salt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $id
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function obfuscateId($id)
|
||||||
|
{
|
||||||
|
return str_pad(base_convert(obfdeobf($id + 1, false), 10, 36), 7, 0, STR_PAD_LEFT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $id
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
function deobfuscateId($id)
|
||||||
|
{
|
||||||
|
return obfdeobf(base_convert($id, 36, 10), true) - 1;
|
||||||
|
}
|
||||||
|
|
298
results/index.php
Normal file → Executable file
|
@ -1,164 +1,224 @@
|
||||||
<?php
|
<?php
|
||||||
$WATERMARK_TEXT="LibreSpeed";
|
|
||||||
|
require_once 'telemetry_db.php';
|
||||||
|
|
||||||
error_reporting(0);
|
error_reporting(0);
|
||||||
putenv('GDFONTPATH='.realpath('.'));
|
putenv('GDFONTPATH='.realpath('.'));
|
||||||
function tryFont($name){
|
|
||||||
$rp=realpath('.');
|
/**
|
||||||
if(imageftbbox(12,0,$name,"M")[5]==0){
|
* @param string $name
|
||||||
$name=$rp."/".$name.".ttf";
|
*
|
||||||
if(imageftbbox(12,0,$name,"M")[5]==0){
|
* @return string|null
|
||||||
return null;
|
*/
|
||||||
}
|
function tryFont($name)
|
||||||
}
|
{
|
||||||
|
if (is_array(imageftbbox(12, 0, $name, 'M'))) {
|
||||||
return $name;
|
return $name;
|
||||||
}
|
}
|
||||||
function format($d){
|
|
||||||
if($d<10) return number_format($d,2,".","");
|
$fullPathToFont = realpath('.').'/'.$name.'.ttf';
|
||||||
if($d<100) return number_format($d,1,".","");
|
if (is_array(imageftbbox(12, 0, $fullPathToFont, 'M'))) {
|
||||||
return number_format($d,0,".","");
|
return $fullPathToFont;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int|float $d
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function format($d)
|
||||||
|
{
|
||||||
|
if ($d < 10) {
|
||||||
|
return number_format($d, 2, '.', '');
|
||||||
|
}
|
||||||
|
if ($d < 100) {
|
||||||
|
return number_format($d, 1, '.', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
return number_format($d, 0, '.', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $speedtest
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
function formatSpeedtestDataForImage($speedtest)
|
||||||
|
{
|
||||||
|
// format values for the image
|
||||||
|
$speedtest['dl'] = format($speedtest['dl']);
|
||||||
|
$speedtest['ul'] = format($speedtest['ul']);
|
||||||
|
$speedtest['ping'] = format($speedtest['ping']);
|
||||||
|
$speedtest['jitter'] = format($speedtest['jitter']);
|
||||||
|
$speedtest['timestamp'] = $speedtest['timestamp'];
|
||||||
|
|
||||||
|
$ispinfo = json_decode($speedtest['ispinfo'], true)['processedString'];
|
||||||
|
$dash = strpos($ispinfo, '-');
|
||||||
|
if ($dash !== false) {
|
||||||
|
$ispinfo = substr($ispinfo, $dash + 2);
|
||||||
|
$par = strrpos($ispinfo, '(');
|
||||||
|
if ($par !== false) {
|
||||||
|
$ispinfo = substr($ispinfo, 0, $par);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$ispinfo = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$speedtest['ispinfo'] = $ispinfo;
|
||||||
|
|
||||||
|
return $speedtest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $speedtest
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function drawImage($speedtest)
|
||||||
|
{
|
||||||
|
// format values for the image
|
||||||
|
$data = formatSpeedtestDataForImage($speedtest);
|
||||||
|
$dl = $data['dl'];
|
||||||
|
$ul = $data['ul'];
|
||||||
|
$ping = $data['ping'];
|
||||||
|
$jit = $data['jitter'];
|
||||||
|
$ispinfo = $data['ispinfo'];
|
||||||
|
$timestamp = $data['timestamp'];
|
||||||
|
|
||||||
|
// initialize the image
|
||||||
$SCALE = 1.25;
|
$SCALE = 1.25;
|
||||||
|
$SMALL_SEP = 8 * $SCALE;
|
||||||
$WIDTH = 400 * $SCALE;
|
$WIDTH = 400 * $SCALE;
|
||||||
$HEIGHT = 229 * $SCALE;
|
$HEIGHT = 229 * $SCALE;
|
||||||
$im = imagecreatetruecolor($WIDTH, $HEIGHT);
|
$im = imagecreatetruecolor($WIDTH, $HEIGHT);
|
||||||
$BACKGROUND_COLOR = imagecolorallocate($im, 255, 255, 255);
|
$BACKGROUND_COLOR = imagecolorallocate($im, 255, 255, 255);
|
||||||
$FONT_LABEL=tryFont("OpenSans-Semibold");
|
|
||||||
|
// configure fonts
|
||||||
|
$FONT_LABEL = tryFont('OpenSans-Semibold');
|
||||||
$FONT_LABEL_SIZE = 14 * $SCALE;
|
$FONT_LABEL_SIZE = 14 * $SCALE;
|
||||||
$FONT_LABEL_SIZE_BIG = 16 * $SCALE;
|
$FONT_LABEL_SIZE_BIG = 16 * $SCALE;
|
||||||
$FONT_METER=tryFont("OpenSans-Light");
|
|
||||||
|
$FONT_METER = tryFont('OpenSans-Light');
|
||||||
$FONT_METER_SIZE = 20 * $SCALE;
|
$FONT_METER_SIZE = 20 * $SCALE;
|
||||||
$FONT_METER_SIZE_BIG = 22 * $SCALE;
|
$FONT_METER_SIZE_BIG = 22 * $SCALE;
|
||||||
$FONT_MEASURE=tryFont("OpenSans-Semibold");
|
|
||||||
|
$FONT_MEASURE = tryFont('OpenSans-Semibold');
|
||||||
$FONT_MEASURE_SIZE = 12 * $SCALE;
|
$FONT_MEASURE_SIZE = 12 * $SCALE;
|
||||||
$FONT_MEASURE_SIZE_BIG = 12 * $SCALE;
|
$FONT_MEASURE_SIZE_BIG = 12 * $SCALE;
|
||||||
$FONT_ISP=tryFont("OpenSans-Semibold");
|
|
||||||
|
$FONT_ISP = tryFont('OpenSans-Semibold');
|
||||||
$FONT_ISP_SIZE = 9 * $SCALE;
|
$FONT_ISP_SIZE = 9 * $SCALE;
|
||||||
$FONT_WATERMARK=tryFont("OpenSans-Light");
|
|
||||||
|
$FONT_TIMESTAMP = tryFont("OpenSans-Light");
|
||||||
|
$FONT_TIMESTAMP_SIZE = 8 * $SCALE;
|
||||||
|
|
||||||
|
$FONT_WATERMARK = tryFont('OpenSans-Light');
|
||||||
$FONT_WATERMARK_SIZE = 8 * $SCALE;
|
$FONT_WATERMARK_SIZE = 8 * $SCALE;
|
||||||
|
|
||||||
|
// configure text colors
|
||||||
$TEXT_COLOR_LABEL = imagecolorallocate($im, 40, 40, 40);
|
$TEXT_COLOR_LABEL = imagecolorallocate($im, 40, 40, 40);
|
||||||
$TEXT_COLOR_DL_METER=imagecolorallocate($im,96,96,170);
|
|
||||||
$TEXT_COLOR_UL_METER=imagecolorallocate($im,96,96,96);
|
|
||||||
$TEXT_COLOR_PING_METER = imagecolorallocate($im, 170, 96, 96);
|
$TEXT_COLOR_PING_METER = imagecolorallocate($im, 170, 96, 96);
|
||||||
$TEXT_COLOR_JIT_METER = imagecolorallocate($im, 170, 96, 96);
|
$TEXT_COLOR_JIT_METER = imagecolorallocate($im, 170, 96, 96);
|
||||||
|
$TEXT_COLOR_DL_METER = imagecolorallocate($im, 96, 96, 170);
|
||||||
|
$TEXT_COLOR_UL_METER = imagecolorallocate($im, 96, 96, 96);
|
||||||
$TEXT_COLOR_MEASURE = imagecolorallocate($im, 40, 40, 40);
|
$TEXT_COLOR_MEASURE = imagecolorallocate($im, 40, 40, 40);
|
||||||
$TEXT_COLOR_ISP = imagecolorallocate($im, 40, 40, 40);
|
$TEXT_COLOR_ISP = imagecolorallocate($im, 40, 40, 40);
|
||||||
$TEXT_COLOR_WATERMARK=imagecolorallocate($im,160,160,160);
|
|
||||||
$POSITION_Y_DL_LABEL=105*$SCALE;
|
|
||||||
$POSITION_Y_UL_LABEL=105*$SCALE;
|
|
||||||
$POSITION_Y_PING_LABEL=24*$SCALE;
|
|
||||||
$POSITION_Y_JIT_LABEL=24*$SCALE;
|
|
||||||
$POSITION_Y_DL_METER=143*$SCALE;
|
|
||||||
$POSITION_Y_UL_METER=143*$SCALE;
|
|
||||||
$POSITION_Y_PING_METER=60*$SCALE;
|
|
||||||
$POSITION_Y_JIT_METER=60*$SCALE;
|
|
||||||
$POSITION_Y_DL_MEASURE=169*$SCALE;
|
|
||||||
$POSITION_Y_UL_MEASURE=169*$SCALE;
|
|
||||||
$POSITION_Y_PING_MEASURE=60*$SCALE;
|
|
||||||
$POSITION_Y_JIT_MEASURE=60*$SCALE;
|
|
||||||
$POSITION_Y_ISP=205*$SCALE;
|
|
||||||
$POSITION_X_DL=120*$SCALE;
|
|
||||||
$POSITION_X_UL=280*$SCALE;
|
|
||||||
$POSITION_X_PING=125*$SCALE;
|
|
||||||
$POSITION_X_JIT=275*$SCALE;
|
|
||||||
$POSITION_X_ISP=4*$SCALE;
|
|
||||||
$SMALL_SEP=8*$SCALE;
|
|
||||||
$SEPARATOR_Y=211*$SCALE;
|
|
||||||
$SEPARATOR_COLOR = imagecolorallocate($im, 192, 192, 192);
|
$SEPARATOR_COLOR = imagecolorallocate($im, 192, 192, 192);
|
||||||
|
$TEXT_COLOR_TIMESTAMP = imagecolorallocate($im, 160, 160, 160);
|
||||||
|
$TEXT_COLOR_WATERMARK = imagecolorallocate($im, 160, 160, 160);
|
||||||
|
|
||||||
|
// configure positioning or the different parts on the image
|
||||||
|
$POSITION_X_PING = 125 * $SCALE;
|
||||||
|
$POSITION_Y_PING_LABEL = 24 * $SCALE;
|
||||||
|
$POSITION_Y_PING_METER = 60 * $SCALE;
|
||||||
|
$POSITION_Y_PING_MEASURE = 60 * $SCALE;
|
||||||
|
|
||||||
|
$POSITION_X_JIT = 275 * $SCALE;
|
||||||
|
$POSITION_Y_JIT_LABEL = 24 * $SCALE;
|
||||||
|
$POSITION_Y_JIT_METER = 60 * $SCALE;
|
||||||
|
$POSITION_Y_JIT_MEASURE = 60 * $SCALE;
|
||||||
|
|
||||||
|
$POSITION_X_DL = 120 * $SCALE;
|
||||||
|
$POSITION_Y_DL_LABEL = 105 * $SCALE;
|
||||||
|
$POSITION_Y_DL_METER = 143 * $SCALE;
|
||||||
|
$POSITION_Y_DL_MEASURE = 169 * $SCALE;
|
||||||
|
|
||||||
|
$POSITION_X_UL = 280 * $SCALE;
|
||||||
|
$POSITION_Y_UL_LABEL = 105 * $SCALE;
|
||||||
|
$POSITION_Y_UL_METER = 143 * $SCALE;
|
||||||
|
$POSITION_Y_UL_MEASURE = 169 * $SCALE;
|
||||||
|
|
||||||
|
$POSITION_X_ISP = 4 * $SCALE;
|
||||||
|
$POSITION_Y_ISP = 205 * $SCALE;
|
||||||
|
|
||||||
|
$SEPARATOR_Y = 211 * $SCALE;
|
||||||
|
|
||||||
|
$POSITION_X_TIMESTAMP= 4 * $SCALE;
|
||||||
|
$POSITION_Y_TIMESTAMP = 223 * $SCALE;
|
||||||
|
|
||||||
$POSITION_Y_WATERMARK = 223 * $SCALE;
|
$POSITION_Y_WATERMARK = 223 * $SCALE;
|
||||||
$DL_TEXT="Download";
|
|
||||||
$UL_TEXT="Upload";
|
|
||||||
$PING_TEXT="Ping";
|
|
||||||
$JIT_TEXT="Jitter";
|
|
||||||
$MBPS_TEXT="Mbps";
|
|
||||||
$MS_TEXT="ms";
|
|
||||||
|
|
||||||
$id=$_GET["id"];
|
// configure labels
|
||||||
include_once('telemetry_settings.php');
|
$MBPS_TEXT = 'Mbit/s';
|
||||||
require 'idObfuscation.php';
|
$MS_TEXT = 'ms';
|
||||||
if($enable_id_obfuscation) $id=deobfuscateId($id);
|
$PING_TEXT = 'Ping';
|
||||||
$conn=null; $q=null;
|
$JIT_TEXT = 'Jitter';
|
||||||
$ispinfo=null; $dl=null; $ul=null; $ping=null; $jit=null;
|
$DL_TEXT = 'Download';
|
||||||
if($db_type=="mysql"){
|
$UL_TEXT = 'Upload';
|
||||||
$conn = new mysqli($MySql_hostname, $MySql_username, $MySql_password, $MySql_databasename, $MySql_port);
|
$WATERMARK_TEXT = 'LibreSpeed';
|
||||||
$q = $conn->prepare("select ispinfo,dl,ul,ping,jitter from speedtest_users where id=?");
|
|
||||||
$q->bind_param("i",$id);
|
|
||||||
$q->execute();
|
|
||||||
$q->bind_result($ispinfo,$dl,$ul,$ping,$jit);
|
|
||||||
$q->fetch();
|
|
||||||
}else if($db_type=="sqlite"){
|
|
||||||
$conn = new PDO("sqlite:$Sqlite_db_file") or die();
|
|
||||||
$q=$conn->prepare("select ispinfo,dl,ul,ping,jitter from speedtest_users where id=?") or die();
|
|
||||||
$q->execute(array($id)) or die();
|
|
||||||
$row=$q->fetch() or die();
|
|
||||||
$ispinfo=$row["ispinfo"];
|
|
||||||
$dl=$row["dl"];
|
|
||||||
$ul=$row["ul"];
|
|
||||||
$ping=$row["ping"];
|
|
||||||
$jit=$row["jitter"];
|
|
||||||
$conn=null;
|
|
||||||
}else if($db_type=="postgresql"){
|
|
||||||
$conn_host = "host=$PostgreSql_hostname";
|
|
||||||
$conn_db = "dbname=$PostgreSql_databasename";
|
|
||||||
$conn_user = "user=$PostgreSql_username";
|
|
||||||
$conn_password = "password=$PostgreSql_password";
|
|
||||||
$conn = new PDO("pgsql:$conn_host;$conn_db;$conn_user;$conn_password") or die();
|
|
||||||
$q=$conn->prepare("select ispinfo,dl,ul,ping,jitter from speedtest_users where id=?") or die();
|
|
||||||
$q->execute(array($id)) or die();
|
|
||||||
$row=$q->fetch() or die();
|
|
||||||
$ispinfo=$row["ispinfo"];
|
|
||||||
$dl=$row["dl"];
|
|
||||||
$ul=$row["ul"];
|
|
||||||
$ping=$row["ping"];
|
|
||||||
$jit=$row["jitter"];
|
|
||||||
$conn=null;
|
|
||||||
}else die();
|
|
||||||
|
|
||||||
$dl=format($dl);
|
// create text boxes for each part of the image
|
||||||
$ul=format($ul);
|
|
||||||
$ping=format($ping);
|
|
||||||
$jit=format($jit);
|
|
||||||
|
|
||||||
$ispinfo=json_decode($ispinfo,true)["processedString"];
|
|
||||||
$dash=strpos($ispinfo,"-");
|
|
||||||
if(!($dash===FALSE)){
|
|
||||||
$ispinfo=substr($ispinfo,$dash+2);
|
|
||||||
$par=strrpos($ispinfo,"(");
|
|
||||||
if(!($par===FALSE)) $ispinfo=substr($ispinfo,0,$par);
|
|
||||||
}else $ispinfo="";
|
|
||||||
|
|
||||||
$dlBbox=imageftbbox($FONT_LABEL_SIZE_BIG,0,$FONT_LABEL,$DL_TEXT);
|
|
||||||
$ulBbox=imageftbbox($FONT_LABEL_SIZE_BIG,0,$FONT_LABEL,$UL_TEXT);
|
|
||||||
$pingBbox=imageftbbox($FONT_LABEL_SIZE,0,$FONT_LABEL,$PING_TEXT);
|
|
||||||
$jitBbox=imageftbbox($FONT_LABEL_SIZE,0,$FONT_LABEL,$JIT_TEXT);
|
|
||||||
$dlMeterBbox=imageftbbox($FONT_METER_SIZE_BIG,0,$FONT_METER,$dl);
|
|
||||||
$ulMeterBbox=imageftbbox($FONT_METER_SIZE_BIG,0,$FONT_METER,$ul);
|
|
||||||
$pingMeterBbox=imageftbbox($FONT_METER_SIZE,0,$FONT_METER,$ping);
|
|
||||||
$jitMeterBbox=imageftbbox($FONT_METER_SIZE,0,$FONT_METER,$jit);
|
|
||||||
$mbpsBbox = imageftbbox($FONT_MEASURE_SIZE_BIG, 0, $FONT_MEASURE, $MBPS_TEXT);
|
$mbpsBbox = imageftbbox($FONT_MEASURE_SIZE_BIG, 0, $FONT_MEASURE, $MBPS_TEXT);
|
||||||
$msBbox = imageftbbox($FONT_MEASURE_SIZE, 0, $FONT_MEASURE, $MS_TEXT);
|
$msBbox = imageftbbox($FONT_MEASURE_SIZE, 0, $FONT_MEASURE, $MS_TEXT);
|
||||||
|
$pingBbox = imageftbbox($FONT_LABEL_SIZE, 0, $FONT_LABEL, $PING_TEXT);
|
||||||
|
$pingMeterBbox = imageftbbox($FONT_METER_SIZE, 0, $FONT_METER, $ping);
|
||||||
|
$jitBbox = imageftbbox($FONT_LABEL_SIZE, 0, $FONT_LABEL, $JIT_TEXT);
|
||||||
|
$jitMeterBbox = imageftbbox($FONT_METER_SIZE, 0, $FONT_METER, $jit);
|
||||||
|
$dlBbox = imageftbbox($FONT_LABEL_SIZE_BIG, 0, $FONT_LABEL, $DL_TEXT);
|
||||||
|
$dlMeterBbox = imageftbbox($FONT_METER_SIZE_BIG, 0, $FONT_METER, $dl);
|
||||||
|
$ulBbox = imageftbbox($FONT_LABEL_SIZE_BIG, 0, $FONT_LABEL, $UL_TEXT);
|
||||||
|
$ulMeterBbox = imageftbbox($FONT_METER_SIZE_BIG, 0, $FONT_METER, $ul);
|
||||||
$watermarkBbox = imageftbbox($FONT_WATERMARK_SIZE, 0, $FONT_WATERMARK, $WATERMARK_TEXT);
|
$watermarkBbox = imageftbbox($FONT_WATERMARK_SIZE, 0, $FONT_WATERMARK, $WATERMARK_TEXT);
|
||||||
$POSITION_X_WATERMARK = $WIDTH - $watermarkBbox[4] - 4 * $SCALE;
|
$POSITION_X_WATERMARK = $WIDTH - $watermarkBbox[4] - 4 * $SCALE;
|
||||||
|
|
||||||
|
// put the parts together to draw the image
|
||||||
imagefilledrectangle($im, 0, 0, $WIDTH, $HEIGHT, $BACKGROUND_COLOR);
|
imagefilledrectangle($im, 0, 0, $WIDTH, $HEIGHT, $BACKGROUND_COLOR);
|
||||||
imagefttext($im,$FONT_LABEL_SIZE_BIG,0,$POSITION_X_DL-$dlBbox[4]/2,$POSITION_Y_DL_LABEL,$TEXT_COLOR_LABEL,$FONT_LABEL,$DL_TEXT);
|
// ping
|
||||||
imagefttext($im,$FONT_LABEL_SIZE_BIG,0,$POSITION_X_UL-$ulBbox[4]/2,$POSITION_Y_UL_LABEL,$TEXT_COLOR_LABEL,$FONT_LABEL,$UL_TEXT);
|
|
||||||
imagefttext($im, $FONT_LABEL_SIZE, 0, $POSITION_X_PING - $pingBbox[4] / 2, $POSITION_Y_PING_LABEL, $TEXT_COLOR_LABEL, $FONT_LABEL, $PING_TEXT);
|
imagefttext($im, $FONT_LABEL_SIZE, 0, $POSITION_X_PING - $pingBbox[4] / 2, $POSITION_Y_PING_LABEL, $TEXT_COLOR_LABEL, $FONT_LABEL, $PING_TEXT);
|
||||||
imagefttext($im,$FONT_LABEL_SIZE,0,$POSITION_X_JIT-$jitBbox[4]/2,$POSITION_Y_JIT_LABEL,$TEXT_COLOR_LABEL,$FONT_LABEL,$JIT_TEXT);
|
|
||||||
imagefttext($im,$FONT_METER_SIZE_BIG,0,$POSITION_X_DL-$dlMeterBbox[4]/2,$POSITION_Y_DL_METER,$TEXT_COLOR_DL_METER,$FONT_METER,$dl);
|
|
||||||
imagefttext($im,$FONT_METER_SIZE_BIG,0,$POSITION_X_UL-$ulMeterBbox[4]/2,$POSITION_Y_UL_METER,$TEXT_COLOR_UL_METER,$FONT_METER,$ul);
|
|
||||||
imagefttext($im, $FONT_METER_SIZE, 0, $POSITION_X_PING - $pingMeterBbox[4] / 2 - $msBbox[4] / 2 - $SMALL_SEP / 2, $POSITION_Y_PING_METER, $TEXT_COLOR_PING_METER, $FONT_METER, $ping);
|
imagefttext($im, $FONT_METER_SIZE, 0, $POSITION_X_PING - $pingMeterBbox[4] / 2 - $msBbox[4] / 2 - $SMALL_SEP / 2, $POSITION_Y_PING_METER, $TEXT_COLOR_PING_METER, $FONT_METER, $ping);
|
||||||
imagefttext($im,$FONT_METER_SIZE,0,$POSITION_X_JIT-$jitMeterBbox[4]/2-$msBbox[4]/2-$SMALL_SEP/2,$POSITION_Y_JIT_METER,$TEXT_COLOR_JIT_METER,$FONT_METER,$jit);
|
|
||||||
imagefttext($im,$FONT_MEASURE_SIZE_BIG,0,$POSITION_X_DL-$mbpsBbox[4]/2,$POSITION_Y_DL_MEASURE,$TEXT_COLOR_MEASURE,$FONT_MEASURE,$MBPS_TEXT);
|
|
||||||
imagefttext($im,$FONT_MEASURE_SIZE_BIG,0,$POSITION_X_UL-$mbpsBbox[4]/2,$POSITION_Y_UL_MEASURE,$TEXT_COLOR_MEASURE,$FONT_MEASURE,$MBPS_TEXT);
|
|
||||||
imagefttext($im, $FONT_MEASURE_SIZE, 0, $POSITION_X_PING + $pingMeterBbox[4] / 2 + $SMALL_SEP / 2 - $msBbox[4] / 2, $POSITION_Y_PING_MEASURE, $TEXT_COLOR_MEASURE, $FONT_MEASURE, $MS_TEXT);
|
imagefttext($im, $FONT_MEASURE_SIZE, 0, $POSITION_X_PING + $pingMeterBbox[4] / 2 + $SMALL_SEP / 2 - $msBbox[4] / 2, $POSITION_Y_PING_MEASURE, $TEXT_COLOR_MEASURE, $FONT_MEASURE, $MS_TEXT);
|
||||||
|
// jitter
|
||||||
|
imagefttext($im, $FONT_LABEL_SIZE, 0, $POSITION_X_JIT - $jitBbox[4] / 2, $POSITION_Y_JIT_LABEL, $TEXT_COLOR_LABEL, $FONT_LABEL, $JIT_TEXT);
|
||||||
|
imagefttext($im, $FONT_METER_SIZE, 0, $POSITION_X_JIT - $jitMeterBbox[4] / 2 - $msBbox[4] / 2 - $SMALL_SEP / 2, $POSITION_Y_JIT_METER, $TEXT_COLOR_JIT_METER, $FONT_METER, $jit);
|
||||||
imagefttext($im, $FONT_MEASURE_SIZE, 0, $POSITION_X_JIT + $jitMeterBbox[4] / 2 + $SMALL_SEP / 2 - $msBbox[4] / 2, $POSITION_Y_JIT_MEASURE, $TEXT_COLOR_MEASURE, $FONT_MEASURE, $MS_TEXT);
|
imagefttext($im, $FONT_MEASURE_SIZE, 0, $POSITION_X_JIT + $jitMeterBbox[4] / 2 + $SMALL_SEP / 2 - $msBbox[4] / 2, $POSITION_Y_JIT_MEASURE, $TEXT_COLOR_MEASURE, $FONT_MEASURE, $MS_TEXT);
|
||||||
|
// dl
|
||||||
|
imagefttext($im, $FONT_LABEL_SIZE_BIG, 0, $POSITION_X_DL - $dlBbox[4] / 2, $POSITION_Y_DL_LABEL, $TEXT_COLOR_LABEL, $FONT_LABEL, $DL_TEXT);
|
||||||
|
imagefttext($im, $FONT_METER_SIZE_BIG, 0, $POSITION_X_DL - $dlMeterBbox[4] / 2, $POSITION_Y_DL_METER, $TEXT_COLOR_DL_METER, $FONT_METER, $dl);
|
||||||
|
imagefttext($im, $FONT_MEASURE_SIZE_BIG, 0, $POSITION_X_DL - $mbpsBbox[4] / 2, $POSITION_Y_DL_MEASURE, $TEXT_COLOR_MEASURE, $FONT_MEASURE, $MBPS_TEXT);
|
||||||
|
// ul
|
||||||
|
imagefttext($im, $FONT_LABEL_SIZE_BIG, 0, $POSITION_X_UL - $ulBbox[4] / 2, $POSITION_Y_UL_LABEL, $TEXT_COLOR_LABEL, $FONT_LABEL, $UL_TEXT);
|
||||||
|
imagefttext($im, $FONT_METER_SIZE_BIG, 0, $POSITION_X_UL - $ulMeterBbox[4] / 2, $POSITION_Y_UL_METER, $TEXT_COLOR_UL_METER, $FONT_METER, $ul);
|
||||||
|
imagefttext($im, $FONT_MEASURE_SIZE_BIG, 0, $POSITION_X_UL - $mbpsBbox[4] / 2, $POSITION_Y_UL_MEASURE, $TEXT_COLOR_MEASURE, $FONT_MEASURE, $MBPS_TEXT);
|
||||||
|
// isp
|
||||||
imagefttext($im, $FONT_ISP_SIZE, 0, $POSITION_X_ISP, $POSITION_Y_ISP, $TEXT_COLOR_ISP, $FONT_ISP, $ispinfo);
|
imagefttext($im, $FONT_ISP_SIZE, 0, $POSITION_X_ISP, $POSITION_Y_ISP, $TEXT_COLOR_ISP, $FONT_ISP, $ispinfo);
|
||||||
imagefttext($im,$FONT_WATERMARK_SIZE,0,$POSITION_X_WATERMARK,$POSITION_Y_WATERMARK,$TEXT_COLOR_WATERMARK,$FONT_WATERMARK,$WATERMARK_TEXT);
|
// separator
|
||||||
imagefilledrectangle($im, 0, $SEPARATOR_Y, $WIDTH, $SEPARATOR_Y, $SEPARATOR_COLOR);
|
imagefilledrectangle($im, 0, $SEPARATOR_Y, $WIDTH, $SEPARATOR_Y, $SEPARATOR_COLOR);
|
||||||
|
// timestamp
|
||||||
|
imagefttext($im, $FONT_TIMESTAMP_SIZE, 0, $POSITION_X_TIMESTAMP, $POSITION_Y_TIMESTAMP, $TEXT_COLOR_TIMESTAMP, $FONT_TIMESTAMP, $timestamp);
|
||||||
|
// watermark
|
||||||
|
imagefttext($im, $FONT_WATERMARK_SIZE, 0, $POSITION_X_WATERMARK, $POSITION_Y_WATERMARK, $TEXT_COLOR_WATERMARK, $FONT_WATERMARK, $WATERMARK_TEXT);
|
||||||
|
|
||||||
|
// send the image to the browser
|
||||||
header('Content-Type: image/png');
|
header('Content-Type: image/png');
|
||||||
imagepng($im);
|
imagepng($im);
|
||||||
imagedestroy($im);
|
}
|
||||||
|
|
||||||
?>
|
$speedtest = getSpeedtestUserById($_GET['id']);
|
||||||
|
if (!is_array($speedtest)) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
drawImage($speedtest);
|
||||||
|
|
62
results/json.php
Executable file
|
@ -0,0 +1,62 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
error_reporting(0);
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
|
||||||
|
require_once 'telemetry_db.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int|float $d
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function format($d)
|
||||||
|
{
|
||||||
|
if ($d < 10) {
|
||||||
|
return number_format($d, 2, '.', '');
|
||||||
|
}
|
||||||
|
if ($d < 100) {
|
||||||
|
return number_format($d, 1, '.', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
return number_format($d, 0, '.', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $speedtest
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
function formatSpeedtestData($speedtest)
|
||||||
|
{
|
||||||
|
// format values for the image
|
||||||
|
$speedtest['dl'] = format($speedtest['dl']);
|
||||||
|
$speedtest['ul'] = format($speedtest['ul']);
|
||||||
|
$speedtest['ping'] = format($speedtest['ping']);
|
||||||
|
$speedtest['jitter'] = format($speedtest['jitter']);
|
||||||
|
$speedtest['timestamp'] = $speedtest['timestamp'];
|
||||||
|
|
||||||
|
$ispinfo = json_decode($speedtest['ispinfo'], true)['processedString'];
|
||||||
|
$dash = strpos($ispinfo, '-');
|
||||||
|
if ($dash !== false) {
|
||||||
|
$ispinfo = substr($ispinfo, $dash + 2);
|
||||||
|
$par = strrpos($ispinfo, '(');
|
||||||
|
if ($par !== false) {
|
||||||
|
$ispinfo = substr($ispinfo, 0, $par);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$ispinfo = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$speedtest['ispinfo'] = $ispinfo;
|
||||||
|
|
||||||
|
return $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']));
|
||||||
|
}
|
189
results/sanitycheck.php
Normal 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);
|
||||||
|
*/
|
||||||
|
?>
|
153
results/stats.php
Normal file → Executable file
|
@ -1,6 +1,10 @@
|
||||||
<?php
|
<?php
|
||||||
session_start();
|
session_start();
|
||||||
error_reporting(0);
|
error_reporting(0);
|
||||||
|
|
||||||
|
require 'telemetry_settings.php';
|
||||||
|
require_once 'telemetry_db.php';
|
||||||
|
|
||||||
header('Content-Type: text/html; charset=utf-8');
|
header('Content-Type: text/html; charset=utf-8');
|
||||||
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0, s-maxage=0');
|
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0, s-maxage=0');
|
||||||
header('Cache-Control: post-check=0, pre-check=0', false);
|
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||||
|
@ -51,109 +55,105 @@ header('Pragma: no-cache');
|
||||||
td {
|
td {
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
|
div {
|
||||||
|
margin: 1em 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>LibreSpeed - Stats</h1>
|
<h1>LibreSpeed - Stats</h1>
|
||||||
<?php
|
<?php
|
||||||
include_once("telemetry_settings.php");
|
if (!isset($stats_password) || $stats_password === 'PASSWORD') {
|
||||||
require "idObfuscation.php";
|
|
||||||
if($stats_password=="PASSWORD"){
|
|
||||||
?>
|
?>
|
||||||
Please set $stats_password in telemetry_settings.php to enable access.
|
Please set $stats_password in telemetry_settings.php to enable access.
|
||||||
<?php
|
<?php
|
||||||
}else if($_SESSION["logged"]===true){
|
} elseif ($_SESSION['logged'] === true) {
|
||||||
if($_GET["op"]=="logout"){
|
if ($_GET['op'] === 'logout') {
|
||||||
$_SESSION["logged"]=false;
|
$_SESSION['logged'] = false;
|
||||||
?><script type="text/javascript">window.location=location.protocol+"//"+location.host+location.pathname;</script><?php
|
?><script type="text/javascript">window.location=location.protocol+"//"+location.host+location.pathname;</script><?php
|
||||||
} else {
|
} else {
|
||||||
$conn=null;
|
|
||||||
if($db_type=="mysql"){
|
|
||||||
$conn = new mysqli($MySql_hostname, $MySql_username, $MySql_password, $MySql_databasename, $MySql_port);
|
|
||||||
}else if($db_type=="sqlite"){
|
|
||||||
$conn = new PDO("sqlite:$Sqlite_db_file");
|
|
||||||
} else if($db_type=="postgresql"){
|
|
||||||
$conn_host = "host=$PostgreSql_hostname";
|
|
||||||
$conn_db = "dbname=$PostgreSql_databasename";
|
|
||||||
$conn_user = "user=$PostgreSql_username";
|
|
||||||
$conn_password = "password=$PostgreSql_password";
|
|
||||||
$conn = new PDO("pgsql:$conn_host;$conn_db;$conn_user;$conn_password");
|
|
||||||
}else die();
|
|
||||||
?>
|
?>
|
||||||
<form action="stats.php" method="GET"><input type="hidden" name="op" value="logout" /><input type="submit" value="Logout" /></form>
|
<form action="stats.php" method="GET"><input type="hidden" name="op" value="logout" /><input type="submit" value="Logout" /></form>
|
||||||
<form action="stats.php" method="GET">
|
<form action="stats.php" method="GET">
|
||||||
<h3>Search test results</h6>
|
<h3>Search test results</h3>
|
||||||
<input type="hidden" name="op" value="id" />
|
<input type="hidden" name="op" value="id" />
|
||||||
<input type="text" name="id" id="id" placeholder="Test ID" value=""/>
|
<input type="text" name="id" id="id" placeholder="Test ID" value=""/>
|
||||||
<input type="submit" value="Find" />
|
<input type="submit" value="Find" />
|
||||||
<input type="submit" onclick="document.getElementById('id').value=''" value="Show last 100 tests" />
|
<input type="submit" onclick="document.getElementById('id').value=''" value="Show last 100 tests" />
|
||||||
</form>
|
</form>
|
||||||
<?php
|
<?php
|
||||||
$q=null;
|
if ($_GET['op'] === 'id' && !empty($_GET['id'])) {
|
||||||
if($_GET["op"]=="id"&&!empty($_GET["id"])){
|
$speedtest = getSpeedtestUserById($_GET['id']);
|
||||||
$id=$_GET["id"];
|
$speedtests = [];
|
||||||
if($enable_id_obfuscation) $id=deobfuscateId($id);
|
if (false === $speedtest) {
|
||||||
if($db_type=="mysql"){
|
echo '<div>There was an error trying to fetch the speedtest result for ID "'.htmlspecialchars($_GET['id'], ENT_HTML5, 'UTF-8').'".</div>';
|
||||||
$q=$conn->prepare("select id,timestamp,ip,ispinfo,ua,lang,dl,ul,ping,jitter,log,extra from speedtest_users where id=?");
|
} elseif (null === $speedtest) {
|
||||||
$q->bind_param("i",$id);
|
echo '<div>Could not find a speedtest result for ID "'.htmlspecialchars($_GET['id'], ENT_HTML5, 'UTF-8').'".</div>';
|
||||||
$q->execute();
|
|
||||||
$q->store_result();
|
|
||||||
$q->bind_result($id,$timestamp,$ip,$ispinfo,$ua,$lang,$dl,$ul,$ping,$jitter,$log,$extra);
|
|
||||||
} else if($db_type=="sqlite"||$db_type=="postgresql"){
|
|
||||||
$q=$conn->prepare("select id,timestamp,ip,ispinfo,ua,lang,dl,ul,ping,jitter,log,extra from speedtest_users where id=?");
|
|
||||||
$q->execute(array($id));
|
|
||||||
} else die();
|
|
||||||
} else {
|
} else {
|
||||||
if($db_type=="mysql"){
|
$speedtests = [$speedtest];
|
||||||
$q=$conn->prepare("select id,timestamp,ip,ispinfo,ua,lang,dl,ul,ping,jitter,log,extra from speedtest_users order by timestamp desc limit 0,100");
|
|
||||||
$q->execute();
|
|
||||||
$q->store_result();
|
|
||||||
$q->bind_result($id,$timestamp,$ip,$ispinfo,$ua,$lang,$dl,$ul,$ping,$jitter,$log,$extra);
|
|
||||||
} else if($db_type=="sqlite"||$db_type=="postgresql"){
|
|
||||||
$q=$conn->prepare("select id,timestamp,ip,ispinfo,ua,lang,dl,ul,ping,jitter,log,extra from speedtest_users order by timestamp desc limit 100");
|
|
||||||
$q->execute();
|
|
||||||
}else die();
|
|
||||||
}
|
}
|
||||||
while(true){
|
} else {
|
||||||
$id=null; $timestamp=null; $ip=null; $ispinfo=null; $ua=null; $lang=null; $dl=null; $ul=null; $ping=null; $jitter=null; $log=null; $extra=null;
|
$speedtests = getLatestSpeedtestUsers();
|
||||||
if($db_type=="mysql"){
|
if (false === $speedtests) {
|
||||||
if(!$q->fetch()) break;
|
echo '<div>There was an error trying to fetch latest speedtest results.</div>';
|
||||||
} else if($db_type=="sqlite"||$db_type=="postgresql"){
|
} elseif (empty($speedtests)) {
|
||||||
if(!($row=$q->fetch())) break;
|
echo '<div>Could not find any speedtest results in database.</div>';
|
||||||
$id=$row["id"];
|
}
|
||||||
$timestamp=$row["timestamp"];
|
}
|
||||||
$ip=$row["ip"];
|
foreach ($speedtests as $speedtest) {
|
||||||
$ispinfo=$row["ispinfo"];
|
|
||||||
$ua=$row["ua"];
|
|
||||||
$lang=$row["lang"];
|
|
||||||
$dl=$row["dl"];
|
|
||||||
$ul=$row["ul"];
|
|
||||||
$ping=$row["ping"];
|
|
||||||
$jitter=$row["jitter"];
|
|
||||||
$log=$row["log"];
|
|
||||||
$extra=$row["extra"];
|
|
||||||
}else die();
|
|
||||||
?>
|
?>
|
||||||
<table>
|
<table>
|
||||||
<tr><th>Test ID</th><td><?=htmlspecialchars(($enable_id_obfuscation?(obfuscateId($id)." (deobfuscated: ".$id.")"):$id), ENT_HTML5, 'UTF-8') ?></td></tr>
|
<tr>
|
||||||
<tr><th>Date and time</th><td><?=htmlspecialchars($timestamp, ENT_HTML5, 'UTF-8') ?></td></tr>
|
<th>Test ID</th>
|
||||||
<tr><th>IP and ISP Info</th><td><?=$ip ?><br/><?=htmlspecialchars($ispinfo, ENT_HTML5, 'UTF-8') ?></td></tr>
|
<td><?= htmlspecialchars($speedtest['id_formatted'], ENT_HTML5, 'UTF-8') ?></td>
|
||||||
<tr><th>User agent and locale</th><td><?=$ua ?><br/><?=htmlspecialchars($lang, ENT_HTML5, 'UTF-8') ?></td></tr>
|
</tr>
|
||||||
<tr><th>Download speed</th><td><?=htmlspecialchars($dl, ENT_HTML5, 'UTF-8') ?></td></tr>
|
<tr>
|
||||||
<tr><th>Upload speed</th><td><?=htmlspecialchars($ul, ENT_HTML5, 'UTF-8') ?></td></tr>
|
<th>Date and time</th>
|
||||||
<tr><th>Ping</th><td><?=htmlspecialchars($ping, ENT_HTML5, 'UTF-8') ?></td></tr>
|
<td><?= htmlspecialchars($speedtest['timestamp'], ENT_HTML5, 'UTF-8') ?></td>
|
||||||
<tr><th>Jitter</th><td><?=htmlspecialchars($jitter, ENT_HTML5, 'UTF-8') ?></td></tr>
|
</tr>
|
||||||
<tr><th>Log</th><td><?=htmlspecialchars($log, ENT_HTML5, 'UTF-8') ?></td></tr>
|
<tr>
|
||||||
<tr><th>Extra info</th><td><?=htmlspecialchars($extra, ENT_HTML5, 'UTF-8') ?></td></tr>
|
<th>IP and ISP Info</th>
|
||||||
|
<td>
|
||||||
|
<?= htmlspecialchars($speedtest['ip'], ENT_HTML5, 'UTF-8') ?><br/>
|
||||||
|
<?= htmlspecialchars($speedtest['ispinfo'], ENT_HTML5, 'UTF-8') ?>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>User agent and locale</th>
|
||||||
|
<td><?= htmlspecialchars($speedtest['ua'], ENT_HTML5, 'UTF-8') ?><br/>
|
||||||
|
<?= htmlspecialchars($speedtest['lang'], ENT_HTML5, 'UTF-8') ?>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Download speed</th>
|
||||||
|
<td><?= htmlspecialchars($speedtest['dl'], ENT_HTML5, 'UTF-8') ?></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Upload speed</th>
|
||||||
|
<td><?= htmlspecialchars($speedtest['ul'], ENT_HTML5, 'UTF-8') ?></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Ping</th>
|
||||||
|
<td><?= htmlspecialchars($speedtest['ping'], ENT_HTML5, 'UTF-8') ?></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Jitter</th>
|
||||||
|
<td><?= htmlspecialchars($speedtest['jitter'], ENT_HTML5, 'UTF-8') ?></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Log</th>
|
||||||
|
<td><?= htmlspecialchars($speedtest['log'], ENT_HTML5, 'UTF-8') ?></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Extra info</th>
|
||||||
|
<td><?= htmlspecialchars($speedtest['extra'], ENT_HTML5, 'UTF-8') ?></td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
?>
|
|
||||||
<?php
|
|
||||||
}
|
}
|
||||||
}else{
|
} elseif ($_GET['op'] === 'login' && $_POST['password'] === $stats_password) {
|
||||||
if($_GET["op"]=="login"&&$_POST["password"]===$stats_password){
|
$_SESSION['logged'] = true;
|
||||||
$_SESSION["logged"]=true;
|
|
||||||
?><script type="text/javascript">window.location=location.protocol+"//"+location.host+location.pathname;</script><?php
|
?><script type="text/javascript">window.location=location.protocol+"//"+location.host+location.pathname;</script><?php
|
||||||
} else {
|
} else {
|
||||||
?>
|
?>
|
||||||
|
@ -164,7 +164,6 @@ if($stats_password=="PASSWORD"){
|
||||||
</form>
|
</form>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
}
|
|
||||||
?>
|
?>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
98
results/telemetry.php
Normal file → Executable file
|
@ -1,81 +1,43 @@
|
||||||
<?php
|
<?php
|
||||||
include_once('telemetry_settings.php');
|
|
||||||
require 'idObfuscation.php';
|
|
||||||
|
|
||||||
$ip=($_SERVER['REMOTE_ADDR']);
|
require 'telemetry_settings.php';
|
||||||
$ispinfo=($_POST["ispinfo"]);
|
require_once 'telemetry_db.php';
|
||||||
$extra=($_POST["extra"]);
|
require_once '../backend/getIP_util.php';
|
||||||
$ua=($_SERVER['HTTP_USER_AGENT']);
|
|
||||||
$lang=""; if(isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) $lang=($_SERVER['HTTP_ACCEPT_LANGUAGE']);
|
|
||||||
$dl=($_POST["dl"]);
|
|
||||||
$ul=($_POST["ul"]);
|
|
||||||
$ping=($_POST["ping"]);
|
|
||||||
$jitter=($_POST["jitter"]);
|
|
||||||
$log=($_POST["log"]);
|
|
||||||
|
|
||||||
if($redact_ip_addresses){
|
$ip = getClientIp();
|
||||||
$ip="0.0.0.0";
|
$ispinfo = $_POST['ispinfo'];
|
||||||
|
$extra = $_POST['extra'];
|
||||||
|
$ua = $_SERVER['HTTP_USER_AGENT'];
|
||||||
|
$lang = '';
|
||||||
|
if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
|
||||||
|
$lang = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
|
||||||
|
}
|
||||||
|
$dl = $_POST['dl'];
|
||||||
|
$ul = $_POST['ul'];
|
||||||
|
$ping = $_POST['ping'];
|
||||||
|
$jitter = $_POST['jitter'];
|
||||||
|
$log = $_POST['log'];
|
||||||
|
|
||||||
|
if (isset($redact_ip_addresses) && true === $redact_ip_addresses) {
|
||||||
|
$ip = '0.0.0.0';
|
||||||
$ipv4_regex = '/(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/';
|
$ipv4_regex = '/(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/';
|
||||||
$ipv6_regex = '/(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/';
|
$ipv6_regex = '/(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/';
|
||||||
$hostname_regex = '/"hostname":"([^\\\\"]|\\\\")*"/';
|
$hostname_regex = '/"hostname":"([^\\\\"]|\\\\")*"/';
|
||||||
$ispinfo=preg_replace($ipv4_regex,"0.0.0.0",$ispinfo);
|
$ispinfo = preg_replace($ipv4_regex, '0.0.0.0', $ispinfo);
|
||||||
$ispinfo=preg_replace($ipv6_regex,"0.0.0.0",$ispinfo);
|
$ispinfo = preg_replace($ipv6_regex, '0.0.0.0', $ispinfo);
|
||||||
$ispinfo=preg_replace($hostname_regex,"\"hostname\":\"REDACTED\"",$ispinfo);
|
$ispinfo = preg_replace($hostname_regex, '"hostname":"REDACTED"', $ispinfo);
|
||||||
$log=preg_replace($ipv4_regex,"0.0.0.0",$log);
|
$log = preg_replace($ipv4_regex, '0.0.0.0', $log);
|
||||||
$log=preg_replace($ipv6_regex,"0.0.0.0",$log);
|
$log = preg_replace($ipv6_regex, '0.0.0.0', $log);
|
||||||
$log=preg_replace($hostname_regex,"\"hostname\":\"REDACTED\"",$log);
|
$log = preg_replace($hostname_regex, '"hostname":"REDACTED"', $log);
|
||||||
}
|
}
|
||||||
|
|
||||||
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0, s-maxage=0');
|
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0, s-maxage=0');
|
||||||
header('Cache-Control: post-check=0, pre-check=0', false);
|
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||||
header('Pragma: no-cache');
|
header('Pragma: no-cache');
|
||||||
|
|
||||||
if($db_type=="mysql"){
|
$id = insertSpeedtestUser($ip, $ispinfo, $extra, $ua, $lang, $dl, $ul, $ping, $jitter, $log);
|
||||||
$conn = new mysqli($MySql_hostname, $MySql_username, $MySql_password, $MySql_databasename, $MySql_port) or die("1");
|
if (false === $id) {
|
||||||
$stmt = $conn->prepare("INSERT INTO speedtest_users (ip,ispinfo,extra,ua,lang,dl,ul,ping,jitter,log) VALUES (?,?,?,?,?,?,?,?,?,?)") or die("2");
|
exit(1);
|
||||||
$stmt->bind_param("ssssssssss",$ip,$ispinfo,$extra,$ua,$lang,$dl,$ul,$ping,$jitter,$log) or die("3");
|
|
||||||
$stmt->execute() or die("4");
|
|
||||||
$stmt->close() or die("5");
|
|
||||||
$id=$conn->insert_id;
|
|
||||||
echo "id ".($enable_id_obfuscation?obfuscateId($id):$id);
|
|
||||||
$conn->close() or die("6");
|
|
||||||
|
|
||||||
}elseif($db_type=="sqlite"){
|
|
||||||
$conn = new PDO("sqlite:$Sqlite_db_file") or die("1");
|
|
||||||
$conn->exec("
|
|
||||||
CREATE TABLE IF NOT EXISTS `speedtest_users` (
|
|
||||||
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
|
||||||
`ispinfo` text,
|
|
||||||
`extra` text,
|
|
||||||
`timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
`ip` text NOT NULL,
|
|
||||||
`ua` text NOT NULL,
|
|
||||||
`lang` text NOT NULL,
|
|
||||||
`dl` text,
|
|
||||||
`ul` text,
|
|
||||||
`ping` text,
|
|
||||||
`jitter` text,
|
|
||||||
`log` longtext
|
|
||||||
);
|
|
||||||
");
|
|
||||||
$stmt = $conn->prepare("INSERT INTO speedtest_users (ip,ispinfo,extra,ua,lang,dl,ul,ping,jitter,log) VALUES (?,?,?,?,?,?,?,?,?,?)") or die("2");
|
|
||||||
$stmt->execute(array($ip,$ispinfo,$extra,$ua,$lang,$dl,$ul,$ping,$jitter,$log)) or die("3");
|
|
||||||
$id=$conn->lastInsertId();
|
|
||||||
echo "id ".($enable_id_obfuscation?obfuscateId($id):$id);
|
|
||||||
$conn = null;
|
|
||||||
}elseif($db_type=="postgresql"){
|
|
||||||
// Prepare connection parameters for db connection
|
|
||||||
$conn_host = "host=$PostgreSql_hostname";
|
|
||||||
$conn_db = "dbname=$PostgreSql_databasename";
|
|
||||||
$conn_user = "user=$PostgreSql_username";
|
|
||||||
$conn_password = "password=$PostgreSql_password";
|
|
||||||
// Create db connection
|
|
||||||
$conn = new PDO("pgsql:$conn_host;$conn_db;$conn_user;$conn_password") or die("1");
|
|
||||||
$stmt = $conn->prepare("INSERT INTO speedtest_users (ip,ispinfo,extra,ua,lang,dl,ul,ping,jitter,log) VALUES (?,?,?,?,?,?,?,?,?,?)") or die("2");
|
|
||||||
$stmt->execute(array($ip,$ispinfo,$extra,$ua,$lang,$dl,$ul,$ping,$jitter,$log)) or die("3");
|
|
||||||
$id=$conn->lastInsertId();
|
|
||||||
echo "id ".($enable_id_obfuscation?obfuscateId($id):$id);
|
|
||||||
$conn = null;
|
|
||||||
}
|
}
|
||||||
else die("-1");
|
|
||||||
?>
|
echo 'id '.$id;
|
||||||
|
|
300
results/telemetry_db.php
Executable file
|
@ -0,0 +1,300 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require_once 'idObfuscation.php';
|
||||||
|
|
||||||
|
define('TELEMETRY_SETTINGS_FILE', 'telemetry_settings.php');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return PDO|false
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdoOptions = [
|
||||||
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
|
||||||
|
];
|
||||||
|
|
||||||
|
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,
|
||||||
|
$MySql_port,
|
||||||
|
$MySql_databasename,
|
||||||
|
$MySql_username,
|
||||||
|
$MySql_password
|
||||||
|
)) {
|
||||||
|
if($returnErrorMessage){
|
||||||
|
return "Required mysql database settings missing in '" . TELEMETRY_SETTINGS_FILE . "'";
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$dsn = 'mysql:'
|
||||||
|
.'host='.$MySql_hostname
|
||||||
|
.';port='.$MySql_port
|
||||||
|
.';dbname='.$MySql_databasename;
|
||||||
|
|
||||||
|
return new PDO($dsn, $MySql_username, $MySql_password, $pdoOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('sqlite' === $db_type) {
|
||||||
|
if (!isset($Sqlite_db_file)) {
|
||||||
|
if($returnErrorMessage){
|
||||||
|
return "Required sqlite database settings missing in '" . TELEMETRY_SETTINGS_FILE . "'";
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo = new PDO('sqlite:'.$Sqlite_db_file, null, null, $pdoOptions);
|
||||||
|
|
||||||
|
$pdo->exec('
|
||||||
|
CREATE TABLE IF NOT EXISTS `speedtest_users` (
|
||||||
|
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
`ispinfo` text,
|
||||||
|
`extra` text,
|
||||||
|
`timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
`ip` text NOT NULL,
|
||||||
|
`ua` text NOT NULL,
|
||||||
|
`lang` text NOT NULL,
|
||||||
|
`dl` text,
|
||||||
|
`ul` text,
|
||||||
|
`ping` text,
|
||||||
|
`jitter` text,
|
||||||
|
`log` longtext
|
||||||
|
);
|
||||||
|
');
|
||||||
|
|
||||||
|
return $pdo;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('postgresql' === $db_type) {
|
||||||
|
if (!isset(
|
||||||
|
$PostgreSql_hostname,
|
||||||
|
$PostgreSql_databasename,
|
||||||
|
$PostgreSql_username,
|
||||||
|
$PostgreSql_password
|
||||||
|
)) {
|
||||||
|
if($returnErrorMessage){
|
||||||
|
return "Required postgresql database settings missing in '" . TELEMETRY_SETTINGS_FILE . "'";
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$dsn = 'pgsql:'
|
||||||
|
.'host='.$PostgreSql_hostname
|
||||||
|
.';dbname='.$PostgreSql_databasename;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
function isObfuscationEnabled()
|
||||||
|
{
|
||||||
|
require TELEMETRY_SETTINGS_FILE;
|
||||||
|
|
||||||
|
return
|
||||||
|
isset($enable_id_obfuscation)
|
||||||
|
&& true === $enable_id_obfuscation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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, $returnExceptionOnError = false)
|
||||||
|
{
|
||||||
|
$pdo = getPdo();
|
||||||
|
if (!($pdo instanceof PDO)) {
|
||||||
|
if($returnExceptionOnError){
|
||||||
|
return new Exception("Failed to get database connection object");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$stmt = $pdo->prepare(
|
||||||
|
'INSERT INTO speedtest_users
|
||||||
|
(ip,ispinfo,extra,ua,lang,dl,ul,ping,jitter,log)
|
||||||
|
VALUES (?,?,?,?,?,?,?,?,?,?)'
|
||||||
|
);
|
||||||
|
$stmt->execute([
|
||||||
|
$ip, $ispinfo, $extra, $ua, $lang, $dl, $ul, $ping, $jitter, $log
|
||||||
|
]);
|
||||||
|
$id = $pdo->lastInsertId();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
if($returnExceptionOnError){
|
||||||
|
return $e;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isObfuscationEnabled()) {
|
||||||
|
return obfuscateId($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int|string $id
|
||||||
|
*
|
||||||
|
* @return array|null|false|exception returns the speedtest data as array, null
|
||||||
|
* if no data is found for the given id or
|
||||||
|
* false or an exception if there was an error (based on returnExceptionOnError)
|
||||||
|
*
|
||||||
|
* @throws RuntimeException
|
||||||
|
*/
|
||||||
|
function getSpeedtestUserById($id,$returnExceptionOnError = false)
|
||||||
|
{
|
||||||
|
$pdo = getPdo();
|
||||||
|
if (!($pdo instanceof PDO)) {
|
||||||
|
if($returnExceptionOnError){
|
||||||
|
return new Exception("Failed to get database connection object");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isObfuscationEnabled()) {
|
||||||
|
$id = deobfuscateId($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$stmt = $pdo->prepare(
|
||||||
|
'SELECT
|
||||||
|
id, timestamp, ip, ispinfo, ua, lang, dl, ul, ping, jitter, log, extra
|
||||||
|
FROM speedtest_users
|
||||||
|
WHERE id = :id'
|
||||||
|
);
|
||||||
|
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
|
||||||
|
$stmt->execute();
|
||||||
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
if($returnExceptionOnError){
|
||||||
|
return $e;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_array($row)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$row['id_formatted'] = $row['id'];
|
||||||
|
if (isObfuscationEnabled()) {
|
||||||
|
$row['id_formatted'] = obfuscateId($row['id']).' (deobfuscated: '.$row['id'].')';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $row;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array|false
|
||||||
|
*/
|
||||||
|
function getLatestSpeedtestUsers()
|
||||||
|
{
|
||||||
|
$pdo = getPdo();
|
||||||
|
if (!($pdo instanceof PDO)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
require TELEMETRY_SETTINGS_FILE;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$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 ';
|
||||||
|
|
||||||
|
if('mssql' !== $db_type) {$sql .= ' LIMIT 100 ';}
|
||||||
|
|
||||||
|
$stmt = $pdo->query($sql);
|
||||||
|
|
||||||
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
foreach ($rows as $i => $row) {
|
||||||
|
$rows[$i]['id_formatted'] = $row['id'];
|
||||||
|
if (isObfuscationEnabled()) {
|
||||||
|
$rows[$i]['id_formatted'] = obfuscateId($row['id']).' (deobfuscated: '.$row['id'].')';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $rows;
|
||||||
|
}
|
30
results/telemetry_mssql.sql
Normal 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
|
||||||
|
|
||||||
|
|
2
results/telemetry_mysql.sql
Normal file → Executable file
|
@ -32,7 +32,7 @@ CREATE TABLE `speedtest_users` (
|
||||||
`ping` text,
|
`ping` text,
|
||||||
`jitter` text,
|
`jitter` text,
|
||||||
`log` longtext
|
`log` longtext
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Indexes for dumped tables
|
-- Indexes for dumped tables
|
||||||
|
|
0
results/telemetry_postgresql.sql
Normal file → Executable file
45
results/telemetry_settings.php
Normal file → Executable file
|
@ -1,26 +1,35 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
$db_type="mysql"; //Type of db: "mysql", "sqlite" or "postgresql"
|
// Type of db: "mssql", "mysql", "sqlite" or "postgresql"
|
||||||
$stats_password="PASSWORD"; //password to login to stats.php. Change this!!!
|
$db_type = 'mysql';
|
||||||
$enable_id_obfuscation=false; //if set to true, test IDs will be obfuscated to prevent users from guessing URLs of other tests
|
// Password to login to stats.php. Change this!!!
|
||||||
$redact_ip_addresses=false; //if set to true, IP addresses will be redacted from IP and ISP info fields, as well as the log
|
$stats_password = 'PASSWORD';
|
||||||
|
// If set to true, test IDs will be obfuscated to prevent users from guessing URLs of other tests
|
||||||
|
$enable_id_obfuscation = false;
|
||||||
|
// If set to true, IP addresses will be redacted from IP and ISP info fields, as well as the log
|
||||||
|
$redact_ip_addresses = false;
|
||||||
|
|
||||||
// Sqlite3 settings
|
// Sqlite3 settings
|
||||||
$Sqlite_db_file = "../../speedtest_telemetry.sql";
|
$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 settings
|
||||||
$MySql_username="USERNAME";
|
$MySql_username = 'USERNAME';
|
||||||
$MySql_password="PASSWORD";
|
$MySql_password = 'PASSWORD';
|
||||||
$MySql_hostname="DB_HOSTNAME";
|
$MySql_hostname = 'DB_HOSTNAME';
|
||||||
$MySql_databasename="DB_NAME";
|
$MySql_databasename = 'DB_NAME';
|
||||||
$MySql_port="3306";
|
$MySql_port = '3306';
|
||||||
|
|
||||||
// Postgresql settings
|
// Postgresql settings
|
||||||
$PostgreSql_username="USERNAME";
|
$PostgreSql_username = 'USERNAME';
|
||||||
$PostgreSql_password="PASSWORD";
|
$PostgreSql_password = 'PASSWORD';
|
||||||
$PostgreSql_hostname="DB_HOSTNAME";
|
$PostgreSql_hostname = 'DB_HOSTNAME';
|
||||||
$PostgreSql_databasename="DB_NAME";
|
$PostgreSql_databasename = 'DB_NAME';
|
||||||
|
|
||||||
|
|
||||||
//IMPORTANT: DO NOT ADD ANYTHING BELOW THIS PHP CLOSING TAG, NOT EVEN EMPTY LINES!
|
|
||||||
?>
|
|
||||||
|
|
32
speedtest.js
Normal file → Executable file
|
@ -27,9 +27,9 @@
|
||||||
}
|
}
|
||||||
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.
|
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
|
- 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:
|
- 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 mbps
|
- dlStatus: download speed in Mbit/s
|
||||||
- ulStatus: upload speed in mbps
|
- ulStatus: upload speed in Mbit/s
|
||||||
- pingStatus: ping in ms
|
- pingStatus: ping in ms
|
||||||
- jitterStatus: jitter in ms
|
- jitterStatus: jitter in ms
|
||||||
- dlProgress: progress of the download test as a float 0-1
|
- dlProgress: progress of the download test as a float 0-1
|
||||||
|
@ -49,7 +49,7 @@ function Speedtest() {
|
||||||
this._settings = {}; //settings for the speed test 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
|
this._state = 0; //0=adding settings, 1=adding servers, 2=server selection done, 3=test running, 4=done
|
||||||
console.log(
|
console.log(
|
||||||
"LibreSpeed by Federico Dossena v5.2 - https://github.com/librespeed/speedtest"
|
"LibreSpeed by Federico Dossena v5.3.1 - https://github.com/librespeed/speedtest"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,10 +69,10 @@ Speedtest.prototype = {
|
||||||
* Invalid values or nonexistant parameters will be ignored by the speed test worker.
|
* Invalid values or nonexistant parameters will be ignored by the speed test worker.
|
||||||
*/
|
*/
|
||||||
setParameter: function(parameter, value) {
|
setParameter: function(parameter, value) {
|
||||||
if (this._state != 0)
|
if (this._state == 3)
|
||||||
throw "You cannot change the test settings after adding server or starting the test";
|
throw "You cannot change the test settings while running the test";
|
||||||
this._settings[parameter] = value;
|
this._settings[parameter] = value;
|
||||||
if(parameter === "temeletry_extra"){
|
if(parameter === "telemetry_extra"){
|
||||||
this._originalExtra=this._settings.telemetry_extra;
|
this._originalExtra=this._settings.telemetry_extra;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -191,9 +191,9 @@ Speedtest.prototype = {
|
||||||
throw "You can't select a server while the test is running";
|
throw "You can't select a server while the test is running";
|
||||||
}
|
}
|
||||||
if (this._selectServerCalled) throw "selectServer already called"; else this._selectServerCalled=true;
|
if (this._selectServerCalled) throw "selectServer already called"; else this._selectServerCalled=true;
|
||||||
/*this function goes through a list of servers. For each server, the ping is measured, then the server with the function result is called with the best server, or null if all the servers were down.
|
/*this function goes through a list of servers. For each server, the ping is measured, then the server with the function selected is called with the best server, or null if all the servers were down.
|
||||||
*/
|
*/
|
||||||
var select = function(serverList, result) {
|
var select = function(serverList, selected) {
|
||||||
//pings the specified URL, then calls the function result. Result will receive a parameter which is either the time it took to ping the URL, or -1 if something went wrong.
|
//pings the specified URL, then calls the function result. Result will receive a parameter which is either the time it took to ping the URL, or -1 if something went wrong.
|
||||||
var PING_TIMEOUT = 2000;
|
var PING_TIMEOUT = 2000;
|
||||||
var USE_PING_TIMEOUT = true; //will be disabled on unsupported browsers
|
var USE_PING_TIMEOUT = true; //will be disabled on unsupported browsers
|
||||||
|
@ -201,7 +201,7 @@ Speedtest.prototype = {
|
||||||
//IE11 doesn't support XHR timeout
|
//IE11 doesn't support XHR timeout
|
||||||
USE_PING_TIMEOUT = false;
|
USE_PING_TIMEOUT = false;
|
||||||
}
|
}
|
||||||
var ping = function(url, result) {
|
var ping = function(url, rtt) {
|
||||||
url += (url.match(/\?/) ? "&" : "?") + "cors=true";
|
url += (url.match(/\?/) ? "&" : "?") + "cors=true";
|
||||||
var xhr = new XMLHttpRequest();
|
var xhr = new XMLHttpRequest();
|
||||||
var t = new Date().getTime();
|
var t = new Date().getTime();
|
||||||
|
@ -217,11 +217,11 @@ Speedtest.prototype = {
|
||||||
if (d <= 0) d = p.duration;
|
if (d <= 0) d = p.duration;
|
||||||
if (d > 0 && d < instspd) instspd = d;
|
if (d > 0 && d < instspd) instspd = d;
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
result(instspd);
|
rtt(instspd);
|
||||||
} else result(-1);
|
} else rtt(-1);
|
||||||
}.bind(this);
|
}.bind(this);
|
||||||
xhr.onerror = function() {
|
xhr.onerror = function() {
|
||||||
result(-1);
|
rtt(-1);
|
||||||
}.bind(this);
|
}.bind(this);
|
||||||
xhr.open("GET", url);
|
xhr.open("GET", url);
|
||||||
if (USE_PING_TIMEOUT) {
|
if (USE_PING_TIMEOUT) {
|
||||||
|
@ -271,7 +271,7 @@ Speedtest.prototype = {
|
||||||
)
|
)
|
||||||
bestServer = serverList[i];
|
bestServer = serverList[i];
|
||||||
}
|
}
|
||||||
result(bestServer);
|
selected(bestServer);
|
||||||
}.bind(this);
|
}.bind(this);
|
||||||
var nextServer = function() {
|
var nextServer = function() {
|
||||||
if (i == serverList.length) {
|
if (i == serverList.length) {
|
||||||
|
@ -330,13 +330,13 @@ Speedtest.prototype = {
|
||||||
console.error("Speedtest onupdate event threw exception: " + e);
|
console.error("Speedtest onupdate event threw exception: " + e);
|
||||||
}
|
}
|
||||||
if (data.testState >= 4) {
|
if (data.testState >= 4) {
|
||||||
|
clearInterval(this.updater);
|
||||||
|
this._state = 4;
|
||||||
try {
|
try {
|
||||||
if (this.onend) this.onend(data.testState == 5);
|
if (this.onend) this.onend(data.testState == 5);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Speedtest onend event threw exception: " + e);
|
console.error("Speedtest onend event threw exception: " + e);
|
||||||
}
|
}
|
||||||
clearInterval(this.updater);
|
|
||||||
this._state = 4;
|
|
||||||
}
|
}
|
||||||
}.bind(this);
|
}.bind(this);
|
||||||
this.updater = setInterval(
|
this.updater = setInterval(
|
||||||
|
|
17
speedtest_worker.js
Normal file → Executable file
|
@ -60,11 +60,12 @@ var settings = {
|
||||||
garbagePhp_chunkSize: 100, // size of chunks sent by garbage.php (can be different if enable_quirks is active)
|
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
|
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.
|
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
|
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)
|
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
|
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
|
telemetry_extra: "", //extra data that can be passed to the telemetry through the settings
|
||||||
|
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
|
var xhr = null; // array of currently active xhr requests
|
||||||
|
@ -403,8 +404,8 @@ function dlTest(done) {
|
||||||
var speed = totLoaded / (t / 1000.0);
|
var speed = totLoaded / (t / 1000.0);
|
||||||
if (settings.time_auto) {
|
if (settings.time_auto) {
|
||||||
//decide how much to shorten the test. Every 200ms, the test is shortened by the bonusT calculated here
|
//decide how much to shorten the test. Every 200ms, the test is shortened by the bonusT calculated here
|
||||||
var bonus = (6.4 * speed) / 100000;
|
var bonus = (5.0 * speed) / 100000;
|
||||||
bonusT += bonus > 800 ? 800 : bonus;
|
bonusT += bonus > 400 ? 400 : bonus;
|
||||||
}
|
}
|
||||||
//update status
|
//update status
|
||||||
dlStatus = ((speed * 8 * settings.overheadCompensationFactor) / (settings.useMebibits ? 1048576 : 1000000)).toFixed(2); // speed is multiplied by 8 to go from bytes to bits, overhead compensation is applied, then everything is divided by 1048576 or 1000000 to go to megabits/mebibits
|
dlStatus = ((speed * 8 * settings.overheadCompensationFactor) / (settings.useMebibits ? 1048576 : 1000000)).toFixed(2); // speed is multiplied by 8 to go from bytes to bits, overhead compensation is applied, then everything is divided by 1048576 or 1000000 to go to megabits/mebibits
|
||||||
|
@ -422,7 +423,7 @@ function dlTest(done) {
|
||||||
200
|
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
|
var ulCalled = false; // used to prevent multiple accidental calls to ulTest
|
||||||
function ulTest(done) {
|
function ulTest(done) {
|
||||||
tverb("ulTest");
|
tverb("ulTest");
|
||||||
|
@ -473,7 +474,7 @@ function ulTest(done) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ie11workaround) {
|
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() {
|
xhr[i].onload = xhr[i].onerror = function() {
|
||||||
tverb("ul stream progress event (ie11wa)");
|
tverb("ul stream progress event (ie11wa)");
|
||||||
totLoaded += reqsmall.size;
|
totLoaded += reqsmall.size;
|
||||||
|
@ -551,8 +552,8 @@ function ulTest(done) {
|
||||||
var speed = totLoaded / (t / 1000.0);
|
var speed = totLoaded / (t / 1000.0);
|
||||||
if (settings.time_auto) {
|
if (settings.time_auto) {
|
||||||
//decide how much to shorten the test. Every 200ms, the test is shortened by the bonusT calculated here
|
//decide how much to shorten the test. Every 200ms, the test is shortened by the bonusT calculated here
|
||||||
var bonus = (6.4 * speed) / 100000;
|
var bonus = (5.0 * speed) / 100000;
|
||||||
bonusT += bonus > 800 ? 800 : bonus;
|
bonusT += bonus > 400 ? 400 : bonus;
|
||||||
}
|
}
|
||||||
//update status
|
//update status
|
||||||
ulStatus = ((speed * 8 * settings.overheadCompensationFactor) / (settings.useMebibits ? 1048576 : 1000000)).toFixed(2); // speed is multiplied by 8 to go from bytes to bits, overhead compensation is applied, then everything is divided by 1048576 or 1000000 to go to megabits/mebibits
|
ulStatus = ((speed * 8 * settings.overheadCompensationFactor) / (settings.useMebibits ? 1048576 : 1000000)).toFixed(2); // speed is multiplied by 8 to go from bytes to bits, overhead compensation is applied, then everything is divided by 1048576 or 1000000 to go to megabits/mebibits
|
||||||
|
|