Move virtually all the toy client logic out of quic_client_bin.cc and into a new QuicToyClient class which can be used in chromium. gfe-relnote: n/a - Tools only PiperOrigin-RevId: 248451756 Change-Id: I7e9b7e2520ee40adcc692ea4e82e26d0a71760ef
diff --git a/quic/tools/quic_client_bin.cc b/quic/tools/quic_client_bin.cc index 1ec47a9..e84b7fe 100644 --- a/quic/tools/quic_client_bin.cc +++ b/quic/tools/quic_client_bin.cc
@@ -51,25 +51,16 @@ #include "net/third_party/quiche/src/quic/core/quic_packets.h" #include "net/third_party/quiche/src/quic/core/quic_server_id.h" -#include "net/third_party/quiche/src/quic/core/quic_utils.h" -#include "net/third_party/quiche/src/quic/core/quic_versions.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_default_proof_providers.h" #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" #include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" #include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" #include "net/third_party/quiche/src/quic/platform/api/quic_system_event_loop.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" -#include "net/third_party/quiche/src/quic/tools/fake_proof_verifier.h" -#include "net/third_party/quiche/src/quic/tools/quic_client.h" -#include "net/third_party/quiche/src/quic/tools/quic_url.h" +#include "net/third_party/quiche/src/quic/tools/quic_toy_client.h" namespace { using quic::QuicSocketAddress; using quic::QuicStringPiece; -using quic::QuicTextUtils; -using quic::QuicUrl; QuicSocketAddress LookupAddress(std::string host, std::string port) { addrinfo hint; @@ -90,101 +81,29 @@ return QuicSocketAddress(*info_list->ai_addr); } +class QuicEpollClientFactory : public quic::QuicToyClientBase::ClientFactory { + public: + std::unique_ptr<quic::QuicSpdyClientBase> CreateClient( + std::string host, + uint16_t port, + quic::ParsedQuicVersionVector versions, + std::unique_ptr<quic::ProofVerifier> verifier) { + quic::QuicSocketAddress addr = LookupAddress(host, quic::QuicStrCat(port)); + if (!addr.IsInitialized()) { + QUIC_LOG(ERROR) << "Unable to resolve address: " << host; + return nullptr; + } + quic::QuicServerId server_id(host, port, false); + return quic::QuicMakeUnique<quic::QuicClient>( + addr, server_id, versions, &epoll_server_, std::move(verifier)); + } + + private: + quic::QuicEpollServer epoll_server_; +}; + } // namespace -DEFINE_QUIC_COMMAND_LINE_FLAG( - std::string, - host, - "", - "The IP or hostname to connect to. If not provided, the host " - "will be derived from the provided URL."); - -DEFINE_QUIC_COMMAND_LINE_FLAG(int32_t, port, 0, "The port to connect to."); - -DEFINE_QUIC_COMMAND_LINE_FLAG(std::string, - body, - "", - "If set, send a POST with this body."); - -DEFINE_QUIC_COMMAND_LINE_FLAG( - std::string, - body_hex, - "", - "If set, contents are converted from hex to ascii, before " - "sending as body of a POST. e.g. --body_hex=\"68656c6c6f\""); - -DEFINE_QUIC_COMMAND_LINE_FLAG( - std::string, - headers, - "", - "A semicolon separated list of key:value pairs to " - "add to request headers."); - -DEFINE_QUIC_COMMAND_LINE_FLAG(bool, - quiet, - false, - "Set to true for a quieter output experience."); - -DEFINE_QUIC_COMMAND_LINE_FLAG( - std::string, - quic_version, - "", - "QUIC version to speak, e.g. 21. If not set, then all available " - "versions are offered in the handshake. Also supports wire versions " - "such as Q043 or T099."); - -DEFINE_QUIC_COMMAND_LINE_FLAG( - int32_t, - quic_ietf_draft, - 0, - "QUIC IETF draft number to use over the wire, e.g. 18. " - "By default this sets quic_version to T099. " - "This also enables required internal QUIC flags."); - -DEFINE_QUIC_COMMAND_LINE_FLAG( - bool, - version_mismatch_ok, - false, - "If true, a version mismatch in the handshake is not considered a " - "failure. Useful for probing a server to determine if it speaks " - "any version of QUIC."); - -DEFINE_QUIC_COMMAND_LINE_FLAG( - bool, - force_version_negotiation, - false, - "If true, start by proposing a version that is reserved for version " - "negotiation."); - -DEFINE_QUIC_COMMAND_LINE_FLAG( - bool, - redirect_is_success, - true, - "If true, an HTTP response code of 3xx is considered to be a " - "successful response, otherwise a failure."); - -DEFINE_QUIC_COMMAND_LINE_FLAG(int32_t, - initial_mtu, - 0, - "Initial MTU of the connection."); - -DEFINE_QUIC_COMMAND_LINE_FLAG( - int32_t, - num_requests, - 1, - "How many sequential requests to make on a single connection."); - -DEFINE_QUIC_COMMAND_LINE_FLAG(bool, - disable_certificate_verification, - false, - "If true, don't verify the server certificate."); - -DEFINE_QUIC_COMMAND_LINE_FLAG( - bool, - drop_response_body, - false, - "If true, drop response body immediately after it is received."); - int main(int argc, char* argv[]) { QuicSystemEventLoop event_loop("quic_client"); const char* usage = "Usage: quic_client [options] <url>"; @@ -197,193 +116,7 @@ exit(0); } - QuicUrl url(urls[0], "https"); - std::string host = GetQuicFlag(FLAGS_host); - if (host.empty()) { - host = url.host(); - } - int port = GetQuicFlag(FLAGS_port); - if (port == 0) { - port = url.port(); - } - - // Determine IP address to connect to from supplied hostname. - QuicSocketAddress addr = LookupAddress(host, quic::QuicStrCat(port)); - if (!addr.IsInitialized()) { - return 1; - } - std::cerr << "Resolved " << url.ToString() << " to " << addr.ToString() - << std::endl; - - // Build the client, and try to connect. - quic::QuicEpollServer epoll_server; - quic::QuicServerId server_id(url.host(), port, false); - quic::ParsedQuicVersionVector versions = quic::CurrentSupportedVersions(); - - std::string quic_version_string = GetQuicFlag(FLAGS_quic_version); - const int32_t quic_ietf_draft = GetQuicFlag(FLAGS_quic_ietf_draft); - if (quic_ietf_draft > 0) { - quic::QuicVersionInitializeSupportForIetfDraft(quic_ietf_draft); - if (quic_version_string.length() == 0) { - quic_version_string = "T099"; - } - } - if (quic_version_string.length() > 0) { - if (quic_version_string[0] == 'T') { - // ParseQuicVersionString checks quic_supports_tls_handshake. - SetQuicFlag(FLAGS_quic_supports_tls_handshake, true); - } - quic::ParsedQuicVersion parsed_quic_version = - quic::ParseQuicVersionString(quic_version_string); - if (parsed_quic_version.transport_version == - quic::QUIC_VERSION_UNSUPPORTED) { - return 1; - } - versions.clear(); - versions.push_back(parsed_quic_version); - quic::QuicEnableVersion(parsed_quic_version); - } - - if (GetQuicFlag(FLAGS_force_version_negotiation)) { - versions.insert(versions.begin(), - quic::QuicVersionReservedForNegotiation()); - } - - const int32_t num_requests(GetQuicFlag(FLAGS_num_requests)); - std::unique_ptr<quic::ProofVerifier> proof_verifier; - if (GetQuicFlag(FLAGS_disable_certificate_verification)) { - proof_verifier = quic::QuicMakeUnique<quic::FakeProofVerifier>(); - } else { - proof_verifier = quic::CreateDefaultProofVerifier(); - } - quic::QuicClient client(addr, server_id, versions, &epoll_server, - std::move(proof_verifier)); - int32_t initial_mtu = GetQuicFlag(FLAGS_initial_mtu); - client.set_initial_max_packet_length( - initial_mtu != 0 ? initial_mtu : quic::kDefaultMaxPacketSize); - client.set_drop_response_body(GetQuicFlag(FLAGS_drop_response_body)); - if (!client.Initialize()) { - std::cerr << "Failed to initialize client." << std::endl; - return 1; - } - if (!client.Connect()) { - quic::QuicErrorCode error = client.session()->error(); - if (error == quic::QUIC_INVALID_VERSION) { - std::cerr << "Server talks QUIC, but none of the versions supported by " - << "this client: " << ParsedQuicVersionVectorToString(versions) - << std::endl; - // 0: No error. - // 20: Failed to connect due to QUIC_INVALID_VERSION. - return GetQuicFlag(FLAGS_version_mismatch_ok) ? 0 : 20; - } - std::cerr << "Failed to connect to " << addr.ToString() - << ". Error: " << quic::QuicErrorCodeToString(error) << std::endl; - return 1; - } - std::cerr << "Connected to " << addr.ToString() << std::endl; - - // Construct the string body from flags, if provided. - std::string body = GetQuicFlag(FLAGS_body); - if (!GetQuicFlag(FLAGS_body_hex).empty()) { - DCHECK(GetQuicFlag(FLAGS_body).empty()) - << "Only set one of --body and --body_hex."; - body = QuicTextUtils::HexDecode(GetQuicFlag(FLAGS_body_hex)); - } - - // Construct a GET or POST request for supplied URL. - spdy::SpdyHeaderBlock header_block; - header_block[":method"] = body.empty() ? "GET" : "POST"; - header_block[":scheme"] = url.scheme(); - header_block[":authority"] = url.HostPort(); - header_block[":path"] = url.PathParamsQuery(); - - // Append any additional headers supplied on the command line. - const std::string headers = GetQuicFlag(FLAGS_headers); - for (QuicStringPiece sp : QuicTextUtils::Split(headers, ';')) { - QuicTextUtils::RemoveLeadingAndTrailingWhitespace(&sp); - if (sp.empty()) { - continue; - } - std::vector<QuicStringPiece> kv = QuicTextUtils::Split(sp, ':'); - QuicTextUtils::RemoveLeadingAndTrailingWhitespace(&kv[0]); - QuicTextUtils::RemoveLeadingAndTrailingWhitespace(&kv[1]); - header_block[kv[0]] = kv[1]; - } - - // Make sure to store the response, for later output. - client.set_store_response(true); - - for (int i = 0; i < num_requests; ++i) { - // Send the request. - client.SendRequestAndWaitForResponse(header_block, body, /*fin=*/true); - - // Print request and response details. - if (!GetQuicFlag(FLAGS_quiet)) { - std::cout << "Request:" << std::endl; - std::cout << "headers:" << header_block.DebugString(); - if (!GetQuicFlag(FLAGS_body_hex).empty()) { - // Print the user provided hex, rather than binary body. - std::cout << "body:\n" - << QuicTextUtils::HexDump( - QuicTextUtils::HexDecode(GetQuicFlag(FLAGS_body_hex))) - << std::endl; - } else { - std::cout << "body: " << body << std::endl; - } - std::cout << std::endl; - - if (!client.preliminary_response_headers().empty()) { - std::cout << "Preliminary response headers: " - << client.preliminary_response_headers() << std::endl; - std::cout << std::endl; - } - - std::cout << "Response:" << std::endl; - std::cout << "headers: " << client.latest_response_headers() << std::endl; - std::string response_body = client.latest_response_body(); - if (!GetQuicFlag(FLAGS_body_hex).empty()) { - // Assume response is binary data. - std::cout << "body:\n" - << QuicTextUtils::HexDump(response_body) << std::endl; - } else { - std::cout << "body: " << response_body << std::endl; - } - std::cout << "trailers: " << client.latest_response_trailers() - << std::endl; - } - - if (!client.connected()) { - std::cerr << "Request caused connection failure. Error: " - << quic::QuicErrorCodeToString(client.session()->error()) - << std::endl; - return 1; - } - - size_t response_code = client.latest_response_code(); - if (response_code >= 200 && response_code < 300) { - std::cout << "Request succeeded (" << response_code << ")." << std::endl; - } else if (response_code >= 300 && response_code < 400) { - if (GetQuicFlag(FLAGS_redirect_is_success)) { - std::cout << "Request succeeded (redirect " << response_code << ")." - << std::endl; - } else { - std::cout << "Request failed (redirect " << response_code << ")." - << std::endl; - return 1; - } - } else { - std::cout << "Request failed (" << response_code << ")." << std::endl; - return 1; - } - - // Change the ephemeral port if there are more requests to do. - if (i + 1 < num_requests) { - if (!client.ChangeEphemeralPort()) { - std::cerr << "Failed to change ephemeral port." << std::endl; - return 1; - } - } - } - - return 0; + QuicEpollClientFactory factory; + quic::QuicToyClientBase client(&factory); + return client.SendRequestsAndPrintResponses(urls); }
diff --git a/quic/tools/quic_toy_client.cc b/quic/tools/quic_toy_client.cc new file mode 100644 index 0000000..ad966ed --- /dev/null +++ b/quic/tools/quic_toy_client.cc
@@ -0,0 +1,361 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// A binary wrapper for QuicClient. +// Connects to a host using QUIC, sends a request to the provided URL, and +// displays the response. +// +// Some usage examples: +// +// Standard request/response: +// quic_client www.google.com +// quic_client www.google.com --quiet +// quic_client www.google.com --port=443 +// +// Use a specific version: +// quic_client www.google.com --quic_version=23 +// +// Send a POST instead of a GET: +// quic_client www.google.com --body="this is a POST body" +// +// Append additional headers to the request: +// quic_client www.google.com --headers="Header-A: 1234; Header-B: 5678" +// +// Connect to a host different to the URL being requested: +// quic_client mail.google.com --host=www.google.com +// +// Connect to a specific IP: +// IP=`dig www.google.com +short | head -1` +// quic_client www.google.com --host=${IP} +// +// Send repeated requests and change ephemeral port between requests +// quic_client www.google.com --num_requests=10 +// +// Try to connect to a host which does not speak QUIC: +// quic_client www.example.com +// +// This tool is available as a built binary at: +// /google/data/ro/teams/quic/tools/quic_client +// After submitting changes to this file, you will need to follow the +// instructions at go/quic_client_binary_update + +#include "net/third_party/quiche/src/quic/tools/quic_toy_client.h" + +#include <netdb.h> +#include <sys/socket.h> +#include <sys/types.h> + +#include <iostream> +#include <memory> +#include <string> +#include <vector> + +#include "net/third_party/quiche/src/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quic/core/quic_server_id.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/core/quic_versions.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_default_proof_providers.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_system_event_loop.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" +#include "net/third_party/quiche/src/quic/tools/fake_proof_verifier.h" +#include "net/third_party/quiche/src/quic/tools/quic_url.h" + +namespace { + +using quic::QuicSocketAddress; +using quic::QuicStringPiece; +using quic::QuicTextUtils; +using quic::QuicUrl; + +} // namespace + +DEFINE_QUIC_COMMAND_LINE_FLAG( + std::string, + host, + "", + "The IP or hostname to connect to. If not provided, the host " + "will be derived from the provided URL."); + +DEFINE_QUIC_COMMAND_LINE_FLAG(int32_t, port, 0, "The port to connect to."); + +DEFINE_QUIC_COMMAND_LINE_FLAG(std::string, + body, + "", + "If set, send a POST with this body."); + +DEFINE_QUIC_COMMAND_LINE_FLAG( + std::string, + body_hex, + "", + "If set, contents are converted from hex to ascii, before " + "sending as body of a POST. e.g. --body_hex=\"68656c6c6f\""); + +DEFINE_QUIC_COMMAND_LINE_FLAG( + std::string, + headers, + "", + "A semicolon separated list of key:value pairs to " + "add to request headers."); + +DEFINE_QUIC_COMMAND_LINE_FLAG(bool, + quiet, + false, + "Set to true for a quieter output experience."); + +DEFINE_QUIC_COMMAND_LINE_FLAG( + std::string, + quic_version, + "", + "QUIC version to speak, e.g. 21. If not set, then all available " + "versions are offered in the handshake. Also supports wire versions " + "such as Q043 or T099."); + +DEFINE_QUIC_COMMAND_LINE_FLAG( + int32_t, + quic_ietf_draft, + 0, + "QUIC IETF draft number to use over the wire, e.g. 18. " + "By default this sets quic_version to T099. " + "This also enables required internal QUIC flags."); + +DEFINE_QUIC_COMMAND_LINE_FLAG( + bool, + version_mismatch_ok, + false, + "If true, a version mismatch in the handshake is not considered a " + "failure. Useful for probing a server to determine if it speaks " + "any version of QUIC."); + +DEFINE_QUIC_COMMAND_LINE_FLAG( + bool, + force_version_negotiation, + false, + "If true, start by proposing a version that is reserved for version " + "negotiation."); + +DEFINE_QUIC_COMMAND_LINE_FLAG( + bool, + redirect_is_success, + true, + "If true, an HTTP response code of 3xx is considered to be a " + "successful response, otherwise a failure."); + +DEFINE_QUIC_COMMAND_LINE_FLAG(int32_t, + initial_mtu, + 0, + "Initial MTU of the connection."); + +DEFINE_QUIC_COMMAND_LINE_FLAG( + int32_t, + num_requests, + 1, + "How many sequential requests to make on a single connection."); + +DEFINE_QUIC_COMMAND_LINE_FLAG(bool, + disable_certificate_verification, + false, + "If true, don't verify the server certificate."); + +DEFINE_QUIC_COMMAND_LINE_FLAG( + bool, + drop_response_body, + false, + "If true, drop response body immediately after it is received."); + +namespace quic { + +QuicToyClientBase::QuicToyClientBase(ClientFactory* client_factory) + : client_factory_(client_factory) {} + +int QuicToyClientBase::SendRequestsAndPrintResponses( + std::vector<std::string> urls) { + QuicUrl url(urls[0], "https"); + std::string host = GetQuicFlag(FLAGS_host); + if (host.empty()) { + host = url.host(); + } + int port = GetQuicFlag(FLAGS_port); + if (port == 0) { + port = url.port(); + } + + quic::ParsedQuicVersionVector versions = quic::CurrentSupportedVersions(); + + std::string quic_version_string = GetQuicFlag(FLAGS_quic_version); + const int32_t quic_ietf_draft = GetQuicFlag(FLAGS_quic_ietf_draft); + if (quic_ietf_draft > 0) { + quic::QuicVersionInitializeSupportForIetfDraft(quic_ietf_draft); + if (quic_version_string.length() == 0) { + quic_version_string = "T099"; + } + } + if (quic_version_string.length() > 0) { + if (quic_version_string[0] == 'T') { + // ParseQuicVersionString checks quic_supports_tls_handshake. + SetQuicFlag(FLAGS_quic_supports_tls_handshake, true); + } + quic::ParsedQuicVersion parsed_quic_version = + quic::ParseQuicVersionString(quic_version_string); + if (parsed_quic_version.transport_version == + quic::QUIC_VERSION_UNSUPPORTED) { + return 1; + } + versions.clear(); + versions.push_back(parsed_quic_version); + quic::QuicEnableVersion(parsed_quic_version); + } + + if (GetQuicFlag(FLAGS_force_version_negotiation)) { + versions.insert(versions.begin(), + quic::QuicVersionReservedForNegotiation()); + } + + const int32_t num_requests(GetQuicFlag(FLAGS_num_requests)); + std::unique_ptr<quic::ProofVerifier> proof_verifier; + if (GetQuicFlag(FLAGS_disable_certificate_verification)) { + proof_verifier = quic::QuicMakeUnique<FakeProofVerifier>(); + } else { + proof_verifier = quic::CreateDefaultProofVerifier(); + } + + // Build the client, and try to connect. + std::unique_ptr<QuicSpdyClientBase> client = client_factory_->CreateClient( + host, port, versions, std::move(proof_verifier)); + + int32_t initial_mtu = GetQuicFlag(FLAGS_initial_mtu); + client->set_initial_max_packet_length( + initial_mtu != 0 ? initial_mtu : quic::kDefaultMaxPacketSize); + client->set_drop_response_body(GetQuicFlag(FLAGS_drop_response_body)); + if (!client->Initialize()) { + std::cerr << "Failed to initialize client." << std::endl; + return 1; + } + if (!client->Connect()) { + quic::QuicErrorCode error = client->session()->error(); + if (error == quic::QUIC_INVALID_VERSION) { + std::cerr << "Server talks QUIC, but none of the versions supported by " + << "this client: " << ParsedQuicVersionVectorToString(versions) + << std::endl; + // 0: No error. + // 20: Failed to connect due to QUIC_INVALID_VERSION. + return GetQuicFlag(FLAGS_version_mismatch_ok) ? 0 : 20; + } + std::cerr << "Failed to connect to " << host << ":" << port + << ". Error: " << quic::QuicErrorCodeToString(error) << std::endl; + return 1; + } + std::cerr << "Connected to " << host << ":" << port << std::endl; + + // Construct the string body from flags, if provided. + std::string body = GetQuicFlag(FLAGS_body); + if (!GetQuicFlag(FLAGS_body_hex).empty()) { + DCHECK(GetQuicFlag(FLAGS_body).empty()) + << "Only set one of --body and --body_hex."; + body = QuicTextUtils::HexDecode(GetQuicFlag(FLAGS_body_hex)); + } + + // Construct a GET or POST request for supplied URL. + spdy::SpdyHeaderBlock header_block; + header_block[":method"] = body.empty() ? "GET" : "POST"; + header_block[":scheme"] = url.scheme(); + header_block[":authority"] = url.HostPort(); + header_block[":path"] = url.PathParamsQuery(); + + // Append any additional headers supplied on the command line. + const std::string headers = GetQuicFlag(FLAGS_headers); + for (QuicStringPiece sp : QuicTextUtils::Split(headers, ';')) { + QuicTextUtils::RemoveLeadingAndTrailingWhitespace(&sp); + if (sp.empty()) { + continue; + } + std::vector<QuicStringPiece> kv = QuicTextUtils::Split(sp, ':'); + QuicTextUtils::RemoveLeadingAndTrailingWhitespace(&kv[0]); + QuicTextUtils::RemoveLeadingAndTrailingWhitespace(&kv[1]); + header_block[kv[0]] = kv[1]; + } + + // Make sure to store the response, for later output. + client->set_store_response(true); + + for (int i = 0; i < num_requests; ++i) { + // Send the request. + client->SendRequestAndWaitForResponse(header_block, body, /*fin=*/true); + + // Print request and response details. + if (!GetQuicFlag(FLAGS_quiet)) { + std::cout << "Request:" << std::endl; + std::cout << "headers:" << header_block.DebugString(); + if (!GetQuicFlag(FLAGS_body_hex).empty()) { + // Print the user provided hex, rather than binary body. + std::cout << "body:\n" + << QuicTextUtils::HexDump( + QuicTextUtils::HexDecode(GetQuicFlag(FLAGS_body_hex))) + << std::endl; + } else { + std::cout << "body: " << body << std::endl; + } + std::cout << std::endl; + + if (!client->preliminary_response_headers().empty()) { + std::cout << "Preliminary response headers: " + << client->preliminary_response_headers() << std::endl; + std::cout << std::endl; + } + + std::cout << "Response:" << std::endl; + std::cout << "headers: " << client->latest_response_headers() + << std::endl; + std::string response_body = client->latest_response_body(); + if (!GetQuicFlag(FLAGS_body_hex).empty()) { + // Assume response is binary data. + std::cout << "body:\n" + << QuicTextUtils::HexDump(response_body) << std::endl; + } else { + std::cout << "body: " << response_body << std::endl; + } + std::cout << "trailers: " << client->latest_response_trailers() + << std::endl; + } + + if (!client->connected()) { + std::cerr << "Request caused connection failure. Error: " + << quic::QuicErrorCodeToString(client->session()->error()) + << std::endl; + return 1; + } + + size_t response_code = client->latest_response_code(); + if (response_code >= 200 && response_code < 300) { + std::cout << "Request succeeded (" << response_code << ")." << std::endl; + } else if (response_code >= 300 && response_code < 400) { + if (GetQuicFlag(FLAGS_redirect_is_success)) { + std::cout << "Request succeeded (redirect " << response_code << ")." + << std::endl; + } else { + std::cout << "Request failed (redirect " << response_code << ")." + << std::endl; + return 1; + } + } else { + std::cout << "Request failed (" << response_code << ")." << std::endl; + return 1; + } + + // Change the ephemeral port if there are more requests to do. + if (i + 1 < num_requests) { + if (!client->ChangeEphemeralPort()) { + std::cerr << "Failed to change ephemeral port." << std::endl; + return 1; + } + } + } + + return 0; +} + +} // namespace quic
diff --git a/quic/tools/quic_toy_client.h b/quic/tools/quic_toy_client.h new file mode 100644 index 0000000..0ba1095 --- /dev/null +++ b/quic/tools/quic_toy_client.h
@@ -0,0 +1,45 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// A toy client, which connects to a specified port and sends QUIC +// requests to that endpoint. + +#ifndef QUICHE_QUIC_TOOLS_QUIC_TOY_CLIENT_H_ +#define QUICHE_QUIC_TOOLS_QUIC_TOY_CLIENT_H_ + +#include "net/third_party/quiche/src/quic/tools/quic_client.h" + +namespace quic { + +class QuicToyClientBase { + public: + class ClientFactory { + public: + virtual ~ClientFactory() = default; + + // Creates a new client configured to connect to |host:port| supporting + // |versions|, and using |verifier| to verify proofs. + virtual std::unique_ptr<QuicSpdyClientBase> CreateClient( + std::string host, + uint16_t port, + ParsedQuicVersionVector versions, + std::unique_ptr<ProofVerifier> verifier) = 0; + }; + + // Constructs a new toy client that will use |client_factory| to create the + // actual QuicSpdyClientBase instance. + QuicToyClientBase(ClientFactory* client_factory); + + // Connects to the QUIC server based on the various flags defined in the + // .cc file, sends requests and prints the responses. Returns 0 on success + // and non-zero otherwise. + int SendRequestsAndPrintResponses(std::vector<std::string> urls); + + private: + ClientFactory* client_factory_; // Unowned. +}; + +} // namespace quic + +#endif // QUICHE_QUIC_TOOLS_QUIC_TOY_CLIENT_H_