From 5ac6f421869e17a71dfcd822991863c98e5b592b Mon Sep 17 00:00:00 2001 From: Jonathan Viney Date: Wed, 5 Feb 2020 12:37:00 +1300 Subject: [PATCH 01/17] Add CURLOPT_ACCEPT_ENCODING option to fix errors downloading server list. --- SpeedTest.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SpeedTest.cpp b/SpeedTest.cpp index ed88406..4a13347 100644 --- a/SpeedTest.cpp +++ b/SpeedTest.cpp @@ -297,7 +297,8 @@ CURL *SpeedTest::curl_setup(CURL *handler) { 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){ + && curl_easy_setopt(curl, CURLOPT_USERAGENT, SPEED_TEST_USER_AGENT) == CURLE_OK + && curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "br, gzip, deflate") == CURLE_OK) { return curl; } else { curl_easy_cleanup(handler); @@ -549,4 +550,3 @@ bool SpeedTest::testLatency(SpeedTestClient &client, const int sample_size, long } return true; } - From a86961e80bcd4a888c579d4925c72067b8bd21dd Mon Sep 17 00:00:00 2001 From: Jonathan Viney Date: Wed, 5 Feb 2020 12:54:53 +1300 Subject: [PATCH 02/17] Version bump --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4383d29..0571e1b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 2.7) project(SpeedTest) set (SpeedTest_VERSION_MAJOR 1) -set (SpeedTest_VERSION_MINOR 14) +set (SpeedTest_VERSION_MINOR 15) set (SpeedTest_SYSTEM_PROCESSOR ${CMAKE_SYSTEM_PROCESSOR}) set (SpeedTest_SYSTEM ${CMAKE_SYSTEM}) From fa0d31de523ca2d0bcb7c17ab9564e85d5d4342a Mon Sep 17 00:00:00 2001 From: JJ Macalinao Date: Wed, 28 Apr 2021 07:42:50 +0800 Subject: [PATCH 03/17] Change conversion of megabits to mebibits --- .gitignore | 31 ++++++++++++++++++++++++++++++- SpeedTest.cpp | 2 +- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 9279f68..887f3c7 100644 --- a/.gitignore +++ b/.gitignore @@ -33,4 +33,33 @@ cmake-build-debug/ cmake-build-linux/ # Visual Studio 2015/2017 cache/options directory -.vs/ \ No newline at end of file +.vs/ +cmake_install.cmake +CMakeCache.txt +install_manifest.txt +Makefile +SpeedTest +SpeedTestConfig.h +CMakeFiles/cmake.check_cache +CMakeFiles/CMakeDirectoryInformation.cmake +CMakeFiles/CMakeOutput.log +CMakeFiles/Makefile.cmake +CMakeFiles/Makefile2 +CMakeFiles/progress.marks +CMakeFiles/TargetDirectories.txt +CMakeFiles/3.16.3/CMakeCCompiler.cmake +CMakeFiles/3.16.3/CMakeCXXCompiler.cmake +CMakeFiles/3.16.3/CMakeDetermineCompilerABI_C.bin +CMakeFiles/3.16.3/CMakeDetermineCompilerABI_CXX.bin +CMakeFiles/3.16.3/CMakeSystem.cmake +CMakeFiles/3.16.3/CompilerIdC/CMakeCCompilerId.c +CMakeFiles/3.16.3/CompilerIdCXX/CMakeCXXCompilerId.cpp +CMakeFiles/SpeedTest.dir/build.make +CMakeFiles/SpeedTest.dir/cmake_clean.cmake +CMakeFiles/SpeedTest.dir/CXX.includecache +CMakeFiles/SpeedTest.dir/depend.internal +CMakeFiles/SpeedTest.dir/depend.make +CMakeFiles/SpeedTest.dir/DependInfo.cmake +CMakeFiles/SpeedTest.dir/flags.make +CMakeFiles/SpeedTest.dir/link.txt +CMakeFiles/SpeedTest.dir/progress.make diff --git a/SpeedTest.cpp b/SpeedTest.cpp index ed88406..cf1bca7 100644 --- a/SpeedTest.cpp +++ b/SpeedTest.cpp @@ -236,7 +236,7 @@ double SpeedTest::execute(const ServerInfo &server, const TestConfig &config, co workers.clear(); - return overall_speed / 1000 / 1000; + return overall_speed / 1024 / 1024; } template From b6d4a8e0df98d101af76e992021400f5ff9bf67e Mon Sep 17 00:00:00 2001 From: JJ Macalinao Date: Wed, 28 Apr 2021 08:59:01 +0800 Subject: [PATCH 04/17] Increased precision for latency and jitter --- SpeedTest.cpp | 28 ++++++++++++++-------------- SpeedTest.h | 10 +++++----- SpeedTestClient.cpp | 5 +++-- SpeedTestClient.h | 2 +- main.cpp | 2 +- 5 files changed, 24 insertions(+), 23 deletions(-) diff --git a/SpeedTest.cpp b/SpeedTest.cpp index cf1bca7..1a71727 100644 --- a/SpeedTest.cpp +++ b/SpeedTest.cpp @@ -97,19 +97,19 @@ bool SpeedTest::uploadSpeed(const ServerInfo &server, const TestConfig &config, return true; } -const long &SpeedTest::latency() { +const double &SpeedTest::latency() { return mLatency; } -bool SpeedTest::jitter(const ServerInfo &server, long& result, const int sample) { +bool SpeedTest::jitter(const ServerInfo &server, double& result, const int sample) { auto client = SpeedTestClient(server); double current_jitter = 0; - long previous_ms = LONG_MAX; + double previous_ms = DBL_MAX; if (client.connect()){ for (int i = 0; i < sample; i++){ - long ms = 0; + double ms = 0; if (client.ping(ms)){ - if (previous_ms == LONG_MAX) { + if (previous_ms == DBL_MAX) { previous_ms = ms; } else { current_jitter += std::abs(previous_ms - ms); @@ -121,21 +121,21 @@ bool SpeedTest::jitter(const ServerInfo &server, long& result, const int sample) return false; } - result = (long) std::floor(current_jitter / sample); + result = 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 + hash << std::setprecision(2) << std::fixed << (mLatency / 1000) << "-" << 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 << "ping=" << std::setprecision(2) << std::fixed << (mLatency / 1000) << "&"; post_data << "upload=" << std::setprecision(2) << std::fixed << (mUploadSpeed * 1000) << "&"; post_data << "pingselect=1&"; post_data << "recommendedserverid=" << server.id << "&"; @@ -492,12 +492,12 @@ bool SpeedTest::fetchServers(const std::string& url, std::vector& ta return true; } -const ServerInfo SpeedTest::findBestServerWithin(const std::vector &serverList, long &latency, +const ServerInfo SpeedTest::findBestServerWithin(const std::vector &serverList, double &latency, const int sample_size, std::function cb) { int i = sample_size; ServerInfo bestServer = serverList[0]; - latency = INT_MAX; + latency = DBL_MAX; for (auto &server : serverList){ auto client = SpeedTestClient(server); @@ -513,7 +513,7 @@ const ServerInfo SpeedTest::findBestServerWithin(const std::vector & continue; } - long current_latency = LONG_MAX; + double current_latency = DBL_MAX; if (testLatency(client, 20, current_latency)){ if (current_latency < latency){ latency = current_latency; @@ -532,12 +532,12 @@ const ServerInfo SpeedTest::findBestServerWithin(const std::vector & return bestServer; } -bool SpeedTest::testLatency(SpeedTestClient &client, const int sample_size, long &latency) { +bool SpeedTest::testLatency(SpeedTestClient &client, const int sample_size, double &latency) { if (!client.connect()){ return false; } - latency = INT_MAX; - long temp_latency = 0; + latency = DBL_MAX; + double temp_latency = 0; for (int i = 0; i < sample_size; i++){ if (client.ping(temp_latency)){ if (temp_latency < latency){ diff --git a/SpeedTest.h b/SpeedTest.h index 9b8c8ce..da7c16d 100644 --- a/SpeedTest.h +++ b/SpeedTest.h @@ -38,15 +38,15 @@ class SpeedTest { const std::vector& serverList(); const ServerInfo bestServer(int sample_size = 5, std::function cb = nullptr); bool setServer(ServerInfo& server); - const long &latency(); + const double &latency(); bool downloadSpeed(const ServerInfo& server, const TestConfig& config, double& result, std::function cb = nullptr); bool uploadSpeed(const ServerInfo& server, const TestConfig& config, double& result, std::function cb = nullptr); - bool jitter(const ServerInfo& server, long& result, int sample = 40); + bool jitter(const ServerInfo& server, double& result, int sample = 40); bool share(const ServerInfo& server, std::string& image_url); private: bool fetchServers(const std::string& url, std::vector& target, int &http_code); - bool testLatency(SpeedTestClient& client, int sample_size, long& latency); - const ServerInfo findBestServerWithin(const std::vector& serverList, long& latency, int sample_size = 5, std::function cb = nullptr); + bool testLatency(SpeedTestClient& client, int sample_size, double& latency); + const ServerInfo findBestServerWithin(const std::vector& serverList, double& latency, int sample_size = 5, std::function 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); @@ -58,7 +58,7 @@ class SpeedTest { IPInfo mIpInfo; std::vector mServerList; - long mLatency; + double mLatency; double mUploadSpeed; double mDownloadSpeed; float mMinSupportedServer; diff --git a/SpeedTestClient.cpp b/SpeedTestClient.cpp index 39ffc14..e6fa20e 100644 --- a/SpeedTestClient.cpp +++ b/SpeedTestClient.cpp @@ -63,7 +63,7 @@ void SpeedTestClient::close() { } // It executes PING command -bool SpeedTestClient::ping(long &millisec) { +bool SpeedTestClient::ping(double &millisec) { if (!mSocketFd) return false; @@ -80,7 +80,8 @@ bool SpeedTestClient::ping(long &millisec) { if (SpeedTestClient::readLine(mSocketFd, reply)){ if (reply.substr(0, 5) == "PONG "){ auto stop = std::chrono::steady_clock::now(); - millisec = std::chrono::duration_cast(stop - start).count(); + double microsec = std::chrono::duration_cast(stop - start).count(); + millisec = microsec / 1000; return true; } } diff --git a/SpeedTestClient.h b/SpeedTestClient.h index 141ecf4..125dee8 100644 --- a/SpeedTestClient.h +++ b/SpeedTestClient.h @@ -22,7 +22,7 @@ class SpeedTestClient { ~SpeedTestClient(); bool connect(); - bool ping(long &millisec); + bool ping(double &millisec); bool upload(long size, long chunk_size, long &millisec); bool download(long size, long chunk_size, long &millisec); float version(); diff --git a/main.cpp b/main.cpp index 8582921..e1e26f6 100644 --- a/main.cpp +++ b/main.cpp @@ -163,7 +163,7 @@ int main(const int argc, const char **argv) { std::cout << sp.latency() << "\","; } - long jitter = 0; + double jitter = 0; if (programOptions.output_type == OutputType::verbose) std::cout << "Jitter: " << std::flush; if (sp.jitter(serverInfo, jitter)){ From 39faf14aee6c4e26f7d72c1b54a24629f991a5b7 Mon Sep 17 00:00:00 2001 From: JJ Macalinao Date: Wed, 28 Apr 2021 12:25:25 +0800 Subject: [PATCH 05/17] Added line-type option --- CmdOptions.h | 21 +++++++++++++++++ TestConfigTemplate.h | 44 ++++++++++++++++++++++++++++++++++ main.cpp | 56 ++++++++++++++++++++++++++++++-------------- 3 files changed, 103 insertions(+), 18 deletions(-) diff --git a/CmdOptions.h b/CmdOptions.h index 79ddf3a..e8a1ea4 100644 --- a/CmdOptions.h +++ b/CmdOptions.h @@ -7,6 +7,7 @@ #include enum OutputType { verbose, text, json }; +enum LineType { automatic, slow, narrow, broad, fiber }; typedef struct program_options_t { @@ -17,6 +18,7 @@ typedef struct program_options_t { bool share = false; std::string selected_server = ""; OutputType output_type = OutputType::verbose; + LineType line_type = LineType::automatic; } ProgramOptions; static struct option CmdLongOptions[] = { @@ -27,6 +29,7 @@ static struct option CmdLongOptions[] = { {"share", no_argument, 0, 's' }, {"test-server", required_argument, 0, 't' }, {"output", required_argument, 0, 'o' }, + {"line-type", required_argument, 0, 'i' }, {0, 0, 0, 0 } }; @@ -68,6 +71,24 @@ bool ParseOptions(const int argc, const char **argv, ProgramOptions& options){ return false; } + break; + case 'i': + if (strcmp(optarg, "auto") == 0) + options.line_type = LineType::automatic; + else if (strcmp(optarg, "slow") == 0) + options.line_type = LineType::slow; + else if (strcmp(optarg, "narrow") == 0) + options.line_type = LineType::narrow; + else if (strcmp(optarg, "broad") == 0) + options.line_type = LineType::broad; + else if (strcmp(optarg, "fiber") == 0) + options.line_type = LineType::fiber; + else { + std::cerr << "Unsupported line type " << optarg << std::endl; + std::cerr << "Supported line type: auto, slow, narrow, broad, fiber" < Date: Wed, 28 Apr 2021 12:45:22 +0800 Subject: [PATCH 06/17] Increase precision for download/upload timers --- SpeedTest.cpp | 4 ++-- SpeedTest.h | 2 +- SpeedTestClient.cpp | 10 ++++++---- SpeedTestClient.h | 6 +++--- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/SpeedTest.cpp b/SpeedTest.cpp index 1a71727..4452169 100644 --- a/SpeedTest.cpp +++ b/SpeedTest.cpp @@ -186,11 +186,11 @@ double SpeedTest::execute(const ServerInfo &server, const TestConfig &config, co auto start = std::chrono::steady_clock::now(); std::vector partial_results; while (curr_size < max_size){ - long op_time = 0; + double op_time = 0; if ((spClient.*pfunc)(curr_size, config.buff_size, op_time)) { total_size += curr_size; total_time += op_time; - double metric = (curr_size * 8) / (static_cast(op_time) / 1000); + double metric = (curr_size * 8) / (op_time / 1000); partial_results.push_back(metric); if (cb) cb(true); diff --git a/SpeedTest.h b/SpeedTest.h index da7c16d..efb6ddf 100644 --- a/SpeedTest.h +++ b/SpeedTest.h @@ -22,7 +22,7 @@ #include "DataTypes.h" class SpeedTestClient; -typedef bool (SpeedTestClient::*opFn)(const long size, const long chunk_size, long &millisec); +typedef bool (SpeedTestClient::*opFn)(const long size, const long chunk_size, double &millisec); typedef void (*progressFn)(bool success); diff --git a/SpeedTestClient.cpp b/SpeedTestClient.cpp index e6fa20e..6e58016 100644 --- a/SpeedTestClient.cpp +++ b/SpeedTestClient.cpp @@ -91,7 +91,7 @@ bool SpeedTestClient::ping(double &millisec) { } // It executes DOWNLOAD command -bool SpeedTestClient::download(const long size, const long chunk_size, long &millisec) { +bool SpeedTestClient::download(const long size, const long chunk_size, double &millisec) { std::stringstream cmd; cmd << "DOWNLOAD " << size; @@ -117,13 +117,14 @@ bool SpeedTestClient::download(const long size, const long chunk_size, long &mil } auto stop = std::chrono::steady_clock::now(); - millisec = std::chrono::duration_cast(stop - start).count(); + double microsec = std::chrono::duration_cast(stop - start).count(); + millisec = microsec / 1000; delete[] buff; return true; } // It executes UPLOAD command -bool SpeedTestClient::upload(const long size, const long chunk_size, long &millisec) { +bool SpeedTestClient::upload(const long size, const long chunk_size, double &millisec) { std::stringstream cmd; cmd << "UPLOAD " << size << "\n"; auto cmd_len = cmd.str().length(); @@ -171,7 +172,8 @@ bool SpeedTestClient::upload(const long size, const long chunk_size, long &milli std::stringstream ss; ss << "OK " << size << " "; - millisec = std::chrono::duration_cast(stop - start).count(); + double microsec = std::chrono::duration_cast(stop - start).count(); + millisec = microsec / 1000; delete[] buff; return reply.substr(0, ss.str().length()) == ss.str(); diff --git a/SpeedTestClient.h b/SpeedTestClient.h index 125dee8..d91d44b 100644 --- a/SpeedTestClient.h +++ b/SpeedTestClient.h @@ -23,8 +23,8 @@ class SpeedTestClient { bool connect(); bool ping(double &millisec); - bool upload(long size, long chunk_size, long &millisec); - bool download(long size, long chunk_size, long &millisec); + bool upload(long size, long chunk_size, double &millisec); + bool download(long size, long chunk_size, double &millisec); float version(); const std::pair hostport(); void close(); @@ -39,5 +39,5 @@ class SpeedTestClient { static bool writeLine(int& fd, const std::string& buffer); }; -typedef bool (SpeedTestClient::*opFn)(const long size, const long chunk_size, long &millisec); +typedef bool (SpeedTestClient::*opFn)(const long size, const long chunk_size, double &millisec); #endif //SPEEDTEST_SPEEDTESTCLIENT_H From 5c3159e4b14e69dd1faa020a2ca24f6b29776e1f Mon Sep 17 00:00:00 2001 From: JJ Macalinao Date: Wed, 28 Apr 2021 13:21:29 +0800 Subject: [PATCH 07/17] Added my own personal test config --- CmdOptions.h | 4 ++- TestConfigTemplate.h | 70 ++++++++++++++++---------------------------- main.cpp | 4 +++ 3 files changed, 33 insertions(+), 45 deletions(-) diff --git a/CmdOptions.h b/CmdOptions.h index e8a1ea4..f9f9995 100644 --- a/CmdOptions.h +++ b/CmdOptions.h @@ -7,7 +7,7 @@ #include enum OutputType { verbose, text, json }; -enum LineType { automatic, slow, narrow, broad, fiber }; +enum LineType { automatic, slow, narrow, broad, fiber, gigasym }; typedef struct program_options_t { @@ -83,6 +83,8 @@ bool ParseOptions(const int argc, const char **argv, ProgramOptions& options){ options.line_type = LineType::broad; else if (strcmp(optarg, "fiber") == 0) options.line_type = LineType::fiber; + else if (strcmp(optarg, "gigasym") == 0) + options.line_type = LineType::gigasym; else { std::cerr << "Unsupported line type " << optarg << std::endl; std::cerr << "Supported line type: auto, slow, narrow, broad, fiber" < 30 && preSpeed < 150) { downloadConfig = broadbandConfigDownload; uploadConfig = broadbandConfigUpload; - } else if (preSpeed >= 150) { + } else if (preSpeed > 150 && preSpeed < 450) { downloadConfig = fiberConfigDownload; uploadConfig = fiberConfigUpload; + } else if (preSpeed >= 450) { + downloadConfig = gigaSymConfigDownload; + uploadConfig = gigaSymConfigUpload; } } diff --git a/main.cpp b/main.cpp index 8cb37bc..9680729 100644 --- a/main.cpp +++ b/main.cpp @@ -226,6 +226,10 @@ int main(const int argc, const char **argv) { uploadConfig = fiberConfigUpload; downloadConfig = fiberConfigDownload; } + else if (programOptions.line_type == LineType::gigasym) { + uploadConfig = gigaSymConfigUpload; + downloadConfig = gigaSymConfigDownload; + } if (programOptions.output_type == OutputType::verbose) std::cout << downloadConfig.label << std::endl; From 2a741b154a2efe296a58e1256d72d4b4f121ba26 Mon Sep 17 00:00:00 2001 From: JJ Macalinao Date: Wed, 28 Apr 2021 13:50:27 +0800 Subject: [PATCH 08/17] Fix typo --- .gitignore | 2 ++ TestConfigTemplate.h | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 887f3c7..042c432 100644 --- a/.gitignore +++ b/.gitignore @@ -63,3 +63,5 @@ CMakeFiles/SpeedTest.dir/DependInfo.cmake CMakeFiles/SpeedTest.dir/flags.make CMakeFiles/SpeedTest.dir/link.txt CMakeFiles/SpeedTest.dir/progress.make +CMakeFiles/Progress/1 +CMakeFiles/Progress/count.txt diff --git a/TestConfigTemplate.h b/TestConfigTemplate.h index 77046ae..fb87811 100644 --- a/TestConfigTemplate.h +++ b/TestConfigTemplate.h @@ -120,7 +120,6 @@ const TestConfig gigaSymConfigUpload = { 8, // concurrency "Gigabit symmetric line type detected: profile selected gigasym" }; -}; void testConfigSelector(const double preSpeed, TestConfig& uploadConfig, TestConfig& downloadConfig){ uploadConfig = slowConfigUpload; From d568762ecc46a4e4134084059177a7ce0f3888f5 Mon Sep 17 00:00:00 2001 From: JJ Macalinao Date: Wed, 28 Apr 2021 14:37:28 +0800 Subject: [PATCH 09/17] Change download/upload speed values to Mbps instead --- main.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/main.cpp b/main.cpp index 9680729..6fdeb7b 100644 --- a/main.cpp +++ b/main.cpp @@ -260,7 +260,8 @@ int main(const int argc, const char **argv) { } else if (programOptions.output_type == OutputType::json) { std::cout << "\"download\":\""; std::cout << std::fixed; - std::cout << (downloadSpeed*1000*1000) << "\","; + std::cout << std::setprecision(2); + std::cout << (downloadSpeed) << "\","; } } else { std::cerr << "Download test failed." << std::endl; @@ -298,7 +299,8 @@ int main(const int argc, const char **argv) { } else if (programOptions.output_type == OutputType::json) { std::cout << "\"upload\":\""; std::cout << std::fixed; - std::cout << (uploadSpeed*1000*1000) << "\","; + std::cout << std::setprecision(2); + std::cout << (uploadSpeed) << "\","; } } else { From b02a644872e76e7b18e34d29678e73e193648354 Mon Sep 17 00:00:00 2001 From: JJ Macalinao Date: Wed, 28 Apr 2021 14:59:44 +0800 Subject: [PATCH 10/17] Remove precision limit of download/upload JSON --- main.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/main.cpp b/main.cpp index 6fdeb7b..90bdfe4 100644 --- a/main.cpp +++ b/main.cpp @@ -260,7 +260,6 @@ int main(const int argc, const char **argv) { } else if (programOptions.output_type == OutputType::json) { std::cout << "\"download\":\""; std::cout << std::fixed; - std::cout << std::setprecision(2); std::cout << (downloadSpeed) << "\","; } } else { @@ -299,7 +298,6 @@ int main(const int argc, const char **argv) { } else if (programOptions.output_type == OutputType::json) { std::cout << "\"upload\":\""; std::cout << std::fixed; - std::cout << std::setprecision(2); std::cout << (uploadSpeed) << "\","; } From 6e678760f539c0c2d49aa465c9a9a1ba07066c14 Mon Sep 17 00:00:00 2001 From: JJ Macalinao Date: Wed, 28 Apr 2021 16:57:00 +0800 Subject: [PATCH 11/17] Reduce test time to 15s --- TestConfigTemplate.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/TestConfigTemplate.h b/TestConfigTemplate.h index fb87811..c5d1a63 100644 --- a/TestConfigTemplate.h +++ b/TestConfigTemplate.h @@ -99,14 +99,14 @@ const TestConfig fiberConfigUpload = { "Fiber / Lan line type detected: profile selected fiber" }; -// The following configuration was deduced based on actual traffic monitoring in my Mikrotik router +// The following configuration was deduced based on actual traffic monitoring on my Mikrotik router const TestConfig gigaSymConfigDownload = { 5000000, // start_size 120000000, // max_size 950000, // inc_size 65536, // buff_size - 20000, // min_test_time_ms + 15000, // min_test_time_ms 8, // concurrency "Gigabit symmetric line type detected: profile selected gigasym" }; @@ -116,7 +116,7 @@ const TestConfig gigaSymConfigUpload = { 70000000, // max_size 950000, // inc_size 65536, // buff_size - 20000, // min_test_time_ms + 15000, // min_test_time_ms 8, // concurrency "Gigabit symmetric line type detected: profile selected gigasym" }; From 1fffbd86b03ef2bf65d72097aa6319200df55a52 Mon Sep 17 00:00:00 2001 From: JJ Macalinao Date: Wed, 28 Apr 2021 17:38:24 +0800 Subject: [PATCH 12/17] Increase concurrency for download --- TestConfigTemplate.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TestConfigTemplate.h b/TestConfigTemplate.h index c5d1a63..1f50545 100644 --- a/TestConfigTemplate.h +++ b/TestConfigTemplate.h @@ -107,7 +107,7 @@ const TestConfig gigaSymConfigDownload = { 950000, // inc_size 65536, // buff_size 15000, // min_test_time_ms - 8, // concurrency + 24, // concurrency "Gigabit symmetric line type detected: profile selected gigasym" }; @@ -117,7 +117,7 @@ const TestConfig gigaSymConfigUpload = { 950000, // inc_size 65536, // buff_size 15000, // min_test_time_ms - 8, // concurrency + 8, // concurrency "Gigabit symmetric line type detected: profile selected gigasym" }; From 2a11563b1b03039aac967cb18869101eb7b08f86 Mon Sep 17 00:00:00 2001 From: JJ Macalinao Date: Wed, 28 Apr 2021 20:08:02 +0800 Subject: [PATCH 13/17] Continue test config adjustments --- TestConfigTemplate.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TestConfigTemplate.h b/TestConfigTemplate.h index 1f50545..e9a7ac8 100644 --- a/TestConfigTemplate.h +++ b/TestConfigTemplate.h @@ -107,7 +107,7 @@ const TestConfig gigaSymConfigDownload = { 950000, // inc_size 65536, // buff_size 15000, // min_test_time_ms - 24, // concurrency + 6, // concurrency "Gigabit symmetric line type detected: profile selected gigasym" }; @@ -117,7 +117,7 @@ const TestConfig gigaSymConfigUpload = { 950000, // inc_size 65536, // buff_size 15000, // min_test_time_ms - 8, // concurrency + 24, // concurrency "Gigabit symmetric line type detected: profile selected gigasym" }; From 83dc9d1bdc8e73567b51379c728a1298d634d2b4 Mon Sep 17 00:00:00 2001 From: JJ Macalinao Date: Thu, 29 Apr 2021 15:31:48 +0800 Subject: [PATCH 14/17] More adjustments to upload --- TestConfigTemplate.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TestConfigTemplate.h b/TestConfigTemplate.h index e9a7ac8..c96518b 100644 --- a/TestConfigTemplate.h +++ b/TestConfigTemplate.h @@ -114,10 +114,10 @@ const TestConfig gigaSymConfigDownload = { const TestConfig gigaSymConfigUpload = { 5000000, // start_size 70000000, // max_size - 950000, // inc_size + 0, // inc_size 65536, // buff_size 15000, // min_test_time_ms - 24, // concurrency + 4, // concurrency "Gigabit symmetric line type detected: profile selected gigasym" }; From caa0013926f8b6f5fb8a899116e5957d496e6853 Mon Sep 17 00:00:00 2001 From: Andy Date: Wed, 4 Aug 2021 21:21:46 +0800 Subject: [PATCH 15/17] Create LICENSE --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e9d0926 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 taganaka + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From 09576e3af8c73b10f2bd440894214deb14ad8140 Mon Sep 17 00:00:00 2001 From: Andy Date: Wed, 4 Aug 2021 21:36:28 +0800 Subject: [PATCH 16/17] Update SpeedTest.cpp --- SpeedTest.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/SpeedTest.cpp b/SpeedTest.cpp index 4452169..4c997a7 100644 --- a/SpeedTest.cpp +++ b/SpeedTest.cpp @@ -6,6 +6,7 @@ #include #include "SpeedTest.h" #include "MD5Util.h" +#include #include SpeedTest::SpeedTest(float minServerVersion): From bebf4f5094a1b903db48f0cbe5b632716f0ed199 Mon Sep 17 00:00:00 2001 From: Shreyans Jain Date: Wed, 15 Dec 2021 21:06:05 +0530 Subject: [PATCH 17/17] Added SPDX License Identifiers --- CmdOptions.h | 170 ++++++++++++---------- DataTypes.h | 16 ++- Dockerfile | 2 + MD5Util.cpp | 7 +- MD5Util.h | 15 +- SpeedTest.cpp | 332 +++++++++++++++++++++++++------------------ SpeedTest.h | 46 +++--- SpeedTestClient.cpp | 140 ++++++++++-------- SpeedTestClient.h | 14 +- SpeedTestConfig.h.in | 10 +- TestConfigTemplate.h | 200 +++++++++++++------------- main.cpp | 202 ++++++++++++++++---------- 12 files changed, 660 insertions(+), 494 deletions(-) diff --git a/CmdOptions.h b/CmdOptions.h index f9f9995..5ee6895 100644 --- a/CmdOptions.h +++ b/CmdOptions.h @@ -1,104 +1,120 @@ // // Created by Francesco Laurita on 9/9/16. +// SPDX-License-Identifier: MIT // #ifndef SPEEDTEST_CMDOPTIONS_H #define SPEEDTEST_CMDOPTIONS_H #include -enum OutputType { verbose, text, json }; -enum LineType { automatic, slow, narrow, broad, fiber, gigasym }; - +enum OutputType +{ + verbose, + text, + json +}; +enum LineType +{ + automatic, + slow, + narrow, + broad, + fiber, + gigasym +}; -typedef struct program_options_t { - bool help = false; - bool latency = false; +typedef struct program_options_t +{ + bool help = false; + bool latency = false; bool download = false; - bool upload = false; - bool share = false; + bool upload = false; + bool share = false; std::string selected_server = ""; OutputType output_type = OutputType::verbose; LineType line_type = LineType::automatic; } 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' }, - {"test-server", required_argument, 0, 't' }, - {"output", required_argument, 0, 'o' }, - {"line-type", required_argument, 0, 'i' }, - {0, 0, 0, 0 } -}; + {"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'}, + {"test-server", required_argument, 0, 't'}, + {"output", required_argument, 0, 'o'}, + {"line-type", required_argument, 0, 'i'}, + {0, 0, 0, 0}}; const char *optStr = "hldusqt:o:"; -bool ParseOptions(const int argc, const char **argv, ProgramOptions& options){ - int long_index =0; +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 '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" < static const float EARTH_RADIUS_KM = 6371.0; -typedef struct ip_info_t { +typedef struct ip_info_t +{ std::string ip_address; std::string isp; float lat; float lon; } IPInfo; -typedef struct server_info_t { +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; + int id; float lat; float lon; float distance; } ServerInfo; -typedef struct test_config_t { +typedef struct test_config_t +{ long start_size; long max_size; long incr_size; long buff_size; long min_test_time_ms; - int concurrency; + int concurrency; std::string label; } TestConfig; -#endif //SPEEDTEST_DATATYPES_H +#endif // SPEEDTEST_DATATYPES_H diff --git a/Dockerfile b/Dockerfile index 830f5ef..d255589 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: MIT + FROM ubuntu:latest RUN mkdir -p /tmp/build /tmp/src diff --git a/MD5Util.cpp b/MD5Util.cpp index 56f6bc5..e45b0f4 100644 --- a/MD5Util.cpp +++ b/MD5Util.cpp @@ -1,11 +1,13 @@ // // Created by Francesco Laurita on 6/3/16. +// SPDX-License-Identifier: MIT // #include #include "MD5Util.h" -std::string MD5Util::hexDigest(const std::string &str) { +std::string MD5Util::hexDigest(const std::string &str) +{ unsigned char digest[MD5_DIGEST_LENGTH]; MD5_CTX ctx; @@ -15,8 +17,7 @@ std::string MD5Util::hexDigest(const std::string &str) { char hexDigest[33] = {'\0'}; for (int i = 0; i < 16; i++) - std::sprintf(&hexDigest[i*2], "%02x", (unsigned int)digest[i]); + std::sprintf(&hexDigest[i * 2], "%02x", (unsigned int)digest[i]); return std::string(hexDigest); } - diff --git a/MD5Util.h b/MD5Util.h index b9d8903..01e3c47 100644 --- a/MD5Util.h +++ b/MD5Util.h @@ -1,23 +1,24 @@ // // Created by Francesco Laurita on 6/3/16. +// SPDX-License-Identifier: MIT // #ifndef SPEEDTEST_MD5UTIL_H #define SPEEDTEST_MD5UTIL_H #if defined(__APPLE__) -# define COMMON_DIGEST_FOR_OPENSSL -# include +#define COMMON_DIGEST_FOR_OPENSSL +#include #include -# define SHA1 CC_SHA1 +#define SHA1 CC_SHA1 #else -# include +#include #endif -class MD5Util { +class MD5Util +{ public: static std::string hexDigest(const std::string &str); }; - -#endif //SPEEDTEST_MD5UTIL_H +#endif // SPEEDTEST_MD5UTIL_H diff --git a/SpeedTest.cpp b/SpeedTest.cpp index 7f76723..51bd239 100644 --- a/SpeedTest.cpp +++ b/SpeedTest.cpp @@ -1,5 +1,6 @@ // // Created by Francesco Laurita on 5/29/16. +// SPDX-License-Identifier: MIT // #include @@ -9,31 +10,35 @@ #include #include -SpeedTest::SpeedTest(float minServerVersion): - mLatency(0), - mUploadSpeed(0), - mDownloadSpeed(0) { +SpeedTest::SpeedTest(float minServerVersion) : mLatency(0), + mUploadSpeed(0), + mDownloadSpeed(0) +{ curl_global_init(CURL_GLOBAL_DEFAULT); mIpInfo = IPInfo(); mServerList = std::vector(); mMinSupportedServer = minServerVersion; } -SpeedTest::~SpeedTest() { +SpeedTest::~SpeedTest() +{ curl_global_cleanup(); mServerList.clear(); } -bool SpeedTest::ipInfo(IPInfo& info) { +bool SpeedTest::ipInfo(IPInfo &info) +{ - if (!mIpInfo.ip_address.empty()){ + 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){ + if (code == CURLE_OK) + { auto values = SpeedTest::parseQueryString(oss.str()); mIpInfo = IPInfo(); mIpInfo.ip_address = values["ip_address"]; @@ -49,19 +54,21 @@ bool SpeedTest::ipInfo(IPInfo& info) { return false; } -const std::vector& SpeedTest::serverList() { +const std::vector &SpeedTest::serverList() +{ if (!mServerList.empty()) return mServerList; int http_code = 0; - if (fetchServers(SPEED_TEST_SERVER_LIST_URL, mServerList, http_code) && http_code == 200){ + 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 cb) { +const ServerInfo SpeedTest::bestServer(const int sample_size, std::function cb) +{ auto best = findBestServerWithin(serverList(), mLatency, sample_size, cb); SpeedTestClient client = SpeedTestClient(best); testLatency(client, SPEED_TEST_LATENCY_SAMPLE_SIZE, mLatency); @@ -69,56 +76,72 @@ const ServerInfo SpeedTest::bestServer(const int sample_size, std::function= mMinSupportedServer){ - if (!testLatency(client, SPEED_TEST_LATENCY_SAMPLE_SIZE, mLatency)){ + if (client.connect() && client.version() >= mMinSupportedServer) + { + if (!testLatency(client, SPEED_TEST_LATENCY_SAMPLE_SIZE, mLatency)) + { return false; } - } else { + } + else + { client.close(); return false; } client.close(); return true; - } -bool SpeedTest::downloadSpeed(const ServerInfo &server, const TestConfig &config, double& result, std::function cb) { +bool SpeedTest::downloadSpeed(const ServerInfo &server, const TestConfig &config, double &result, std::function 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 cb) { +bool SpeedTest::uploadSpeed(const ServerInfo &server, const TestConfig &config, double &result, std::function cb) +{ opFn pfunc = &SpeedTestClient::upload; mUploadSpeed = execute(server, config, pfunc, cb); result = mUploadSpeed; return true; } -const double &SpeedTest::latency() { +const double &SpeedTest::latency() +{ return mLatency; } -bool SpeedTest::jitter(const ServerInfo &server, double& result, const int sample) { +bool SpeedTest::jitter(const ServerInfo &server, double &result, const int sample) +{ auto client = SpeedTestClient(server); double current_jitter = 0; double previous_ms = DBL_MAX; - if (client.connect()){ - for (int i = 0; i < sample; i++){ + if (client.connect()) + { + for (int i = 0; i < sample; i++) + { double ms = 0; - if (client.ping(ms)){ - if (previous_ms == DBL_MAX) { + if (client.ping(ms)) + { + if (previous_ms == DBL_MAX) + { previous_ms = ms; - } else { + } + else + { current_jitter += std::abs(previous_ms - ms); } } } client.close(); - } else { + } + else + { return false; } @@ -126,13 +149,13 @@ bool SpeedTest::jitter(const ServerInfo &server, double& result, const int sampl return true; } - -bool SpeedTest::share(const ServerInfo& server, std::string& image_url) { +bool SpeedTest::share(const ServerInfo &server, std::string &image_url) +{ std::stringstream hash; hash << std::setprecision(2) << std::fixed << (mLatency / 1000) - << "-" << std::setprecision(2) << std::fixed << (mUploadSpeed * 1000) - << "-" << std::setprecision(2) << std::fixed << (mDownloadSpeed * 1000) - << "-" << SPEED_TEST_API_KEY; + << "-" << 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) << "&"; @@ -151,14 +174,16 @@ bool SpeedTest::share(const ServerInfo& server, std::string& image_url) { auto cres = SpeedTest::httpPost(SPEED_TEST_API_URL, post_data.str(), result, c); long http_code = 0; image_url.clear(); - if (cres == CURLE_OK){ + if (cres == CURLE_OK) + { curl_easy_getinfo(c, CURLINFO_HTTP_CODE, &http_code); - if (http_code == 200 && !result.str().empty()){ + if (http_code == 200 && !result.str().empty()) + { auto data = SpeedTest::parseQueryString(result.str()); - if (data.count("resultid") == 1){ + if (data.count("resultid") == 1) + { image_url = "http://www.speedtest.net/result/" + data["resultid"] + ".png"; } - } } @@ -168,12 +193,15 @@ bool SpeedTest::share(const ServerInfo& server, std::string& image_url) { // private -double SpeedTest::execute(const ServerInfo &server, const TestConfig &config, const opFn &pfunc, std::function cb) { +double SpeedTest::execute(const ServerInfo &server, const TestConfig &config, const opFn &pfunc, std::function cb) +{ std::vector 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](){ + 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; @@ -227,11 +255,10 @@ double SpeedTest::execute(const ServerInfo &server, const TestConfig &config, co } else { if (cb) cb(false); - } - })); - + } })); } - for (auto &t : workers){ + for (auto &t : workers) + { t.join(); } @@ -240,13 +267,15 @@ double SpeedTest::execute(const ServerInfo &server, const TestConfig &config, co return overall_speed / 1024 / 1024; } -template -T SpeedTest::deg2rad(T n) { +template +T SpeedTest::deg2rad(T n) +{ return (n * M_PI / 180); } -template -T SpeedTest::harversine(std::pair n1, std::pair n2) { +template +T SpeedTest::harversine(std::pair n1, std::pair n2) +{ T lat1r = deg2rad(n1.first); T lon1r = deg2rad(n1.second); T lat2r = deg2rad(n2.first); @@ -256,16 +285,16 @@ T SpeedTest::harversine(std::pair n1, std::pair n2) { 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 SpeedTest::httpGet(const std::string &url, std::stringstream &ss, CURL *handler, long timeout) +{ CURLcode code(CURLE_FAILED_INIT); - CURL* curl = SpeedTest::curl_setup(handler); + 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_URL, url.c_str()))) { + 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_URL, url.c_str()))) + { code = curl_easy_perform(curl); } if (handler == nullptr) @@ -274,16 +303,16 @@ CURLcode SpeedTest::httpGet(const std::string &url, std::stringstream &ss, CURL return code; } -CURLcode SpeedTest::httpPost(const std::string &url, const std::string &postdata, std::stringstream &os, void *handler, long timeout) { +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); + 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_POSTFIELDS, postdata.c_str()))) { + 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_POSTFIELDS, postdata.c_str()))) + { code = curl_easy_perform(curl); } if (handler == nullptr) @@ -292,55 +321,61 @@ CURLcode SpeedTest::httpPost(const std::string &url, const std::string &postdata 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 - && curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "br, gzip, deflate") == CURLE_OK) { +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 && curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "br, gzip, deflate") == CURLE_OK) + { return curl; - } else { + } + else + { curl_easy_cleanup(handler); return nullptr; } } return nullptr; - - } -size_t SpeedTest::writeFunc(void *buf, size_t size, size_t nmemb, void *userp) { +size_t SpeedTest::writeFunc(void *buf, size_t size, size_t nmemb, void *userp) +{ - if (userp){ + if (userp) + { std::stringstream &os = *static_cast(userp); std::streamsize len = size * nmemb; - if(os.write(static_cast(buf), len)) - return static_cast(len); + if (os.write(static_cast(buf), len)) + return static_cast(len); } return 0; } -std::map SpeedTest::parseQueryString(const std::string &query) { +std::map SpeedTest::parseQueryString(const std::string &query) +{ auto map = std::map(); auto pairs = splitString(query, '&'); - for (auto &p : pairs){ + for (auto &p : pairs) + { auto kv = splitString(p, '='); - if (kv.size() == 2){ + if (kv.size() == 2) + { map[kv[0]] = kv[1]; } } return map; } -std::vector SpeedTest::splitString(const std::string &instr, const char separator) { +std::vector SpeedTest::splitString(const std::string &instr, const char separator) +{ if (instr.empty()) return std::vector(); std::vector tokens; std::size_t start = 0, end = 0; - while ((end = instr.find(separator, start)) != std::string::npos) { + while ((end = instr.find(separator, start)) != std::string::npos) + { std::string temp = instr.substr(start, end - start); if (!temp.empty()) tokens.push_back(temp); @@ -350,56 +385,58 @@ std::vector SpeedTest::splitString(const std::string &instr, const if (!temp.empty()) tokens.push_back(temp); return tokens; - } -ServerInfo SpeedTest::processServerXMLNode(xmlTextReaderPtr reader) { +ServerInfo SpeedTest::processServerXMLNode(xmlTextReaderPtr reader) +{ auto name = xmlTextReaderConstName(reader); - auto nodeName = std::string((char*)name); + auto nodeName = std::string((char *)name); - if (!name || nodeName != "server"){ + if (!name || nodeName != "server") + { return ServerInfo(); } - if (xmlTextReaderAttributeCount(reader) > 0){ + 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"); + 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); + info.name.append((char *)server_name); if (server_url) - info.url.append((char*)server_url); + info.url.append((char *)server_url); if (server_county) - info.country.append((char*)server_county); + info.country.append((char *)server_county); if (server_cc) - info.country_code.append((char*)server_cc); + info.country_code.append((char *)server_cc); if (server_host) - info.host.append((char*)server_host); + info.host.append((char *)server_host); if (server_sponsor) - info.sponsor.append((char*)server_sponsor); + info.sponsor.append((char *)server_sponsor); if (server_id) - info.id = std::atoi((char*)server_id); + info.id = std::atoi((char *)server_id); if (server_lat) - info.lat = std::stof((char*)server_lat); + info.lat = std::stof((char *)server_lat); if (server_lon) - info.lon = std::stof((char*)server_lon); + info.lon = std::stof((char *)server_lon); xmlFree(server_url); xmlFree(server_lat); @@ -416,34 +453,40 @@ ServerInfo SpeedTest::processServerXMLNode(xmlTextReaderPtr reader) { return ServerInfo(); } -bool SpeedTest::fetchServers(const std::string& url, std::vector& target, int &http_code) { +bool SpeedTest::fetchServers(const std::string &url, std::vector &target, int &http_code) +{ std::stringstream oss; target.clear(); auto isHttpSchema = url.find_first_of("http") == 0; - CURL* curl = curl_easy_init(); + CURL *curl = curl_easy_init(); auto cres = httpGet(url, oss, curl, 20); if (cres != CURLE_OK) return false; - if (isHttpSchema) { + if (isHttpSchema) + { int req_status; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &req_status); http_code = req_status; - if (http_code != 200){ + if (http_code != 200) + { curl_easy_cleanup(curl); return false; } - } else { + } + else + { http_code = 200; } size_t len = oss.str().length(); - auto *xmlbuff = (char*)calloc(len + 1, sizeof(char)); - if (!xmlbuff){ + auto *xmlbuff = (char *)calloc(len + 1, sizeof(char)); + if (!xmlbuff) + { std::cerr << "Unable to calloc" << std::endl; curl_easy_cleanup(curl); return false; @@ -453,32 +496,39 @@ bool SpeedTest::fetchServers(const std::string& url, std::vector& ta xmlTextReaderPtr reader = xmlReaderForMemory(xmlbuff, static_cast(len), nullptr, nullptr, 0); - if (reader != nullptr) { + if (reader != nullptr) + { IPInfo ipInfo; - if (!SpeedTest::ipInfo(ipInfo)){ + if (!SpeedTest::ipInfo(ipInfo)) + { curl_easy_cleanup(curl); free(xmlbuff); xmlFreeTextReader(reader); - std::cerr << "OOPS!" <& ta 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; - }); + 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 &serverList, double &latency, - const int sample_size, std::function cb) { + const int sample_size, std::function cb) +{ int i = sample_size; ServerInfo bestServer = serverList[0]; latency = DBL_MAX; - for (auto &server : serverList){ + for (auto &server : serverList) + { auto client = SpeedTestClient(server); - if (!client.connect()){ + if (!client.connect()) + { if (cb) cb(false); continue; } - if (client.version() < mMinSupportedServer){ + if (client.version() < mMinSupportedServer) + { client.close(); continue; } double current_latency = DBL_MAX; - if (testLatency(client, 20, current_latency)){ - if (current_latency < latency){ + if (testLatency(client, 20, current_latency)) + { + if (current_latency < latency) + { latency = current_latency; bestServer = server; } @@ -526,26 +581,33 @@ const ServerInfo SpeedTest::findBestServerWithin(const std::vector & if (cb) cb(true); - if (i-- < 0){ + if (i-- < 0) + { break; } - } return bestServer; } -bool SpeedTest::testLatency(SpeedTestClient &client, const int sample_size, double &latency) { - if (!client.connect()){ +bool SpeedTest::testLatency(SpeedTestClient &client, const int sample_size, double &latency) +{ + if (!client.connect()) + { return false; } latency = DBL_MAX; double temp_latency = 0; - for (int i = 0; i < sample_size; i++){ - if (client.ping(temp_latency)){ - if (temp_latency < latency){ + for (int i = 0; i < sample_size; i++) + { + if (client.ping(temp_latency)) + { + if (temp_latency < latency) + { latency = temp_latency; } - } else { + } + else + { return false; } } diff --git a/SpeedTest.h b/SpeedTest.h index efb6ddf..806b5b6 100644 --- a/SpeedTest.h +++ b/SpeedTest.h @@ -1,5 +1,6 @@ // // Created by Francesco Laurita on 5/29/16. +// SPDX-License-Identifier: MIT // #ifndef SPEEDTEST_SPEEDTEST_H @@ -25,36 +26,37 @@ class SpeedTestClient; typedef bool (SpeedTestClient::*opFn)(const long size, const long chunk_size, double &millisec); typedef void (*progressFn)(bool success); - -class SpeedTest { +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 parseQueryString(const std::string& query); - static std::vector splitString(const std::string& instr, char separator); - bool ipInfo(IPInfo& info); - const std::vector& serverList(); + 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 parseQueryString(const std::string &query); + static std::vector splitString(const std::string &instr, char separator); + bool ipInfo(IPInfo &info); + const std::vector &serverList(); const ServerInfo bestServer(int sample_size = 5, std::function cb = nullptr); - bool setServer(ServerInfo& server); + bool setServer(ServerInfo &server); const double &latency(); - bool downloadSpeed(const ServerInfo& server, const TestConfig& config, double& result, std::function cb = nullptr); - bool uploadSpeed(const ServerInfo& server, const TestConfig& config, double& result, std::function cb = nullptr); - bool jitter(const ServerInfo& server, double& result, int sample = 40); - bool share(const ServerInfo& server, std::string& image_url); + bool downloadSpeed(const ServerInfo &server, const TestConfig &config, double &result, std::function cb = nullptr); + bool uploadSpeed(const ServerInfo &server, const TestConfig &config, double &result, std::function cb = nullptr); + bool jitter(const ServerInfo &server, double &result, int sample = 40); + bool share(const ServerInfo &server, std::string &image_url); + private: - bool fetchServers(const std::string& url, std::vector& target, int &http_code); - bool testLatency(SpeedTestClient& client, int sample_size, double& latency); - const ServerInfo findBestServerWithin(const std::vector& serverList, double& latency, int sample_size = 5, std::function cb = nullptr); - static CURL* curl_setup(CURL* curl = nullptr); - static size_t writeFunc(void* buf, size_t size, size_t nmemb, void* userp); + bool fetchServers(const std::string &url, std::vector &target, int &http_code); + bool testLatency(SpeedTestClient &client, int sample_size, double &latency); + const ServerInfo findBestServerWithin(const std::vector &serverList, double &latency, int sample_size = 5, std::function 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 cb = nullptr); template - static T deg2rad(T n); + static T deg2rad(T n); template - static T harversine(std::pair n1, std::pair n2); + static T harversine(std::pair n1, std::pair n2); IPInfo mIpInfo; std::vector mServerList; @@ -62,8 +64,6 @@ class SpeedTest { double mUploadSpeed; double mDownloadSpeed; float mMinSupportedServer; - }; - -#endif //SPEEDTEST_SPEEDTEST_H +#endif // SPEEDTEST_SPEEDTEST_H diff --git a/SpeedTestClient.cpp b/SpeedTestClient.cpp index 6e58016..96f480d 100644 --- a/SpeedTestClient.cpp +++ b/SpeedTestClient.cpp @@ -1,25 +1,28 @@ // // Created by Francesco Laurita on 5/30/16. +// SPDX-License-Identifier: MIT // #include #include #include "SpeedTestClient.h" -SpeedTestClient::SpeedTestClient(const ServerInfo &serverInfo): mServerInfo(serverInfo), - mSocketFd(0), - mServerVersion(-1.0){} -SpeedTestClient::~SpeedTestClient() { +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() { +bool SpeedTestClient::connect() +{ - if (mSocketFd){ + if (mSocketFd) + { return true; } @@ -29,41 +32,45 @@ bool SpeedTestClient::connect() { std::string reply; - if (!SpeedTestClient::writeLine(mSocketFd, "HI")){ + if (!SpeedTestClient::writeLine(mSocketFd, "HI")) + { close(); return false; } - - if (SpeedTestClient::readLine(mSocketFd, reply)){ + if (SpeedTestClient::readLine(mSocketFd, reply)) + { std::stringstream reply_stream(reply); std::string hello; reply_stream >> hello >> mServerVersion; - if (reply_stream.fail()) { + if (reply_stream.fail()) + { close(); return false; } - if (!reply.empty() && "HELLO" == hello){ + if (!reply.empty() && "HELLO" == hello) + { return true; } - } close(); return false; } // It closes a connection -void SpeedTestClient::close() { - if (mSocketFd){ +void SpeedTestClient::close() +{ + if (mSocketFd) + { SpeedTestClient::writeLine(mSocketFd, "QUIT"); ::close(mSocketFd); } - } // It executes PING command -bool SpeedTestClient::ping(double &millisec) { +bool SpeedTestClient::ping(double &millisec) +{ if (!mSocketFd) return false; @@ -73,12 +80,15 @@ bool SpeedTestClient::ping(double &millisec) { auto start = std::chrono::steady_clock::now(); cmd << "PING " << start.time_since_epoch().count(); - if (!SpeedTestClient::writeLine(mSocketFd, cmd.str())){ + if (!SpeedTestClient::writeLine(mSocketFd, cmd.str())) + { return false; } - if (SpeedTestClient::readLine(mSocketFd, reply)){ - if (reply.substr(0, 5) == "PONG "){ + if (SpeedTestClient::readLine(mSocketFd, reply)) + { + if (reply.substr(0, 5) == "PONG ") + { auto stop = std::chrono::steady_clock::now(); double microsec = std::chrono::duration_cast(stop - start).count(); millisec = microsec / 1000; @@ -91,25 +101,28 @@ bool SpeedTestClient::ping(double &millisec) { } // It executes DOWNLOAD command -bool SpeedTestClient::download(const long size, const long chunk_size, double &millisec) { +bool SpeedTestClient::download(const long size, const long chunk_size, double &millisec) +{ std::stringstream cmd; cmd << "DOWNLOAD " << size; - if (!SpeedTestClient::writeLine(mSocketFd, cmd.str())){ + if (!SpeedTestClient::writeLine(mSocketFd, cmd.str())) + { return false; } - char *buff = new char[chunk_size]; for (size_t i = 0; i < static_cast(chunk_size); i++) buff[i] = '\0'; long missing = 0; auto start = std::chrono::steady_clock::now(); - while (missing != size){ + while (missing != size) + { auto current = read(mSocketFd, buff, static_cast(chunk_size)); - if (current <= 0){ + if (current <= 0) + { delete[] buff; return false; } @@ -124,19 +137,21 @@ bool SpeedTestClient::download(const long size, const long chunk_size, double &m } // It executes UPLOAD command -bool SpeedTestClient::upload(const long size, const long chunk_size, double &millisec) { +bool SpeedTestClient::upload(const long size, const long chunk_size, double &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(chunk_size); i++) + for (size_t i = 0; i < static_cast(chunk_size); i++) buff[i] = static_cast(rand() % 256); long missing = size; auto start = std::chrono::steady_clock::now(); - if (!SpeedTestClient::writeLine(mSocketFd, cmd.str())){ + if (!SpeedTestClient::writeLine(mSocketFd, cmd.str())) + { delete[] buff; return false; } @@ -144,27 +159,33 @@ bool SpeedTestClient::upload(const long size, const long chunk_size, double &mil ssize_t w = cmd_len; missing -= w; - while(missing > 0){ - if (missing - chunk_size > 0){ + while (missing > 0) + { + if (missing - chunk_size > 0) + { w = write(mSocketFd, buff, static_cast(chunk_size)); - if (w != chunk_size){ + if (w != chunk_size) + { delete[] buff; return false; } missing -= w; - } else { + } + else + { buff[missing - 1] = '\n'; w = write(mSocketFd, buff, static_cast(missing)); - if (w != missing){ + if (w != missing) + { delete[] buff; return false; } missing -= w; } - } std::string reply; - if (!SpeedTestClient::readLine(mSocketFd, reply)){ + if (!SpeedTestClient::readLine(mSocketFd, reply)) + { delete[] buff; return false; } @@ -176,20 +197,22 @@ bool SpeedTestClient::upload(const long size, const long chunk_size, double &mil millisec = microsec / 1000; delete[] buff; return reply.substr(0, ss.str().length()) == ss.str(); - } -bool SpeedTestClient::mkSocket() { +bool SpeedTestClient::mkSocket() +{ mSocketFd = socket(AF_INET, SOCK_STREAM, 0); - if (!mSocketFd){ + if (!mSocketFd) + { return false; } auto hostp = hostport(); #if __APPLE__ struct hostent *server = gethostbyname(hostp.first.c_str()); - if (server == nullptr) { + if (server == nullptr) + { return false; } #else @@ -197,13 +220,16 @@ bool SpeedTestClient::mkSocket() { char tmpbuf[BUFSIZ]; struct hostent *result; int errnop; - if (gethostbyname_r(hostp.first.c_str(), &server, (char *)&tmpbuf, BUFSIZ, &result, &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{}; + struct sockaddr_in serv_addr + { + }; memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; @@ -216,28 +242,31 @@ bool SpeedTestClient::mkSocket() { serv_addr.sin_port = htons(static_cast(portno)); /* Dial */ - return ::connect(mSocketFd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) >= 0; + return ::connect(mSocketFd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) >= 0; } - -float SpeedTestClient::version() { +float SpeedTestClient::version() +{ return mServerVersion; } -const std::pair SpeedTestClient::hostport() { +const std::pair 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); + std::string host = targetHost.substr(0, found); + std::string port = targetHost.substr(found + 1, targetHost.length() - found); return std::pair(host, std::atoi(port.c_str())); } -bool SpeedTestClient::readLine(int &fd, std::string &buffer) { +bool SpeedTestClient::readLine(int &fd, std::string &buffer) +{ buffer.clear(); if (!fd) return false; char c; - while(true){ + while (true) + { auto n = read(fd, &c, 1); if (n == -1) return false; @@ -245,29 +274,26 @@ bool SpeedTestClient::readLine(int &fd, std::string &buffer) { break; buffer += c; - } return true; } -bool SpeedTestClient::writeLine(int &fd, const std::string &buffer) { +bool SpeedTestClient::writeLine(int &fd, const std::string &buffer) +{ if (!fd) return false; - auto len = static_cast(buffer.length()); + auto len = static_cast(buffer.length()); if (len == 0) return false; std::string buff_copy = buffer; - if (buff_copy.find_first_of('\n') == std::string::npos){ + 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; } - - - - diff --git a/SpeedTestClient.h b/SpeedTestClient.h index d91d44b..5c108b0 100644 --- a/SpeedTestClient.h +++ b/SpeedTestClient.h @@ -1,11 +1,11 @@ // // Created by Francesco Laurita on 5/30/16. +// SPDX-License-Identifier: MIT // #ifndef SPEEDTEST_SPEEDTESTCLIENT_H #define SPEEDTEST_SPEEDTESTCLIENT_H - #include #include #include @@ -16,9 +16,10 @@ #include #include "SpeedTest.h" #include "DataTypes.h" -class SpeedTestClient { +class SpeedTestClient +{ public: - explicit SpeedTestClient(const ServerInfo& serverInfo); + explicit SpeedTestClient(const ServerInfo &serverInfo); ~SpeedTestClient(); bool connect(); @@ -29,15 +30,14 @@ class SpeedTestClient { const std::pair 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); + 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, double &millisec); -#endif //SPEEDTEST_SPEEDTESTCLIENT_H +#endif // SPEEDTEST_SPEEDTESTCLIENT_H diff --git a/SpeedTestConfig.h.in b/SpeedTestConfig.h.in index f8a6ff5..b8de918 100644 --- a/SpeedTestConfig.h.in +++ b/SpeedTestConfig.h.in @@ -1,6 +1,8 @@ +// SPDX-License-Identifier: MIT + // The configured options and settings for SpeedTest -#define SpeedTest_VERSION_MAJOR @SpeedTest_VERSION_MAJOR@ -#define SpeedTest_VERSION_MINOR @SpeedTest_VERSION_MINOR@ +#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@" @@ -13,5 +15,5 @@ #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@ +#define SPEED_TEST_MIN_SERVER_VERSION @SpeedTest_MIN_SERVER_VERSION @ +#define SPEED_TEST_LATENCY_SAMPLE_SIZE @SpeedTest_LATENCY_SAMPLE_SIZE @ diff --git a/TestConfigTemplate.h b/TestConfigTemplate.h index c96518b..6e765a4 100644 --- a/TestConfigTemplate.h +++ b/TestConfigTemplate.h @@ -1,5 +1,6 @@ // // Created by Francesco Laurita on 6/2/16. +// SPDX-License-Identifier: MIT // #ifndef SPEEDTEST_TESTCONFIGTEMPLATE_H @@ -8,138 +9,133 @@ #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" -}; + 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" -}; + 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" -}; - + 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" -}; + 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" -}; + 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" + 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" -}; + 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" -}; + 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" -}; + 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"}; // The following configuration was deduced based on actual traffic monitoring on my Mikrotik router const TestConfig gigaSymConfigDownload = { - 5000000, // start_size - 120000000, // max_size - 950000, // inc_size - 65536, // buff_size - 15000, // min_test_time_ms - 6, // concurrency - "Gigabit symmetric line type detected: profile selected gigasym" -}; + 5000000, // start_size + 120000000, // max_size + 950000, // inc_size + 65536, // buff_size + 15000, // min_test_time_ms + 6, // concurrency + "Gigabit symmetric line type detected: profile selected gigasym"}; const TestConfig gigaSymConfigUpload = { - 5000000, // start_size - 70000000, // max_size - 0, // inc_size - 65536, // buff_size - 15000, // min_test_time_ms - 4, // concurrency - "Gigabit symmetric line type detected: profile selected gigasym" -}; - -void testConfigSelector(const double preSpeed, TestConfig& uploadConfig, TestConfig& downloadConfig){ - uploadConfig = slowConfigUpload; + 5000000, // start_size + 70000000, // max_size + 0, // inc_size + 65536, // buff_size + 15000, // min_test_time_ms + 4, // concurrency + "Gigabit symmetric line type detected: profile selected gigasym"}; + +void testConfigSelector(const double preSpeed, TestConfig &uploadConfig, TestConfig &downloadConfig) +{ + uploadConfig = slowConfigUpload; downloadConfig = slowConfigDownload; - - if (preSpeed > 4 && preSpeed <= 30){ + if (preSpeed > 4 && preSpeed <= 30) + { downloadConfig = narrowConfigDownload; - uploadConfig = narrowConfigUpload; - } else if (preSpeed > 30 && preSpeed < 150) { + uploadConfig = narrowConfigUpload; + } + else if (preSpeed > 30 && preSpeed < 150) + { downloadConfig = broadbandConfigDownload; - uploadConfig = broadbandConfigUpload; - } else if (preSpeed > 150 && preSpeed < 450) { + uploadConfig = broadbandConfigUpload; + } + else if (preSpeed > 150 && preSpeed < 450) + { downloadConfig = fiberConfigDownload; - uploadConfig = fiberConfigUpload; - } else if (preSpeed >= 450) { + uploadConfig = fiberConfigUpload; + } + else if (preSpeed >= 450) + { downloadConfig = gigaSymConfigDownload; - uploadConfig = gigaSymConfigUpload; + uploadConfig = gigaSymConfigUpload; } - } -#endif //SPEEDTEST_TESTCONFIGTEMPLATE_H +#endif // SPEEDTEST_TESTCONFIGTEMPLATE_H diff --git a/main.cpp b/main.cpp index 90bdfe4..99d7f94 100644 --- a/main.cpp +++ b/main.cpp @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: MIT + #include #include #include @@ -6,18 +8,20 @@ #include "CmdOptions.h" #include -void banner(){ +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 << "Author: " << SpeedTest_AUTHOR << std::endl; } -void usage(const char* name){ +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" - " [--line-type auto|slow|narrow|broad|fiber]\n"; + " [--test-server host:port] [--output verbose|text|json]\n" + " [--line-type auto|slow|narrow|broad|fiber]\n"; std::cerr << "optional arguments:" << std::endl; std::cerr << " --help Show this message and exit\n"; std::cerr << " --latency Perform latency test only\n"; @@ -31,29 +35,31 @@ void usage(const char* name){ std::cerr << " --output auto|slow|narrow|broad|fiber Set line type. Default: auto\n"; } -int main(const int argc, const char **argv) { +int main(const int argc, const char **argv) +{ ProgramOptions programOptions; - if (!ParseOptions(argc, argv, programOptions)){ + if (!ParseOptions(argc, argv, programOptions)) + { usage(argv[0]); return EXIT_FAILURE; } - if (programOptions.output_type == OutputType::verbose){ + if (programOptions.output_type == OutputType::verbose) + { banner(); std::cout << std::endl; } - - if (programOptions.help) { + 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; @@ -62,25 +68,31 @@ int main(const int argc, const char **argv) { if (programOptions.output_type == OutputType::json) std::cout << "{"; - if (!sp.ipInfo(info)){ + 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){ + 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) { + } + 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) { + } + else if (programOptions.output_type == OutputType::json) + { std::cout << "\"client\":{"; - std::cout << "\"ip\":\"" << info.ip_address << "\","; + std::cout << "\"ip\":\"" << info.ip_address << "\","; std::cout << "\"lat\":\"" << info.lat << "\","; std::cout << "\"lon\":\"" << info.lon << "\","; std::cout << "\"isp\":\"" << info.isp << "\""; @@ -89,11 +101,13 @@ int main(const int argc, const char **argv) { auto serverList = sp.serverList(); - if (programOptions.selected_server.empty()){ + if (programOptions.selected_server.empty()) + { if (programOptions.output_type == OutputType::verbose) std::cout << "Finding fastest server... " << std::flush; - if (serverList.empty()){ + 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; @@ -105,25 +119,27 @@ int main(const int argc, const char **argv) { else if (programOptions.output_type == OutputType::json) std::cout << "\"servers_online\":\"" << serverList.size() << "\","; - - serverInfo = sp.bestServer(10, [&programOptions](bool success) { + serverInfo = sp.bestServer(10, [&programOptions](bool success) + { if (programOptions.output_type == OutputType::verbose) - std::cout << (success ? '.' : '*') << std::flush; - }); + std::cout << (success ? '.' : '*') << std::flush; }); - if (programOptions.output_type == OutputType::verbose){ + 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) { + } + 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) { + else if (programOptions.output_type == OutputType::json) + { std::cout << "\"server\":{"; std::cout << "\"name\":\"" << serverInfo.name << "\","; std::cout << "\"sponsor\":\"" << serverInfo.sponsor << "\","; @@ -132,23 +148,27 @@ int main(const int argc, const char **argv) { std::cout << "\"host\":\"" << serverInfo.host << "\""; std::cout << "},"; } - - } else { + } + else + { serverInfo.host.append(programOptions.selected_server); sp.setServer(serverInfo); - for (auto &s : serverList) { + 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) { + else if (programOptions.output_type == OutputType::text) + { std::cout << "TEST_SERVER_HOST=" << serverInfo.host << std::endl; } - else if (programOptions.output_type == OutputType::json) { + else if (programOptions.output_type == OutputType::json) + { std::cout << "\"server\":{"; std::cout << "\"host\":\"" << serverInfo.host << "\""; std::cout << "},"; @@ -159,7 +179,8 @@ int main(const int argc, const char **argv) { 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) { + else if (programOptions.output_type == OutputType::json) + { std::cout << "\"ping\":\""; std::cout << std::fixed; std::cout << sp.latency() << "\","; @@ -168,21 +189,26 @@ int main(const int argc, const char **argv) { double jitter = 0; if (programOptions.output_type == OutputType::verbose) std::cout << "Jitter: " << std::flush; - if (sp.jitter(serverInfo, jitter)){ + 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) { + else if (programOptions.output_type == OutputType::json) + { std::cout << "\"jitter\":\""; std::cout << std::fixed; std::cout << jitter << "\","; } - } else { + } + else + { std::cerr << "Jitter measurement is unavailable at this time." << std::endl; } - if (programOptions.latency) { + if (programOptions.latency) + { if (programOptions.output_type == OutputType::json) std::cout << "\"_\":\"only latency requested\"}" << std::endl; return EXIT_SUCCESS; @@ -191,14 +217,16 @@ int main(const int argc, const char **argv) { TestConfig uploadConfig; TestConfig downloadConfig; - if (programOptions.line_type == LineType::automatic) { + if (programOptions.line_type == LineType::automatic) + { if (programOptions.output_type == OutputType::verbose) - std::cout << "Determine line type (" << preflightConfigDownload.concurrency << ") " << std::flush; + std::cout << "Determine line type (" << preflightConfigDownload.concurrency << ") " << std::flush; double preSpeed = 0; - if (!sp.downloadSpeed(serverInfo, preflightConfigDownload, preSpeed, [&programOptions](bool success){ + if (!sp.downloadSpeed(serverInfo, preflightConfigDownload, preSpeed, [&programOptions](bool success) + { if (programOptions.output_type == OutputType::verbose) - std::cout << (success ? '.' : '*') << std::flush; - })){ + 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; @@ -207,26 +235,31 @@ int main(const int argc, const char **argv) { if (programOptions.output_type == OutputType::verbose) std::cout << std::endl; - + testConfigSelector(preSpeed, uploadConfig, downloadConfig); } - else if (programOptions.line_type == LineType::slow) { + else if (programOptions.line_type == LineType::slow) + { uploadConfig = slowConfigUpload; downloadConfig = slowConfigDownload; } - else if (programOptions.line_type == LineType::narrow) { + else if (programOptions.line_type == LineType::narrow) + { uploadConfig = narrowConfigUpload; downloadConfig = narrowConfigDownload; } - else if (programOptions.line_type == LineType::broad) { + else if (programOptions.line_type == LineType::broad) + { uploadConfig = broadbandConfigUpload; downloadConfig = broadbandConfigDownload; } - else if (programOptions.line_type == LineType::fiber) { + else if (programOptions.line_type == LineType::fiber) + { uploadConfig = fiberConfigUpload; downloadConfig = fiberConfigDownload; } - else if (programOptions.line_type == LineType::gigasym) { + else if (programOptions.line_type == LineType::gigasym) + { uploadConfig = gigaSymConfigUpload; downloadConfig = gigaSymConfigDownload; } @@ -234,35 +267,44 @@ int main(const int argc, const char **argv) { if (programOptions.output_type == OutputType::verbose) std::cout << downloadConfig.label << std::endl; - - if (!programOptions.upload){ - if (programOptions.output_type == OutputType::verbose){ + if (!programOptions.upload) + { + if (programOptions.output_type == OutputType::verbose) + { std::cout << std::endl; - std::cout << "Testing download speed (" << downloadConfig.concurrency << ") " << std::flush; + std::cout << "Testing download speed (" << downloadConfig.concurrency << ") " << std::flush; } double downloadSpeed = 0; - if (sp.downloadSpeed(serverInfo, downloadConfig, downloadSpeed, [&programOptions](bool success){ + 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 << (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) { + } + 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) { + } + else if (programOptions.output_type == OutputType::json) + { std::cout << "\"download\":\""; std::cout << std::fixed; std::cout << (downloadSpeed) << "\","; } - } else { + } + else + { std::cerr << "Download test failed." << std::endl; if (programOptions.output_type == OutputType::json) std::cout << "\"error\":\"download test failed\"}" << std::endl; @@ -270,53 +312,67 @@ int main(const int argc, const char **argv) { } } - if (programOptions.download) { + 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; + std::cout << "Testing upload speed (" << uploadConfig.concurrency << ") " << std::flush; double uploadSpeed = 0; - if (sp.uploadSpeed(serverInfo, uploadConfig, uploadSpeed, [&programOptions](bool success){ + 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 << (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) { + } + 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) { + } + else if (programOptions.output_type == OutputType::json) + { std::cout << "\"upload\":\""; std::cout << std::fixed; std::cout << (uploadSpeed) << "\","; } - - } else { + } + 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){ + if (programOptions.share) + { std::string share_it; - if (sp.share(serverInfo, share_it)) { - if (programOptions.output_type == OutputType::verbose) { + 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) { + } + else if (programOptions.output_type == OutputType::text) + { std::cout << "IMAGE_URL=" << share_it << std::endl; - } else if (programOptions.output_type == OutputType::json) { + } + else if (programOptions.output_type == OutputType::json) + { std::cout << "\"share\":\"" << share_it << "\","; } }