1 // Copyright 2013 The Chromium Authors
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/socket/tcp_client_socket.h"
6
7 #include <memory>
8 #include <utility>
9
10 #include "base/check_op.h"
11 #include "base/functional/bind.h"
12 #include "base/functional/callback_helpers.h"
13 #include "base/memory/ptr_util.h"
14 #include "base/metrics/histogram_macros.h"
15 #include "base/notreached.h"
16 #include "base/time/time.h"
17 #include "net/base/features.h"
18 #include "net/base/io_buffer.h"
19 #include "net/base/ip_endpoint.h"
20 #include "net/base/net_errors.h"
21 #include "net/nqe/network_quality_estimator.h"
22 #include "net/socket/socket_performance_watcher.h"
23 #include "net/traffic_annotation/network_traffic_annotation.h"
24
25 #if defined(TCP_CLIENT_SOCKET_OBSERVES_SUSPEND)
26 #include "base/power_monitor/power_monitor.h"
27 #endif
28
29 namespace net {
30
31 class NetLogWithSource;
32
TCPClientSocket(const AddressList & addresses,std::unique_ptr<SocketPerformanceWatcher> socket_performance_watcher,NetworkQualityEstimator * network_quality_estimator,net::NetLog * net_log,const net::NetLogSource & source,handles::NetworkHandle network)33 TCPClientSocket::TCPClientSocket(
34 const AddressList& addresses,
35 std::unique_ptr<SocketPerformanceWatcher> socket_performance_watcher,
36 NetworkQualityEstimator* network_quality_estimator,
37 net::NetLog* net_log,
38 const net::NetLogSource& source,
39 handles::NetworkHandle network)
40 : TCPClientSocket(
41 std::make_unique<TCPSocket>(std::move(socket_performance_watcher),
42 net_log,
43 source),
44 addresses,
45 -1 /* current_address_index */,
46 nullptr /* bind_address */,
47 network_quality_estimator,
48 network) {}
49
TCPClientSocket(std::unique_ptr<TCPSocket> connected_socket,const IPEndPoint & peer_address)50 TCPClientSocket::TCPClientSocket(std::unique_ptr<TCPSocket> connected_socket,
51 const IPEndPoint& peer_address)
52 : TCPClientSocket(std::move(connected_socket),
53 AddressList(peer_address),
54 0 /* current_address_index */,
55 nullptr /* bind_address */,
56 // TODO(https://crbug.com/1123197: Pass non-null
57 // NetworkQualityEstimator
58 nullptr /* network_quality_estimator */,
59 handles::kInvalidNetworkHandle) {}
60
TCPClientSocket(std::unique_ptr<TCPSocket> unconnected_socket,const AddressList & addresses,NetworkQualityEstimator * network_quality_estimator)61 TCPClientSocket::TCPClientSocket(
62 std::unique_ptr<TCPSocket> unconnected_socket,
63 const AddressList& addresses,
64 NetworkQualityEstimator* network_quality_estimator)
65 : TCPClientSocket(std::move(unconnected_socket),
66 addresses,
67 -1 /* current_address_index */,
68 nullptr /* bind_address */,
69 network_quality_estimator,
70 handles::kInvalidNetworkHandle) {}
71
~TCPClientSocket()72 TCPClientSocket::~TCPClientSocket() {
73 Disconnect();
74 #if defined(TCP_CLIENT_SOCKET_OBSERVES_SUSPEND)
75 base::PowerMonitor::RemovePowerSuspendObserver(this);
76 #endif // defined(TCP_CLIENT_SOCKET_OBSERVES_SUSPEND)
77 }
78
CreateFromBoundSocket(std::unique_ptr<TCPSocket> bound_socket,const AddressList & addresses,const IPEndPoint & bound_address,NetworkQualityEstimator * network_quality_estimator)79 std::unique_ptr<TCPClientSocket> TCPClientSocket::CreateFromBoundSocket(
80 std::unique_ptr<TCPSocket> bound_socket,
81 const AddressList& addresses,
82 const IPEndPoint& bound_address,
83 NetworkQualityEstimator* network_quality_estimator) {
84 return base::WrapUnique(new TCPClientSocket(
85 std::move(bound_socket), addresses, -1 /* current_address_index */,
86 std::make_unique<IPEndPoint>(bound_address), network_quality_estimator,
87 handles::kInvalidNetworkHandle));
88 }
89
Bind(const IPEndPoint & address)90 int TCPClientSocket::Bind(const IPEndPoint& address) {
91 if (current_address_index_ >= 0 || bind_address_) {
92 // Cannot bind the socket if we are already connected or connecting.
93 NOTREACHED();
94 return ERR_UNEXPECTED;
95 }
96
97 int result = OK;
98 if (!socket_->IsValid()) {
99 result = OpenSocket(address.GetFamily());
100 if (result != OK)
101 return result;
102 }
103
104 result = socket_->Bind(address);
105 if (result != OK)
106 return result;
107
108 bind_address_ = std::make_unique<IPEndPoint>(address);
109 return OK;
110 }
111
SetKeepAlive(bool enable,int delay)112 bool TCPClientSocket::SetKeepAlive(bool enable, int delay) {
113 return socket_->SetKeepAlive(enable, delay);
114 }
115
SetNoDelay(bool no_delay)116 bool TCPClientSocket::SetNoDelay(bool no_delay) {
117 return socket_->SetNoDelay(no_delay);
118 }
119
SetBeforeConnectCallback(const BeforeConnectCallback & before_connect_callback)120 void TCPClientSocket::SetBeforeConnectCallback(
121 const BeforeConnectCallback& before_connect_callback) {
122 DCHECK_EQ(CONNECT_STATE_NONE, next_connect_state_);
123 before_connect_callback_ = before_connect_callback;
124 }
125
Connect(CompletionOnceCallback callback)126 int TCPClientSocket::Connect(CompletionOnceCallback callback) {
127 DCHECK(!callback.is_null());
128
129 // If connecting or already connected, then just return OK.
130 if (socket_->IsValid() && current_address_index_ >= 0)
131 return OK;
132
133 DCHECK(!read_callback_);
134 DCHECK(!write_callback_);
135
136 if (was_disconnected_on_suspend_) {
137 Disconnect();
138 was_disconnected_on_suspend_ = false;
139 }
140
141 socket_->StartLoggingMultipleConnectAttempts(addresses_);
142
143 // We will try to connect to each address in addresses_. Start with the
144 // first one in the list.
145 next_connect_state_ = CONNECT_STATE_CONNECT;
146 current_address_index_ = 0;
147
148 int rv = DoConnectLoop(OK);
149 if (rv == ERR_IO_PENDING) {
150 connect_callback_ = std::move(callback);
151 } else {
152 socket_->EndLoggingMultipleConnectAttempts(rv);
153 }
154
155 return rv;
156 }
157
TCPClientSocket(std::unique_ptr<TCPSocket> socket,const AddressList & addresses,int current_address_index,std::unique_ptr<IPEndPoint> bind_address,NetworkQualityEstimator * network_quality_estimator,handles::NetworkHandle network)158 TCPClientSocket::TCPClientSocket(
159 std::unique_ptr<TCPSocket> socket,
160 const AddressList& addresses,
161 int current_address_index,
162 std::unique_ptr<IPEndPoint> bind_address,
163 NetworkQualityEstimator* network_quality_estimator,
164 handles::NetworkHandle network)
165 : socket_(std::move(socket)),
166 bind_address_(std::move(bind_address)),
167 addresses_(addresses),
168 current_address_index_(current_address_index),
169 network_quality_estimator_(network_quality_estimator),
170 network_(network) {
171 DCHECK(socket_);
172 if (socket_->IsValid())
173 socket_->SetDefaultOptionsForClient();
174 #if defined(TCP_CLIENT_SOCKET_OBSERVES_SUSPEND)
175 base::PowerMonitor::AddPowerSuspendObserver(this);
176 #endif // defined(TCP_CLIENT_SOCKET_OBSERVES_SUSPEND)
177 }
178
ReadCommon(IOBuffer * buf,int buf_len,CompletionOnceCallback callback,bool read_if_ready)179 int TCPClientSocket::ReadCommon(IOBuffer* buf,
180 int buf_len,
181 CompletionOnceCallback callback,
182 bool read_if_ready) {
183 DCHECK(!callback.is_null());
184 DCHECK(read_callback_.is_null());
185
186 if (was_disconnected_on_suspend_)
187 return ERR_NETWORK_IO_SUSPENDED;
188
189 // |socket_| is owned by |this| and the callback won't be run once |socket_|
190 // is gone/closed. Therefore, it is safe to use base::Unretained() here.
191 CompletionOnceCallback complete_read_callback =
192 base::BindOnce(&TCPClientSocket::DidCompleteRead, base::Unretained(this));
193 int result =
194 read_if_ready
195 ? socket_->ReadIfReady(buf, buf_len,
196 std::move(complete_read_callback))
197 : socket_->Read(buf, buf_len, std::move(complete_read_callback));
198 if (result == ERR_IO_PENDING) {
199 read_callback_ = std::move(callback);
200 } else if (result > 0) {
201 was_ever_used_ = true;
202 total_received_bytes_ += result;
203 }
204
205 return result;
206 }
207
DoConnectLoop(int result)208 int TCPClientSocket::DoConnectLoop(int result) {
209 DCHECK_NE(next_connect_state_, CONNECT_STATE_NONE);
210
211 int rv = result;
212 do {
213 ConnectState state = next_connect_state_;
214 next_connect_state_ = CONNECT_STATE_NONE;
215 switch (state) {
216 case CONNECT_STATE_CONNECT:
217 DCHECK_EQ(OK, rv);
218 rv = DoConnect();
219 break;
220 case CONNECT_STATE_CONNECT_COMPLETE:
221 rv = DoConnectComplete(rv);
222 break;
223 default:
224 NOTREACHED() << "bad state " << state;
225 rv = ERR_UNEXPECTED;
226 break;
227 }
228 } while (rv != ERR_IO_PENDING && next_connect_state_ != CONNECT_STATE_NONE);
229
230 return rv;
231 }
232
DoConnect()233 int TCPClientSocket::DoConnect() {
234 DCHECK_GE(current_address_index_, 0);
235 DCHECK_LT(current_address_index_, static_cast<int>(addresses_.size()));
236
237 const IPEndPoint& endpoint = addresses_[current_address_index_];
238
239 if (previously_disconnected_) {
240 was_ever_used_ = false;
241 previously_disconnected_ = false;
242 }
243
244 next_connect_state_ = CONNECT_STATE_CONNECT_COMPLETE;
245
246 if (!socket_->IsValid()) {
247 int result = OpenSocket(endpoint.GetFamily());
248 if (result != OK)
249 return result;
250
251 if (bind_address_) {
252 result = socket_->Bind(*bind_address_);
253 if (result != OK) {
254 socket_->Close();
255 return result;
256 }
257 }
258 }
259
260 if (before_connect_callback_) {
261 int result = before_connect_callback_.Run();
262 DCHECK_NE(ERR_IO_PENDING, result);
263 if (result != net::OK)
264 return result;
265 }
266
267 // Notify |socket_performance_watcher_| only if the |socket_| is reused to
268 // connect to a different IP Address.
269 if (socket_->socket_performance_watcher() && current_address_index_ != 0)
270 socket_->socket_performance_watcher()->OnConnectionChanged();
271
272 start_connect_attempt_ = base::TimeTicks::Now();
273
274 // Start a timer to fail the connect attempt if it takes too long.
275 base::TimeDelta attempt_timeout = GetConnectAttemptTimeout();
276 if (!attempt_timeout.is_max()) {
277 DCHECK(!connect_attempt_timer_.IsRunning());
278 connect_attempt_timer_.Start(
279 FROM_HERE, attempt_timeout,
280 base::BindOnce(&TCPClientSocket::OnConnectAttemptTimeout,
281 base::Unretained(this)));
282 }
283
284 return ConnectInternal(endpoint);
285 }
286
DoConnectComplete(int result)287 int TCPClientSocket::DoConnectComplete(int result) {
288 if (start_connect_attempt_) {
289 EmitConnectAttemptHistograms(result);
290 start_connect_attempt_ = absl::nullopt;
291 connect_attempt_timer_.Stop();
292 }
293
294 if (result == OK)
295 return OK; // Done!
296
297 // Don't try the next address if entering suspend mode.
298 if (result == ERR_NETWORK_IO_SUSPENDED)
299 return result;
300
301 // Close whatever partially connected socket we currently have.
302 DoDisconnect();
303
304 // Try to fall back to the next address in the list.
305 if (current_address_index_ + 1 < static_cast<int>(addresses_.size())) {
306 next_connect_state_ = CONNECT_STATE_CONNECT;
307 ++current_address_index_;
308 return OK;
309 }
310
311 // Otherwise there is nothing to fall back to, so give up.
312 return result;
313 }
314
OnConnectAttemptTimeout()315 void TCPClientSocket::OnConnectAttemptTimeout() {
316 DidCompleteConnect(ERR_TIMED_OUT);
317 }
318
ConnectInternal(const IPEndPoint & endpoint)319 int TCPClientSocket::ConnectInternal(const IPEndPoint& endpoint) {
320 // |socket_| is owned by this class and the callback won't be run once
321 // |socket_| is gone. Therefore, it is safe to use base::Unretained() here.
322 return socket_->Connect(endpoint,
323 base::BindOnce(&TCPClientSocket::DidCompleteConnect,
324 base::Unretained(this)));
325 }
326
Disconnect()327 void TCPClientSocket::Disconnect() {
328 DoDisconnect();
329 current_address_index_ = -1;
330 bind_address_.reset();
331
332 // Cancel any pending callbacks. Not done in DoDisconnect() because that's
333 // called on connection failure, when the connect callback will need to be
334 // invoked.
335 was_disconnected_on_suspend_ = false;
336 connect_callback_.Reset();
337 read_callback_.Reset();
338 write_callback_.Reset();
339 }
340
DoDisconnect()341 void TCPClientSocket::DoDisconnect() {
342 if (start_connect_attempt_) {
343 EmitConnectAttemptHistograms(ERR_ABORTED);
344 start_connect_attempt_ = absl::nullopt;
345 connect_attempt_timer_.Stop();
346 }
347
348 total_received_bytes_ = 0;
349 EmitTCPMetricsHistogramsOnDisconnect();
350
351 // If connecting or already connected, record that the socket has been
352 // disconnected.
353 previously_disconnected_ = socket_->IsValid() && current_address_index_ >= 0;
354 socket_->Close();
355
356 // Invalidate weak pointers, so if in the middle of a callback in OnSuspend,
357 // and something destroys this, no other callback is invoked.
358 weak_ptr_factory_.InvalidateWeakPtrs();
359 }
360
IsConnected() const361 bool TCPClientSocket::IsConnected() const {
362 return socket_->IsConnected();
363 }
364
IsConnectedAndIdle() const365 bool TCPClientSocket::IsConnectedAndIdle() const {
366 return socket_->IsConnectedAndIdle();
367 }
368
GetPeerAddress(IPEndPoint * address) const369 int TCPClientSocket::GetPeerAddress(IPEndPoint* address) const {
370 return socket_->GetPeerAddress(address);
371 }
372
GetLocalAddress(IPEndPoint * address) const373 int TCPClientSocket::GetLocalAddress(IPEndPoint* address) const {
374 DCHECK(address);
375
376 if (!socket_->IsValid()) {
377 if (bind_address_) {
378 *address = *bind_address_;
379 return OK;
380 }
381 return ERR_SOCKET_NOT_CONNECTED;
382 }
383
384 return socket_->GetLocalAddress(address);
385 }
386
NetLog() const387 const NetLogWithSource& TCPClientSocket::NetLog() const {
388 return socket_->net_log();
389 }
390
WasEverUsed() const391 bool TCPClientSocket::WasEverUsed() const {
392 return was_ever_used_;
393 }
394
WasAlpnNegotiated() const395 bool TCPClientSocket::WasAlpnNegotiated() const {
396 return false;
397 }
398
GetNegotiatedProtocol() const399 NextProto TCPClientSocket::GetNegotiatedProtocol() const {
400 return kProtoUnknown;
401 }
402
GetSSLInfo(SSLInfo * ssl_info)403 bool TCPClientSocket::GetSSLInfo(SSLInfo* ssl_info) {
404 return false;
405 }
406
Read(IOBuffer * buf,int buf_len,CompletionOnceCallback callback)407 int TCPClientSocket::Read(IOBuffer* buf,
408 int buf_len,
409 CompletionOnceCallback callback) {
410 return ReadCommon(buf, buf_len, std::move(callback), /*read_if_ready=*/false);
411 }
412
ReadIfReady(IOBuffer * buf,int buf_len,CompletionOnceCallback callback)413 int TCPClientSocket::ReadIfReady(IOBuffer* buf,
414 int buf_len,
415 CompletionOnceCallback callback) {
416 return ReadCommon(buf, buf_len, std::move(callback), /*read_if_ready=*/true);
417 }
418
CancelReadIfReady()419 int TCPClientSocket::CancelReadIfReady() {
420 DCHECK(read_callback_);
421 read_callback_.Reset();
422 return socket_->CancelReadIfReady();
423 }
424
Write(IOBuffer * buf,int buf_len,CompletionOnceCallback callback,const NetworkTrafficAnnotationTag & traffic_annotation)425 int TCPClientSocket::Write(
426 IOBuffer* buf,
427 int buf_len,
428 CompletionOnceCallback callback,
429 const NetworkTrafficAnnotationTag& traffic_annotation) {
430 DCHECK(!callback.is_null());
431 DCHECK(write_callback_.is_null());
432
433 if (was_disconnected_on_suspend_)
434 return ERR_NETWORK_IO_SUSPENDED;
435
436 // |socket_| is owned by this class and the callback won't be run once
437 // |socket_| is gone. Therefore, it is safe to use base::Unretained() here.
438 CompletionOnceCallback complete_write_callback = base::BindOnce(
439 &TCPClientSocket::DidCompleteWrite, base::Unretained(this));
440 int result = socket_->Write(buf, buf_len, std::move(complete_write_callback),
441 traffic_annotation);
442 if (result == ERR_IO_PENDING) {
443 write_callback_ = std::move(callback);
444 } else if (result > 0) {
445 was_ever_used_ = true;
446 }
447
448 return result;
449 }
450
SetReceiveBufferSize(int32_t size)451 int TCPClientSocket::SetReceiveBufferSize(int32_t size) {
452 return socket_->SetReceiveBufferSize(size);
453 }
454
SetSendBufferSize(int32_t size)455 int TCPClientSocket::SetSendBufferSize(int32_t size) {
456 return socket_->SetSendBufferSize(size);
457 }
458
SocketDescriptorForTesting() const459 SocketDescriptor TCPClientSocket::SocketDescriptorForTesting() const {
460 return socket_->SocketDescriptorForTesting();
461 }
462
GetTotalReceivedBytes() const463 int64_t TCPClientSocket::GetTotalReceivedBytes() const {
464 return total_received_bytes_;
465 }
466
ApplySocketTag(const SocketTag & tag)467 void TCPClientSocket::ApplySocketTag(const SocketTag& tag) {
468 socket_->ApplySocketTag(tag);
469 }
470
OnSuspend()471 void TCPClientSocket::OnSuspend() {
472 #if defined(TCP_CLIENT_SOCKET_OBSERVES_SUSPEND)
473 // If the socket is connected, or connecting, act as if current and future
474 // operations on the socket fail with ERR_NETWORK_IO_SUSPENDED, until the
475 // socket is reconnected.
476
477 if (next_connect_state_ != CONNECT_STATE_NONE) {
478 socket_->Close();
479 DidCompleteConnect(ERR_NETWORK_IO_SUSPENDED);
480 return;
481 }
482
483 // Nothing to do. Use IsValid() rather than IsConnected() because it results
484 // in more testable code, as when calling OnSuspend mode on two sockets
485 // connected to each other will otherwise cause two sockets to behave
486 // differently from each other.
487 if (!socket_->IsValid())
488 return;
489
490 // Use Close() rather than Disconnect() / DoDisconnect() to avoid mutating
491 // state, which more closely matches normal read/write error behavior.
492 socket_->Close();
493
494 was_disconnected_on_suspend_ = true;
495
496 // Grab a weak pointer just in case calling read callback results in |this|
497 // being destroyed, or disconnected. In either case, should not run the write
498 // callback.
499 base::WeakPtr<TCPClientSocket> weak_this = weak_ptr_factory_.GetWeakPtr();
500
501 // Have to grab the write callback now, as it's theoretically possible for the
502 // read callback to reconnects the socket, that reconnection to complete
503 // synchronously, and then for it to start a new write. That also means this
504 // code can't use DidCompleteWrite().
505 CompletionOnceCallback write_callback = std::move(write_callback_);
506 if (read_callback_)
507 DidCompleteRead(ERR_NETWORK_IO_SUSPENDED);
508 if (weak_this && write_callback)
509 std::move(write_callback).Run(ERR_NETWORK_IO_SUSPENDED);
510 #endif // defined(TCP_CLIENT_SOCKET_OBSERVES_SUSPEND)
511 }
512
DidCompleteConnect(int result)513 void TCPClientSocket::DidCompleteConnect(int result) {
514 DCHECK_EQ(next_connect_state_, CONNECT_STATE_CONNECT_COMPLETE);
515 DCHECK_NE(result, ERR_IO_PENDING);
516 DCHECK(!connect_callback_.is_null());
517
518 result = DoConnectLoop(result);
519 if (result != ERR_IO_PENDING) {
520 socket_->EndLoggingMultipleConnectAttempts(result);
521 std::move(connect_callback_).Run(result);
522 }
523 }
524
DidCompleteRead(int result)525 void TCPClientSocket::DidCompleteRead(int result) {
526 DCHECK(!read_callback_.is_null());
527
528 if (result > 0)
529 total_received_bytes_ += result;
530 DidCompleteReadWrite(std::move(read_callback_), result);
531 }
532
DidCompleteWrite(int result)533 void TCPClientSocket::DidCompleteWrite(int result) {
534 DCHECK(!write_callback_.is_null());
535
536 DidCompleteReadWrite(std::move(write_callback_), result);
537 }
538
DidCompleteReadWrite(CompletionOnceCallback callback,int result)539 void TCPClientSocket::DidCompleteReadWrite(CompletionOnceCallback callback,
540 int result) {
541 if (result > 0)
542 was_ever_used_ = true;
543 std::move(callback).Run(result);
544 }
545
OpenSocket(AddressFamily family)546 int TCPClientSocket::OpenSocket(AddressFamily family) {
547 DCHECK(!socket_->IsValid());
548
549 int result = socket_->Open(family);
550 if (result != OK)
551 return result;
552
553 if (network_ != handles::kInvalidNetworkHandle) {
554 result = socket_->BindToNetwork(network_);
555 if (result != OK) {
556 socket_->Close();
557 return result;
558 }
559 }
560
561 socket_->SetDefaultOptionsForClient();
562
563 return OK;
564 }
565
EmitTCPMetricsHistogramsOnDisconnect()566 void TCPClientSocket::EmitTCPMetricsHistogramsOnDisconnect() {
567 base::TimeDelta rtt;
568 if (socket_->GetEstimatedRoundTripTime(&rtt)) {
569 UMA_HISTOGRAM_CUSTOM_TIMES("Net.TcpRtt.AtDisconnect", rtt,
570 base::Milliseconds(1), base::Minutes(10), 100);
571 }
572 }
573
EmitConnectAttemptHistograms(int result)574 void TCPClientSocket::EmitConnectAttemptHistograms(int result) {
575 // This should only be called in response to completing a connect attempt.
576 DCHECK(start_connect_attempt_);
577
578 base::TimeDelta duration =
579 base::TimeTicks::Now() - start_connect_attempt_.value();
580
581 // Histogram the total time the connect attempt took, grouped by success and
582 // failure. Note that failures also include cases when the connect attempt
583 // was cancelled by the client before the handshake completed.
584 if (result == OK) {
585 UMA_HISTOGRAM_MEDIUM_TIMES("Net.TcpConnectAttempt.Latency.Success",
586 duration);
587 } else {
588 UMA_HISTOGRAM_MEDIUM_TIMES("Net.TcpConnectAttempt.Latency.Error", duration);
589 }
590 }
591
GetConnectAttemptTimeout()592 base::TimeDelta TCPClientSocket::GetConnectAttemptTimeout() {
593 if (!base::FeatureList::IsEnabled(features::kTimeoutTcpConnectAttempt))
594 return base::TimeDelta::Max();
595
596 absl::optional<base::TimeDelta> transport_rtt = absl::nullopt;
597 if (network_quality_estimator_)
598 transport_rtt = network_quality_estimator_->GetTransportRTT();
599
600 base::TimeDelta min_timeout = features::kTimeoutTcpConnectAttemptMin.Get();
601 base::TimeDelta max_timeout = features::kTimeoutTcpConnectAttemptMax.Get();
602
603 if (!transport_rtt)
604 return max_timeout;
605
606 base::TimeDelta adaptive_timeout =
607 transport_rtt.value() *
608 features::kTimeoutTcpConnectAttemptRTTMultiplier.Get();
609
610 if (adaptive_timeout <= min_timeout)
611 return min_timeout;
612
613 if (adaptive_timeout >= max_timeout)
614 return max_timeout;
615
616 return adaptive_timeout;
617 }
618
619 } // namespace net
620