rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | // A binary wrapper for QuicClient. |
| 6 | // Connects to a host using QUIC, sends a request to the provided URL, and |
| 7 | // displays the response. |
| 8 | // |
| 9 | // Some usage examples: |
| 10 | // |
| 11 | // TODO(rtenneti): make --host optional by getting IP Address of URL's host. |
| 12 | // |
| 13 | // Get IP address of the www.google.com |
| 14 | // IP=`dig www.google.com +short | head -1` |
| 15 | // |
| 16 | // Standard request/response: |
| 17 | // quic_client http://www.google.com --host=${IP} |
| 18 | // quic_client http://www.google.com --quiet --host=${IP} |
| 19 | // quic_client https://www.google.com --port=443 --host=${IP} |
| 20 | // |
| 21 | // Use a specific version: |
rjshade | c86dbfa | 2015-11-12 20:16:25 | [diff] [blame] | 22 | // quic_client http://www.google.com --quic_version=23 --host=${IP} |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 23 | // |
| 24 | // Send a POST instead of a GET: |
| 25 | // quic_client http://www.google.com --body="this is a POST body" --host=${IP} |
| 26 | // |
| 27 | // Append additional headers to the request: |
| 28 | // quic_client http://www.google.com --host=${IP} |
| 29 | // --headers="Header-A: 1234; Header-B: 5678" |
| 30 | // |
| 31 | // Connect to a host different to the URL being requested: |
| 32 | // Get IP address of the www.google.com |
| 33 | // IP=`dig www.google.com +short | head -1` |
| 34 | // quic_client mail.google.com --host=${IP} |
| 35 | // |
| 36 | // Try to connect to a host which does not speak QUIC: |
| 37 | // Get IP address of the www.example.com |
| 38 | // IP=`dig www.example.com +short | head -1` |
| 39 | // quic_client http://www.example.com --host=${IP} |
| 40 | |
| 41 | #include <iostream> |
| 42 | |
| 43 | #include "base/at_exit.h" |
| 44 | #include "base/command_line.h" |
| 45 | #include "base/logging.h" |
Alexander Timin | 4f9c35c | 2018-11-01 20:15:20 | [diff] [blame^] | 46 | #include "base/message_loop/message_loop.h" |
Gabriel Charette | 44db142 | 2018-08-06 11:19:33 | [diff] [blame] | 47 | #include "base/task/task_scheduler/task_scheduler.h" |
rch | da78df5a | 2015-03-22 05:16:37 | [diff] [blame] | 48 | #include "net/base/net_errors.h" |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 49 | #include "net/base/privacy_mode.h" |
| 50 | #include "net/cert/cert_verifier.h" |
rch | 71bd233 | 2017-02-04 00:06:47 | [diff] [blame] | 51 | #include "net/cert/ct_log_verifier.h" |
Ryan Sleevi | 8a9c9c1 | 2018-05-09 02:36:23 | [diff] [blame] | 52 | #include "net/cert/ct_policy_enforcer.h" |
rtenneti | 052774e | 2015-11-24 21:00:12 | [diff] [blame] | 53 | #include "net/cert/multi_log_ct_verifier.h" |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 54 | #include "net/http/transport_security_state.h" |
Ryan Hamilton | a3ee93a7 | 2018-08-01 22:03:08 | [diff] [blame] | 55 | #include "net/quic/crypto/proof_verifier_chromium.h" |
Bence Béky | 94658bf | 2018-05-11 19:22:58 | [diff] [blame] | 56 | #include "net/spdy/spdy_http_utils.h" |
Ryan Hamilton | 56b10c5d | 2018-05-11 13:40:16 | [diff] [blame] | 57 | #include "net/third_party/quic/core/quic_error_codes.h" |
| 58 | #include "net/third_party/quic/core/quic_packets.h" |
| 59 | #include "net/third_party/quic/core/quic_server_id.h" |
| 60 | #include "net/third_party/quic/platform/api/quic_socket_address.h" |
| 61 | #include "net/third_party/quic/platform/api/quic_str_cat.h" |
| 62 | #include "net/third_party/quic/platform/api/quic_string_piece.h" |
| 63 | #include "net/third_party/quic/platform/api/quic_text_utils.h" |
Ryan Hamilton | 2e003eea | 2018-05-02 00:24:29 | [diff] [blame] | 64 | #include "net/third_party/spdy/core/spdy_header_block.h" |
rch | a9d12ce1 | 2015-03-19 23:06:49 | [diff] [blame] | 65 | #include "net/tools/quic/quic_simple_client.h" |
rch | da78df5a | 2015-03-22 05:16:37 | [diff] [blame] | 66 | #include "net/tools/quic/synchronous_host_resolver.h" |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 67 | #include "url/gurl.h" |
| 68 | |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 69 | using net::CertVerifier; |
rtenneti | 052774e | 2015-11-24 21:00:12 | [diff] [blame] | 70 | using net::CTVerifier; |
| 71 | using net::MultiLogCTVerifier; |
Ryan Hamilton | 8d9ee76e | 2018-05-29 23:52:52 | [diff] [blame] | 72 | using quic::ProofVerifier; |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 73 | using net::ProofVerifierChromium; |
Ryan Hamilton | 8d9ee76e | 2018-05-29 23:52:52 | [diff] [blame] | 74 | using quic::QuicStringPiece; |
| 75 | using quic::QuicTextUtils; |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 76 | using net::TransportSecurityState; |
Ryan Hamilton | 0239aac | 2018-05-19 00:03:13 | [diff] [blame] | 77 | using spdy::SpdyHeaderBlock; |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 78 | using std::cout; |
| 79 | using std::cerr; |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 80 | using std::endl; |
rch | 872e00e | 2016-12-02 02:48:18 | [diff] [blame] | 81 | using std::string; |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 82 | |
| 83 | // The IP or hostname the quic client will connect to. |
| 84 | string FLAGS_host = ""; |
| 85 | // The port to connect to. |
Avi Drissman | 13fc893 | 2015-12-20 04:40:46 | [diff] [blame] | 86 | int32_t FLAGS_port = 0; |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 87 | // If set, send a POST with this body. |
| 88 | string FLAGS_body = ""; |
danzh | b755134 | 2015-12-18 02:06:40 | [diff] [blame] | 89 | // If set, contents are converted from hex to ascii, before sending as body of |
| 90 | // a POST. e.g. --body_hex=\"68656c6c6f\" |
| 91 | string FLAGS_body_hex = ""; |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 92 | // A semicolon separated list of key:value pairs to add to request headers. |
| 93 | string FLAGS_headers = ""; |
| 94 | // Set to true for a quieter output experience. |
| 95 | bool FLAGS_quiet = false; |
| 96 | // QUIC version to speak, e.g. 21. If not set, then all available versions are |
| 97 | // offered in the handshake. |
Avi Drissman | 13fc893 | 2015-12-20 04:40:46 | [diff] [blame] | 98 | int32_t FLAGS_quic_version = -1; |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 99 | // If true, a version mismatch in the handshake is not considered a failure. |
| 100 | // Useful for probing a server to determine if it speaks any version of QUIC. |
| 101 | bool FLAGS_version_mismatch_ok = false; |
| 102 | // If true, an HTTP response code of 3xx is considered to be a successful |
| 103 | // response, otherwise a failure. |
| 104 | bool FLAGS_redirect_is_success = true; |
rtenneti | 6bd660b | 2015-07-18 00:19:53 | [diff] [blame] | 105 | // Initial MTU of the connection. |
Avi Drissman | 13fc893 | 2015-12-20 04:40:46 | [diff] [blame] | 106 | int32_t FLAGS_initial_mtu = 0; |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 107 | |
Ryan Hamilton | 8d9ee76e | 2018-05-29 23:52:52 | [diff] [blame] | 108 | class FakeProofVerifier : public quic::ProofVerifier { |
rch | e22c2fb | 2015-11-17 00:22:32 | [diff] [blame] | 109 | public: |
Ryan Hamilton | 8d9ee76e | 2018-05-29 23:52:52 | [diff] [blame] | 110 | quic::QuicAsyncStatus VerifyProof( |
shigeki.ohtsu | bf71e63 | 2016-10-23 15:43:54 | [diff] [blame] | 111 | const string& hostname, |
| 112 | const uint16_t port, |
| 113 | const string& server_config, |
Ryan Hamilton | 8d9ee76e | 2018-05-29 23:52:52 | [diff] [blame] | 114 | quic::QuicTransportVersion quic_version, |
| 115 | quic::QuicStringPiece chlo_hash, |
rch | 872e00e | 2016-12-02 02:48:18 | [diff] [blame] | 116 | const std::vector<string>& certs, |
shigeki.ohtsu | bf71e63 | 2016-10-23 15:43:54 | [diff] [blame] | 117 | const string& cert_sct, |
| 118 | const string& signature, |
Ryan Hamilton | 8d9ee76e | 2018-05-29 23:52:52 | [diff] [blame] | 119 | const quic::ProofVerifyContext* context, |
shigeki.ohtsu | bf71e63 | 2016-10-23 15:43:54 | [diff] [blame] | 120 | string* error_details, |
Ryan Hamilton | 8d9ee76e | 2018-05-29 23:52:52 | [diff] [blame] | 121 | std::unique_ptr<quic::ProofVerifyDetails>* details, |
| 122 | std::unique_ptr<quic::ProofVerifierCallback> callback) override { |
| 123 | return quic::QUIC_SUCCESS; |
rch | e22c2fb | 2015-11-17 00:22:32 | [diff] [blame] | 124 | } |
| 125 | |
Ryan Hamilton | 8d9ee76e | 2018-05-29 23:52:52 | [diff] [blame] | 126 | quic::QuicAsyncStatus VerifyCertChain( |
shigeki.ohtsu | bf71e63 | 2016-10-23 15:43:54 | [diff] [blame] | 127 | const std::string& hostname, |
| 128 | const std::vector<std::string>& certs, |
Ryan Hamilton | 8d9ee76e | 2018-05-29 23:52:52 | [diff] [blame] | 129 | const quic::ProofVerifyContext* verify_context, |
shigeki.ohtsu | bf71e63 | 2016-10-23 15:43:54 | [diff] [blame] | 130 | std::string* error_details, |
Ryan Hamilton | 8d9ee76e | 2018-05-29 23:52:52 | [diff] [blame] | 131 | std::unique_ptr<quic::ProofVerifyDetails>* verify_details, |
| 132 | std::unique_ptr<quic::ProofVerifierCallback> callback) override { |
| 133 | return quic::QUIC_SUCCESS; |
shigeki.ohtsu | bf71e63 | 2016-10-23 15:43:54 | [diff] [blame] | 134 | } |
Ryan Hamilton | 8bb19a1 | 2018-07-23 20:29:24 | [diff] [blame] | 135 | |
| 136 | std::unique_ptr<quic::ProofVerifyContext> CreateDefaultContext() override { |
| 137 | return nullptr; |
| 138 | } |
rch | e22c2fb | 2015-11-17 00:22:32 | [diff] [blame] | 139 | }; |
| 140 | |
rjshade | d5ced07 | 2015-12-18 19:26:02 | [diff] [blame] | 141 | int main(int argc, char* argv[]) { |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 142 | base::CommandLine::Init(argc, argv); |
| 143 | base::CommandLine* line = base::CommandLine::ForCurrentProcess(); |
| 144 | const base::CommandLine::StringVector& urls = line->GetArgs(); |
Francois Doray | b3b1d34a | 2018-04-05 14:31:23 | [diff] [blame] | 145 | base::TaskScheduler::CreateAndStartWithDefaultParams("quic_client"); |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 146 | |
| 147 | logging::LoggingSettings settings; |
| 148 | settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; |
| 149 | CHECK(logging::InitLogging(settings)); |
| 150 | |
| 151 | if (line->HasSwitch("h") || line->HasSwitch("help") || urls.empty()) { |
| 152 | const char* help_str = |
| 153 | "Usage: quic_client [options] <url>\n" |
| 154 | "\n" |
| 155 | "<url> with scheme must be provided (e.g. http://www.google.com)\n\n" |
| 156 | "Options:\n" |
| 157 | "-h, --help show this help message and exit\n" |
| 158 | "--host=<host> specify the IP address of the hostname to " |
| 159 | "connect to\n" |
| 160 | "--port=<port> specify the port to connect to\n" |
| 161 | "--body=<body> specify the body to post\n" |
danzh | b755134 | 2015-12-18 02:06:40 | [diff] [blame] | 162 | "--body_hex=<body_hex> specify the body_hex to be printed out\n" |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 163 | "--headers=<headers> specify a semicolon separated list of " |
| 164 | "key:value pairs to add to request headers\n" |
| 165 | "--quiet specify for a quieter output experience\n" |
| 166 | "--quic-version=<quic version> specify QUIC version to speak\n" |
| 167 | "--version_mismatch_ok if specified a version mismatch in the " |
| 168 | "handshake is not considered a failure\n" |
| 169 | "--redirect_is_success if specified an HTTP response code of 3xx " |
rtenneti | 6bd660b | 2015-07-18 00:19:53 | [diff] [blame] | 170 | "is considered to be a successful response, otherwise a failure\n" |
| 171 | "--initial_mtu=<initial_mtu> specify the initial MTU of the connection" |
rch | e22c2fb | 2015-11-17 00:22:32 | [diff] [blame] | 172 | "\n" |
| 173 | "--disable-certificate-verification do not verify certificates\n"; |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 174 | cout << help_str; |
| 175 | exit(0); |
| 176 | } |
| 177 | if (line->HasSwitch("host")) { |
| 178 | FLAGS_host = line->GetSwitchValueASCII("host"); |
| 179 | } |
| 180 | if (line->HasSwitch("port")) { |
| 181 | if (!base::StringToInt(line->GetSwitchValueASCII("port"), &FLAGS_port)) { |
| 182 | std::cerr << "--port must be an integer\n"; |
| 183 | return 1; |
| 184 | } |
| 185 | } |
| 186 | if (line->HasSwitch("body")) { |
| 187 | FLAGS_body = line->GetSwitchValueASCII("body"); |
| 188 | } |
danzh | b755134 | 2015-12-18 02:06:40 | [diff] [blame] | 189 | if (line->HasSwitch("body_hex")) { |
| 190 | FLAGS_body_hex = line->GetSwitchValueASCII("body_hex"); |
| 191 | } |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 192 | if (line->HasSwitch("headers")) { |
| 193 | FLAGS_headers = line->GetSwitchValueASCII("headers"); |
| 194 | } |
| 195 | if (line->HasSwitch("quiet")) { |
| 196 | FLAGS_quiet = true; |
| 197 | } |
| 198 | if (line->HasSwitch("quic-version")) { |
| 199 | int quic_version; |
| 200 | if (base::StringToInt(line->GetSwitchValueASCII("quic-version"), |
| 201 | &quic_version)) { |
| 202 | FLAGS_quic_version = quic_version; |
| 203 | } |
| 204 | } |
| 205 | if (line->HasSwitch("version_mismatch_ok")) { |
| 206 | FLAGS_version_mismatch_ok = true; |
| 207 | } |
| 208 | if (line->HasSwitch("redirect_is_success")) { |
| 209 | FLAGS_redirect_is_success = true; |
| 210 | } |
rtenneti | 6bd660b | 2015-07-18 00:19:53 | [diff] [blame] | 211 | if (line->HasSwitch("initial_mtu")) { |
| 212 | if (!base::StringToInt(line->GetSwitchValueASCII("initial_mtu"), |
| 213 | &FLAGS_initial_mtu)) { |
| 214 | std::cerr << "--initial_mtu must be an integer\n"; |
| 215 | return 1; |
| 216 | } |
| 217 | } |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 218 | |
| 219 | VLOG(1) << "server host: " << FLAGS_host << " port: " << FLAGS_port |
| 220 | << " body: " << FLAGS_body << " headers: " << FLAGS_headers |
| 221 | << " quiet: " << FLAGS_quiet |
| 222 | << " quic-version: " << FLAGS_quic_version |
| 223 | << " version_mismatch_ok: " << FLAGS_version_mismatch_ok |
rtenneti | 6bd660b | 2015-07-18 00:19:53 | [diff] [blame] | 224 | << " redirect_is_success: " << FLAGS_redirect_is_success |
| 225 | << " initial_mtu: " << FLAGS_initial_mtu; |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 226 | |
| 227 | base::AtExitManager exit_manager; |
rch | a9d12ce1 | 2015-03-19 23:06:49 | [diff] [blame] | 228 | base::MessageLoopForIO message_loop; |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 229 | |
| 230 | // Determine IP address to connect to from supplied hostname. |
Ryan Hamilton | 8d9ee76e | 2018-05-29 23:52:52 | [diff] [blame] | 231 | quic::QuicIpAddress ip_addr; |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 232 | |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 233 | GURL url(urls[0]); |
| 234 | string host = FLAGS_host; |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 235 | if (host.empty()) { |
rch | da78df5a | 2015-03-22 05:16:37 | [diff] [blame] | 236 | host = url.host(); |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 237 | } |
rch | f114d98 | 2015-10-21 01:34:56 | [diff] [blame] | 238 | int port = FLAGS_port; |
| 239 | if (port == 0) { |
| 240 | port = url.EffectiveIntPort(); |
| 241 | } |
rch | 2239d36 | 2016-12-29 01:23:00 | [diff] [blame] | 242 | if (!ip_addr.FromString(host)) { |
rch | da78df5a | 2015-03-22 05:16:37 | [diff] [blame] | 243 | net::AddressList addresses; |
rch | 750447f9 | 2016-01-31 02:54:53 | [diff] [blame] | 244 | int rv = net::SynchronousHostResolver::Resolve(host, &addresses); |
rch | da78df5a | 2015-03-22 05:16:37 | [diff] [blame] | 245 | if (rv != net::OK) { |
rjshade | d5ced07 | 2015-12-18 19:26:02 | [diff] [blame] | 246 | LOG(ERROR) << "Unable to resolve '" << host |
| 247 | << "' : " << net::ErrorToShortString(rv); |
rch | da78df5a | 2015-03-22 05:16:37 | [diff] [blame] | 248 | return 1; |
| 249 | } |
rch | 2239d36 | 2016-12-29 01:23:00 | [diff] [blame] | 250 | ip_addr = |
Ryan Hamilton | 8d9ee76e | 2018-05-29 23:52:52 | [diff] [blame] | 251 | quic::QuicIpAddress(quic::QuicIpAddressImpl(addresses[0].address())); |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 252 | } |
| 253 | |
Ryan Hamilton | 8d9ee76e | 2018-05-29 23:52:52 | [diff] [blame] | 254 | string host_port = quic::QuicStrCat(ip_addr.ToString(), ":", port); |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 255 | VLOG(1) << "Resolved " << host << " to " << host_port << endl; |
| 256 | |
| 257 | // Build the client, and try to connect. |
Ryan Hamilton | 8d9ee76e | 2018-05-29 23:52:52 | [diff] [blame] | 258 | quic::QuicServerId server_id(url.host(), url.EffectiveIntPort(), |
| 259 | net::PRIVACY_MODE_DISABLED); |
Ryan Hamilton | a5e002ea | 2018-06-27 00:59:13 | [diff] [blame] | 260 | quic::ParsedQuicVersionVector versions = quic::CurrentSupportedVersions(); |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 261 | if (FLAGS_quic_version != -1) { |
| 262 | versions.clear(); |
Ryan Hamilton | 8d9ee76e | 2018-05-29 23:52:52 | [diff] [blame] | 263 | versions.push_back(quic::ParsedQuicVersion( |
| 264 | quic::PROTOCOL_QUIC_CRYPTO, |
| 265 | static_cast<quic::QuicTransportVersion>(FLAGS_quic_version))); |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 266 | } |
rtenneti | 5a87ce4 | 2015-11-10 05:35:31 | [diff] [blame] | 267 | // For secure QUIC we need to verify the cert chain. |
danakj | a9850e1 | 2016-04-18 22:28:08 | [diff] [blame] | 268 | std::unique_ptr<CertVerifier> cert_verifier(CertVerifier::CreateDefault()); |
danakj | a9850e1 | 2016-04-18 22:28:08 | [diff] [blame] | 269 | std::unique_ptr<TransportSecurityState> transport_security_state( |
rtenneti | 5a87ce4 | 2015-11-10 05:35:31 | [diff] [blame] | 270 | new TransportSecurityState); |
rch | 71bd233 | 2017-02-04 00:06:47 | [diff] [blame] | 271 | std::unique_ptr<MultiLogCTVerifier> ct_verifier(new MultiLogCTVerifier()); |
Ryan Sleevi | 8a9c9c1 | 2018-05-09 02:36:23 | [diff] [blame] | 272 | std::unique_ptr<net::CTPolicyEnforcer> ct_policy_enforcer( |
| 273 | new net::DefaultCTPolicyEnforcer()); |
Ryan Hamilton | 8d9ee76e | 2018-05-29 23:52:52 | [diff] [blame] | 274 | std::unique_ptr<quic::ProofVerifier> proof_verifier; |
shigeki.ohtsu | bf71e63 | 2016-10-23 15:43:54 | [diff] [blame] | 275 | if (line->HasSwitch("disable-certificate-verification")) { |
| 276 | proof_verifier.reset(new FakeProofVerifier()); |
| 277 | } else { |
| 278 | proof_verifier.reset(new ProofVerifierChromium( |
| 279 | cert_verifier.get(), ct_policy_enforcer.get(), |
| 280 | transport_security_state.get(), ct_verifier.get())); |
| 281 | } |
Ryan Hamilton | 8d9ee76e | 2018-05-29 23:52:52 | [diff] [blame] | 282 | net::QuicSimpleClient client(quic::QuicSocketAddress(ip_addr, port), |
| 283 | server_id, versions, std::move(proof_verifier)); |
rtenneti | bb2780c | 2015-07-22 19:16:08 | [diff] [blame] | 284 | client.set_initial_max_packet_length( |
Ryan Hamilton | 8d9ee76e | 2018-05-29 23:52:52 | [diff] [blame] | 285 | FLAGS_initial_mtu != 0 ? FLAGS_initial_mtu : quic::kDefaultMaxPacketSize); |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 286 | if (!client.Initialize()) { |
| 287 | cerr << "Failed to initialize client." << endl; |
| 288 | return 1; |
| 289 | } |
| 290 | if (!client.Connect()) { |
Ryan Hamilton | 8d9ee76e | 2018-05-29 23:52:52 | [diff] [blame] | 291 | quic::QuicErrorCode error = client.session()->error(); |
| 292 | if (FLAGS_version_mismatch_ok && error == quic::QUIC_INVALID_VERSION) { |
rtenneti | 85816fdf | 2015-05-25 03:01:10 | [diff] [blame] | 293 | cout << "Server talks QUIC, but none of the versions supported by " |
Dan Zhang | fefaf5b | 2017-12-11 17:06:24 | [diff] [blame] | 294 | << "this client: " << ParsedQuicVersionVectorToString(versions) |
Michael Warres | 74ee3ce | 2017-10-09 15:26:37 | [diff] [blame] | 295 | << endl; |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 296 | // Version mismatch is not deemed a failure. |
| 297 | return 0; |
| 298 | } |
| 299 | cerr << "Failed to connect to " << host_port |
Ryan Hamilton | 8d9ee76e | 2018-05-29 23:52:52 | [diff] [blame] | 300 | << ". Error: " << quic::QuicErrorCodeToString(error) << endl; |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 301 | return 1; |
| 302 | } |
| 303 | cout << "Connected to " << host_port << endl; |
| 304 | |
danzh | b755134 | 2015-12-18 02:06:40 | [diff] [blame] | 305 | // Construct the string body from flags, if provided. |
| 306 | string body = FLAGS_body; |
| 307 | if (!FLAGS_body_hex.empty()) { |
| 308 | DCHECK(FLAGS_body.empty()) << "Only set one of --body and --body_hex."; |
Ryan Hamilton | 8d9ee76e | 2018-05-29 23:52:52 | [diff] [blame] | 309 | body = quic::QuicTextUtils::HexDecode(FLAGS_body_hex); |
danzh | b755134 | 2015-12-18 02:06:40 | [diff] [blame] | 310 | } |
| 311 | |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 312 | // Construct a GET or POST request for supplied URL. |
rch | 2239d36 | 2016-12-29 01:23:00 | [diff] [blame] | 313 | SpdyHeaderBlock header_block; |
| 314 | header_block[":method"] = body.empty() ? "GET" : "POST"; |
| 315 | header_block[":scheme"] = url.scheme(); |
| 316 | header_block[":authority"] = url.host(); |
| 317 | header_block[":path"] = url.path(); |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 318 | |
| 319 | // Append any additional headers supplied on the command line. |
Ryan Hamilton | 8d9ee76e | 2018-05-29 23:52:52 | [diff] [blame] | 320 | for (quic::QuicStringPiece sp : |
| 321 | quic::QuicTextUtils::Split(FLAGS_headers, ';')) { |
| 322 | quic::QuicTextUtils::RemoveLeadingAndTrailingWhitespace(&sp); |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 323 | if (sp.empty()) { |
| 324 | continue; |
| 325 | } |
Ryan Hamilton | 8d9ee76e | 2018-05-29 23:52:52 | [diff] [blame] | 326 | std::vector<quic::QuicStringPiece> kv = quic::QuicTextUtils::Split(sp, ':'); |
| 327 | quic::QuicTextUtils::RemoveLeadingAndTrailingWhitespace(&kv[0]); |
| 328 | quic::QuicTextUtils::RemoveLeadingAndTrailingWhitespace(&kv[1]); |
rch | 2239d36 | 2016-12-29 01:23:00 | [diff] [blame] | 329 | header_block[kv[0]] = kv[1]; |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 330 | } |
| 331 | |
| 332 | // Make sure to store the response, for later output. |
| 333 | client.set_store_response(true); |
| 334 | |
| 335 | // Send the request. |
rch | c29e68c | 2016-09-27 17:57:39 | [diff] [blame] | 336 | client.SendRequestAndWaitForResponse(header_block, body, /*fin=*/true); |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 337 | |
| 338 | // Print request and response details. |
| 339 | if (!FLAGS_quiet) { |
| 340 | cout << "Request:" << endl; |
jri | 86a3c60 | 2015-12-15 06:01:03 | [diff] [blame] | 341 | cout << "headers:" << header_block.DebugString(); |
fayang | a31a74b | 2015-12-28 17:27:14 | [diff] [blame] | 342 | if (!FLAGS_body_hex.empty()) { |
| 343 | // Print the user provided hex, rather than binary body. |
ckrasic | 6567aa5 | 2016-07-08 09:24:35 | [diff] [blame] | 344 | cout << "body:\n" |
Ryan Hamilton | 8d9ee76e | 2018-05-29 23:52:52 | [diff] [blame] | 345 | << quic::QuicTextUtils::HexDump( |
| 346 | quic::QuicTextUtils::HexDecode(FLAGS_body_hex)) |
rch | 16fe57a | 2016-05-10 21:34:33 | [diff] [blame] | 347 | << endl; |
fayang | a31a74b | 2015-12-28 17:27:14 | [diff] [blame] | 348 | } else { |
| 349 | cout << "body: " << body << endl; |
| 350 | } |
rch | a9d12ce1 | 2015-03-19 23:06:49 | [diff] [blame] | 351 | cout << endl; |
| 352 | cout << "Response:" << endl; |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 353 | cout << "headers: " << client.latest_response_headers() << endl; |
fayang | a31a74b | 2015-12-28 17:27:14 | [diff] [blame] | 354 | string response_body = client.latest_response_body(); |
| 355 | if (!FLAGS_body_hex.empty()) { |
| 356 | // Assume response is binary data. |
Ryan Hamilton | 8d9ee76e | 2018-05-29 23:52:52 | [diff] [blame] | 357 | cout << "body:\n" << quic::QuicTextUtils::HexDump(response_body) << endl; |
fayang | a31a74b | 2015-12-28 17:27:14 | [diff] [blame] | 358 | } else { |
| 359 | cout << "body: " << response_body << endl; |
| 360 | } |
rch | 2239d36 | 2016-12-29 01:23:00 | [diff] [blame] | 361 | cout << "trailers: " << client.latest_response_trailers() << endl; |
rch | fdfdc8ed | 2015-03-18 00:35:20 | [diff] [blame] | 362 | } |
| 363 | |
| 364 | size_t response_code = client.latest_response_code(); |
| 365 | if (response_code >= 200 && response_code < 300) { |
| 366 | cout << "Request succeeded (" << response_code << ")." << endl; |
| 367 | return 0; |
| 368 | } else if (response_code >= 300 && response_code < 400) { |
| 369 | if (FLAGS_redirect_is_success) { |
| 370 | cout << "Request succeeded (redirect " << response_code << ")." << endl; |
| 371 | return 0; |
| 372 | } else { |
| 373 | cout << "Request failed (redirect " << response_code << ")." << endl; |
| 374 | return 1; |
| 375 | } |
| 376 | } else { |
| 377 | cerr << "Request failed (" << response_code << ")." << endl; |
| 378 | return 1; |
| 379 | } |
| 380 | } |