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_client.h"
8 #include <netinet/in.h>
10 #include <sys/epoll.h>
11 #include <sys/socket.h>
14 #include "base/logging.h"
15 #include "net/base/net_util.h"
16 #include "net/quic/crypto/quic_random.h"
17 #include "net/quic/quic_connection.h"
18 #include "net/quic/quic_data_reader.h"
19 #include "net/quic/quic_flags.h"
20 #include "net/quic/quic_protocol.h"
21 #include "net/quic/quic_server_id.h"
22 #include "net/tools/quic/quic_epoll_connection_helper.h"
23 #include "net/tools/quic/quic_socket_utils.h"
24 #include "net/tools/quic/spdy_balsa_utils.h"
27 #define SO_RXQ_OVFL 40
36 const int kEpollFlags
= EPOLLIN
| EPOLLOUT
| EPOLLET
;
38 void QuicClient::ClientQuicDataToResend::Resend() {
39 client_
->SendRequest(*headers_
, body_
, fin_
);
44 QuicClient::QuicClient(IPEndPoint server_address
,
45 const QuicServerId
& server_id
,
46 const QuicVersionVector
& supported_versions
,
47 EpollServer
* epoll_server
)
48 : QuicClient(server_address
,
54 QuicClient::QuicClient(IPEndPoint server_address
,
55 const QuicServerId
& server_id
,
56 const QuicVersionVector
& supported_versions
,
57 const QuicConfig
& config
,
58 EpollServer
* epoll_server
)
59 : QuicClientBase(server_id
, supported_versions
, config
),
60 server_address_(server_address
),
62 epoll_server_(epoll_server
),
64 helper_(CreateQuicConnectionHelper()),
67 overflow_supported_(false),
68 store_response_(false),
69 latest_response_code_(-1) {}
71 QuicClient::~QuicClient() {
73 session()->connection()->SendConnectionClose(QUIC_PEER_GOING_AWAY
);
76 STLDeleteElements(&data_to_resend_on_connect_
);
77 STLDeleteElements(&data_sent_before_handshake_
);
79 CleanUpUDPSocketImpl();
82 bool QuicClient::Initialize() {
83 QuicClientBase::Initialize();
85 // If an initial flow control window has not explicitly been set, then use the
86 // same values that Chrome uses.
87 const uint32 kSessionMaxRecvWindowSize
= 15 * 1024 * 1024; // 15 MB
88 const uint32 kStreamMaxRecvWindowSize
= 6 * 1024 * 1024; // 6 MB
89 if (config()->GetInitialStreamFlowControlWindowToSend() ==
90 kMinimumFlowControlSendWindow
) {
91 config()->SetInitialStreamFlowControlWindowToSend(kStreamMaxRecvWindowSize
);
93 if (config()->GetInitialSessionFlowControlWindowToSend() ==
94 kMinimumFlowControlSendWindow
) {
95 config()->SetInitialSessionFlowControlWindowToSend(
96 kSessionMaxRecvWindowSize
);
99 epoll_server_
->set_timeout_in_us(50 * 1000);
101 if (!CreateUDPSocket()) {
105 epoll_server_
->RegisterFD(fd_
, this, kEpollFlags
);
110 QuicClient::QuicDataToResend::QuicDataToResend(BalsaHeaders
* headers
,
113 : headers_(headers
), body_(body
), fin_(fin
) {}
115 QuicClient::QuicDataToResend::~QuicDataToResend() {
121 bool QuicClient::CreateUDPSocket() {
122 int address_family
= server_address_
.GetSockAddrFamily();
123 fd_
= socket(address_family
, SOCK_DGRAM
| SOCK_NONBLOCK
, IPPROTO_UDP
);
125 LOG(ERROR
) << "CreateSocket() failed: " << strerror(errno
);
129 int get_overflow
= 1;
130 int rc
= setsockopt(fd_
, SOL_SOCKET
, SO_RXQ_OVFL
, &get_overflow
,
131 sizeof(get_overflow
));
133 DLOG(WARNING
) << "Socket overflow detection not supported";
135 overflow_supported_
= true;
138 if (!QuicSocketUtils::SetReceiveBufferSize(fd_
,
139 kDefaultSocketReceiveBuffer
)) {
143 if (!QuicSocketUtils::SetSendBufferSize(fd_
, kDefaultSocketReceiveBuffer
)) {
147 rc
= QuicSocketUtils::SetGetAddressInfo(fd_
, address_family
);
149 LOG(ERROR
) << "IP detection not supported" << strerror(errno
);
153 if (bind_to_address_
.size() != 0) {
154 client_address_
= IPEndPoint(bind_to_address_
, local_port_
);
155 } else if (address_family
== AF_INET
) {
156 IPAddressNumber any4
;
157 CHECK(net::ParseIPLiteralToNumber("0.0.0.0", &any4
));
158 client_address_
= IPEndPoint(any4
, local_port_
);
160 IPAddressNumber any6
;
161 CHECK(net::ParseIPLiteralToNumber("::", &any6
));
162 client_address_
= IPEndPoint(any6
, local_port_
);
165 sockaddr_storage raw_addr
;
166 socklen_t raw_addr_len
= sizeof(raw_addr
);
167 CHECK(client_address_
.ToSockAddr(reinterpret_cast<sockaddr
*>(&raw_addr
),
170 reinterpret_cast<const sockaddr
*>(&raw_addr
),
173 LOG(ERROR
) << "Bind failed: " << strerror(errno
);
177 SockaddrStorage storage
;
178 if (getsockname(fd_
, storage
.addr
, &storage
.addr_len
) != 0 ||
179 !client_address_
.FromSockAddr(storage
.addr
, storage
.addr_len
)) {
180 LOG(ERROR
) << "Unable to get self address. Error: " << strerror(errno
);
186 bool QuicClient::Connect() {
187 // Attempt multiple connects until the maximum number of client hellos have
189 while (!connected() &&
190 GetNumSentClientHellos() <= QuicCryptoClientStream::kMaxClientHellos
) {
192 while (EncryptionBeingEstablished()) {
195 if (FLAGS_enable_quic_stateless_reject_support
&& connected() &&
196 !data_to_resend_on_connect_
.empty()) {
197 // A connection has been established and there was previously queued data
198 // to resend. Resend it and empty the queue.
199 for (QuicDataToResend
* data
: data_to_resend_on_connect_
) {
202 STLDeleteElements(&data_to_resend_on_connect_
);
204 if (session() != nullptr &&
205 session()->error() != QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT
) {
206 // We've successfully created a session but we're not connected, and there
207 // is no stateless reject to recover from. Give up trying.
212 GetNumSentClientHellos() > QuicCryptoClientStream::kMaxClientHellos
&&
213 session() != nullptr &&
214 session()->error() == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT
) {
215 // The overall connection failed due too many stateless rejects.
216 set_connection_error(QUIC_CRYPTO_TOO_MANY_REJECTS
);
218 return session()->connection()->connected();
221 void QuicClient::StartConnect() {
222 DCHECK(initialized_
);
223 DCHECK(!connected());
225 QuicPacketWriter
* writer
= CreateQuicPacketWriter();
227 DummyPacketWriterFactory
factory(writer
);
229 if (connected_or_attempting_connect()) {
230 // Before we destroy the last session and create a new one, gather its stats
231 // and update the stats for the overall connection.
233 if (session()->error() == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT
) {
234 // If the last error was due to a stateless reject, queue up the data to
235 // be resent on the next successful connection.
236 // TODO(jokulik): I'm a little bit concerned about ordering here. Maybe
237 // we should just maintain one queue?
238 DCHECK(data_to_resend_on_connect_
.empty());
239 data_to_resend_on_connect_
.swap(data_sent_before_handshake_
);
243 CreateQuicClientSession(new QuicConnection(
244 GetNextConnectionId(), server_address_
, helper_
.get(), factory
,
245 /* owns_writer= */ false, Perspective::IS_CLIENT
, server_id().is_https(),
246 supported_versions()));
248 // Reset |writer_| after |session()| so that the old writer outlives the old
251 session()->Initialize();
252 session()->CryptoConnect();
253 set_connected_or_attempting_connect(true);
256 void QuicClient::Disconnect() {
257 DCHECK(initialized_
);
260 session()->connection()->SendConnectionClose(QUIC_PEER_GOING_AWAY
);
262 STLDeleteElements(&data_to_resend_on_connect_
);
263 STLDeleteElements(&data_sent_before_handshake_
);
267 initialized_
= false;
270 void QuicClient::CleanUpUDPSocket() {
271 CleanUpUDPSocketImpl();
274 void QuicClient::CleanUpUDPSocketImpl() {
276 epoll_server_
->UnregisterFD(fd_
);
283 void QuicClient::SendRequest(const BalsaHeaders
& headers
,
286 QuicSpdyClientStream
* stream
= CreateReliableClientStream();
287 if (stream
== nullptr) {
288 LOG(DFATAL
) << "stream creation failed!";
291 stream
->set_visitor(this);
293 SpdyBalsaUtils::RequestHeadersToSpdyHeaders(headers
, stream
->version()),
295 if (FLAGS_enable_quic_stateless_reject_support
) {
296 // Record this in case we need to resend.
297 auto new_headers
= new BalsaHeaders
;
298 new_headers
->CopyFrom(headers
);
299 auto data_to_resend
=
300 new ClientQuicDataToResend(new_headers
, body
, fin
, this);
301 MaybeAddQuicDataToResend(data_to_resend
);
305 void QuicClient::MaybeAddQuicDataToResend(QuicDataToResend
* data_to_resend
) {
306 DCHECK(FLAGS_enable_quic_stateless_reject_support
);
307 if (session()->IsCryptoHandshakeConfirmed()) {
308 // The handshake is confirmed. No need to continue saving requests to
310 STLDeleteElements(&data_sent_before_handshake_
);
311 delete data_to_resend
;
315 // The handshake is not confirmed. Push the data onto the queue of data to
316 // resend if statelessly rejected.
317 data_sent_before_handshake_
.push_back(data_to_resend
);
320 void QuicClient::SendRequestAndWaitForResponse(
321 const BalsaHeaders
& headers
,
324 SendRequest(headers
, body
, fin
);
325 while (WaitForEvents()) {}
328 void QuicClient::SendRequestsAndWaitForResponse(
329 const vector
<string
>& url_list
) {
330 for (size_t i
= 0; i
< url_list
.size(); ++i
) {
331 BalsaHeaders headers
;
332 headers
.SetRequestFirstlineFromStringPieces("GET", url_list
[i
], "HTTP/1.1");
333 SendRequest(headers
, "", true);
335 while (WaitForEvents()) {}
338 bool QuicClient::WaitForEvents() {
341 epoll_server_
->WaitForEventsAndExecuteCallbacks();
343 DCHECK(session() != nullptr);
345 session()->error() == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT
) {
346 DCHECK(FLAGS_enable_quic_stateless_reject_support
);
347 DVLOG(1) << "Detected stateless reject while waiting for events. "
348 << "Attempting to reconnect.";
352 return session()->num_active_requests() != 0;
355 bool QuicClient::MigrateSocket(const IPAddressNumber
& new_host
) {
362 bind_to_address_
= new_host
;
363 if (!CreateUDPSocket()) {
367 epoll_server_
->RegisterFD(fd_
, this, kEpollFlags
);
368 session()->connection()->SetSelfAddress(client_address_
);
370 QuicPacketWriter
* writer
= CreateQuicPacketWriter();
371 DummyPacketWriterFactory
factory(writer
);
373 session()->connection()->SetQuicPacketWriter(writer
, false);
378 void QuicClient::OnEvent(int fd
, EpollEvent
* event
) {
381 if (event
->in_events
& EPOLLIN
) {
382 while (connected() && ReadAndProcessPacket()) {
385 if (connected() && (event
->in_events
& EPOLLOUT
)) {
386 writer()->SetWritable();
387 session()->connection()->OnCanWrite();
389 if (event
->in_events
& EPOLLERR
) {
390 DVLOG(1) << "Epollerr";
394 void QuicClient::OnClose(QuicDataStream
* stream
) {
395 DCHECK(stream
!= nullptr);
396 QuicSpdyClientStream
* client_stream
=
397 static_cast<QuicSpdyClientStream
*>(stream
);
398 BalsaHeaders headers
;
399 SpdyBalsaUtils::SpdyHeadersToResponseHeaders(client_stream
->headers(),
400 &headers
, stream
->version());
402 if (response_listener_
.get() != nullptr) {
403 response_listener_
->OnCompleteResponse(
404 stream
->id(), headers
, client_stream
->data());
407 // Store response headers and body.
408 if (store_response_
) {
409 latest_response_code_
= headers
.parsed_response_code();
410 headers
.DumpHeadersToString(&latest_response_headers_
);
411 latest_response_body_
= client_stream
->data();
415 size_t QuicClient::latest_response_code() const {
416 LOG_IF(DFATAL
, !store_response_
) << "Response not stored!";
417 return latest_response_code_
;
420 const string
& QuicClient::latest_response_headers() const {
421 LOG_IF(DFATAL
, !store_response_
) << "Response not stored!";
422 return latest_response_headers_
;
425 const string
& QuicClient::latest_response_body() const {
426 LOG_IF(DFATAL
, !store_response_
) << "Response not stored!";
427 return latest_response_body_
;
430 QuicEpollConnectionHelper
* QuicClient::CreateQuicConnectionHelper() {
431 return new QuicEpollConnectionHelper(epoll_server_
);
434 QuicPacketWriter
* QuicClient::CreateQuicPacketWriter() {
435 return new QuicDefaultPacketWriter(fd_
);
438 int QuicClient::ReadPacket(char* buffer
,
440 IPEndPoint
* server_address
,
441 IPAddressNumber
* client_ip
) {
442 return QuicSocketUtils::ReadPacket(
443 fd_
, buffer
, buffer_len
,
444 overflow_supported_
? &packets_dropped_
: nullptr, client_ip
,
448 bool QuicClient::ReadAndProcessPacket() {
449 // Allocate some extra space so we can send an error if the server goes over
451 char buf
[2 * kMaxPacketSize
];
453 IPEndPoint server_address
;
454 IPAddressNumber client_ip
;
456 int bytes_read
= ReadPacket(buf
, arraysize(buf
), &server_address
, &client_ip
);
458 if (bytes_read
< 0) {
462 QuicEncryptedPacket
packet(buf
, bytes_read
, false);
464 IPEndPoint
client_address(client_ip
, client_address_
.port());
465 session()->connection()->ProcessUdpPacket(client_address
, server_address
,