| /* |
| * Copyright 2015 Google Inc. All rights reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "options.h" |
| |
| #include <assert.h> |
| #include <getopt.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <strings.h> |
| #include "url.h" |
| |
| namespace speedtest { |
| |
| const char* kDefaultHost = "speedtest.googlefiber.net"; |
| |
| namespace { |
| |
| bool ParseLong(const char *s, char **endptr, long *size) { |
| assert(s != nullptr); |
| assert(size != nullptr); |
| *size = strtol(s, endptr, 10); |
| return !**endptr; |
| } |
| |
| bool ParseSize(const char *s, long *size) { |
| long result; |
| char *endptr; |
| bool success = ParseLong(s, &endptr, &result); |
| if (result <= 0) { |
| return false; |
| } |
| if (success) { |
| *size = result; |
| return true; |
| } |
| if (strcasecmp(endptr, "m") == 0) { |
| result <<= 20; |
| } else if (strcasecmp(endptr, "k") == 0) { |
| result <<= 10; |
| } else { |
| return false; |
| } |
| *size = result; |
| return true; |
| } |
| |
| const char *kShortOpts = "d:u:t:n:p:s:vh"; |
| struct option kLongOpts[] = { |
| {"help", no_argument, nullptr, 'h'}, |
| {"number", required_argument, nullptr, 'n'}, |
| {"download_size", required_argument, nullptr, 'd'}, |
| {"upload_size", required_argument, nullptr, 'u'}, |
| {"progress", required_argument, nullptr, 'p'}, |
| {"serverid", required_argument, nullptr, 's'}, // ignored |
| {"time", required_argument, nullptr, 't'}, |
| {"verbose", no_argument, nullptr, 'v'}, |
| {nullptr, 0, nullptr, 0}, |
| }; |
| const int kDefaultNumber = 10; |
| const int kDefaultDownloadSize = 10000000; |
| const int kDefaultUploadSize = 10000000; |
| const int kDefaultTimeMillis = 5000; |
| const int kMaxNumber = 1000; |
| const int kMaxProgress = 1000000; |
| |
| } // namespace |
| |
| bool ParseOptions(int argc, char *argv[], Options *options) { |
| assert(options != nullptr); |
| options->number = kDefaultNumber; |
| options->download_size = kDefaultDownloadSize; |
| options->upload_size = kDefaultUploadSize; |
| options->time_millis = kDefaultTimeMillis; |
| options->progress_millis = 0; |
| options->verbose = false; |
| options->usage = false; |
| options->hosts.clear(); |
| |
| // Manually set this to 0 to allow repeated calls |
| optind = 0; |
| |
| int opt = 0, long_index = 0; |
| while ((opt = getopt_long(argc, argv, |
| kShortOpts, kLongOpts, &long_index)) != -1) { |
| switch (opt) { |
| case 'd': |
| if (!ParseSize(optarg, &options->download_size)) { |
| std::cerr << "Invalid download size '" << optarg << "'\n"; |
| return false; |
| } |
| break; |
| case 'u': |
| if (!ParseSize(optarg, &options->upload_size)) { |
| std::cerr << "Invalid upload size '" << optarg << "'\n"; |
| return false; |
| } |
| break; |
| case 't': |
| options->time_millis = atoi(optarg); |
| break; |
| case 'v': |
| options->verbose = true; |
| break; |
| case 'h': |
| options->usage = true; |
| return true; |
| case 'n': { |
| long number; |
| char *endptr; |
| if (!ParseLong(optarg, &endptr, &number)) { |
| std::cerr << "Could not parse number '" << optarg << "'\n"; |
| return false; |
| } |
| if (number < 1 || number > kMaxNumber) { |
| std::cerr << "Number must be between 1 and " << kMaxNumber |
| << ", got '" << optarg << "'\n"; |
| return false; |
| } |
| options->number = static_cast<int>(number); |
| break; |
| } |
| case 'p': { |
| long progress; |
| char *endptr; |
| if (!ParseLong(optarg, &endptr, &progress)) { |
| std::cerr << "Could not parse interval '" << optarg << "'\n"; |
| return false; |
| } |
| if (progress < 0 || progress > kMaxProgress) { |
| std::cerr << "Number must be between 0 and " << kMaxProgress |
| << ", got '" << optarg << "'\n"; |
| return false; |
| } |
| options->progress_millis = static_cast<int>(progress); |
| break; |
| } |
| case 's': |
| // serverid is an argument supported by the older speedtest |
| // implementation. It is ignored here to ease the transition. |
| break; |
| default: |
| return false; |
| } |
| } |
| if (optind < argc) { |
| for (int i = optind; i < argc; i++) { |
| http::Url url; |
| if (!url.Parse(argv[i])) { |
| std::cerr << "Could not parse URL '" << argv[i] << "'\n"; |
| return false; |
| } |
| if (!url.absolute()) { |
| std::cerr << "URL '" << argv[i] << "' is not absolute\n"; |
| return false; |
| } |
| url.clear_path(); |
| url.clear_query_string(); |
| url.clear_fragment(); |
| options->hosts.emplace_back(url); |
| } |
| } |
| if (options->hosts.empty()) { |
| options->hosts.emplace_back(http::Url(kDefaultHost)); |
| } |
| return true; |
| } |
| |
| void PrintOptions(const Options &options) { |
| PrintOptions(std::cout, options); |
| } |
| |
| void PrintOptions(std::ostream &out, const Options &options) { |
| out << "Number: " << options.number << "\n" |
| << "Upload size: " << options.upload_size << "\n" |
| << "Download size: " << options.download_size << "\n" |
| << "Time: " << options.time_millis << " ms\n" |
| << "Progress interval: " << options.progress_millis << " ms\n" |
| << "Verbose: " << (options.verbose ? "true" : "false") << "\n" |
| << "Usage: " << (options.usage ? "true" : "false") << "\n" |
| << "Hosts:\n"; |
| for (const http::Url &host : options.hosts) { |
| out << " " << host.url() << "\n"; |
| } |
| } |
| |
| void PrintUsage(const char *app_name) { |
| PrintUsage(std::cout, app_name); |
| } |
| |
| void PrintUsage(std::ostream &out, const char *app_path) { |
| assert(app_path != nullptr); |
| const char *last_slash = strrchr(app_path, '/'); |
| const char *app_name = last_slash == nullptr ? app_path : last_slash + 1; |
| out << basename(app_name) << ": run an HTTP speedtest\n\n" |
| << "Usage: speedtest [options] <host> ...\n" |
| << " -h, --help This help text\n" |
| << " -n, --number NUM Number of simultaneous transfers\n" |
| << " -d, --download_size SIZE Download size in bytes\n" |
| << " -u, --upload_size SIZE Upload size in bytes\n" |
| << " -t, --time TIME Time per test in milliseconds\n" |
| << " -p, --progress TIME Progress intervals in milliseconds\n" |
| << " -v, --verbose Verbose output\n"; |
| } |
| |
| } // namespace speedtest |