Skip to content

Commit cadf784

Browse files
feat: add LibreSpeedtest
1 parent 2aafb90 commit cadf784

24 files changed

+3215
-0
lines changed
46.7 MB
Binary file not shown.

public/speedtest/backend/empty.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
header('HTTP/1.1 200 OK');
4+
5+
if (isset($_GET['cors'])) {
6+
header('Access-Control-Allow-Origin: *');
7+
header('Access-Control-Allow-Methods: GET, POST');
8+
header('Access-Control-Allow-Headers: Content-Encoding, Content-Type');
9+
}
10+
11+
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0, s-maxage=0');
12+
header('Cache-Control: post-check=0, pre-check=0', false);
13+
header('Pragma: no-cache');
14+
header('Connection: keep-alive');
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
3+
// Disable Compression
4+
@ini_set('zlib.output_compression', 'Off');
5+
@ini_set('output_buffering', 'Off');
6+
@ini_set('output_handler', '');
7+
8+
/**
9+
* @return int
10+
*/
11+
function getChunkCount()
12+
{
13+
if (
14+
!array_key_exists('ckSize', $_GET)
15+
|| !ctype_digit($_GET['ckSize'])
16+
|| (int) $_GET['ckSize'] <= 0
17+
) {
18+
return 4;
19+
}
20+
21+
if ((int) $_GET['ckSize'] > 1024) {
22+
return 1024;
23+
}
24+
25+
return (int) $_GET['ckSize'];
26+
}
27+
28+
/**
29+
* @return void
30+
*/
31+
function sendHeaders()
32+
{
33+
header('HTTP/1.1 200 OK');
34+
35+
if (isset($_GET['cors'])) {
36+
header('Access-Control-Allow-Origin: *');
37+
header('Access-Control-Allow-Methods: GET, POST');
38+
}
39+
40+
// Indicate a file download
41+
header('Content-Description: File Transfer');
42+
header('Content-Type: application/octet-stream');
43+
header('Content-Disposition: attachment; filename=random.dat');
44+
header('Content-Transfer-Encoding: binary');
45+
46+
// Cache settings: never cache this request
47+
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0, s-maxage=0');
48+
header('Cache-Control: post-check=0, pre-check=0', false);
49+
header('Pragma: no-cache');
50+
}
51+
52+
// Determine how much data we should send
53+
$chunks = getChunkCount();
54+
55+
// Generate data
56+
$data = openssl_random_pseudo_bytes(1048576);
57+
58+
// Deliver chunks of 1048576 bytes
59+
sendHeaders();
60+
for ($i = 0; $i < $chunks; $i++) {
61+
echo $data;
62+
flush();
63+
}
530 KB
Binary file not shown.

