Refactor WebsiteSettings to operate on a SecurityInfo
[chromium-blink-merge.git] / net / tools / quic / quic_simple_client_bin.cc
blob762a9177b5dda7e5f723a9967b559f4e89517f5e
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.
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:
11 // TODO(rtenneti): make --host optional by getting IP Address of URL's host.
13 // Get IP address of the www.google.com
14 // IP=`dig www.google.com +short | head -1`
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}
21 // Use a specific version:
22 // quic_client http://www.google.com --version=23 --host=${IP}
24 // Send a POST instead of a GET:
25 // quic_client http://www.google.com --body="this is a POST body" --host=${IP}
27 // Append additional headers to the request:
28 // quic_client http://www.google.com --host=${IP}
29 // --headers="Header-A: 1234; Header-B: 5678"
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}
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}
41 #include <iostream>
43 #include "base/at_exit.h"
44 #include "base/command_line.h"
45 #include "base/logging.h"
46 #include "base/message_loop/message_loop.h"
47 #include "base/strings/string_number_conversions.h"
48 #include "base/strings/string_split.h"
49 #include "base/strings/string_util.h"
50 #include "net/base/ip_endpoint.h"
51 #include "net/base/net_errors.h"
52 #include "net/base/privacy_mode.h"
53 #include "net/cert/cert_verifier.h"
54 #include "net/http/http_request_info.h"
55 #include "net/http/transport_security_state.h"
56 #include "net/log/net_log.h"
57 #include "net/quic/crypto/proof_verifier_chromium.h"
58 #include "net/quic/quic_protocol.h"
59 #include "net/quic/quic_server_id.h"
60 #include "net/quic/quic_utils.h"
61 #include "net/spdy/spdy_http_utils.h"
62 #include "net/tools/quic/quic_simple_client.h"
63 #include "net/tools/quic/synchronous_host_resolver.h"
64 #include "url/gurl.h"
66 using base::StringPiece;
67 using net::CertVerifier;
68 using net::ProofVerifierChromium;
69 using net::TransportSecurityState;
70 using std::cout;
71 using std::cerr;
72 using std::map;
73 using std::string;
74 using std::vector;
75 using std::endl;
77 // The IP or hostname the quic client will connect to.
78 string FLAGS_host = "";
79 // The port to connect to.
80 int32 FLAGS_port = 80;
81 // If set, send a POST with this body.
82 string FLAGS_body = "";
83 // A semicolon separated list of key:value pairs to add to request headers.
84 string FLAGS_headers = "";
85 // Set to true for a quieter output experience.
86 bool FLAGS_quiet = false;
87 // QUIC version to speak, e.g. 21. If not set, then all available versions are
88 // offered in the handshake.
89 int32 FLAGS_quic_version = -1;
90 // If true, a version mismatch in the handshake is not considered a failure.
91 // Useful for probing a server to determine if it speaks any version of QUIC.
92 bool FLAGS_version_mismatch_ok = false;
93 // If true, an HTTP response code of 3xx is considered to be a successful
94 // response, otherwise a failure.
95 bool FLAGS_redirect_is_success = true;
96 // Initial MTU of the connection.
97 int32 FLAGS_initial_mtu = 0;
99 int main(int argc, char *argv[]) {
100 base::CommandLine::Init(argc, argv);
101 base::CommandLine* line = base::CommandLine::ForCurrentProcess();
102 const base::CommandLine::StringVector& urls = line->GetArgs();
104 logging::LoggingSettings settings;
105 settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
106 CHECK(logging::InitLogging(settings));
108 if (line->HasSwitch("h") || line->HasSwitch("help") || urls.empty()) {
109 const char* help_str =
110 "Usage: quic_client [options] <url>\n"
111 "\n"
112 "<url> with scheme must be provided (e.g. http://www.google.com)\n\n"
113 "Options:\n"
114 "-h, --help show this help message and exit\n"
115 "--host=<host> specify the IP address of the hostname to "
116 "connect to\n"
117 "--port=<port> specify the port to connect to\n"
118 "--body=<body> specify the body to post\n"
119 "--headers=<headers> specify a semicolon separated list of "
120 "key:value pairs to add to request headers\n"
121 "--quiet specify for a quieter output experience\n"
122 "--quic-version=<quic version> specify QUIC version to speak\n"
123 "--version_mismatch_ok if specified a version mismatch in the "
124 "handshake is not considered a failure\n"
125 "--redirect_is_success if specified an HTTP response code of 3xx "
126 "is considered to be a successful response, otherwise a failure\n"
127 "--initial_mtu=<initial_mtu> specify the initial MTU of the connection"
128 "\n";
129 cout << help_str;
130 exit(0);
132 if (line->HasSwitch("host")) {
133 FLAGS_host = line->GetSwitchValueASCII("host");
135 if (line->HasSwitch("port")) {
136 if (!base::StringToInt(line->GetSwitchValueASCII("port"), &FLAGS_port)) {
137 std::cerr << "--port must be an integer\n";
138 return 1;
141 if (line->HasSwitch("body")) {
142 FLAGS_body = line->GetSwitchValueASCII("body");
144 if (line->HasSwitch("headers")) {
145 FLAGS_headers = line->GetSwitchValueASCII("headers");
147 if (line->HasSwitch("quiet")) {
148 FLAGS_quiet = true;
150 if (line->HasSwitch("quic-version")) {
151 int quic_version;
152 if (base::StringToInt(line->GetSwitchValueASCII("quic-version"),
153 &quic_version)) {
154 FLAGS_quic_version = quic_version;
157 if (line->HasSwitch("version_mismatch_ok")) {
158 FLAGS_version_mismatch_ok = true;
160 if (line->HasSwitch("redirect_is_success")) {
161 FLAGS_redirect_is_success = true;
163 if (line->HasSwitch("initial_mtu")) {
164 if (!base::StringToInt(line->GetSwitchValueASCII("initial_mtu"),
165 &FLAGS_initial_mtu)) {
166 std::cerr << "--initial_mtu must be an integer\n";
167 return 1;
171 VLOG(1) << "server host: " << FLAGS_host << " port: " << FLAGS_port
172 << " body: " << FLAGS_body << " headers: " << FLAGS_headers
173 << " quiet: " << FLAGS_quiet
174 << " quic-version: " << FLAGS_quic_version
175 << " version_mismatch_ok: " << FLAGS_version_mismatch_ok
176 << " redirect_is_success: " << FLAGS_redirect_is_success
177 << " initial_mtu: " << FLAGS_initial_mtu;
179 base::AtExitManager exit_manager;
180 base::MessageLoopForIO message_loop;
182 // Determine IP address to connect to from supplied hostname.
183 net::IPAddressNumber ip_addr;
185 // TODO(rtenneti): GURL's doesn't support default_protocol argument, thus
186 // protocol is required in the URL.
187 GURL url(urls[0]);
188 string host = FLAGS_host;
189 if (host.empty()) {
190 host = url.host();
192 if (!net::ParseIPLiteralToNumber(host, &ip_addr)) {
193 net::AddressList addresses;
194 int rv = net::tools::SynchronousHostResolver::Resolve(host, &addresses);
195 if (rv != net::OK) {
196 LOG(ERROR) << "Unable to resolve '" << host << "' : "
197 << net::ErrorToShortString(rv);
198 return 1;
200 ip_addr = addresses[0].address();
203 string host_port = net::IPAddressToStringWithPort(ip_addr, FLAGS_port);
204 VLOG(1) << "Resolved " << host << " to " << host_port << endl;
206 // Build the client, and try to connect.
207 net::QuicServerId server_id(host, FLAGS_port, /*is_https=*/true,
208 net::PRIVACY_MODE_DISABLED);
209 net::QuicVersionVector versions = net::QuicSupportedVersions();
210 if (FLAGS_quic_version != -1) {
211 versions.clear();
212 versions.push_back(static_cast<net::QuicVersion>(FLAGS_quic_version));
214 net::tools::QuicSimpleClient client(net::IPEndPoint(ip_addr, FLAGS_port),
215 server_id, versions);
216 scoped_ptr<CertVerifier> cert_verifier;
217 scoped_ptr<TransportSecurityState> transport_security_state;
218 client.set_initial_max_packet_length(
219 FLAGS_initial_mtu != 0 ? FLAGS_initial_mtu : net::kDefaultMaxPacketSize);
220 // For secure QUIC we need to verify the cert chain.
221 cert_verifier = CertVerifier::CreateDefault();
222 transport_security_state.reset(new TransportSecurityState);
223 client.SetProofVerifier(new ProofVerifierChromium(
224 cert_verifier.get(), nullptr, transport_security_state.get()));
225 if (!client.Initialize()) {
226 cerr << "Failed to initialize client." << endl;
227 return 1;
229 if (!client.Connect()) {
230 net::QuicErrorCode error = client.session()->error();
231 if (FLAGS_version_mismatch_ok && error == net::QUIC_INVALID_VERSION) {
232 cout << "Server talks QUIC, but none of the versions supported by "
233 << "this client: " << QuicVersionVectorToString(versions) << endl;
234 // Version mismatch is not deemed a failure.
235 return 0;
237 cerr << "Failed to connect to " << host_port
238 << ". Error: " << net::QuicUtils::ErrorToString(error) << endl;
239 return 1;
241 cout << "Connected to " << host_port << endl;
243 // Construct a GET or POST request for supplied URL.
244 net::HttpRequestInfo request;
245 request.method = FLAGS_body.empty() ? "GET" : "POST";
246 request.url = url;
248 // Append any additional headers supplied on the command line.
249 for (const std::string& header :
250 base::SplitString(FLAGS_headers, ";", base::KEEP_WHITESPACE,
251 base::SPLIT_WANT_NONEMPTY)) {
252 string sp;
253 base::TrimWhitespaceASCII(header, base::TRIM_ALL, &sp);
254 if (sp.empty()) {
255 continue;
257 vector<string> kv =
258 base::SplitString(sp, ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
259 CHECK_EQ(2u, kv.size());
260 string key;
261 base::TrimWhitespaceASCII(kv[0], base::TRIM_ALL, &key);
262 string value;
263 base::TrimWhitespaceASCII(kv[1], base::TRIM_ALL, &value);
264 request.extra_headers.SetHeader(key, value);
267 // Make sure to store the response, for later output.
268 client.set_store_response(true);
270 // Send the request.
271 net::SpdyHeaderBlock header_block;
272 net::CreateSpdyHeadersFromHttpRequest(request, request.extra_headers,
273 net::HTTP2, /*direct=*/true,
274 &header_block);
275 client.SendRequestAndWaitForResponse(request, FLAGS_body, /*fin=*/true);
277 // Print request and response details.
278 if (!FLAGS_quiet) {
279 cout << "Request:" << endl;
280 cout << "headers:" << endl;
281 for (const std::pair<string, string>& kv : header_block) {
282 cout << " " << kv.first << ": " << kv.second << endl;
284 cout << "body: " << FLAGS_body << endl;
285 cout << endl;
286 cout << "Response:" << endl;
287 cout << "headers: " << client.latest_response_headers() << endl;
288 cout << "body: " << client.latest_response_body() << endl;
291 size_t response_code = client.latest_response_code();
292 if (response_code >= 200 && response_code < 300) {
293 cout << "Request succeeded (" << response_code << ")." << endl;
294 return 0;
295 } else if (response_code >= 300 && response_code < 400) {
296 if (FLAGS_redirect_is_success) {
297 cout << "Request succeeded (redirect " << response_code << ")." << endl;
298 return 0;
299 } else {
300 cout << "Request failed (redirect " << response_code << ")." << endl;
301 return 1;
303 } else {
304 cerr << "Request failed (" << response_code << ")." << endl;
305 return 1;