Switch global error menu icon to vectorized MD asset
[chromium-blink-merge.git] / net / tools / quic / quic_simple_client.cc
blobd3d811aa935bed172ead5f86c817a533ef8736ad
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 #include "net/tools/quic/quic_simple_client.h"
7 #include "base/logging.h"
8 #include "base/run_loop.h"
9 #include "base/thread_task_runner_handle.h"
10 #include "net/base/net_errors.h"
11 #include "net/http/http_request_info.h"
12 #include "net/http/http_response_info.h"
13 #include "net/quic/crypto/quic_random.h"
14 #include "net/quic/quic_connection.h"
15 #include "net/quic/quic_connection_helper.h"
16 #include "net/quic/quic_default_packet_writer.h"
17 #include "net/quic/quic_flags.h"
18 #include "net/quic/quic_protocol.h"
19 #include "net/quic/quic_server_id.h"
20 #include "net/quic/spdy_utils.h"
21 #include "net/spdy/spdy_http_utils.h"
22 #include "net/udp/udp_client_socket.h"
24 using std::string;
25 using std::vector;
27 namespace net {
28 namespace tools {
30 void QuicSimpleClient::ClientQuicDataToResend::Resend() {
31 client_->SendRequest(*headers_, body_, fin_);
32 delete headers_;
33 headers_ = nullptr;
36 QuicSimpleClient::QuicSimpleClient(IPEndPoint server_address,
37 const QuicServerId& server_id,
38 const QuicVersionVector& supported_versions)
39 : QuicClientBase(server_id, supported_versions, QuicConfig()),
40 server_address_(server_address),
41 local_port_(0),
42 helper_(CreateQuicConnectionHelper()),
43 initialized_(false),
44 packet_reader_started_(false),
45 weak_factory_(this) {}
47 QuicSimpleClient::QuicSimpleClient(IPEndPoint server_address,
48 const QuicServerId& server_id,
49 const QuicVersionVector& supported_versions,
50 const QuicConfig& config)
51 : QuicClientBase(server_id, supported_versions, config),
52 server_address_(server_address),
53 local_port_(0),
54 helper_(CreateQuicConnectionHelper()),
55 initialized_(false),
56 packet_reader_started_(false),
57 weak_factory_(this) {}
59 QuicSimpleClient::~QuicSimpleClient() {
60 if (connected()) {
61 session()->connection()->SendConnectionClosePacket(
62 QUIC_PEER_GOING_AWAY, "");
64 STLDeleteElements(&data_to_resend_on_connect_);
65 STLDeleteElements(&data_sent_before_handshake_);
68 bool QuicSimpleClient::Initialize() {
69 DCHECK(!initialized_);
71 QuicClientBase::Initialize();
73 if (!CreateUDPSocket()) {
74 return false;
77 initialized_ = true;
78 return true;
81 QuicSimpleClient::QuicDataToResend::QuicDataToResend(HttpRequestInfo* headers,
82 StringPiece body,
83 bool fin)
84 : headers_(headers), body_(body), fin_(fin) {}
86 QuicSimpleClient::QuicDataToResend::~QuicDataToResend() {
87 if (headers_) {
88 delete headers_;
92 bool QuicSimpleClient::CreateUDPSocket() {
93 scoped_ptr<UDPClientSocket> socket(
94 new UDPClientSocket(DatagramSocket::DEFAULT_BIND,
95 RandIntCallback(),
96 &net_log_,
97 NetLog::Source()));
99 int address_family = server_address_.GetSockAddrFamily();
100 if (bind_to_address_.size() != 0) {
101 client_address_ = IPEndPoint(bind_to_address_, local_port_);
102 } else if (address_family == AF_INET) {
103 IPAddressNumber any4;
104 CHECK(net::ParseIPLiteralToNumber("0.0.0.0", &any4));
105 client_address_ = IPEndPoint(any4, local_port_);
106 } else {
107 IPAddressNumber any6;
108 CHECK(net::ParseIPLiteralToNumber("::", &any6));
109 client_address_ = IPEndPoint(any6, local_port_);
112 int rc = socket->Connect(server_address_);
113 if (rc != OK) {
114 LOG(ERROR) << "Connect failed: " << ErrorToShortString(rc);
115 return false;
118 rc = socket->SetReceiveBufferSize(kDefaultSocketReceiveBuffer);
119 if (rc != OK) {
120 LOG(ERROR) << "SetReceiveBufferSize() failed: " << ErrorToShortString(rc);
121 return false;
124 rc = socket->SetSendBufferSize(kDefaultSocketReceiveBuffer);
125 if (rc != OK) {
126 LOG(ERROR) << "SetSendBufferSize() failed: " << ErrorToShortString(rc);
127 return false;
130 rc = socket->GetLocalAddress(&client_address_);
131 if (rc != OK) {
132 LOG(ERROR) << "GetLocalAddress failed: " << ErrorToShortString(rc);
133 return false;
136 socket_.swap(socket);
137 packet_reader_.reset(new QuicPacketReader(socket_.get(), this,
138 BoundNetLog()));
140 if (socket != nullptr) {
141 socket->Close();
144 return true;
147 void QuicSimpleClient::StartPacketReaderIfNotStarted() {
148 if (!packet_reader_started_) {
149 packet_reader_->StartReading();
150 packet_reader_started_ = true;
154 bool QuicSimpleClient::Connect() {
155 // Attempt multiple connects until the maximum number of client hellos have
156 // been sent.
157 while (!connected() &&
158 GetNumSentClientHellos() <= QuicCryptoClientStream::kMaxClientHellos) {
159 StartConnect();
160 StartPacketReaderIfNotStarted();
161 while (EncryptionBeingEstablished()) {
162 WaitForEvents();
164 if (FLAGS_enable_quic_stateless_reject_support && connected() &&
165 !data_to_resend_on_connect_.empty()) {
166 // A connection has been established and there was previously queued data
167 // to resend. Resend it and empty the queue.
168 for (QuicDataToResend* data : data_to_resend_on_connect_) {
169 data->Resend();
171 STLDeleteElements(&data_to_resend_on_connect_);
173 if (session() != nullptr &&
174 session()->error() != QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) {
175 // We've successfully created a session but we're not connected, and there
176 // is no stateless reject to recover from. Give up trying.
177 break;
180 if (!connected() &&
181 GetNumSentClientHellos() > QuicCryptoClientStream::kMaxClientHellos &&
182 session() != nullptr &&
183 session()->error() == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) {
184 // The overall connection failed due too many stateless rejects.
185 set_connection_error(QUIC_CRYPTO_TOO_MANY_REJECTS);
187 return session()->connection()->connected();
190 void QuicSimpleClient::StartConnect() {
191 DCHECK(initialized_);
192 DCHECK(!connected());
194 set_writer(CreateQuicPacketWriter());
196 DummyPacketWriterFactory factory(writer());
198 if (connected_or_attempting_connect()) {
199 // Before we destroy the last session and create a new one, gather its stats
200 // and update the stats for the overall connection.
201 UpdateStats();
202 if (session()->error() == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) {
203 // If the last error was due to a stateless reject, queue up the data to
204 // be resent on the next successful connection.
205 // TODO(jokulik): I'm a little bit concerned about ordering here. Maybe
206 // we should just maintain one queue?
207 DCHECK(data_to_resend_on_connect_.empty());
208 data_to_resend_on_connect_.swap(data_sent_before_handshake_);
212 CreateQuicClientSession(new QuicConnection(
213 GetNextConnectionId(), server_address_, helper_.get(), factory,
214 /* owns_writer= */ false, Perspective::IS_CLIENT, server_id().is_https(),
215 supported_versions()));
217 session()->Initialize();
218 session()->CryptoConnect();
219 set_connected_or_attempting_connect(true);
222 void QuicSimpleClient::Disconnect() {
223 DCHECK(initialized_);
225 if (connected()) {
226 session()->connection()->SendConnectionClose(QUIC_PEER_GOING_AWAY);
228 STLDeleteElements(&data_to_resend_on_connect_);
229 STLDeleteElements(&data_sent_before_handshake_);
231 reset_writer();
232 packet_reader_.reset();
234 initialized_ = false;
237 void QuicSimpleClient::SendRequest(const HttpRequestInfo& headers,
238 StringPiece body,
239 bool fin) {
240 QuicSpdyClientStream* stream = CreateReliableClientStream();
241 if (stream == nullptr) {
242 LOG(DFATAL) << "stream creation failed!";
243 return;
245 SpdyHeaderBlock header_block;
246 SpdyMajorVersion spdy_version =
247 SpdyUtils::GetSpdyVersionForQuicVersion(stream->version());
248 CreateSpdyHeadersFromHttpRequest(headers, headers.extra_headers, spdy_version,
249 true, &header_block);
250 stream->set_visitor(this);
251 stream->SendRequest(header_block, body, fin);
252 if (FLAGS_enable_quic_stateless_reject_support) {
253 // Record this in case we need to resend.
254 auto new_headers = new HttpRequestInfo;
255 *new_headers = headers;
256 auto data_to_resend =
257 new ClientQuicDataToResend(new_headers, body, fin, this);
258 MaybeAddQuicDataToResend(data_to_resend);
262 void QuicSimpleClient::MaybeAddQuicDataToResend(
263 QuicDataToResend* data_to_resend) {
264 DCHECK(FLAGS_enable_quic_stateless_reject_support);
265 if (session()->IsCryptoHandshakeConfirmed()) {
266 // The handshake is confirmed. No need to continue saving requests to
267 // resend.
268 STLDeleteElements(&data_sent_before_handshake_);
269 delete data_to_resend;
270 return;
273 // The handshake is not confirmed. Push the data onto the queue of data to
274 // resend if statelessly rejected.
275 data_sent_before_handshake_.push_back(data_to_resend);
278 void QuicSimpleClient::SendRequestAndWaitForResponse(
279 const HttpRequestInfo& request,
280 StringPiece body,
281 bool fin) {
282 SendRequest(request, body, fin);
283 while (WaitForEvents()) {}
286 void QuicSimpleClient::SendRequestsAndWaitForResponse(
287 const base::CommandLine::StringVector& url_list) {
288 for (size_t i = 0; i < url_list.size(); ++i) {
289 HttpRequestInfo request;
290 request.method = "GET";
291 request.url = GURL(url_list[i]);
292 SendRequest(request, "", true);
295 while (WaitForEvents()) {}
298 bool QuicSimpleClient::WaitForEvents() {
299 DCHECK(connected());
301 base::RunLoop().RunUntilIdle();
303 DCHECK(session() != nullptr);
304 if (!connected() &&
305 session()->error() == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) {
306 DCHECK(FLAGS_enable_quic_stateless_reject_support);
307 DVLOG(1) << "Detected stateless reject while waiting for events. "
308 << "Attempting to reconnect.";
309 Connect();
312 return session()->num_active_requests() != 0;
315 bool QuicSimpleClient::MigrateSocket(const IPAddressNumber& new_host) {
316 if (!connected()) {
317 return false;
320 bind_to_address_ = new_host;
321 if (!CreateUDPSocket()) {
322 return false;
325 session()->connection()->SetSelfAddress(client_address_);
327 QuicPacketWriter* writer = CreateQuicPacketWriter();
328 DummyPacketWriterFactory factory(writer);
329 set_writer(writer);
330 session()->connection()->SetQuicPacketWriter(writer, false);
332 return true;
335 void QuicSimpleClient::OnClose(QuicDataStream* stream) {
336 DCHECK(stream != nullptr);
337 QuicSpdyClientStream* client_stream =
338 static_cast<QuicSpdyClientStream*>(stream);
339 HttpResponseInfo response;
340 SpdyMajorVersion spdy_version =
341 SpdyUtils::GetSpdyVersionForQuicVersion(client_stream->version());
342 SpdyHeadersToHttpResponse(client_stream->headers(), spdy_version, &response);
343 if (response_listener_.get() != nullptr) {
344 response_listener_->OnCompleteResponse(
345 stream->id(), *response.headers, client_stream->data());
348 // Store response headers and body.
349 if (store_response_) {
350 latest_response_code_ = client_stream->response_code();
351 response.headers->GetNormalizedHeaders(&latest_response_headers_);
352 latest_response_body_ = client_stream->data();
356 size_t QuicSimpleClient::latest_response_code() const {
357 LOG_IF(DFATAL, !store_response_) << "Response not stored!";
358 return latest_response_code_;
361 const string& QuicSimpleClient::latest_response_headers() const {
362 LOG_IF(DFATAL, !store_response_) << "Response not stored!";
363 return latest_response_headers_;
366 const string& QuicSimpleClient::latest_response_body() const {
367 LOG_IF(DFATAL, !store_response_) << "Response not stored!";
368 return latest_response_body_;
371 QuicConnectionId QuicSimpleClient::GenerateNewConnectionId() {
372 return helper_->GetRandomGenerator()->RandUint64();
375 QuicConnectionHelper* QuicSimpleClient::CreateQuicConnectionHelper() {
376 return new QuicConnectionHelper(base::ThreadTaskRunnerHandle::Get().get(),
377 &clock_, QuicRandom::GetInstance());
380 QuicPacketWriter* QuicSimpleClient::CreateQuicPacketWriter() {
381 return new QuicDefaultPacketWriter(socket_.get());
384 void QuicSimpleClient::OnReadError(int result) {
385 LOG(ERROR) << "QuicSimpleClient read failed: " << ErrorToShortString(result);
386 Disconnect();
389 bool QuicSimpleClient::OnPacket(const QuicEncryptedPacket& packet,
390 IPEndPoint local_address,
391 IPEndPoint peer_address) {
392 session()->connection()->ProcessUdpPacket(local_address, peer_address,
393 packet);
394 if (!session()->connection()->connected()) {
395 return false;
398 return true;
401 } // namespace tools
402 } // namespace net