public/speedtest/backend/getIP.php

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
<?php
2+
3+
/*
4+
* This script detects the client's IP address and fetches ISP info from ipinfo.io/
5+
* 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 (or an empty string if isp detection is disabled or if it failed).
6+
* 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.
7+
*/
8+
9+
error_reporting(0);
10+
11+
define('API_KEY_FILE', 'getIP_ipInfo_apikey.php');
12+
define('SERVER_LOCATION_CACHE_FILE', 'getIP_serverLocation.php');
13+
define('OFFLINE_IPINFO_DB_FILE', 'country_asn.mmdb');
14+
15+
require_once 'getIP_util.php';
16+
17+
function getLocalOrPrivateIpInfo($ip){
18+
// ::1/128 is the only localhost ipv6 address. there are no others, no need to strpos this
19+
if ('::1' === $ip) {
20+
return 'localhost IPv6 access';
21+
}
22+
// simplified IPv6 link-local address (should match fe80::/10)
23+
if (stripos($ip, 'fe80:') === 0) {
24+
return 'link-local IPv6 access';
25+
}
26+
// fc00::/7 Unique Local IPv6 Unicast Addresses
27+
if (preg_match('/^(fc|fd)([0-9a-f]{0,4}:){1,7}[0-9a-f]{1,4}$/i', $ip) === 1) {
28+
return 'ULA IPv6 access';
29+
}
30+
// anything within the 127/8 range is localhost ipv4, the ip must start with 127.0
31+
if (strpos($ip, '127.') === 0) {
32+
return 'localhost IPv4 access';
33+
}
34+
// 10/8 private IPv4
35+
if (strpos($ip, '10.') === 0) {
36+
return 'private IPv4 access';
37+
}
38+
// 172.16/12 private IPv4
39+
if (preg_match('/^172\.(1[6-9]|2\d|3[01])\./', $ip) === 1) {
40+
return 'private IPv4 access';
41+
}
42+
// 192.168/16 private IPv4
43+
if (strpos($ip, '192.168.') === 0) {
44+
return 'private IPv4 access';
45+
}
46+
// IPv4 link-local
47+
if (strpos($ip, '169.254.') === 0) {
48+
return 'link-local IPv4 access';
49+
}
50+
return null;
51+
}
52+
53+
function getIspInfo_ipinfoApi($ip){
54+
if (!file_exists(API_KEY_FILE) || !is_readable(API_KEY_FILE)){
55+
return null;
56+
}
57+
require API_KEY_FILE;
58+
if(empty($IPINFO_APIKEY)){
59+
return null;
60+
}
61+
$json = file_get_contents('https://ipinfo.io/' . $ip . '/json?token=' . $IPINFO_APIKEY);
62+
if (!is_string($json)) {
63+
return null;
64+
}
65+
$data = json_decode($json, true);
66+
if (!is_array($data)) {
67+
return null;
68+
}
69+
$isp=null;
70+
//ISP name, if present, is either in org or asn.name
71+
if (array_key_exists('org', $data) && is_string($data['org']) && !empty($data['org'])) {
72+
// Remove AS##### from ISP name, if present
73+
$isp = preg_replace('/AS\\d+\\s/', '', $data['org']);
74+
} elseif (array_key_exists('asn', $data) && is_array($data['asn']) && !empty($data['asn']) && array_key_exists('name', $data['asn']) && is_string($data['asn']['name'])) {
75+
$isp = $data['asn']['name'];
76+
} else{
77+
return null;
78+
}
79+
$country=null;
80+
if(array_key_exists('country',$data) && is_string($data['country'])){
81+
$country = $data['country'];
82+
}
83+
//If requested by the client (and we have the required information), calculate the distance
84+
$distance=null;
85+
if(isset($_GET['distance']) && ($_GET['distance']==='mi' || $_GET['distance']==='km') && array_key_exists('loc', $data) && is_string($data['loc'])){
86+
$unit = $_GET['distance'];
87+
$clientLoc = $data['loc'];
88+
$serverLoc = null;
89+
if (file_exists(SERVER_LOCATION_CACHE_FILE) && is_readable(SERVER_LOCATION_CACHE_FILE)) {
90+
require SERVER_LOCATION_CACHE_FILE;
91+
}
92+
if (!is_string($serverLoc) || empty($serverLoc)) {
93+
$json = file_get_contents('https://ipinfo.io/json?token=' . $IPINFO_APIKEY);
94+
if (!is_string($json)) {
95+
return null;
96+
}
97+
$sdata = json_decode($json, true);
98+
if (!is_array($sdata) || !array_key_exists('loc', $sdata) || !is_string($sdata['loc']) || empty($sdata['loc'])) {
99+
return null;
100+
}
101+
$serverLoc = $sdata['loc'];
102+
file_put_contents(SERVER_LOCATION_CACHE_FILE, "<?php\n\n\$serverLoc = '" . addslashes($serverLoc) . "';\n");
103+
}
104+
list($clientLatitude, $clientLongitude) = explode(',', $clientLoc);
105+
list($serverLatitude, $serverLongitude) = explode(',', $serverLoc);
106+
//distance calculation adapted from http://www.codexworld.com
107+
$rad = M_PI / 180;
108+
$dist = acos(sin($clientLatitude * $rad) * sin($serverLatitude * $rad) + cos($clientLatitude * $rad) * cos($serverLatitude * $rad) * cos(($clientLongitude - $serverLongitude) * $rad)) / $rad * 60 * 1.853;
109+
if ($unit === 'mi') {
110+
$dist /= 1.609344;
111+
$dist = round($dist, -1);
112+
if ($dist < 15) {
113+
$dist = '<15';
114+
}
115+
$distance = $dist . ' mi';
116+
}elseif ($unit === 'km') {
117+
$dist = round($dist, -1);
118+
if ($dist < 20) {
119+
$dist = '<20';
120+
}
121+
$distance = $dist . ' km';
122+
}
123+
}
124+
$processedString=$ip.' - '.$isp;
125+
if(is_string($country)){
126+
$processedString.=', '.$country;
127+
}
128+
if(is_string($distance)){
129+
$processedString.=' ('.$distance.')';
130+
}
131+
return json_encode([
132+
'processedString' => $processedString,
133+
'rawIspInfo' => $data ?: '',
134+
]);
135+
}
136+
137+
if (PHP_MAJOR_VERSION >= 8){
138+
require_once("geoip2.phar");
139+
}
140+
function getIspInfo_ipinfoOfflineDb($ip){
141+
if (PHP_MAJOR_VERSION < 8 || !file_exists(OFFLINE_IPINFO_DB_FILE) || !is_readable(OFFLINE_IPINFO_DB_FILE)){
142+
return null;
143+
}
144+
$reader = new MaxMind\Db\Reader(OFFLINE_IPINFO_DB_FILE);
145+
$data = $reader->get($ip);
146+
if(!is_array($data)){
147+
return null;
148+
}
149+
$processedString = $ip.' - ' . $data['as_name'] . ', ' . $data['country_name'];
150+
return json_encode([
151+
'processedString' => $processedString,
152+
'rawIspInfo' => $data ?: '',
153+
]);
154+
}
155+
156+
function formatResponse_simple($ip,$ispName=null){
157+
$processedString=$ip;
158+
if(is_string($ispName)){
159+
$processedString.=' - '.$ispName;
160+
}
161+
return json_encode([
162+
'processedString' => $processedString,
163+
'rawIspInfo' => '',
164+
]);
165+
}
166+
167+
header('Content-Type: application/json; charset=utf-8');
168+
if (isset($_GET['cors'])) {
169+
header('Access-Control-Allow-Origin: *');
170+
header('Access-Control-Allow-Methods: GET, POST');
171+
}
172+
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0, s-maxage=0');
173+
header('Cache-Control: post-check=0, pre-check=0', false);
174+
header('Pragma: no-cache');
175+
176+
$ip = getClientIp();
177+
//if the user requested the ISP info, we first try to fetch it using ipinfo.io (if there is no api key set it fails without sending data, it can also fail because of rate limiting or invalid responses), then we try with the offline db, if that also fails (or if ISP info was not requested) we just respond with the IP address
178+
if(isset($_GET['isp'])){
179+
$localIpInfo = getLocalOrPrivateIpInfo($ip);
180+
//local ip, no need to fetch further information
181+
if (is_string($localIpInfo)) {
182+
echo formatResponse_simple($ip,$localIpInfo);
183+
}else{
184+
$r=getIspInfo_ipinfoApi($ip);
185+
if(!is_null($r)){
186+
echo $r;
187+
}else{
188+
$r=getIspInfo_ipinfoOfflineDb($ip);
189+
if(!is_null($r)){
190+
echo $r;
191+
}else{
192+
echo formatResponse_simple($ip);
193+
}
194+
}
195+
}
196+
}else{
197+
echo formatResponse_simple($ip);
198+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?php
2+
3+
// put your token between the quotes if you have one
4+
$IPINFO_APIKEY = '';
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
/**
4+
* @return string
5+
*/
6+
function getClientIp() {
7+
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
8+
$ip = $_SERVER['HTTP_CLIENT_IP'];
9+
} elseif (!empty($_SERVER['HTTP_X_REAL_IP'])) {
10+
$ip = $_SERVER['HTTP_X_REAL_IP'];
11+
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
12+
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
13+
$ip = preg_replace('/,.*/', '', $ip); # hosts are comma-separated, client is first
14+
} else {
15+
$ip = $_SERVER['REMOTE_ADDR'];
16+
}
17+
18+
return preg_replace('/^::ffff:/', '', $ip);
19+
}

public/speedtest/favicon.ico

16.6 KB
Binary file not shown.

0 commit comments

Comments
 (0)