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"
30 void QuicSimpleClient::ClientQuicDataToResend::Resend() {
31 client_
->SendRequest(*headers_
, body_
, fin_
);
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
),
42 helper_(CreateQuicConnectionHelper()),
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
),
54 helper_(CreateQuicConnectionHelper()),
56 packet_reader_started_(false),
57 weak_factory_(this) {}
59 QuicSimpleClient::~QuicSimpleClient() {
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()) {
81 QuicSimpleClient::QuicDataToResend::QuicDataToResend(HttpRequestInfo
* headers
,
84 : headers_(headers
), body_(body
), fin_(fin
) {}
86 QuicSimpleClient::QuicDataToResend::~QuicDataToResend() {
92 bool QuicSimpleClient::CreateUDPSocket() {
93 scoped_ptr
<UDPClientSocket
> socket(
94 new UDPClientSocket(DatagramSocket::DEFAULT_BIND
,
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_
);
107 IPAddressNumber any6
;
108 CHECK(net::ParseIPLiteralToNumber("::", &any6
));
109 client_address_
= IPEndPoint(any6
, local_port_
);
112 int rc
= socket
->Connect(server_address_
);
114 LOG(ERROR
) << "Connect failed: " << ErrorToShortString(rc
);
118 rc
= socket
->SetReceiveBufferSize(kDefaultSocketReceiveBuffer
);
120 LOG(ERROR
) << "SetReceiveBufferSize() failed: " << ErrorToShortString(rc
);
124 rc
= socket
->SetSendBufferSize(kDefaultSocketReceiveBuffer
);
126 LOG(ERROR
) << "SetSendBufferSize() failed: " << ErrorToShortString(rc
);
130 rc
= socket
->GetLocalAddress(&client_address_
);
132 LOG(ERROR
) << "GetLocalAddress failed: " << ErrorToShortString(rc
);
136 socket_
.swap(socket
);
137 packet_reader_
.reset(new QuicPacketReader(socket_
.get(), this,
140 if (socket
!= nullptr) {
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
157 while (!connected() &&
158 GetNumSentClientHellos() <= QuicCryptoClientStream::kMaxClientHellos
) {
160 StartPacketReaderIfNotStarted();
161 while (EncryptionBeingEstablished()) {
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_
) {
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.
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.
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_
);
226 session()->connection()->SendConnectionClose(QUIC_PEER_GOING_AWAY
);
228 STLDeleteElements(&data_to_resend_on_connect_
);
229 STLDeleteElements(&data_sent_before_handshake_
);
232 packet_reader_
.reset();
234 initialized_
= false;
237 void QuicSimpleClient::SendRequest(const HttpRequestInfo
& headers
,
240 QuicSpdyClientStream
* stream
= CreateReliableClientStream();
241 if (stream
== nullptr) {
242 LOG(DFATAL
) << "stream creation failed!";
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
268 STLDeleteElements(&data_sent_before_handshake_
);
269 delete data_to_resend
;
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
,
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() {
301 base::RunLoop().RunUntilIdle();
303 DCHECK(session() != nullptr);
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.";
312 return session()->num_active_requests() != 0;
315 bool QuicSimpleClient::MigrateSocket(const IPAddressNumber
& new_host
) {
320 bind_to_address_
= new_host
;
321 if (!CreateUDPSocket()) {
325 session()->connection()->SetSelfAddress(client_address_
);
327 QuicPacketWriter
* writer
= CreateQuicPacketWriter();
328 DummyPacketWriterFactory
factory(writer
);
330 session()->connection()->SetQuicPacketWriter(writer
, false);
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
);
389 bool QuicSimpleClient::OnPacket(const QuicEncryptedPacket
& packet
,
390 IPEndPoint local_address
,
391 IPEndPoint peer_address
) {
392 session()->connection()->ProcessUdpPacket(local_address
, peer_address
,
394 if (!session()->connection()->connected()) {