1 // Copyright (c) 2015 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 #include "net/third_party/quiche/src/quic/tools/quic_client_base.h"
6
7 #include "net/third_party/quiche/src/quic/core/crypto/quic_random.h"
8 #include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
9 #include "net/third_party/quiche/src/quic/core/quic_server_id.h"
10 #include "net/third_party/quiche/src/quic/core/quic_utils.h"
11 #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
12 #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
13
14 namespace quic {
15
16 QuicClientBase::NetworkHelper::~NetworkHelper() = default;
17
QuicClientBase(const QuicServerId & server_id,const ParsedQuicVersionVector & supported_versions,const QuicConfig & config,QuicConnectionHelperInterface * helper,QuicAlarmFactory * alarm_factory,std::unique_ptr<NetworkHelper> network_helper,std::unique_ptr<ProofVerifier> proof_verifier,std::unique_ptr<SessionCache> session_cache)18 QuicClientBase::QuicClientBase(
19 const QuicServerId& server_id,
20 const ParsedQuicVersionVector& supported_versions,
21 const QuicConfig& config,
22 QuicConnectionHelperInterface* helper,
23 QuicAlarmFactory* alarm_factory,
24 std::unique_ptr<NetworkHelper> network_helper,
25 std::unique_ptr<ProofVerifier> proof_verifier,
26 std::unique_ptr<SessionCache> session_cache)
27 : server_id_(server_id),
28 initialized_(false),
29 local_port_(0),
30 config_(config),
31 crypto_config_(std::move(proof_verifier), std::move(session_cache)),
32 helper_(helper),
33 alarm_factory_(alarm_factory),
34 supported_versions_(supported_versions),
35 initial_max_packet_length_(0),
36 num_sent_client_hellos_(0),
37 connection_error_(QUIC_NO_ERROR),
38 connected_or_attempting_connect_(false),
39 network_helper_(std::move(network_helper)),
40 connection_debug_visitor_(nullptr) {}
41
42 QuicClientBase::~QuicClientBase() = default;
43
Initialize()44 bool QuicClientBase::Initialize() {
45 num_sent_client_hellos_ = 0;
46 connection_error_ = QUIC_NO_ERROR;
47 connected_or_attempting_connect_ = false;
48
49 // If an initial flow control window has not explicitly been set, then use the
50 // same values that Chrome uses.
51 const uint32_t kSessionMaxRecvWindowSize = 15 * 1024 * 1024; // 15 MB
52 const uint32_t kStreamMaxRecvWindowSize = 6 * 1024 * 1024; // 6 MB
53 if (config()->GetInitialStreamFlowControlWindowToSend() ==
54 kDefaultFlowControlSendWindow) {
55 config()->SetInitialStreamFlowControlWindowToSend(kStreamMaxRecvWindowSize);
56 }
57 if (config()->GetInitialSessionFlowControlWindowToSend() ==
58 kDefaultFlowControlSendWindow) {
59 config()->SetInitialSessionFlowControlWindowToSend(
60 kSessionMaxRecvWindowSize);
61 }
62
63 if (!network_helper_->CreateUDPSocketAndBind(server_address_,
64 bind_to_address_, local_port_)) {
65 return false;
66 }
67
68 initialized_ = true;
69 return true;
70 }
71
Connect()72 bool QuicClientBase::Connect() {
73 // Attempt multiple connects until the maximum number of client hellos have
74 // been sent.
75 int num_attempts = 0;
76 while (!connected() &&
77 num_attempts <= QuicCryptoClientStream::kMaxClientHellos) {
78 StartConnect();
79 while (EncryptionBeingEstablished()) {
80 WaitForEvents();
81 }
82 ParsedQuicVersion version = UnsupportedQuicVersion();
83 if (session() != nullptr && !CanReconnectWithDifferentVersion(&version)) {
84 // We've successfully created a session but we're not connected, and we
85 // cannot reconnect with a different version. Give up trying.
86 break;
87 }
88 num_attempts++;
89 }
90 return session()->connection()->connected();
91 }
92
StartConnect()93 void QuicClientBase::StartConnect() {
94 DCHECK(initialized_);
95 DCHECK(!connected());
96 QuicPacketWriter* writer = network_helper_->CreateQuicPacketWriter();
97 ParsedQuicVersion mutual_version = UnsupportedQuicVersion();
98 const bool can_reconnect_with_different_version =
99 CanReconnectWithDifferentVersion(&mutual_version);
100 if (connected_or_attempting_connect()) {
101 // Clear queued up data if client can not try to connect with a different
102 // version.
103 if (!can_reconnect_with_different_version) {
104 ClearDataToResend();
105 }
106 // Before we destroy the last session and create a new one, gather its stats
107 // and update the stats for the overall connection.
108 UpdateStats();
109 }
110
111 session_ = CreateQuicClientSession(
112 supported_versions(),
113 new QuicConnection(GetNextConnectionId(), server_address(), helper(),
114 alarm_factory(), writer,
115 /* owns_writer= */ false, Perspective::IS_CLIENT,
116 can_reconnect_with_different_version
117 ? ParsedQuicVersionVector{mutual_version}
118 : supported_versions()));
119 if (connection_debug_visitor_ != nullptr) {
120 session()->connection()->set_debug_visitor(connection_debug_visitor_);
121 }
122 session()->connection()->set_client_connection_id(GetClientConnectionId());
123 if (initial_max_packet_length_ != 0) {
124 session()->connection()->SetMaxPacketLength(initial_max_packet_length_);
125 }
126 // Reset |writer()| after |session()| so that the old writer outlives the old
127 // session.
128 set_writer(writer);
129 InitializeSession();
130 if (can_reconnect_with_different_version) {
131 // This is a reconnect using server supported |mutual_version|.
132 session()->connection()->SetVersionNegotiated();
133 }
134 set_connected_or_attempting_connect(true);
135 }
136
InitializeSession()137 void QuicClientBase::InitializeSession() {
138 session()->Initialize();
139 }
140
Disconnect()141 void QuicClientBase::Disconnect() {
142 DCHECK(initialized_);
143
144 initialized_ = false;
145 if (connected()) {
146 session()->connection()->CloseConnection(
147 QUIC_PEER_GOING_AWAY, "Client disconnecting",
148 ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
149 }
150
151 ClearDataToResend();
152
153 network_helper_->CleanUpAllUDPSockets();
154 }
155
proof_verifier() const156 ProofVerifier* QuicClientBase::proof_verifier() const {
157 return crypto_config_.proof_verifier();
158 }
159
EncryptionBeingEstablished()160 bool QuicClientBase::EncryptionBeingEstablished() {
161 return !session_->IsEncryptionEstablished() &&
162 session_->connection()->connected();
163 }
164
WaitForEvents()165 bool QuicClientBase::WaitForEvents() {
166 DCHECK(connected());
167
168 network_helper_->RunEventLoop();
169
170 DCHECK(session() != nullptr);
171 ParsedQuicVersion version = UnsupportedQuicVersion();
172 if (!connected() && CanReconnectWithDifferentVersion(&version)) {
173 QUIC_DLOG(INFO) << "Can reconnect with version: " << version
174 << ", attempting to reconnect.";
175
176 Connect();
177 }
178
179 return HasActiveRequests();
180 }
181
MigrateSocket(const QuicIpAddress & new_host)182 bool QuicClientBase::MigrateSocket(const QuicIpAddress& new_host) {
183 return MigrateSocketWithSpecifiedPort(new_host, local_port_);
184 }
185
MigrateSocketWithSpecifiedPort(const QuicIpAddress & new_host,int port)186 bool QuicClientBase::MigrateSocketWithSpecifiedPort(
187 const QuicIpAddress& new_host,
188 int port) {
189 if (!connected()) {
190 return false;
191 }
192
193 network_helper_->CleanUpAllUDPSockets();
194
195 set_bind_to_address(new_host);
196 if (!network_helper_->CreateUDPSocketAndBind(server_address_,
197 bind_to_address_, port)) {
198 return false;
199 }
200
201 session()->connection()->SetSelfAddress(
202 network_helper_->GetLatestClientAddress());
203
204 QuicPacketWriter* writer = network_helper_->CreateQuicPacketWriter();
205 set_writer(writer);
206 session()->connection()->SetQuicPacketWriter(writer, false);
207
208 return true;
209 }
210
ChangeEphemeralPort()211 bool QuicClientBase::ChangeEphemeralPort() {
212 auto current_host = network_helper_->GetLatestClientAddress().host();
213 return MigrateSocketWithSpecifiedPort(current_host, 0 /*any ephemeral port*/);
214 }
215
session()216 QuicSession* QuicClientBase::session() {
217 return session_.get();
218 }
219
network_helper()220 QuicClientBase::NetworkHelper* QuicClientBase::network_helper() {
221 return network_helper_.get();
222 }
223
network_helper() const224 const QuicClientBase::NetworkHelper* QuicClientBase::network_helper() const {
225 return network_helper_.get();
226 }
227
WaitForStreamToClose(QuicStreamId id)228 void QuicClientBase::WaitForStreamToClose(QuicStreamId id) {
229 DCHECK(connected());
230
231 while (connected() && !session_->IsClosedStream(id)) {
232 WaitForEvents();
233 }
234 }
235
WaitForCryptoHandshakeConfirmed()236 bool QuicClientBase::WaitForCryptoHandshakeConfirmed() {
237 DCHECK(connected());
238
239 while (connected() && !session_->OneRttKeysAvailable()) {
240 WaitForEvents();
241 }
242
243 // If the handshake fails due to a timeout, the connection will be closed.
244 QUIC_LOG_IF(ERROR, !connected()) << "Handshake with server failed.";
245 return connected();
246 }
247
connected() const248 bool QuicClientBase::connected() const {
249 return session_.get() && session_->connection() &&
250 session_->connection()->connected();
251 }
252
goaway_received() const253 bool QuicClientBase::goaway_received() const {
254 return session_ != nullptr && session_->goaway_received();
255 }
256
GetNumSentClientHellos()257 int QuicClientBase::GetNumSentClientHellos() {
258 // If we are not actively attempting to connect, the session object
259 // corresponds to the previous connection and should not be used.
260 const int current_session_hellos = !connected_or_attempting_connect_
261 ? 0
262 : GetNumSentClientHellosFromSession();
263 return num_sent_client_hellos_ + current_session_hellos;
264 }
265
UpdateStats()266 void QuicClientBase::UpdateStats() {
267 num_sent_client_hellos_ += GetNumSentClientHellosFromSession();
268 }
269
GetNumReceivedServerConfigUpdates()270 int QuicClientBase::GetNumReceivedServerConfigUpdates() {
271 // If we are not actively attempting to connect, the session object
272 // corresponds to the previous connection and should not be used.
273 return !connected_or_attempting_connect_
274 ? 0
275 : GetNumReceivedServerConfigUpdatesFromSession();
276 }
277
connection_error() const278 QuicErrorCode QuicClientBase::connection_error() const {
279 // Return the high-level error if there was one. Otherwise, return the
280 // connection error from the last session.
281 if (connection_error_ != QUIC_NO_ERROR) {
282 return connection_error_;
283 }
284 if (session_ == nullptr) {
285 return QUIC_NO_ERROR;
286 }
287 return session_->error();
288 }
289
GetNextConnectionId()290 QuicConnectionId QuicClientBase::GetNextConnectionId() {
291 QuicConnectionId server_designated_id = GetNextServerDesignatedConnectionId();
292 return !server_designated_id.IsEmpty() ? server_designated_id
293 : GenerateNewConnectionId();
294 }
295
GetNextServerDesignatedConnectionId()296 QuicConnectionId QuicClientBase::GetNextServerDesignatedConnectionId() {
297 QuicCryptoClientConfig::CachedState* cached =
298 crypto_config_.LookupOrCreate(server_id_);
299 // If the cached state indicates that we should use a server-designated
300 // connection ID, then return that connection ID.
301 CHECK(cached != nullptr) << "QuicClientCryptoConfig::LookupOrCreate returned "
302 << "unexpected nullptr.";
303 return cached->has_server_designated_connection_id()
304 ? cached->GetNextServerDesignatedConnectionId()
305 : EmptyQuicConnectionId();
306 }
307
GenerateNewConnectionId()308 QuicConnectionId QuicClientBase::GenerateNewConnectionId() {
309 return QuicUtils::CreateRandomConnectionId();
310 }
311
GetClientConnectionId()312 QuicConnectionId QuicClientBase::GetClientConnectionId() {
313 return EmptyQuicConnectionId();
314 }
315
CanReconnectWithDifferentVersion(ParsedQuicVersion * version) const316 bool QuicClientBase::CanReconnectWithDifferentVersion(
317 ParsedQuicVersion* version) const {
318 if (session_ == nullptr || session_->connection() == nullptr ||
319 session_->error() != QUIC_INVALID_VERSION ||
320 session_->connection()->server_supported_versions().empty()) {
321 return false;
322 }
323 for (const auto& client_version : supported_versions_) {
324 if (QuicContainsValue(session_->connection()->server_supported_versions(),
325 client_version)) {
326 *version = client_version;
327 return true;
328 }
329 }
330 return false;
331 }
332
333 } // namespace quic
334