• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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