From dd02c4cd9b964826c97798788dbc74a93c2ea1a1 Mon Sep 17 00:00:00 2001 From: Kenny Peng Date: Fri, 17 Oct 2014 15:25:59 -0700 Subject: [PATCH 1/5] support header add/override --- src/h2load.cc | 44 +++++++++++++++++++++++++++++++++++++++++++- src/h2load.h | 1 + 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/h2load.cc b/src/h2load.cc index 53db71c9..bab93652 100644 --- a/src/h2load.cc +++ b/src/h2load.cc @@ -642,6 +642,8 @@ Options: (2**)-1. For SPDY, if is strictly less than 16, this option is ignored. Otherwise 2** is used for SPDY. + -H, --header + Add/Override a header to the requests. -p, --no-tls-proto= Specify ALPN identifier of the protocol to be used when accessing http URI without SSL/TLS.)"; @@ -674,6 +676,7 @@ int main(int argc, char **argv) {"max-concurrent-streams", required_argument, nullptr, 'm'}, {"window-bits", required_argument, nullptr, 'w'}, {"connection-window-bits", required_argument, nullptr, 'W'}, + {"header", no_argument, nullptr, 'H'}, {"no-tls-proto", required_argument, nullptr, 'p'}, {"verbose", no_argument, nullptr, 'v'}, {"help", no_argument, nullptr, 'h'}, @@ -681,7 +684,7 @@ int main(int argc, char **argv) {nullptr, 0, nullptr, 0 } }; int option_index = 0; - auto c = getopt_long(argc, argv, "hvW:c:m:n:p:t:w:", long_options, + auto c = getopt_long(argc, argv, "hvW:c:m:n:p:t:w:H:", long_options, &option_index); if(c == -1) { break; @@ -727,6 +730,31 @@ int main(int argc, char **argv) } break; } + case 'H': { + char *header = optarg; + // Skip first possible ':' in the header name + char *value = strchr( optarg + 1, ':' ); + if ( ! value || (header[0] == ':' && header + 1 == value)) { + std::cerr << "-H: invalid header: " << optarg + << std::endl; + exit(EXIT_FAILURE); + } + *value = 0; + value++; + while( isspace( *value ) ) { value++; } + if ( *value == 0 ) { + // This could also be a valid case for suppressing a header + // similar to curl + std::cerr << "-H: invalid header - value missing: " << optarg + << std::endl; + exit(EXIT_FAILURE); + } + // Note that there is no processing currently to handle multiple + // message-header fields with the same field name + config.custom_headers.emplace_back(header, value); + util::inp_strlower(config.custom_headers.back().first); + break; + } case 'p': if(util::strieq(NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, optarg)) { config.no_tls_proto = Config::PROTO_HTTP2; @@ -875,6 +903,20 @@ int main(int argc, char **argv) } shared_nva.emplace_back(":method", "GET"); + for(auto& kv : config.custom_headers) { + if(util::strieq(":host", kv.first.c_str())) { + // replace :authority as :host header + for(auto& nv : shared_nva) { + if(nv.name == ":authority") { + nv.value = kv.second; + } + } + } else { + // add additional headers + shared_nva.emplace_back(kv.first.c_str(), kv.second.c_str()); + } + } + for(auto& req : reqlines) { // For nghttp2 std::vector nva; diff --git a/src/h2load.h b/src/h2load.h index 70e3b0cb..acd1b216 100644 --- a/src/h2load.h +++ b/src/h2load.h @@ -50,6 +50,7 @@ class Session; struct Config { std::vector> nva; std::vector> nv; + std::vector> custom_headers; std::string scheme; std::string host; addrinfo *addrs; From a6e1a40c05e1f7998b6b4dafc591ab6e78d8b2ec Mon Sep 17 00:00:00 2001 From: "Kenny (kang-yen) Peng" Date: Mon, 20 Oct 2014 20:59:55 +0000 Subject: [PATCH 2/5] support uri list file input --- src/h2load.cc | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/src/h2load.cc b/src/h2load.cc index bab93652..38fffc8a 100644 --- a/src/h2load.cc +++ b/src/h2load.cc @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -605,7 +606,9 @@ void print_version(std::ostream& out) namespace { void print_usage(std::ostream& out) { - out << R"(Usage: h2load [OPTIONS]... ... + out << R"( +Usage: h2load [OPTIONS]... ... + h2load [OPTIONS]... benchmarking tool for HTTP/2 and SPDY server)" << std::endl; } } // namespace @@ -623,6 +626,13 @@ void print_help(std::ostream& out) host and port in the subsequent URIs, if present, are ignored. Those in the first URI are used solely. + Path of a file with multiple URIs are seperated + by EOLs. URIs are used in this order for each + client. All URIs are used, then first URI is + used and then 2nd URI, and so on. The scheme, + host and port in the subsequent URIs, if present, + are ignored. Those in the first URI are used + solely. Options: -n, --requests= Number of requests. Default: )" << config.nreqs << R"( @@ -857,9 +867,20 @@ int main(int argc, char **argv) http_parser_url u; memset(&u, 0, sizeof(u)); auto uri = argv[optind]; + + std::cout << uri << std::endl; + + std::ifstream uri_file; + std::string line_uri; + if (std::ifstream(uri)) { + uri_file.open(uri, std::ifstream::in); + std::getline (uri_file, line_uri); + uri = (char *)line_uri.c_str(); + } + if(http_parser_parse_url(uri, strlen(uri), 0, &u) != 0 || !util::has_uri_field(u, UF_SCHEMA) || !util::has_uri_field(u, UF_HOST)) { - std::cerr << "invalid URI: " << uri << std::endl; + std::cerr << "invalid URI/URI_LIST_FILE: " << uri << std::endl; exit(EXIT_FAILURE); } @@ -889,6 +910,21 @@ int main(int argc, char **argv) reqlines.push_back(get_reqline(uri, u)); } + if (uri_file.is_open()) { + //load rest uris from URI_LIST_FILE + while(std::getline (uri_file, line_uri)) { + auto uri = (char *)line_uri.c_str(); + + if(http_parser_parse_url(uri, strlen(uri), 0, &u) != 0) { + std::cerr << "invalid URI in URI_LIST_FILE: " << uri << std::endl; + exit(EXIT_FAILURE); + } + + reqlines.push_back(get_reqline(uri, u)); + } + uri_file.close(); + } + if(config.max_concurrent_streams == -1) { config.max_concurrent_streams = reqlines.size(); } From db071ca35cc2ce503f48099d537ebc3dd4aa85b6 Mon Sep 17 00:00:00 2001 From: "Kenny (kang-yen) Peng" Date: Tue, 21 Oct 2014 21:25:38 +0000 Subject: [PATCH 3/5] fix comments of header add/override --- src/h2load.cc | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/h2load.cc b/src/h2load.cc index 38fffc8a..ebfe5e12 100644 --- a/src/h2load.cc +++ b/src/h2load.cc @@ -652,7 +652,7 @@ Options: (2**)-1. For SPDY, if is strictly less than 16, this option is ignored. Otherwise 2** is used for SPDY. - -H, --header + -H, --header=
Add/Override a header to the requests. -p, --no-tls-proto= Specify ALPN identifier of the protocol to be @@ -686,7 +686,7 @@ int main(int argc, char **argv) {"max-concurrent-streams", required_argument, nullptr, 'm'}, {"window-bits", required_argument, nullptr, 'w'}, {"connection-window-bits", required_argument, nullptr, 'W'}, - {"header", no_argument, nullptr, 'H'}, + {"header", required_argument, nullptr, 'H'}, {"no-tls-proto", required_argument, nullptr, 'p'}, {"verbose", no_argument, nullptr, 'v'}, {"help", no_argument, nullptr, 'h'}, @@ -868,8 +868,6 @@ int main(int argc, char **argv) memset(&u, 0, sizeof(u)); auto uri = argv[optind]; - std::cout << uri << std::endl; - std::ifstream uri_file; std::string line_uri; if (std::ifstream(uri)) { @@ -910,7 +908,7 @@ int main(int argc, char **argv) reqlines.push_back(get_reqline(uri, u)); } - if (uri_file.is_open()) { + if(uri_file.is_open()) { //load rest uris from URI_LIST_FILE while(std::getline (uri_file, line_uri)) { auto uri = (char *)line_uri.c_str(); @@ -939,11 +937,15 @@ int main(int argc, char **argv) } shared_nva.emplace_back(":method", "GET"); + //list overridalbe headers + std::vector override_hdrs = {":host", ":scheme", ":method"}; + for(auto& kv : config.custom_headers) { - if(util::strieq(":host", kv.first.c_str())) { - // replace :authority as :host header + if(std::find(override_hdrs.begin(), override_hdrs.end(), kv.first) != override_hdrs.end()) { + // override header for(auto& nv : shared_nva) { - if(nv.name == ":authority") { + if( (nv.name == ":authority" && kv.first == ":host") + || (nv.name == kv.first) ) { nv.value = kv.second; } } From 41dd6d020510bbccefb1663a759488d2abe7df57 Mon Sep 17 00:00:00 2001 From: "Kenny (kang-yen) Peng" Date: Tue, 21 Oct 2014 22:29:36 +0000 Subject: [PATCH 4/5] use option i to accept input URI list file --- src/h2load.cc | 91 +++++++++++++++++++++++++++++++-------------------- src/h2load.h | 1 + 2 files changed, 56 insertions(+), 36 deletions(-) diff --git a/src/h2load.cc b/src/h2load.cc index ebfe5e12..a2a7b6f9 100644 --- a/src/h2load.cc +++ b/src/h2load.cc @@ -607,8 +607,7 @@ namespace { void print_usage(std::ostream& out) { out << R"( -Usage: h2load [OPTIONS]... ... - h2load [OPTIONS]... +Usage: h2load [OPTIONS]... [URI]... benchmarking tool for HTTP/2 and SPDY server)" << std::endl; } } // namespace @@ -626,13 +625,6 @@ void print_help(std::ostream& out) host and port in the subsequent URIs, if present, are ignored. Those in the first URI are used solely. - Path of a file with multiple URIs are seperated - by EOLs. URIs are used in this order for each - client. All URIs are used, then first URI is - used and then 2nd URI, and so on. The scheme, - host and port in the subsequent URIs, if present, - are ignored. Those in the first URI are used - solely. Options: -n, --requests= Number of requests. Default: )" << config.nreqs << R"( @@ -640,6 +632,15 @@ Options: << config.nclients << R"( -t, --threads= Number of native threads. Default: )" << config.nthreads << R"( + -i, --input-file= + Path of a file with multiple URIs are seperated + by EOLs. This option will disable URIs getting + from stdin. URIs are used in this order for each + client. All URIs are used, then first URI is + used and then 2nd URI, and so on. The scheme, + host and port in the subsequent URIs, if present, + are ignored. Those in the first URI are used + solely. -m, --max-concurrent-streams=(auto|) Max concurrent streams to issue per session. If "auto" is given, the number of given URIs is @@ -686,6 +687,7 @@ int main(int argc, char **argv) {"max-concurrent-streams", required_argument, nullptr, 'm'}, {"window-bits", required_argument, nullptr, 'w'}, {"connection-window-bits", required_argument, nullptr, 'W'}, + {"input-file", required_argument, nullptr, 'i'}, {"header", required_argument, nullptr, 'H'}, {"no-tls-proto", required_argument, nullptr, 'p'}, {"verbose", no_argument, nullptr, 'v'}, @@ -694,7 +696,7 @@ int main(int argc, char **argv) {nullptr, 0, nullptr, 0 } }; int option_index = 0; - auto c = getopt_long(argc, argv, "hvW:c:m:n:p:t:w:H:", long_options, + auto c = getopt_long(argc, argv, "hvW:c:m:n:p:t:w:H:i:", long_options, &option_index); if(c == -1) { break; @@ -765,6 +767,10 @@ int main(int argc, char **argv) util::inp_strlower(config.custom_headers.back().first); break; } + case 'i': { + config.ifile = std::string(optarg); + break; + } case 'p': if(util::strieq(NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, optarg)) { config.no_tls_proto = Config::PROTO_HTTP2; @@ -804,8 +810,10 @@ int main(int argc, char **argv) } if(argc == optind) { - std::cerr << "no URI given" << std::endl; - exit(EXIT_FAILURE); + if(config.ifile.empty()){ + std::cerr << "no URI or input file given" << std::endl; + exit(EXIT_FAILURE); + } } if(config.nreqs == 0) { @@ -866,14 +874,23 @@ int main(int argc, char **argv) // this URI and ignore those in the remaining URIs if present. http_parser_url u; memset(&u, 0, sizeof(u)); - auto uri = argv[optind]; + auto uri = ""; std::ifstream uri_file; std::string line_uri; - if (std::ifstream(uri)) { - uri_file.open(uri, std::ifstream::in); - std::getline (uri_file, line_uri); - uri = (char *)line_uri.c_str(); + + if (config.ifile.empty()) { + uri = argv[optind]; + } else { + if (std::ifstream(config.ifile)) { + uri_file.open(config.ifile, std::ifstream::in); + // get first line as first uri + std::getline (uri_file, line_uri); + uri = (char *)line_uri.c_str(); + } else { + std::cerr << "cannot read input file: " << config.ifile << std::endl; + exit(EXIT_FAILURE); + } } if(http_parser_parse_url(uri, strlen(uri), 0, &u) != 0 || @@ -894,33 +911,35 @@ int main(int argc, char **argv) reqlines.push_back(get_reqline(uri, u)); - ++optind; - for(int i = optind; i < argc; ++i) { - memset(&u, 0, sizeof(u)); + if (config.ifile.empty()) { + ++optind; + for(int i = optind; i < argc; ++i) { + memset(&u, 0, sizeof(u)); - auto uri = argv[i]; - - if(http_parser_parse_url(uri, strlen(uri), 0, &u) != 0) { - std::cerr << "invalid URI: " << uri << std::endl; - exit(EXIT_FAILURE); - } - - reqlines.push_back(get_reqline(uri, u)); - } - - if(uri_file.is_open()) { - //load rest uris from URI_LIST_FILE - while(std::getline (uri_file, line_uri)) { - auto uri = (char *)line_uri.c_str(); + auto uri = argv[i]; if(http_parser_parse_url(uri, strlen(uri), 0, &u) != 0) { - std::cerr << "invalid URI in URI_LIST_FILE: " << uri << std::endl; + std::cerr << "invalid URI: " << uri << std::endl; exit(EXIT_FAILURE); } reqlines.push_back(get_reqline(uri, u)); } - uri_file.close(); + } else { + if(uri_file.is_open()) { + //load rest uris from URI_LIST_FILE + while(std::getline (uri_file, line_uri)) { + auto uri = (char *)line_uri.c_str(); + + if(http_parser_parse_url(uri, strlen(uri), 0, &u) != 0) { + std::cerr << "invalid URI in URI_LIST_FILE: " << uri << std::endl; + exit(EXIT_FAILURE); + } + + reqlines.push_back(get_reqline(uri, u)); + } + uri_file.close(); + } } if(config.max_concurrent_streams == -1) { diff --git a/src/h2load.h b/src/h2load.h index acd1b216..9c7122cb 100644 --- a/src/h2load.h +++ b/src/h2load.h @@ -53,6 +53,7 @@ struct Config { std::vector> custom_headers; std::string scheme; std::string host; + std::string ifile; addrinfo *addrs; size_t nreqs; size_t nclients; From 27c766cb04fee0856255500f75f07e806b05854d Mon Sep 17 00:00:00 2001 From: "Kenny (kang-yen) Peng" Date: Tue, 21 Oct 2014 22:38:45 +0000 Subject: [PATCH 5/5] fix some comments and descriptions --- src/h2load.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/h2load.cc b/src/h2load.cc index a2a7b6f9..14dea93a 100644 --- a/src/h2load.cc +++ b/src/h2load.cc @@ -895,7 +895,7 @@ int main(int argc, char **argv) if(http_parser_parse_url(uri, strlen(uri), 0, &u) != 0 || !util::has_uri_field(u, UF_SCHEMA) || !util::has_uri_field(u, UF_HOST)) { - std::cerr << "invalid URI/URI_LIST_FILE: " << uri << std::endl; + std::cerr << "invalid URI: " << uri << std::endl; exit(EXIT_FAILURE); } @@ -927,12 +927,12 @@ int main(int argc, char **argv) } } else { if(uri_file.is_open()) { - //load rest uris from URI_LIST_FILE + //load rest uris from file while(std::getline (uri_file, line_uri)) { auto uri = (char *)line_uri.c_str(); if(http_parser_parse_url(uri, strlen(uri), 0, &u) != 0) { - std::cerr << "invalid URI in URI_LIST_FILE: " << uri << std::endl; + std::cerr << "invalid URI in input file: " << uri << std::endl; exit(EXIT_FAILURE); }