File SpeedTest++-1.11+git20240407.b743996.obscpio of Package SpeedTest++
07070100000000000081A40000000000000000000000016612D51D00000181000000000000000000000000000000000000003100000000SpeedTest++-1.11+git20240407.b743996/.travis.ymlsudo: false
language: cpp
compiler:
- gcc
install:
- if [ "$CXX" = "g++" ]; then export CXX="g++-4.8" CC="gcc-4.8"; fi
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- gcc-4.8
- g++-4.8
- cmake
- libcurl4-openssl-dev
- libxml2-dev
- libssl-dev
script: mkdir cmake_build && cd cmake_build && cmake -DCMAKE_BUILD_TYPE=Release .. && make07070100000001000081A40000000000000000000000016612D51D000007FD000000000000000000000000000000000000003400000000SpeedTest++-1.11+git20240407.b743996/CMakeLists.txtcmake_minimum_required(VERSION 2.7)
project(SpeedTest)
set (SpeedTest_VERSION_MAJOR 1)
set (SpeedTest_VERSION_MINOR 15)
set (SpeedTest_SYSTEM_PROCESSOR ${CMAKE_SYSTEM_PROCESSOR})
set (SpeedTest_SYSTEM ${CMAKE_SYSTEM})
set (SpeedTest_AUTHOR "Francesco Laurita <francesco.laurita@gmail.com>")
set (SpeedTest_HOME_PAGE "https://github.com/taganaka/SpeedTest")
set (SpeedTest_USER_AGENT "Mozilla/5.0 ${CMAKE_SYSTEM}; U; ${CMAKE_SYSTEM_PROCESSOR}; en-us (KHTML, like Gecko) SpeedTest++/${SpeedTest_VERSION_MAJOR}.${SpeedTest_VERSION_MINOR}")
set (SpeedTest_SERVER_LIST_URL "https://www.speedtest.net/speedtest-servers.php")
set (SpeedTest_IP_INFO_API_URL "https://api.ipapi.is/")
set (SpeedTest_API_URL "http://www.speedtest.net/api/api.php")
set (SpeedTest_API_REFERER "http://c.speedtest.net/flash/speedtest.swf")
set (SpeedTest_API_KEY "297aae72")
set (SpeedTest_MIN_SERVER_VERSION "2.3")
set (SpeedTest_LATENCY_SAMPLE_SIZE 80)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Wno-unused-result -Wno-pessimizing-move -Wno-deprecated-declarations")
set(SOURCE_FILES
main.cpp
SpeedTest.cpp
SpeedTest.h
SpeedTestClient.cpp
SpeedTestClient.h
TestConfigTemplate.h
MD5Util.cpp
MD5Util.h
DataTypes.h
CmdOptions.h)
configure_file (
"${PROJECT_SOURCE_DIR}/SpeedTestConfig.h.in"
"${PROJECT_BINARY_DIR}/SpeedTestConfig.h"
)
include_directories("${PROJECT_BINARY_DIR}")
add_executable(SpeedTest ${SOURCE_FILES})
INCLUDE (CheckIncludeFiles)
find_package(CURL REQUIRED)
find_package(LibXml2 REQUIRED)
if (NOT (APPLE))
find_package(OpenSSL REQUIRED)
else()
CHECK_INCLUDE_FILES("CommonCrypto/CommonDigest.h" HAVE_COMMON_DIGEST_H)
endif()
include_directories(${CURL_INCLUDE_DIRS} ${LIBXML2_INCLUDE_DIR})
target_link_libraries(SpeedTest ${CURL_LIBRARIES} ${LIBXML2_LIBRARIES} -lpthread ${OPENSSL_LIBRARIES})
install(TARGETS SpeedTest RUNTIME DESTINATION bin)
07070100000002000081A40000000000000000000000016612D51D00000A60000000000000000000000000000000000000003200000000SpeedTest++-1.11+git20240407.b743996/CmdOptions.h//
// Created by Francesco Laurita on 9/9/16.
//
#ifndef SPEEDTEST_CMDOPTIONS_H
#define SPEEDTEST_CMDOPTIONS_H
#include <getopt.h>
enum OutputType { verbose, text, json };
typedef struct program_options_t {
bool help = false;
bool latency = false;
bool download = false;
bool upload = false;
bool share = false;
bool insecure = false;
std::string selected_server = "";
OutputType output_type = OutputType::verbose;
} ProgramOptions;
static struct option CmdLongOptions[] = {
{"help", no_argument, 0, 'h' },
{"latency", no_argument, 0, 'l' },
{"download", no_argument, 0, 'd' },
{"upload", no_argument, 0, 'u' },
{"share", no_argument, 0, 's' },
{"insecure", no_argument, 0, 'i' },
{"test-server", required_argument, 0, 't' },
{"output", required_argument, 0, 'o' },
{0, 0, 0, 0 }
};
const char *optStr = "hldusiqt:o:";
bool ParseOptions(const int argc, const char **argv, ProgramOptions& options){
int long_index =0;
int opt = 0;
while ((opt = getopt_long(argc, (char **)argv, optStr, CmdLongOptions, &long_index )) != -1) {
switch (opt){
case 'h':
options.help = true;
break;
case 'l':
options.latency = true;
break;
case 'd':
options.download = true;
break;
case 'u':
options.upload = true;
break;
case 's':
options.share = true;
break;
case 'i':
options.insecure = true;
break;
case 't':
options.selected_server.append(optarg);
break;
case 'o':
if (strcmp(optarg, "verbose") == 0)
options.output_type = OutputType::verbose;
else if (strcmp(optarg, "text") == 0)
options.output_type = OutputType::text;
else if (strcmp(optarg, "json") == 0)
options.output_type = OutputType::json;
else {
std::cerr << "Unsupported output type " << optarg << std::endl;
std::cerr << "Supported output type: default, text, json" <<std::endl;
return false;
}
break;
default:
return false;
}
}
return true;
}
#endif //SPEEDTEST_CMDOPTIONS_H
07070100000003000081A40000000000000000000000016612D51D00000331000000000000000000000000000000000000003100000000SpeedTest++-1.11+git20240407.b743996/DataTypes.h//
// Created by Francesco Laurita on 6/8/16.
//
#ifndef SPEEDTEST_DATATYPES_H
#define SPEEDTEST_DATATYPES_H
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
static const float EARTH_RADIUS_KM = 6371.0;
typedef struct ip_info_t {
std::string ip_address;
std::string isp;
float lat;
float lon;
} IPInfo;
typedef struct server_info_t {
std::string url;
std::string name;
std::string country;
std::string country_code;
std::string host;
std::string sponsor;
int id;
float lat;
float lon;
float distance;
} ServerInfo;
typedef struct test_config_t {
long start_size;
long max_size;
long incr_size;
long buff_size;
long min_test_time_ms;
int concurrency;
std::string label;
} TestConfig;
#endif //SPEEDTEST_DATATYPES_H
07070100000004000081A40000000000000000000000016612D51D000001D6000000000000000000000000000000000000003000000000SpeedTest++-1.11+git20240407.b743996/DockerfileFROM ubuntu:latest as builder
RUN mkdir -p /tmp/build /tmp/src
COPY *.h *.cpp *.h.in CMakeLists.txt /tmp/src/
RUN apt-get update && apt-get install -y g++ cmake make libcurl4-openssl-dev libxml2-dev libssl-dev && \
cd /tmp/build && cmake -DCMAKE_BUILD_TYPE=Release ../src && make install
FROM ubuntu:latest
RUN apt-get update && apt-get install -y libxml2 libcurl4
COPY --from=builder /tmp/build/SpeedTest /usr/local/bin
ENTRYPOINT ["/usr/local/bin/SpeedTest"]
07070100000005000081A40000000000000000000000016612D51D000001E5000000000000000000000000000000000000003100000000SpeedTest++-1.11+git20240407.b743996/MD5Util.cpp//
// Created by Francesco Laurita on 6/3/16.
//
#include <sstream>
#include "MD5Util.h"
std::string MD5Util::hexDigest(const std::string &str) {
unsigned char digest[MD5_DIGEST_LENGTH];
MD5_CTX ctx;
MD5_Init(&ctx);
MD5_Update(&ctx, str.c_str(), str.size());
MD5_Final(digest, &ctx);
char hexDigest[33] = {'\0'};
for (int i = 0; i < 16; i++)
std::sprintf(&hexDigest[i*2], "%02x", (unsigned int)digest[i]);
return std::string(hexDigest);
}
07070100000006000081A40000000000000000000000016612D51D00000195000000000000000000000000000000000000002F00000000SpeedTest++-1.11+git20240407.b743996/MD5Util.h//
// Created by Francesco Laurita on 6/3/16.
//
#ifndef SPEEDTEST_MD5UTIL_H
#define SPEEDTEST_MD5UTIL_H
#if defined(__APPLE__)
# define COMMON_DIGEST_FOR_OPENSSL
# include <CommonCrypto/CommonDigest.h>
#include <string>
# define SHA1 CC_SHA1
#else
# include <openssl/md5.h>
#endif
class MD5Util {
public:
static std::string hexDigest(const std::string &str);
};
#endif //SPEEDTEST_MD5UTIL_H
07070100000007000081A40000000000000000000000016612D51D00000A0D000000000000000000000000000000000000002F00000000SpeedTest++-1.11+git20240407.b743996/README.md[](https://travis-ci.org/taganaka/SpeedTest)
# SpeedTest++
Yet another unofficial speedtest.net client cli interface
It supports the new (undocumented) raw TCP protocol for better accuracy.
## Features
1. Best server discovery based on speed and distance from you.
2. Line type discovery to select the best test profile based on your line speed.
3. Aggressive multi-threading program in order to saturate your bandwidth quickly.
4. Test supported: Ping / Jitter / Download speed / Upload speed / Packet loss (UDP).
5. Provide a URL to the speedtest.net share results image using option --share
## Installation
### Requirements
1. A modern C++ compiler
2. cmake
3. libcurl
4. libssl
5. libxml2
### On Mac OS X
```
$ brew install cmake
$ cd cmake_build
$ cmake -DCMAKE_BUILD_TYPE=Release ..
$ make install
```
### On Ubuntu/Debian
```
$ sudo apt-get install build-essential libcurl4-openssl-dev libxml2-dev libssl-dev cmake
$ git clone https://github.com/taganaka/SpeedTest
$ cd SpeedTest
$ cmake -DCMAKE_BUILD_TYPE=Release .
$ sudo make install
```
### On OpenSuse
```
$ sudo zypper install cmake gcc-c++ libcurl-devel libxml2-devel libopenssl-devel git
$ git clone https://github.com/taganaka/SpeedTest
$ cd SpeedTest
$ cmake -DCMAKE_BUILD_TYPE=Release .
$ sudo make install
```
## Usage
```
$ ./SpeedTest --help
SpeedTest++ version 1.8
Speedtest.net command line interface
Info: https://github.com/taganaka/SpeedTest
Author: Francesco Laurita <francesco.laurita@gmail.com>
Usage: ./SpeedTest [--latency] [--quality] [--download] [--upload] [--share] [--help]
[--test-server host:port] [--quality-server host:port] [--output verbose|text]
optional arguments:
--help Show this message and exit
--latency Perform latency test only
--quality Perform quality test only. It includes latency test
--download Perform download test only. It includes latency test
--upload Perform upload test only. It includes latency test
--share Generate and provide a URL to the speedtest.net share results image
--test-server host:port Run speed test against a specific server
--quality-server host:port Run line quality test against a specific server
--output verbose|text Set output type. Default: verbose
$
```
## License
SpeedTest++ is available as open source program under the terms of the [MIT License](http://opensource.org/licenses/MIT).
07070100000008000081A40000000000000000000000016612D51D00004726000000000000000000000000000000000000003300000000SpeedTest++-1.11+git20240407.b743996/SpeedTest.cpp//
// Created by Francesco Laurita on 5/29/16.
//
#include <cmath>
#include <iomanip>
#include "SpeedTest.h"
#include "MD5Util.h"
#include <netdb.h>
#include "json.h"
SpeedTest::SpeedTest(float minServerVersion):
mLatency(0),
mUploadSpeed(0),
mDownloadSpeed(0) {
curl_global_init(CURL_GLOBAL_DEFAULT);
mIpInfo = IPInfo();
mServerList = std::vector<ServerInfo>();
mMinSupportedServer = minServerVersion;
}
SpeedTest::~SpeedTest() {
curl_global_cleanup();
mServerList.clear();
}
bool SpeedTest::ipInfo(IPInfo& info) {
if (!mIpInfo.ip_address.empty()){
info = mIpInfo;
return true;
}
std::stringstream oss;
auto code = httpGet(SPEED_TEST_IP_INFO_API_URL, oss);
if (code == CURLE_OK){
auto values = SpeedTest::parseJSON(oss.str());
mIpInfo = IPInfo();
try {
mIpInfo.ip_address = values["ip_address"];
mIpInfo.isp = values["isp"];
mIpInfo.lat = std::stof(values["lat"]);
mIpInfo.lon = std::stof(values["lon"]);
} catch(...) {}
values.clear();
oss.clear();
info = mIpInfo;
return true;
}
return false;
}
const std::vector<ServerInfo>& SpeedTest::serverList() {
if (!mServerList.empty())
return mServerList;
int http_code = 0;
if (fetchServers(SPEED_TEST_SERVER_LIST_URL, mServerList, http_code) && http_code == 200){
return mServerList;
}
return mServerList;
}
const ServerInfo SpeedTest::bestServer(const int sample_size, std::function<void(bool)> cb) {
auto best = findBestServerWithin(serverList(), mLatency, sample_size, cb);
SpeedTestClient client = SpeedTestClient(best);
testLatency(client, SPEED_TEST_LATENCY_SAMPLE_SIZE, mLatency);
client.close();
return best;
}
bool SpeedTest::setServer(ServerInfo& server){
SpeedTestClient client = SpeedTestClient(server);
if (client.connect() && client.version() >= mMinSupportedServer){
if (!testLatency(client, SPEED_TEST_LATENCY_SAMPLE_SIZE, mLatency)){
return false;
}
} else {
client.close();
return false;
}
client.close();
return true;
}
bool SpeedTest::downloadSpeed(const ServerInfo &server, const TestConfig &config, double& result, std::function<void(bool)> cb) {
opFn pfunc = &SpeedTestClient::download;
mDownloadSpeed = execute(server, config, pfunc, cb);
result = mDownloadSpeed;
return true;
}
bool SpeedTest::uploadSpeed(const ServerInfo &server, const TestConfig &config, double& result, std::function<void(bool)> cb) {
opFn pfunc = &SpeedTestClient::upload;
mUploadSpeed = execute(server, config, pfunc, cb);
result = mUploadSpeed;
return true;
}
const long &SpeedTest::latency() {
return mLatency;
}
bool SpeedTest::jitter(const ServerInfo &server, long& result, const int sample) {
auto client = SpeedTestClient(server);
double current_jitter = 0;
long previous_ms = LONG_MAX;
if (client.connect()){
for (int i = 0; i < sample; i++){
long ms = 0;
if (client.ping(ms)){
if (previous_ms == LONG_MAX) {
previous_ms = ms;
} else {
current_jitter += std::abs(previous_ms - ms);
}
}
}
client.close();
} else {
return false;
}
result = (long) std::floor(current_jitter / sample);
return true;
}
bool SpeedTest::share(const ServerInfo& server, std::string& image_url) {
std::stringstream hash;
hash << std::setprecision(0) << std::fixed << mLatency
<< "-" << std::setprecision(2) << std::fixed << (mUploadSpeed * 1000)
<< "-" << std::setprecision(2) << std::fixed << (mDownloadSpeed * 1000)
<< "-" << SPEED_TEST_API_KEY;
std::string hex_digest = MD5Util::hexDigest(hash.str());
std::stringstream post_data;
post_data << "download=" << std::setprecision(2) << std::fixed << (mDownloadSpeed * 1000) << "&";
post_data << "ping=" << std::setprecision(0) << std::fixed << mLatency << "&";
post_data << "upload=" << std::setprecision(2) << std::fixed << (mUploadSpeed * 1000) << "&";
post_data << "pingselect=1&";
post_data << "recommendedserverid=" << server.id << "&";
post_data << "accuracy=1&";
post_data << "serverid=" << server.id << "&";
post_data << "hash=";
post_data << hex_digest;
std::stringstream result;
CURL *c = curl_easy_init();
curl_easy_setopt(c, CURLOPT_REFERER, SPEED_TEST_API_REFERER);
auto cres = SpeedTest::httpPost(SPEED_TEST_API_URL, post_data.str(), result, c);
long http_code = 0;
image_url.clear();
if (cres == CURLE_OK){
curl_easy_getinfo(c, CURLINFO_HTTP_CODE, &http_code);
if (http_code == 200 && !result.str().empty()){
auto data = SpeedTest::parseQueryString(result.str());
if (data.count("resultid") == 1){
image_url = "http://www.speedtest.net/result/" + data["resultid"] + ".png";
}
}
}
curl_easy_cleanup(c);
return !image_url.empty();
}
// private
double SpeedTest::execute(const ServerInfo &server, const TestConfig &config, const opFn &pfunc, std::function<void(bool)> cb) {
std::vector<std::thread> workers;
double overall_speed = 0;
std::mutex mtx;
for (int i = 0; i < config.concurrency; i++) {
workers.push_back(std::thread([&server, &overall_speed, &pfunc, &config, &mtx, cb](){
long start_size = config.start_size;
long max_size = config.max_size;
long incr_size = config.incr_size;
long curr_size = start_size;
auto spClient = SpeedTestClient(server);
if (spClient.connect()) {
auto start = std::chrono::steady_clock::now();
std::vector<double> partial_results;
while (curr_size < max_size){
long op_time = 0;
if ((spClient.*pfunc)(curr_size, config.buff_size, op_time)) {
double metric = (curr_size * 8) / (static_cast<double>(op_time) / 1000);
partial_results.push_back(metric);
if (cb)
cb(true);
} else {
if (cb)
cb(false);
}
curr_size += incr_size;
auto stop = std::chrono::steady_clock::now();
if (std::chrono::duration_cast<std::chrono::milliseconds>(stop - start).count() > config.min_test_time_ms)
break;
}
spClient.close();
std::sort(partial_results.begin(), partial_results.end());
size_t skip = 0;
size_t drop = 0;
if (partial_results.size() >= 10){
skip = partial_results.size() / 4;
drop = 2;
}
size_t iter = 0;
double real_sum = 0;
for (auto it = partial_results.begin() + skip; it != partial_results.end() - drop; ++it ){
iter++;
real_sum += (*it);
}
mtx.lock();
overall_speed += (real_sum / iter);
mtx.unlock();
} else {
if (cb)
cb(false);
}
}));
}
for (auto &t : workers){
t.join();
}
workers.clear();
return overall_speed / 1000 / 1000;
}
template<typename T>
T SpeedTest::deg2rad(T n) {
return (n * M_PI / 180);
}
template<typename T>
T SpeedTest::harversine(std::pair<T, T> n1, std::pair<T, T> n2) {
T lat1r = deg2rad(n1.first);
T lon1r = deg2rad(n1.second);
T lat2r = deg2rad(n2.first);
T lon2r = deg2rad(n2.second);
T u = std::sin((lat2r - lat1r) / 2);
T v = std::sin((lon2r - lon1r) / 2);
return 2.0 * EARTH_RADIUS_KM * std::asin(std::sqrt(u * u + std::cos(lat1r) * std::cos(lat2r) * v * v));
}
CURLcode SpeedTest::httpGet(const std::string &url, std::stringstream &ss, CURL *handler, long timeout) {
CURLcode code(CURLE_FAILED_INIT);
CURL* curl = SpeedTest::curl_setup(handler);
if (curl){
if (CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_FILE, &ss))
&& CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout))
&& CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, this->strict_ssl_verify))
&& CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_URL, url.c_str()))) {
code = curl_easy_perform(curl);
}
if (handler == nullptr)
curl_easy_cleanup(curl);
}
return code;
}
CURLcode SpeedTest::httpPost(const std::string &url, const std::string &postdata, std::stringstream &os, void *handler, long timeout) {
CURLcode code(CURLE_FAILED_INIT);
CURL* curl = SpeedTest::curl_setup(handler);
if (curl){
if (CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_FILE, &os))
&& CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout))
&& CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_URL, url.c_str()))
&& CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, this->strict_ssl_verify))
&& CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postdata.c_str()))) {
code = curl_easy_perform(curl);
}
if (handler == nullptr)
curl_easy_cleanup(curl);
}
return code;
}
CURL *SpeedTest::curl_setup(CURL *handler) {
CURL* curl = handler == nullptr ? curl_easy_init() : handler;
if (curl){
if (curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &writeFunc) == CURLE_OK
&& curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L) == CURLE_OK
&& curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L) == CURLE_OK
&& curl_easy_setopt(curl, CURLOPT_USERAGENT, SPEED_TEST_USER_AGENT) == CURLE_OK){
return curl;
} else {
curl_easy_cleanup(handler);
return nullptr;
}
}
return nullptr;
}
size_t SpeedTest::writeFunc(void *buf, size_t size, size_t nmemb, void *userp) {
if (userp){
std::stringstream &os = *static_cast<std::stringstream *>(userp);
std::streamsize len = size * nmemb;
if(os.write(static_cast<char*>(buf), len))
return static_cast<size_t>(len);
}
return 0;
}
std::map<std::string, std::string> SpeedTest::parseQueryString(const std::string &query) {
auto map = std::map<std::string, std::string>();
auto pairs = splitString(query, '&');
for (auto &p : pairs){
auto kv = splitString(p, '=');
if (kv.size() == 2){
map[kv[0]] = kv[1];
}
}
return map;
}
std::map<std::string, std::string> SpeedTest::parseJSON(const std::string &data) {
auto map = std::map<std::string, std::string>();
json::JSON obj;
obj = json::JSON::Load(data);
try {
map["ip_address"] = obj["ip"].ToString();
map["isp"] = obj["company"]["name"].ToString();
map["lat"] = obj["location"]["latitude"].dump();
map["lon"] = obj["location"]["longitude"].dump();
} catch(...) {}
return map;
}
std::vector<std::string> SpeedTest::splitString(const std::string &instr, const char separator) {
if (instr.empty())
return std::vector<std::string>();
std::vector<std::string> tokens;
std::size_t start = 0, end = 0;
while ((end = instr.find(separator, start)) != std::string::npos) {
std::string temp = instr.substr(start, end - start);
if (!temp.empty())
tokens.push_back(temp);
start = end + 1;
}
std::string temp = instr.substr(start);
if (!temp.empty())
tokens.push_back(temp);
return tokens;
}
ServerInfo SpeedTest::processServerXMLNode(xmlTextReaderPtr reader) {
auto name = xmlTextReaderConstName(reader);
auto nodeName = std::string((char*)name);
if (!name || nodeName != "server"){
return ServerInfo();
}
if (xmlTextReaderAttributeCount(reader) > 0){
auto info = ServerInfo();
auto server_url = xmlTextReaderGetAttribute(reader, BAD_CAST "url");
auto server_lat = xmlTextReaderGetAttribute(reader, BAD_CAST "lat");
auto server_lon = xmlTextReaderGetAttribute(reader, BAD_CAST "lon");
auto server_name = xmlTextReaderGetAttribute(reader, BAD_CAST "name");
auto server_county = xmlTextReaderGetAttribute(reader, BAD_CAST "country");
auto server_cc = xmlTextReaderGetAttribute(reader, BAD_CAST "cc");
auto server_host = xmlTextReaderGetAttribute(reader, BAD_CAST "host");
auto server_id = xmlTextReaderGetAttribute(reader, BAD_CAST "id");
auto server_sponsor = xmlTextReaderGetAttribute(reader, BAD_CAST "sponsor");
if (server_name)
info.name.append((char*)server_name);
if (server_url)
info.url.append((char*)server_url);
if (server_county)
info.country.append((char*)server_county);
if (server_cc)
info.country_code.append((char*)server_cc);
if (server_host)
info.host.append((char*)server_host);
if (server_sponsor)
info.sponsor.append((char*)server_sponsor);
if (server_id)
info.id = std::atoi((char*)server_id);
if (server_lat)
info.lat = std::stof((char*)server_lat);
if (server_lon)
info.lon = std::stof((char*)server_lon);
xmlFree(server_url);
xmlFree(server_lat);
xmlFree(server_lon);
xmlFree(server_name);
xmlFree(server_county);
xmlFree(server_cc);
xmlFree(server_host);
xmlFree(server_id);
xmlFree(server_sponsor);
return info;
}
return ServerInfo();
}
bool SpeedTest::fetchServers(const std::string& url, std::vector<ServerInfo>& target, int &http_code) {
std::stringstream oss;
target.clear();
auto isHttpSchema = url.find_first_of("http") == 0;
CURL* curl = curl_easy_init();
auto cres = httpGet(url, oss, curl, 20);
if (cres != CURLE_OK)
return false;
if (isHttpSchema) {
int req_status;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &req_status);
http_code = req_status;
if (http_code != 200){
curl_easy_cleanup(curl);
return false;
}
} else {
http_code = 200;
}
size_t len = oss.str().length();
auto *xmlbuff = (char*)calloc(len + 1, sizeof(char));
if (!xmlbuff){
std::cerr << "Unable to calloc" << std::endl;
curl_easy_cleanup(curl);
return false;
}
memcpy(xmlbuff, oss.str().c_str(), len);
oss.str("");
xmlTextReaderPtr reader = xmlReaderForMemory(xmlbuff, static_cast<int>(len), nullptr, nullptr, 0);
if (reader != nullptr) {
IPInfo ipInfo;
if (!SpeedTest::ipInfo(ipInfo)){
curl_easy_cleanup(curl);
free(xmlbuff);
xmlFreeTextReader(reader);
std::cerr << "OOPS!" <<std::endl;
return false;
}
auto ret = xmlTextReaderRead(reader);
while (ret == 1) {
ServerInfo info = processServerXMLNode(reader);
if (!info.url.empty()){
info.distance = harversine(std::make_pair(ipInfo.lat, ipInfo.lon), std::make_pair(info.lat, info.lon));
target.push_back(info);
}
ret = xmlTextReaderRead(reader);
}
xmlFreeTextReader(reader);
if (ret != 0) {
curl_easy_cleanup(curl);
free(xmlbuff);
std::cerr << "Failed to parse" << std::endl;
return false;
}
} else {
std::cerr << "Unable to initialize xml parser" << std::endl;
curl_easy_cleanup(curl);
free(xmlbuff);
return false;
}
curl_easy_cleanup(curl);
free(xmlbuff);
xmlCleanupParser();
std::sort(target.begin(), target.end(), [](const ServerInfo &a, const ServerInfo &b) -> bool {
return a.distance < b.distance;
});
return true;
}
const ServerInfo SpeedTest::findBestServerWithin(const std::vector<ServerInfo> &serverList, long &latency,
const int sample_size, std::function<void(bool)> cb) {
int i = sample_size;
ServerInfo bestServer = serverList[0];
latency = INT_MAX;
for (auto &server : serverList){
auto client = SpeedTestClient(server);
if (!client.connect()){
if (cb)
cb(false);
continue;
}
if (client.version() < mMinSupportedServer){
client.close();
continue;
}
long current_latency = LONG_MAX;
if (testLatency(client, 20, current_latency)){
if (current_latency < latency){
latency = current_latency;
bestServer = server;
}
}
client.close();
if (cb)
cb(true);
if (i-- < 0){
break;
}
}
return bestServer;
}
bool SpeedTest::testLatency(SpeedTestClient &client, const int sample_size, long &latency) {
if (!client.connect()){
return false;
}
latency = INT_MAX;
long temp_latency = 0;
for (int i = 0; i < sample_size; i++){
if (client.ping(temp_latency)){
if (temp_latency < latency){
latency = temp_latency;
}
} else {
return false;
}
}
return true;
}
void SpeedTest::setInsecure(bool insecure) {
// when insecure is on, we dont want ssl cert to be verified.
// when insecure is off, we want ssl cert to be verified.
this->strict_ssl_verify = !insecure;
}
07070100000009000081A40000000000000000000000016612D51D00000B86000000000000000000000000000000000000003100000000SpeedTest++-1.11+git20240407.b743996/SpeedTest.h//
// Created by Francesco Laurita on 5/29/16.
//
#ifndef SPEEDTEST_SPEEDTEST_H
#define SPEEDTEST_SPEEDTEST_H
#include "SpeedTestConfig.h"
#include "SpeedTestClient.h"
#include <libxml/xmlreader.h>
#include <functional>
#include <cmath>
#include <curl/curl.h>
#include <fstream>
#include <sstream>
#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
#include <thread>
#include <mutex>
#include "DataTypes.h"
class SpeedTestClient;
typedef bool (SpeedTestClient::*opFn)(const long size, const long chunk_size, long &millisec);
typedef void (*progressFn)(bool success);
class SpeedTest {
public:
explicit SpeedTest(float minServerVersion);
~SpeedTest();
CURLcode httpGet(const std::string& url, std::stringstream& os, CURL *handler = nullptr, long timeout = 30);
CURLcode httpPost(const std::string& url, const std::string& postdata, std::stringstream& os, CURL *handler = nullptr, long timeout = 30);
static std::map<std::string, std::string> parseQueryString(const std::string& query);
static std::map<std::string, std::string> parseJSON(const std::string &data);
static std::vector<std::string> splitString(const std::string& instr, char separator);
bool ipInfo(IPInfo& info);
const std::vector<ServerInfo>& serverList();
const ServerInfo bestServer(int sample_size = 5, std::function<void(bool)> cb = nullptr);
bool setServer(ServerInfo& server);
void setInsecure(bool insecure = false);
const long &latency();
bool downloadSpeed(const ServerInfo& server, const TestConfig& config, double& result, std::function<void(bool)> cb = nullptr);
bool uploadSpeed(const ServerInfo& server, const TestConfig& config, double& result, std::function<void(bool)> cb = nullptr);
bool jitter(const ServerInfo& server, long& result, int sample = 40);
bool share(const ServerInfo& server, std::string& image_url);
private:
bool fetchServers(const std::string& url, std::vector<ServerInfo>& target, int &http_code);
bool testLatency(SpeedTestClient& client, int sample_size, long& latency);
const ServerInfo findBestServerWithin(const std::vector<ServerInfo>& serverList, long& latency, int sample_size = 5, std::function<void(bool)> cb = nullptr);
static CURL* curl_setup(CURL* curl = nullptr);
static size_t writeFunc(void* buf, size_t size, size_t nmemb, void* userp);
static ServerInfo processServerXMLNode(xmlTextReaderPtr reader);
double execute(const ServerInfo &server, const TestConfig &config, const opFn &fnc, std::function<void(bool)> cb = nullptr);
template <typename T>
static T deg2rad(T n);
template <typename T>
static T harversine(std::pair<T, T> n1, std::pair<T, T> n2);
IPInfo mIpInfo;
std::vector<ServerInfo> mServerList;
long mLatency;
double mUploadSpeed;
double mDownloadSpeed;
float mMinSupportedServer;
bool strict_ssl_verify;
};
#endif //SPEEDTEST_SPEEDTEST_H
0707010000000A000081A40000000000000000000000016612D51D00001A84000000000000000000000000000000000000003900000000SpeedTest++-1.11+git20240407.b743996/SpeedTestClient.cpp//
// Created by Francesco Laurita on 5/30/16.
//
#include <arpa/inet.h>
#include <netdb.h>
#include "SpeedTestClient.h"
SpeedTestClient::SpeedTestClient(const ServerInfo &serverInfo): mServerInfo(serverInfo),
mSocketFd(0),
mServerVersion(-1.0){}
SpeedTestClient::~SpeedTestClient() {
close();
}
// It returns current timestamp in ms
// It connects and initiates client/server handshaking
bool SpeedTestClient::connect() {
if (mSocketFd){
return true;
}
auto ret = mkSocket();
if (!ret)
return ret;
std::string reply;
if (!SpeedTestClient::writeLine(mSocketFd, "HI")){
close();
return false;
}
if (SpeedTestClient::readLine(mSocketFd, reply)){
std::stringstream reply_stream(reply);
std::string hello;
reply_stream >> hello >> mServerVersion;
if (reply_stream.fail()) {
close();
return false;
}
if (!reply.empty() && "HELLO" == hello){
return true;
}
}
close();
return false;
}
// It closes a connection
void SpeedTestClient::close() {
if (mSocketFd){
SpeedTestClient::writeLine(mSocketFd, "QUIT");
::close(mSocketFd);
}
}
// It executes PING command
bool SpeedTestClient::ping(long &millisec) {
if (!mSocketFd)
return false;
std::stringstream cmd;
std::string reply;
auto start = std::chrono::steady_clock::now();
cmd << "PING " << start.time_since_epoch().count();
if (!SpeedTestClient::writeLine(mSocketFd, cmd.str())){
return false;
}
if (SpeedTestClient::readLine(mSocketFd, reply)){
if (reply.substr(0, 5) == "PONG "){
auto stop = std::chrono::steady_clock::now();
millisec = std::chrono::duration_cast<std::chrono::milliseconds>(stop - start).count();
return true;
}
}
close();
return false;
}
// It executes DOWNLOAD command
bool SpeedTestClient::download(const long size, const long chunk_size, long &millisec) {
std::stringstream cmd;
cmd << "DOWNLOAD " << size;
if (!SpeedTestClient::writeLine(mSocketFd, cmd.str())){
return false;
}
char *buff = new char[chunk_size];
for (size_t i = 0; i < static_cast<size_t>(chunk_size); i++)
buff[i] = '\0';
long missing = 0;
auto start = std::chrono::steady_clock::now();
while (missing != size){
auto current = read(mSocketFd, buff, static_cast<size_t>(chunk_size));
if (current <= 0){
delete[] buff;
return false;
}
missing += current;
}
auto stop = std::chrono::steady_clock::now();
millisec = std::chrono::duration_cast<std::chrono::milliseconds>(stop - start).count();
delete[] buff;
return true;
}
// It executes UPLOAD command
bool SpeedTestClient::upload(const long size, const long chunk_size, long &millisec) {
std::stringstream cmd;
cmd << "UPLOAD " << size << "\n";
auto cmd_len = cmd.str().length();
char *buff = new char[chunk_size];
for(size_t i = 0; i < static_cast<size_t>(chunk_size); i++)
buff[i] = static_cast<char>(rand() % 256);
long missing = size;
auto start = std::chrono::steady_clock::now();
if (!SpeedTestClient::writeLine(mSocketFd, cmd.str())){
delete[] buff;
return false;
}
ssize_t w = cmd_len;
missing -= w;
while(missing > 0){
if (missing - chunk_size > 0){
w = write(mSocketFd, buff, static_cast<size_t>(chunk_size));
if (w != chunk_size){
delete[] buff;
return false;
}
missing -= w;
} else {
buff[missing - 1] = '\n';
w = write(mSocketFd, buff, static_cast<size_t>(missing));
if (w != missing){
delete[] buff;
return false;
}
missing -= w;
}
}
std::string reply;
if (!SpeedTestClient::readLine(mSocketFd, reply)){
delete[] buff;
return false;
}
auto stop = std::chrono::steady_clock::now();
std::stringstream ss;
ss << "OK " << size << " ";
millisec = std::chrono::duration_cast<std::chrono::milliseconds>(stop - start).count();
delete[] buff;
return reply.substr(0, ss.str().length()) == ss.str();
}
bool SpeedTestClient::mkSocket() {
mSocketFd = socket(AF_INET, SOCK_STREAM, 0);
if (!mSocketFd){
return false;
}
auto hostp = hostport();
#if __APPLE__
struct hostent *server = gethostbyname(hostp.first.c_str());
if (server == nullptr) {
return false;
}
#else
struct hostent server;
char tmpbuf[BUFSIZ];
struct hostent *result;
int errnop;
if (gethostbyname_r(hostp.first.c_str(), &server, (char *)&tmpbuf, BUFSIZ, &result, &errnop)) {
return false;
}
#endif
int portno = hostp.second;
struct sockaddr_in serv_addr{};
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
#if __APPLE__
memcpy(&serv_addr.sin_addr.s_addr, server->h_addr, (size_t)server->h_length);
#else
memcpy(&serv_addr.sin_addr.s_addr, server.h_addr, (size_t)server.h_length);
#endif
serv_addr.sin_port = htons(static_cast<uint16_t>(portno));
/* Dial */
return ::connect(mSocketFd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) >= 0;
}
float SpeedTestClient::version() {
return mServerVersion;
}
const std::pair<std::string, int> SpeedTestClient::hostport() {
std::string targetHost = mServerInfo.host;
std::size_t found = targetHost.find(':');
std::string host = targetHost.substr(0, found);
std::string port = targetHost.substr(found + 1, targetHost.length() - found);
return std::pair<std::string, int>(host, std::atoi(port.c_str()));
}
bool SpeedTestClient::readLine(int &fd, std::string &buffer) {
buffer.clear();
if (!fd)
return false;
char c;
while(true){
auto n = read(fd, &c, 1);
if (n == -1)
return false;
if (c == '\n' || c == '\r')
break;
buffer += c;
}
return true;
}
bool SpeedTestClient::writeLine(int &fd, const std::string &buffer) {
if (!fd)
return false;
auto len = static_cast<ssize_t >(buffer.length());
if (len == 0)
return false;
std::string buff_copy = buffer;
if (buff_copy.find_first_of('\n') == std::string::npos){
buff_copy += '\n';
len += 1;
}
auto n = write(fd, buff_copy.c_str(), len);
return n == len;
}
0707010000000B000081A40000000000000000000000016612D51D00000432000000000000000000000000000000000000003700000000SpeedTest++-1.11+git20240407.b743996/SpeedTestClient.h//
// Created by Francesco Laurita on 5/30/16.
//
#ifndef SPEEDTEST_SPEEDTESTCLIENT_H
#define SPEEDTEST_SPEEDTESTCLIENT_H
#include <ctime>
#include <netdb.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include <chrono>
#include <unistd.h>
#include "SpeedTest.h"
#include "DataTypes.h"
class SpeedTestClient {
public:
explicit SpeedTestClient(const ServerInfo& serverInfo);
~SpeedTestClient();
bool connect();
bool ping(long &millisec);
bool upload(long size, long chunk_size, long &millisec);
bool download(long size, long chunk_size, long &millisec);
float version();
const std::pair<std::string, int> hostport();
void close();
private:
bool mkSocket();
ServerInfo mServerInfo;
int mSocketFd;
float mServerVersion;
static bool readLine(int& fd, std::string& buffer);
static bool writeLine(int& fd, const std::string& buffer);
};
typedef bool (SpeedTestClient::*opFn)(const long size, const long chunk_size, long &millisec);
#endif //SPEEDTEST_SPEEDTESTCLIENT_H
0707010000000C000081A40000000000000000000000016612D51D0000035D000000000000000000000000000000000000003A00000000SpeedTest++-1.11+git20240407.b743996/SpeedTestConfig.h.in// The configured options and settings for SpeedTest
#define SpeedTest_VERSION_MAJOR @SpeedTest_VERSION_MAJOR@
#define SpeedTest_VERSION_MINOR @SpeedTest_VERSION_MINOR@
#define SpeedTest_SYSTEM_PROCESSOR "@SpeedTest_SYSTEM_PROCESSOR@"
#define SpeedTest_SYSTEM "@SpeedTest_SYSTEM@"
#define SpeedTest_AUTHOR "@SpeedTest_AUTHOR@"
#define SpeedTest_HOME_PAGE "@SpeedTest_HOME_PAGE@"
#define SPEED_TEST_USER_AGENT "@SpeedTest_USER_AGENT@"
#define SPEED_TEST_SERVER_LIST_URL "@SpeedTest_SERVER_LIST_URL@"
#define SPEED_TEST_IP_INFO_API_URL "@SpeedTest_IP_INFO_API_URL@"
#define SPEED_TEST_API_URL "@SpeedTest_API_URL@"
#define SPEED_TEST_API_REFERER "@SpeedTest_API_REFERER@"
#define SPEED_TEST_API_KEY "@SpeedTest_API_KEY@"
#define SPEED_TEST_MIN_SERVER_VERSION @SpeedTest_MIN_SERVER_VERSION@
#define SPEED_TEST_LATENCY_SAMPLE_SIZE @SpeedTest_LATENCY_SAMPLE_SIZE@
0707010000000D000081A40000000000000000000000016612D51D00000DBD000000000000000000000000000000000000003A00000000SpeedTest++-1.11+git20240407.b743996/TestConfigTemplate.h//
// Created by Francesco Laurita on 6/2/16.
//
#ifndef SPEEDTEST_TESTCONFIGTEMPLATE_H
#define SPEEDTEST_TESTCONFIGTEMPLATE_H
#include "SpeedTest.h"
const TestConfig preflightConfigDownload = {
600000, // start_size
2000000, // max_size
125000, // inc_size
4096, // buff_size
10000, // min_test_time_ms
2, // Concurrency
"Preflight check"
};
const TestConfig slowConfigDownload = {
100000, // start_size
500000, // max_size
10000, // inc_size
1024, // buff_size
20000, // min_test_time_ms
2, // Concurrency
"Very-slow-line line type detected: profile selected slowband"
};
const TestConfig slowConfigUpload = {
50000, // start_size
80000, // max_size
1000, // inc_size
1024, // buff_size
20000, // min_test_time_ms
2, // Concurrency
"Very-slow-line line type detected: profile selected slowband"
};
const TestConfig narrowConfigDownload = {
1000000, // start_size
100000000, // max_size
750000, // inc_size
4096, // buff_size
20000, // min_test_time_ms
2, // Concurrency
"Buffering-lover line type detected: profile selected narrowband"
};
const TestConfig narrowConfigUpload = {
1000000, // start_size
100000000, // max_size
550000, // inc_size
4096, // buff_size
20000, // min_test_time_ms
2, // Concurrency
"Buffering-lover line type detected: profile selected narrowband"
};
const TestConfig broadbandConfigDownload = {
1000000, // start_size
100000000, // max_size
750000, // inc_size
65536, // buff_size
20000, // min_test_time_ms
32, // concurrency
"Broadband line type detected: profile selected broadband"
};
const TestConfig broadbandConfigUpload = {
1000000, // start_size
70000000, // max_size
250000, // inc_size
65536, // buff_size
20000, // min_test_time_ms
8, // concurrency
"Broadband line type detected: profile selected broadband"
};
const TestConfig fiberConfigDownload = {
5000000, // start_size
120000000, // max_size
950000, // inc_size
65536, // buff_size
20000, // min_test_time_ms
32, // concurrency
"Fiber / Lan line type detected: profile selected fiber"
};
const TestConfig fiberConfigUpload = {
1000000, // start_size
70000000, // max_size
250000, // inc_size
65536, // buff_size
20000, // min_test_time_ms
12, // concurrency
"Fiber / Lan line type detected: profile selected fiber"
};
void testConfigSelector(const double preSpeed, TestConfig& uploadConfig, TestConfig& downloadConfig){
uploadConfig = slowConfigUpload;
downloadConfig = slowConfigDownload;
if (preSpeed > 4 && preSpeed <= 30){
downloadConfig = narrowConfigDownload;
uploadConfig = narrowConfigUpload;
} else if (preSpeed > 30 && preSpeed < 150) {
downloadConfig = broadbandConfigDownload;
uploadConfig = broadbandConfigUpload;
} else if (preSpeed >= 150) {
downloadConfig = fiberConfigDownload;
uploadConfig = fiberConfigUpload;
}
}
#endif //SPEEDTEST_TESTCONFIGTEMPLATE_H
0707010000000E000081A40000000000000000000000016612D51D00005614000000000000000000000000000000000000002C00000000SpeedTest++-1.11+git20240407.b743996/json.h
#pragma once
#include <cstdint>
#include <cmath>
#include <cctype>
#include <string>
#include <deque>
#include <map>
#include <type_traits>
#include <initializer_list>
#include <ostream>
#include <iostream>
namespace json {
using std::map;
using std::deque;
using std::string;
using std::enable_if;
using std::initializer_list;
using std::is_same;
using std::is_convertible;
using std::is_integral;
using std::is_floating_point;
namespace {
string json_escape( const string &str ) {
string output;
for( unsigned i = 0; i < str.length(); ++i )
switch( str[i] ) {
case '\"': output += "\\\""; break;
case '\\': output += "\\\\"; break;
case '\b': output += "\\b"; break;
case '\f': output += "\\f"; break;
case '\n': output += "\\n"; break;
case '\r': output += "\\r"; break;
case '\t': output += "\\t"; break;
default : output += str[i]; break;
}
return std::move( output );
}
}
class JSON
{
union BackingData {
BackingData( double d ) : Float( d ){}
BackingData( long l ) : Int( l ){}
BackingData( bool b ) : Bool( b ){}
BackingData( string s ) : String( new string( s ) ){}
BackingData() : Int( 0 ){}
deque<JSON> *List;
map<string,JSON> *Map;
string *String;
double Float;
long Int;
bool Bool;
} Internal;
public:
enum class Class {
Null,
Object,
Array,
String,
Floating,
Integral,
Boolean
};
template <typename Container>
class JSONWrapper {
Container *object;
public:
JSONWrapper( Container *val ) : object( val ) {}
JSONWrapper( std::nullptr_t ) : object( nullptr ) {}
typename Container::iterator begin() { return object ? object->begin() : typename Container::iterator(); }
typename Container::iterator end() { return object ? object->end() : typename Container::iterator(); }
typename Container::const_iterator begin() const { return object ? object->begin() : typename Container::iterator(); }
typename Container::const_iterator end() const { return object ? object->end() : typename Container::iterator(); }
};
template <typename Container>
class JSONConstWrapper {
const Container *object;
public:
JSONConstWrapper( const Container *val ) : object( val ) {}
JSONConstWrapper( std::nullptr_t ) : object( nullptr ) {}
typename Container::const_iterator begin() const { return object ? object->begin() : typename Container::const_iterator(); }
typename Container::const_iterator end() const { return object ? object->end() : typename Container::const_iterator(); }
};
JSON() : Internal(), Type( Class::Null ){}
JSON( initializer_list<JSON> list )
: JSON()
{
SetType( Class::Object );
for( auto i = list.begin(), e = list.end(); i != e; ++i, ++i )
operator[]( i->ToString() ) = *std::next( i );
}
JSON( JSON&& other )
: Internal( other.Internal )
, Type( other.Type )
{ other.Type = Class::Null; other.Internal.Map = nullptr; }
JSON& operator=( JSON&& other ) {
ClearInternal();
Internal = other.Internal;
Type = other.Type;
other.Internal.Map = nullptr;
other.Type = Class::Null;
return *this;
}
JSON( const JSON &other ) {
switch( other.Type ) {
case Class::Object:
Internal.Map =
new map<string,JSON>( other.Internal.Map->begin(),
other.Internal.Map->end() );
break;
case Class::Array:
Internal.List =
new deque<JSON>( other.Internal.List->begin(),
other.Internal.List->end() );
break;
case Class::String:
Internal.String =
new string( *other.Internal.String );
break;
default:
Internal = other.Internal;
}
Type = other.Type;
}
JSON& operator=( const JSON &other ) {
ClearInternal();
switch( other.Type ) {
case Class::Object:
Internal.Map =
new map<string,JSON>( other.Internal.Map->begin(),
other.Internal.Map->end() );
break;
case Class::Array:
Internal.List =
new deque<JSON>( other.Internal.List->begin(),
other.Internal.List->end() );
break;
case Class::String:
Internal.String =
new string( *other.Internal.String );
break;
default:
Internal = other.Internal;
}
Type = other.Type;
return *this;
}
~JSON() {
switch( Type ) {
case Class::Array:
delete Internal.List;
break;
case Class::Object:
delete Internal.Map;
break;
case Class::String:
delete Internal.String;
break;
default:;
}
}
template <typename T>
JSON( T b, typename enable_if<is_same<T,bool>::value>::type* = 0 ) : Internal( b ), Type( Class::Boolean ){}
template <typename T>
JSON( T i, typename enable_if<is_integral<T>::value && !is_same<T,bool>::value>::type* = 0 ) : Internal( (long)i ), Type( Class::Integral ){}
template <typename T>
JSON( T f, typename enable_if<is_floating_point<T>::value>::type* = 0 ) : Internal( (double)f ), Type( Class::Floating ){}
template <typename T>
JSON( T s, typename enable_if<is_convertible<T,string>::value>::type* = 0 ) : Internal( string( s ) ), Type( Class::String ){}
JSON( std::nullptr_t ) : Internal(), Type( Class::Null ){}
static JSON Make( Class type ) {
JSON ret; ret.SetType( type );
return ret;
}
static JSON Load( const string & );
template <typename T>
void append( T arg ) {
SetType( Class::Array ); Internal.List->emplace_back( arg );
}
template <typename T, typename... U>
void append( T arg, U... args ) {
append( arg ); append( args... );
}
template <typename T>
typename enable_if<is_same<T,bool>::value, JSON&>::type operator=( T b ) {
SetType( Class::Boolean ); Internal.Bool = b; return *this;
}
template <typename T>
typename enable_if<is_integral<T>::value && !is_same<T,bool>::value, JSON&>::type operator=( T i ) {
SetType( Class::Integral ); Internal.Int = i; return *this;
}
template <typename T>
typename enable_if<is_floating_point<T>::value, JSON&>::type operator=( T f ) {
SetType( Class::Floating ); Internal.Float = f; return *this;
}
template <typename T>
typename enable_if<is_convertible<T,string>::value, JSON&>::type operator=( T s ) {
SetType( Class::String ); *Internal.String = string( s ); return *this;
}
JSON& operator[]( const string &key ) {
SetType( Class::Object ); return Internal.Map->operator[]( key );
}
JSON& operator[]( unsigned index ) {
SetType( Class::Array );
if( index >= Internal.List->size() ) Internal.List->resize( index + 1 );
return Internal.List->operator[]( index );
}
JSON &at( const string &key ) {
return operator[]( key );
}
const JSON &at( const string &key ) const {
return Internal.Map->at( key );
}
JSON &at( unsigned index ) {
return operator[]( index );
}
const JSON &at( unsigned index ) const {
return Internal.List->at( index );
}
int length() const {
if( Type == Class::Array )
return Internal.List->size();
else
return -1;
}
bool hasKey( const string &key ) const {
if( Type == Class::Object )
return Internal.Map->find( key ) != Internal.Map->end();
return false;
}
int size() const {
if( Type == Class::Object )
return Internal.Map->size();
else if( Type == Class::Array )
return Internal.List->size();
else
return -1;
}
Class JSONType() const { return Type; }
/// Functions for getting primitives from the JSON object.
bool IsNull() const { return Type == Class::Null; }
string ToString() const { bool b; return std::move( ToString( b ) ); }
string ToString( bool &ok ) const {
ok = (Type == Class::String);
return ok ? std::move( json_escape( *Internal.String ) ): string("");
}
double ToFloat() const { bool b; return ToFloat( b ); }
double ToFloat( bool &ok ) const {
ok = (Type == Class::Floating);
return ok ? Internal.Float : 0.0;
}
long ToInt() const { bool b; return ToInt( b ); }
long ToInt( bool &ok ) const {
ok = (Type == Class::Integral);
return ok ? Internal.Int : 0;
}
bool ToBool() const { bool b; return ToBool( b ); }
bool ToBool( bool &ok ) const {
ok = (Type == Class::Boolean);
return ok ? Internal.Bool : false;
}
JSONWrapper<map<string,JSON>> ObjectRange() {
if( Type == Class::Object )
return JSONWrapper<map<string,JSON>>( Internal.Map );
return JSONWrapper<map<string,JSON>>( nullptr );
}
JSONWrapper<deque<JSON>> ArrayRange() {
if( Type == Class::Array )
return JSONWrapper<deque<JSON>>( Internal.List );
return JSONWrapper<deque<JSON>>( nullptr );
}
JSONConstWrapper<map<string,JSON>> ObjectRange() const {
if( Type == Class::Object )
return JSONConstWrapper<map<string,JSON>>( Internal.Map );
return JSONConstWrapper<map<string,JSON>>( nullptr );
}
JSONConstWrapper<deque<JSON>> ArrayRange() const {
if( Type == Class::Array )
return JSONConstWrapper<deque<JSON>>( Internal.List );
return JSONConstWrapper<deque<JSON>>( nullptr );
}
string dump( int depth = 1, string tab = " ") const {
string pad = "";
for( int i = 0; i < depth; ++i, pad += tab );
switch( Type ) {
case Class::Null:
return "null";
case Class::Object: {
string s = "{\n";
bool skip = true;
for( auto &p : *Internal.Map ) {
if( !skip ) s += ",\n";
s += ( pad + "\"" + p.first + "\" : " + p.second.dump( depth + 1, tab ) );
skip = false;
}
s += ( "\n" + pad.erase( 0, 2 ) + "}" ) ;
return s;
}
case Class::Array: {
string s = "[";
bool skip = true;
for( auto &p : *Internal.List ) {
if( !skip ) s += ", ";
s += p.dump( depth + 1, tab );
skip = false;
}
s += "]";
return s;
}
case Class::String:
return "\"" + json_escape( *Internal.String ) + "\"";
case Class::Floating:
return std::to_string( Internal.Float );
case Class::Integral:
return std::to_string( Internal.Int );
case Class::Boolean:
return Internal.Bool ? "true" : "false";
default:
return "";
}
return "";
}
friend std::ostream& operator<<( std::ostream&, const JSON & );
private:
void SetType( Class type ) {
if( type == Type )
return;
ClearInternal();
switch( type ) {
case Class::Null: Internal.Map = nullptr; break;
case Class::Object: Internal.Map = new map<string,JSON>(); break;
case Class::Array: Internal.List = new deque<JSON>(); break;
case Class::String: Internal.String = new string(); break;
case Class::Floating: Internal.Float = 0.0; break;
case Class::Integral: Internal.Int = 0; break;
case Class::Boolean: Internal.Bool = false; break;
}
Type = type;
}
private:
/* beware: only call if YOU know that Internal is allocated. No checks performed here.
This function should be called in a constructed JSON just before you are going to
overwrite Internal...
*/
void ClearInternal() {
switch( Type ) {
case Class::Object: delete Internal.Map; break;
case Class::Array: delete Internal.List; break;
case Class::String: delete Internal.String; break;
default:;
}
}
private:
Class Type = Class::Null;
};
JSON Array() {
return std::move( JSON::Make( JSON::Class::Array ) );
}
template <typename... T>
JSON Array( T... args ) {
JSON arr = JSON::Make( JSON::Class::Array );
arr.append( args... );
return std::move( arr );
}
JSON Object() {
return std::move( JSON::Make( JSON::Class::Object ) );
}
std::ostream& operator<<( std::ostream &os, const JSON &json ) {
os << json.dump();
return os;
}
namespace {
JSON parse_next( const string &, size_t & );
void consume_ws( const string &str, size_t &offset ) {
while( isspace( str[offset] ) ) ++offset;
}
JSON parse_object( const string &str, size_t &offset ) {
JSON Object = JSON::Make( JSON::Class::Object );
++offset;
consume_ws( str, offset );
if( str[offset] == '}' ) {
++offset; return std::move( Object );
}
while( true ) {
JSON Key = parse_next( str, offset );
consume_ws( str, offset );
if( str[offset] != ':' ) {
std::cerr << "Error: Object: Expected colon, found '" << str[offset] << "'\n";
break;
}
consume_ws( str, ++offset );
JSON Value = parse_next( str, offset );
Object[Key.ToString()] = Value;
consume_ws( str, offset );
if( str[offset] == ',' ) {
++offset; continue;
}
else if( str[offset] == '}' ) {
++offset; break;
}
else {
std::cerr << "ERROR: Object: Expected comma, found '" << str[offset] << "'\n";
break;
}
}
return std::move( Object );
}
JSON parse_array( const string &str, size_t &offset ) {
JSON Array = JSON::Make( JSON::Class::Array );
unsigned index = 0;
++offset;
consume_ws( str, offset );
if( str[offset] == ']' ) {
++offset; return std::move( Array );
}
while( true ) {
Array[index++] = parse_next( str, offset );
consume_ws( str, offset );
if( str[offset] == ',' ) {
++offset; continue;
}
else if( str[offset] == ']' ) {
++offset; break;
}
else {
std::cerr << "ERROR: Array: Expected ',' or ']', found '" << str[offset] << "'\n";
return std::move( JSON::Make( JSON::Class::Array ) );
}
}
return std::move( Array );
}
JSON parse_string( const string &str, size_t &offset ) {
JSON String;
string val;
for( char c = str[++offset]; c != '\"' ; c = str[++offset] ) {
if( c == '\\' ) {
switch( str[ ++offset ] ) {
case '\"': val += '\"'; break;
case '\\': val += '\\'; break;
case '/' : val += '/' ; break;
case 'b' : val += '\b'; break;
case 'f' : val += '\f'; break;
case 'n' : val += '\n'; break;
case 'r' : val += '\r'; break;
case 't' : val += '\t'; break;
case 'u' : {
val += "\\u" ;
for( unsigned i = 1; i <= 4; ++i ) {
c = str[offset+i];
if( (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') )
val += c;
else {
std::cerr << "ERROR: String: Expected hex character in unicode escape, found '" << c << "'\n";
return std::move( JSON::Make( JSON::Class::String ) );
}
}
offset += 4;
} break;
default : val += '\\'; break;
}
}
else
val += c;
}
++offset;
String = val;
return std::move( String );
}
JSON parse_number( const string &str, size_t &offset ) {
JSON Number;
string val, exp_str;
char c;
bool isDouble = false;
long exp = 0;
while( true ) {
c = str[offset++];
if( (c == '-') || (c >= '0' && c <= '9') )
val += c;
else if( c == '.' ) {
val += c;
isDouble = true;
}
else
break;
}
if( c == 'E' || c == 'e' ) {
c = str[ offset++ ];
if( c == '-' ){ ++offset; exp_str += '-';}
while( true ) {
c = str[ offset++ ];
if( c >= '0' && c <= '9' )
exp_str += c;
else if( !isspace( c ) && c != ',' && c != ']' && c != '}' ) {
std::cerr << "ERROR: Number: Expected a number for exponent, found '" << c << "'\n";
return std::move( JSON::Make( JSON::Class::Null ) );
}
else
break;
}
exp = std::stol( exp_str );
}
else if( !isspace( c ) && c != ',' && c != ']' && c != '}' ) {
std::cerr << "ERROR: Number: unexpected character '" << c << "'\n";
return std::move( JSON::Make( JSON::Class::Null ) );
}
--offset;
if( isDouble )
Number = std::stod( val ) * std::pow( 10, exp );
else {
if( !exp_str.empty() )
Number = std::stol( val ) * std::pow( 10, exp );
else
Number = std::stol( val );
}
return std::move( Number );
}
JSON parse_bool( const string &str, size_t &offset ) {
JSON Bool;
if( str.substr( offset, 4 ) == "true" )
Bool = true;
else if( str.substr( offset, 5 ) == "false" )
Bool = false;
else {
std::cerr << "ERROR: Bool: Expected 'true' or 'false', found '" << str.substr( offset, 5 ) << "'\n";
return std::move( JSON::Make( JSON::Class::Null ) );
}
offset += (Bool.ToBool() ? 4 : 5);
return std::move( Bool );
}
JSON parse_null( const string &str, size_t &offset ) {
JSON Null;
if( str.substr( offset, 4 ) != "null" ) {
std::cerr << "ERROR: Null: Expected 'null', found '" << str.substr( offset, 4 ) << "'\n";
return std::move( JSON::Make( JSON::Class::Null ) );
}
offset += 4;
return std::move( Null );
}
JSON parse_next( const string &str, size_t &offset ) {
char value;
consume_ws( str, offset );
value = str[offset];
switch( value ) {
case '[' : return std::move( parse_array( str, offset ) );
case '{' : return std::move( parse_object( str, offset ) );
case '\"': return std::move( parse_string( str, offset ) );
case 't' :
case 'f' : return std::move( parse_bool( str, offset ) );
case 'n' : return std::move( parse_null( str, offset ) );
default : if( ( value <= '9' && value >= '0' ) || value == '-' )
return std::move( parse_number( str, offset ) );
}
std::cerr << "ERROR: Parse: Unknown starting character '" << value << "'\n";
return JSON();
}
}
JSON JSON::Load( const string &str ) {
size_t offset = 0;
return std::move( parse_next( str, offset ) );
}
} // End Namespace json
0707010000000F000081A40000000000000000000000016612D51D000032FC000000000000000000000000000000000000002E00000000SpeedTest++-1.11+git20240407.b743996/main.cpp#include <iostream>
#include <map>
#include <iomanip>
#include "SpeedTest.h"
#include "TestConfigTemplate.h"
#include "CmdOptions.h"
#include <csignal>
void banner(){
std::cout << "SpeedTest++ version " << SpeedTest_VERSION_MAJOR << "." << SpeedTest_VERSION_MINOR << std::endl;
std::cout << "Speedtest.net command line interface" << std::endl;
std::cout << "Info: " << SpeedTest_HOME_PAGE << std::endl;
std::cout << "Fixed: https://github.com/gglluukk/SpeedTest" << std::endl;
std::cout << "Author: " << SpeedTest_AUTHOR << std::endl;
}
void usage(const char* name){
std::cerr << "Usage: " << name << " ";
std::cerr << " [--latency] [--quality] [--download] [--upload] [--share] [--help]\n"
" [--test-server host:port] [--output verbose|text|json]\n";
std::cerr << "optional arguments:" << std::endl;
std::cerr << " --help Show this message and exit\n";
std::cerr << " --latency Perform latency test only\n";
std::cerr << " --quality Perform quality test only. It includes latency test\n";
std::cerr << " --download Perform download test only. It includes latency test\n";
std::cerr << " --upload Perform upload test only. It includes latency test\n";
std::cerr << " --share Generate and provide a URL to the speedtest.net share results image\n";
std::cerr << " --insecure Skip SSL certificate verify (Useful for Embedded devices)\n";
std::cerr << " --test-server host:port Run speed test against a specific server\n";
std::cerr << " --quality-server host:port Run line quality test against a specific server\n";
std::cerr << " --output verbose|text|json Set output type. Default: verbose\n";
}
int main(const int argc, const char **argv) {
ProgramOptions programOptions;
if (!ParseOptions(argc, argv, programOptions)){
usage(argv[0]);
return EXIT_FAILURE;
}
if (programOptions.output_type == OutputType::verbose){
banner();
std::cout << std::endl;
}
if (programOptions.help) {
usage(argv[0]);
return EXIT_SUCCESS;
}
signal(SIGPIPE, SIG_IGN);
auto sp = SpeedTest(SPEED_TEST_MIN_SERVER_VERSION);
IPInfo info;
ServerInfo serverInfo;
ServerInfo serverQualityInfo;
if (programOptions.insecure) {
sp.setInsecure(programOptions.insecure);
}
if (programOptions.output_type == OutputType::json)
std::cout << "{";
if (!sp.ipInfo(info)){
std::cerr << "Unable to retrieve your IP info. Try again later" << std::endl;
if (programOptions.output_type == OutputType::json)
std::cout << "\"error\":\"unable to retrieve your ip info\"}" << std::endl;
return EXIT_FAILURE;
}
if (programOptions.output_type == OutputType::verbose){
std::cout << "IP: \"" << info.ip_address << "\""
<< " (\"" << info.isp << "\") "
<< "Location: [" << info.lat << ", " << info.lon << "]" << std::endl;
} else if (programOptions.output_type == OutputType::text) {
std::cout << "IP=" << info.ip_address << std::endl;
std::cout << "IP_LAT=" << info.lat << std::endl;
std::cout << "IP_LON=" << info.lon << std::endl;
std::cout << "PROVIDER=" << info.isp << std::endl;
} else if (programOptions.output_type == OutputType::json) {
std::cout << "\"client\":{";
std::cout << "\"ip\":\"" << info.ip_address << "\",";
std::cout << "\"lat\":\"" << info.lat << "\",";
std::cout << "\"lon\":\"" << info.lon << "\",";
std::cout << "\"isp\":\"" << info.isp << "\"";
std::cout << "},";
}
auto serverList = sp.serverList();
if (programOptions.selected_server.empty()){
if (programOptions.output_type == OutputType::verbose)
std::cout << "Finding fastest server... " << std::flush;
if (serverList.empty()){
std::cerr << "Unable to download server list. Try again later" << std::endl;
if (programOptions.output_type == OutputType::json)
std::cout << "\"error\":\"unable to download server list\"}" << std::endl;
return EXIT_FAILURE;
}
if (programOptions.output_type == OutputType::verbose)
std::cout << serverList.size() << " Servers online" << std::endl;
else if (programOptions.output_type == OutputType::json)
std::cout << "\"servers_online\":\"" << serverList.size() << "\",";
serverInfo = sp.bestServer(10, [&programOptions](bool success) {
if (programOptions.output_type == OutputType::verbose)
std::cout << (success ? '.' : '*') << std::flush;
});
if (programOptions.output_type == OutputType::verbose){
std::cout << std::endl;
std::cout << "Server: " << serverInfo.name
<< " " << serverInfo.host
<< " by " << serverInfo.sponsor
<< " (" << serverInfo.distance << " km from you): "
<< sp.latency() << " ms" << std::endl;
} else if (programOptions.output_type == OutputType::text) {
std::cout << "TEST_SERVER_HOST=" << serverInfo.host << std::endl;
std::cout << "TEST_SERVER_DISTANCE=" << serverInfo.distance << std::endl;
}
else if (programOptions.output_type == OutputType::json) {
std::cout << "\"server\":{";
std::cout << "\"name\":\"" << serverInfo.name << "\",";
std::cout << "\"sponsor\":\"" << serverInfo.sponsor << "\",";
std::cout << "\"distance\":\"" << serverInfo.distance << "\",";
std::cout << "\"latency\":\"" << sp.latency() << "\",";
std::cout << "\"host\":\"" << serverInfo.host << "\"";
std::cout << "},";
}
} else {
serverInfo.host.append(programOptions.selected_server);
sp.setServer(serverInfo);
for (auto &s : serverList) {
if (s.host == serverInfo.host)
serverInfo.id = s.id;
}
if (programOptions.output_type == OutputType::verbose)
std::cout << "Selected server: " << serverInfo.host << std::endl;
else if (programOptions.output_type == OutputType::text) {
std::cout << "TEST_SERVER_HOST=" << serverInfo.host << std::endl;
}
else if (programOptions.output_type == OutputType::json) {
std::cout << "\"server\":{";
std::cout << "\"host\":\"" << serverInfo.host << "\"";
std::cout << "},";
}
}
if (programOptions.output_type == OutputType::verbose)
std::cout << "Ping: " << sp.latency() << " ms." << std::endl;
else if (programOptions.output_type == OutputType::text)
std::cout << "LATENCY=" << sp.latency() << std::endl;
else if (programOptions.output_type == OutputType::json) {
std::cout << "\"ping\":\"";
std::cout << std::fixed;
std::cout << sp.latency() << "\",";
}
long jitter = 0;
if (programOptions.output_type == OutputType::verbose)
std::cout << "Jitter: " << std::flush;
if (sp.jitter(serverInfo, jitter)){
if (programOptions.output_type == OutputType::verbose)
std::cout << jitter << " ms." << std::endl;
else if (programOptions.output_type == OutputType::text)
std::cout << "JITTER=" << jitter << std::endl;
else if (programOptions.output_type == OutputType::json) {
std::cout << "\"jitter\":\"";
std::cout << std::fixed;
std::cout << jitter << "\",";
}
} else {
std::cerr << "Jitter measurement is unavailable at this time." << std::endl;
}
if (programOptions.latency) {
if (programOptions.output_type == OutputType::json)
std::cout << "\"_\":\"only latency requested\"}" << std::endl;
return EXIT_SUCCESS;
}
if (programOptions.output_type == OutputType::verbose)
std::cout << "Determine line type (" << preflightConfigDownload.concurrency << ") " << std::flush;
double preSpeed = 0;
if (!sp.downloadSpeed(serverInfo, preflightConfigDownload, preSpeed, [&programOptions](bool success){
if (programOptions.output_type == OutputType::verbose)
std::cout << (success ? '.' : '*') << std::flush;
})){
std::cerr << "Pre-flight check failed." << std::endl;
if (programOptions.output_type == OutputType::json)
std::cout << "\"error\":\"pre-flight check failed\"}" << std::endl;
return EXIT_FAILURE;
}
if (programOptions.output_type == OutputType::verbose)
std::cout << std::endl;
TestConfig uploadConfig;
TestConfig downloadConfig;
testConfigSelector(preSpeed, uploadConfig, downloadConfig);
if (programOptions.output_type == OutputType::verbose)
std::cout << downloadConfig.label << std::endl;
if (!programOptions.upload){
if (programOptions.output_type == OutputType::verbose){
std::cout << std::endl;
std::cout << "Testing download speed (" << downloadConfig.concurrency << ") " << std::flush;
}
double downloadSpeed = 0;
if (sp.downloadSpeed(serverInfo, downloadConfig, downloadSpeed, [&programOptions](bool success){
if (programOptions.output_type == OutputType::verbose)
std::cout << (success ? '.' : '*') << std::flush;
})){
if (programOptions.output_type == OutputType::verbose){
std::cout << std::endl;
std::cout << "Download: ";
std::cout << std::fixed;
std::cout << std::setprecision(2);
std::cout << downloadSpeed << " Mbit/s" << std::endl;
} else if (programOptions.output_type == OutputType::text) {
std::cout << "DOWNLOAD_SPEED=";
std::cout << std::fixed;
std::cout << std::setprecision(2);
std::cout << downloadSpeed << std::endl;
} else if (programOptions.output_type == OutputType::json) {
std::cout << "\"download\":\"";
std::cout << std::fixed;
std::cout << (downloadSpeed*1000*1000) << "\",";
}
} else {
std::cerr << "Download test failed." << std::endl;
if (programOptions.output_type == OutputType::json)
std::cout << "\"error\":\"download test failed\"}" << std::endl;
return EXIT_FAILURE;
}
}
if (programOptions.download) {
if (programOptions.output_type == OutputType::json)
std::cout << "\"_\":\"only download requested\"}" << std::endl;
return EXIT_SUCCESS;
}
if (programOptions.output_type == OutputType::verbose)
std::cout << "Testing upload speed (" << uploadConfig.concurrency << ") " << std::flush;
double uploadSpeed = 0;
if (sp.uploadSpeed(serverInfo, uploadConfig, uploadSpeed, [&programOptions](bool success){
if (programOptions.output_type == OutputType::verbose)
std::cout << (success ? '.' : '*') << std::flush;
})){
if (programOptions.output_type == OutputType::verbose){
std::cout << std::endl;
std::cout << "Upload: ";
std::cout << std::fixed;
std::cout << std::setprecision(2);
std::cout << uploadSpeed << " Mbit/s" << std::endl;
} else if (programOptions.output_type == OutputType::text) {
std::cout << "UPLOAD_SPEED=";
std::cout << std::fixed;
std::cout << std::setprecision(2);
std::cout << uploadSpeed << std::endl;
} else if (programOptions.output_type == OutputType::json) {
std::cout << "\"upload\":\"";
std::cout << std::fixed;
std::cout << (uploadSpeed*1000*1000) << "\",";
}
} else {
std::cerr << "Upload test failed." << std::endl;
if (programOptions.output_type == OutputType::json)
std::cout << "\"error\":\"upload test failed\"}" << std::endl;
return EXIT_FAILURE;
}
if (programOptions.share){
std::string share_it;
if (sp.share(serverInfo, share_it)) {
if (programOptions.output_type == OutputType::verbose) {
std::cout << "Results image: " << share_it << std::endl;
} else if (programOptions.output_type == OutputType::text) {
std::cout << "IMAGE_URL=" << share_it << std::endl;
} else if (programOptions.output_type == OutputType::json) {
std::cout << "\"share\":\"" << share_it << "\",";
}
}
}
if (programOptions.output_type == OutputType::json)
std::cout << "\"_\":\"all ok\"}" << std::endl;
return EXIT_SUCCESS;
}
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!159 blocks