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[![Build Status](https://travis-ci.org/taganaka/SpeedTest.svg?branch=master)](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
openSUSE Build Service is sponsored by