Collect all ConnectionAttempts from both sockets in TransportConnectJob.

Before, the TransportConnectJob simply inferred that, if the main socket
failed to connect, the address it was using was the last address in the
list. With this change, the TCPClientSocket actually tracks all of the
connection attempts made (as it tries each address in the list), and the
TransportConnectJob copies the attempts from both the main and fallback
sockets and records all of them in the ClientSocketHandle in
GetAdditionalErrorState.

BUG=480565
TBR=jam

Review URL: https://codereview.chromium.org/1096203006

Cr-Commit-Position: refs/heads/master@{#330012}
diff --git a/chrome/browser/devtools/device/usb/android_usb_socket.cc b/chrome/browser/devtools/device/usb/android_usb_socket.cc
index fde3230..1310f6e 100644
--- a/chrome/browser/devtools/device/usb/android_usb_socket.cc
+++ b/chrome/browser/devtools/device/usb/android_usb_socket.cc
@@ -234,6 +234,11 @@
   return false;
 }
 
+void AndroidUsbSocket::GetConnectionAttempts(
+    net::ConnectionAttempts* out) const {
+  out->clear();
+}
+
 void AndroidUsbSocket::RespondToReader(bool disconnect) {
   if (read_callback_.is_null() || (read_buffer_.empty() && !disconnect))
     return;
diff --git a/chrome/browser/devtools/device/usb/android_usb_socket.h b/chrome/browser/devtools/device/usb/android_usb_socket.h
index 85ef112..fa22deb 100644
--- a/chrome/browser/devtools/device/usb/android_usb_socket.h
+++ b/chrome/browser/devtools/device/usb/android_usb_socket.h
@@ -52,6 +52,10 @@
   bool WasNpnNegotiated() const override;
   net::NextProto GetNegotiatedProtocol() const override;
   bool GetSSLInfo(net::SSLInfo* ssl_info) override;
+  void GetConnectionAttempts(net::ConnectionAttempts* out) const override;
+  void ClearConnectionAttempts() override {}
+  void AddConnectionAttempts(const net::ConnectionAttempts& attempts) override {
+  }
 
  private:
   void RespondToReader(bool disconnect);
diff --git a/chrome/browser/extensions/api/socket/tls_socket_unittest.cc b/chrome/browser/extensions/api/socket/tls_socket_unittest.cc
index adebfbf9..49edd64 100644
--- a/chrome/browser/extensions/api/socket/tls_socket_unittest.cc
+++ b/chrome/browser/extensions/api/socket/tls_socket_unittest.cc
@@ -53,6 +53,9 @@
   MOCK_CONST_METHOD0(WasEverUsed, bool());
   MOCK_CONST_METHOD0(UsingTCPFastOpen, bool());
   MOCK_METHOD1(GetSSLInfo, bool(net::SSLInfo*));
+  MOCK_CONST_METHOD1(GetConnectionAttempts, void(net::ConnectionAttempts*));
+  MOCK_METHOD0(ClearConnectionAttempts, void());
+  MOCK_METHOD1(AddConnectionAttempts, void(const net::ConnectionAttempts&));
   MOCK_METHOD5(ExportKeyingMaterial,
                int(const StringPiece&,
                    bool,
diff --git a/content/browser/renderer_host/p2p/socket_host_test_utils.cc b/content/browser/renderer_host/p2p/socket_host_test_utils.cc
index 12257b9b..ed07966 100644
--- a/content/browser/renderer_host/p2p/socket_host_test_utils.cc
+++ b/content/browser/renderer_host/p2p/socket_host_test_utils.cc
@@ -172,6 +172,10 @@
   return false;
 }
 
+void FakeSocket::GetConnectionAttempts(net::ConnectionAttempts* out) const {
+  out->clear();
+}
+
 void CreateRandomPacket(std::vector<char>* packet) {
   size_t size = kStunHeaderSize + rand() % 1000;
   packet->resize(size);
diff --git a/content/browser/renderer_host/p2p/socket_host_test_utils.h b/content/browser/renderer_host/p2p/socket_host_test_utils.h
index b008f9f..9ab4634 100644
--- a/content/browser/renderer_host/p2p/socket_host_test_utils.h
+++ b/content/browser/renderer_host/p2p/socket_host_test_utils.h
@@ -63,6 +63,10 @@
   bool WasNpnNegotiated() const override;
   net::NextProto GetNegotiatedProtocol() const override;
   bool GetSSLInfo(net::SSLInfo* ssl_info) override;
+  void GetConnectionAttempts(net::ConnectionAttempts* out) const override;
+  void ClearConnectionAttempts() override {}
+  void AddConnectionAttempts(const net::ConnectionAttempts& attempts) override {
+  }
 
  private:
   void DoAsyncWrite(scoped_refptr<net::IOBuffer> buf, int buf_len,
diff --git a/jingle/glue/fake_ssl_client_socket.cc b/jingle/glue/fake_ssl_client_socket.cc
index 4cc6867..657bd76 100644
--- a/jingle/glue/fake_ssl_client_socket.cc
+++ b/jingle/glue/fake_ssl_client_socket.cc
@@ -344,4 +344,9 @@
   return transport_socket_->GetSSLInfo(ssl_info);
 }
 
+void FakeSSLClientSocket::GetConnectionAttempts(
+    net::ConnectionAttempts* out) const {
+  out->clear();
+}
+
 }  // namespace jingle_glue
diff --git a/jingle/glue/fake_ssl_client_socket.h b/jingle/glue/fake_ssl_client_socket.h
index bf951d6..8f90429 100644
--- a/jingle/glue/fake_ssl_client_socket.h
+++ b/jingle/glue/fake_ssl_client_socket.h
@@ -67,6 +67,10 @@
   bool WasNpnNegotiated() const override;
   net::NextProto GetNegotiatedProtocol() const override;
   bool GetSSLInfo(net::SSLInfo* ssl_info) override;
+  void GetConnectionAttempts(net::ConnectionAttempts* out) const override;
+  void ClearConnectionAttempts() override {}
+  void AddConnectionAttempts(const net::ConnectionAttempts& attempts) override {
+  }
 
  private:
   enum HandshakeState {
diff --git a/jingle/glue/fake_ssl_client_socket_unittest.cc b/jingle/glue/fake_ssl_client_socket_unittest.cc
index 4df4a7de..0f81127 100644
--- a/jingle/glue/fake_ssl_client_socket_unittest.cc
+++ b/jingle/glue/fake_ssl_client_socket_unittest.cc
@@ -68,6 +68,9 @@
   MOCK_CONST_METHOD0(WasNpnNegotiated, bool());
   MOCK_CONST_METHOD0(GetNegotiatedProtocol, net::NextProto());
   MOCK_METHOD1(GetSSLInfo, bool(net::SSLInfo*));
+  MOCK_CONST_METHOD1(GetConnectionAttempts, void(net::ConnectionAttempts*));
+  MOCK_METHOD0(ClearConnectionAttempts, void());
+  MOCK_METHOD1(AddConnectionAttempts, void(const net::ConnectionAttempts&));
 };
 
 // Break up |data| into a bunch of chunked MockReads/Writes and push
diff --git a/jingle/glue/proxy_resolving_client_socket.cc b/jingle/glue/proxy_resolving_client_socket.cc
index 5a6e068..9eb2da3 100644
--- a/jingle/glue/proxy_resolving_client_socket.cc
+++ b/jingle/glue/proxy_resolving_client_socket.cc
@@ -420,6 +420,11 @@
   return false;
 }
 
+void ProxyResolvingClientSocket::GetConnectionAttempts(
+    net::ConnectionAttempts* out) const {
+  out->clear();
+}
+
 void ProxyResolvingClientSocket::CloseTransportSocket() {
   if (transport_.get() && transport_->socket())
     transport_->socket()->Disconnect();
diff --git a/jingle/glue/proxy_resolving_client_socket.h b/jingle/glue/proxy_resolving_client_socket.h
index 696a097..e8f22ceb 100644
--- a/jingle/glue/proxy_resolving_client_socket.h
+++ b/jingle/glue/proxy_resolving_client_socket.h
@@ -70,6 +70,10 @@
   bool WasNpnNegotiated() const override;
   net::NextProto GetNegotiatedProtocol() const override;
   bool GetSSLInfo(net::SSLInfo* ssl_info) override;
+  void GetConnectionAttempts(net::ConnectionAttempts* out) const override;
+  void ClearConnectionAttempts() override {}
+  void AddConnectionAttempts(const net::ConnectionAttempts& attempts) override {
+  }
 
  private:
   // Proxy resolution and connection functions.
diff --git a/jingle/glue/pseudotcp_adapter.cc b/jingle/glue/pseudotcp_adapter.cc
index 5a26f7d..db27502 100644
--- a/jingle/glue/pseudotcp_adapter.cc
+++ b/jingle/glue/pseudotcp_adapter.cc
@@ -582,6 +582,12 @@
   return false;
 }
 
+void PseudoTcpAdapter::GetConnectionAttempts(
+    net::ConnectionAttempts* out) const {
+  DCHECK(CalledOnValidThread());
+  out->clear();
+}
+
 void PseudoTcpAdapter::SetAckDelay(int delay_ms) {
   DCHECK(CalledOnValidThread());
   core_->SetAckDelay(delay_ms);
diff --git a/jingle/glue/pseudotcp_adapter.h b/jingle/glue/pseudotcp_adapter.h
index 14730005..8b11e2e 100644
--- a/jingle/glue/pseudotcp_adapter.h
+++ b/jingle/glue/pseudotcp_adapter.h
@@ -53,6 +53,10 @@
   bool WasNpnNegotiated() const override;
   net::NextProto GetNegotiatedProtocol() const override;
   bool GetSSLInfo(net::SSLInfo* ssl_info) override;
+  void GetConnectionAttempts(net::ConnectionAttempts* out) const override;
+  void ClearConnectionAttempts() override {}
+  void AddConnectionAttempts(const net::ConnectionAttempts& attempts) override {
+  }
 
   // Set the delay for sending ACK.
   void SetAckDelay(int delay_ms);
diff --git a/net/http/http_proxy_client_socket.cc b/net/http/http_proxy_client_socket.cc
index 2eec09c..2f0619b0 100644
--- a/net/http/http_proxy_client_socket.cc
+++ b/net/http/http_proxy_client_socket.cc
@@ -213,6 +213,11 @@
   return false;
 }
 
+void HttpProxyClientSocket::GetConnectionAttempts(
+    ConnectionAttempts* out) const {
+  out->clear();
+}
+
 int HttpProxyClientSocket::Read(IOBuffer* buf, int buf_len,
                                 const CompletionCallback& callback) {
   DCHECK(user_callback_.is_null());
diff --git a/net/http/http_proxy_client_socket.h b/net/http/http_proxy_client_socket.h
index cc82c57..439278aa 100644
--- a/net/http/http_proxy_client_socket.h
+++ b/net/http/http_proxy_client_socket.h
@@ -72,6 +72,9 @@
   bool WasNpnNegotiated() const override;
   NextProto GetNegotiatedProtocol() const override;
   bool GetSSLInfo(SSLInfo* ssl_info) override;
+  void GetConnectionAttempts(ConnectionAttempts* out) const override;
+  void ClearConnectionAttempts() override {}
+  void AddConnectionAttempts(const ConnectionAttempts& attempts) override {}
 
   // Socket implementation.
   int Read(IOBuffer* buf,
diff --git a/net/http/http_stream_factory_impl_job.cc b/net/http/http_stream_factory_impl_job.cc
index 1172bf1f..1d17b08 100644
--- a/net/http/http_stream_factory_impl_job.cc
+++ b/net/http/http_stream_factory_impl_job.cc
@@ -296,7 +296,7 @@
   DCHECK(!IsPreconnecting());
   DCHECK(!stream_factory_->for_websockets_);
 
-  MaybeCopyConnectionAttemptsFromClientSocketHandleToRequest();
+  MaybeCopyConnectionAttemptsFromSocketOrHandle();
 
   if (IsOrphaned()) {
     stream_factory_->OnOrphanedJobComplete(this);
@@ -319,7 +319,7 @@
   // never be ready.
   DCHECK(!IsOrphaned());
 
-  MaybeCopyConnectionAttemptsFromClientSocketHandleToRequest();
+  MaybeCopyConnectionAttemptsFromSocketOrHandle();
 
   request_->Complete(was_npn_negotiated(),
                      protocol_negotiated(),
@@ -341,7 +341,7 @@
   base::WeakPtr<SpdySession> spdy_session = new_spdy_session_;
   new_spdy_session_.reset();
 
-  MaybeCopyConnectionAttemptsFromClientSocketHandleToRequest();
+  MaybeCopyConnectionAttemptsFromSocketOrHandle();
 
   // TODO(jgraettinger): Notify the factory, and let that notify |request_|,
   // rather than notifying |request_| directly.
@@ -362,7 +362,7 @@
 void HttpStreamFactoryImpl::Job::OnStreamFailedCallback(int result) {
   DCHECK(!IsPreconnecting());
 
-  MaybeCopyConnectionAttemptsFromClientSocketHandleToRequest();
+  MaybeCopyConnectionAttemptsFromSocketOrHandle();
 
   if (IsOrphaned())
     stream_factory_->OnOrphanedJobComplete(this);
@@ -375,7 +375,7 @@
     int result, const SSLInfo& ssl_info) {
   DCHECK(!IsPreconnecting());
 
-  MaybeCopyConnectionAttemptsFromClientSocketHandleToRequest();
+  MaybeCopyConnectionAttemptsFromSocketOrHandle();
 
   if (IsOrphaned())
     stream_factory_->OnOrphanedJobComplete(this);
@@ -1526,12 +1526,23 @@
   return ClientSocketPoolManager::NORMAL_GROUP;
 }
 
+// If the connection succeeds, failed connection attempts leading up to the
+// success will be returned via the successfully connected socket. If the
+// connection fails, failed connection attempts will be returned via the
+// ClientSocketHandle. Check whether a socket was returned and copy the
+// connection attempts from the proper place.
 void HttpStreamFactoryImpl::Job::
-    MaybeCopyConnectionAttemptsFromClientSocketHandleToRequest() {
+    MaybeCopyConnectionAttemptsFromSocketOrHandle() {
   if (IsOrphaned() || !connection_)
     return;
 
-  request_->AddConnectionAttempts(connection_->connection_attempts());
+  if (connection_->socket()) {
+    ConnectionAttempts socket_attempts;
+    connection_->socket()->GetConnectionAttempts(&socket_attempts);
+    request_->AddConnectionAttempts(socket_attempts);
+  } else {
+    request_->AddConnectionAttempts(connection_->connection_attempts());
+  }
 }
 
 }  // namespace net
diff --git a/net/http/http_stream_factory_impl_job.h b/net/http/http_stream_factory_impl_job.h
index a5793d16..f7285be3 100644
--- a/net/http/http_stream_factory_impl_job.h
+++ b/net/http/http_stream_factory_impl_job.h
@@ -280,7 +280,7 @@
 
   ClientSocketPoolManager::SocketGroupType GetSocketGroup() const;
 
-  void MaybeCopyConnectionAttemptsFromClientSocketHandleToRequest();
+  void MaybeCopyConnectionAttemptsFromSocketOrHandle();
 
   // Record histograms of latency until Connect() completes.
   static void LogHttpConnectedMetrics(const ClientSocketHandle& handle);
diff --git a/net/server/http_server_unittest.cc b/net/server/http_server_unittest.cc
index a6070e8..c2426a3 100644
--- a/net/server/http_server_unittest.cc
+++ b/net/server/http_server_unittest.cc
@@ -497,6 +497,11 @@
   bool WasNpnNegotiated() const override { return false; }
   NextProto GetNegotiatedProtocol() const override { return kProtoUnknown; }
   bool GetSSLInfo(SSLInfo* ssl_info) override { return false; }
+  void GetConnectionAttempts(ConnectionAttempts* out) const override {
+    out->clear();
+  }
+  void ClearConnectionAttempts() override {}
+  void AddConnectionAttempts(const ConnectionAttempts& attempts) override {}
 
   // Socket
   int Read(IOBuffer* buf,
diff --git a/net/socket/client_socket_handle.h b/net/socket/client_socket_handle.h
index 58cf4ca..2f8bed2 100644
--- a/net/socket/client_socket_handle.h
+++ b/net/socket/client_socket_handle.h
@@ -156,6 +156,9 @@
   ClientSocketHandle* release_pending_http_proxy_connection() {
     return pending_http_proxy_connection_.release();
   }
+  // If the connection failed, returns the connection attempts made. (If it
+  // succeeded, they will be returned through the socket instead; see
+  // |StreamSocket::GetConnectionAttempts|.)
   const ConnectionAttempts& connection_attempts() {
     return connection_attempts_;
   }
diff --git a/net/socket/client_socket_pool_base.cc b/net/socket/client_socket_pool_base.cc
index a1080c29..c210e69a 100644
--- a/net/socket/client_socket_pool_base.cc
+++ b/net/socket/client_socket_pool_base.cc
@@ -489,6 +489,12 @@
         idle_socket.socket->WasEverUsed() ?
             ClientSocketHandle::REUSED_IDLE :
             ClientSocketHandle::UNUSED_IDLE;
+
+    // If this socket took multiple attempts to obtain, don't report those
+    // every time it's reused, just to the first user.
+    if (idle_socket.socket->WasEverUsed())
+      idle_socket.socket->ClearConnectionAttempts();
+
     HandOutSocket(
         scoped_ptr<StreamSocket>(idle_socket.socket),
         reuse_type,
diff --git a/net/socket/client_socket_pool_base_unittest.cc b/net/socket/client_socket_pool_base_unittest.cc
index 087db231..cfa9c6e 100644
--- a/net/socket/client_socket_pool_base_unittest.cc
+++ b/net/socket/client_socket_pool_base_unittest.cc
@@ -177,6 +177,11 @@
   bool WasNpnNegotiated() const override { return false; }
   NextProto GetNegotiatedProtocol() const override { return kProtoUnknown; }
   bool GetSSLInfo(SSLInfo* ssl_info) override { return false; }
+  void GetConnectionAttempts(ConnectionAttempts* out) const override {
+    out->clear();
+  }
+  void ClearConnectionAttempts() override {}
+  void AddConnectionAttempts(const ConnectionAttempts& attempts) override {}
 
  private:
   bool connected_;
diff --git a/net/socket/connection_attempts.h b/net/socket/connection_attempts.h
index 241ffb0..185defa 100644
--- a/net/socket/connection_attempts.h
+++ b/net/socket/connection_attempts.h
@@ -22,6 +22,8 @@
   int result;
 };
 
+// Multiple connection attempts, as might be tracked in an HttpTransaction or a
+// URLRequest. Order is insignificant.
 typedef std::vector<ConnectionAttempt> ConnectionAttempts;
 
 }  // namespace net
diff --git a/net/socket/socket_test_util.cc b/net/socket/socket_test_util.cc
index cf033c2..c5861faa 100644
--- a/net/socket/socket_test_util.cc
+++ b/net/socket/socket_test_util.cc
@@ -1006,6 +1006,10 @@
   return net_log_;
 }
 
+void MockClientSocket::GetConnectionAttempts(ConnectionAttempts* out) const {
+  out->clear();
+}
+
 void MockClientSocket::GetSSLCertRequestInfo(
   SSLCertRequestInfo* cert_request_info) {
 }
@@ -1148,6 +1152,22 @@
   return write_result.result;
 }
 
+void MockTCPClientSocket::GetConnectionAttempts(ConnectionAttempts* out) const {
+  int connect_result = data_->connect_data().result;
+
+  out->clear();
+  if (connected_ && connect_result != OK)
+    out->push_back(ConnectionAttempt(addresses_[0], connect_result));
+}
+
+void MockTCPClientSocket::ClearConnectionAttempts() {
+  NOTIMPLEMENTED();
+}
+
+void MockTCPClientSocket::AddConnectionAttempts(const ConnectionAttempts& in) {
+  NOTIMPLEMENTED();
+}
+
 int MockTCPClientSocket::Connect(const CompletionCallback& callback) {
   if (connected_)
     return OK;
diff --git a/net/socket/socket_test_util.h b/net/socket/socket_test_util.h
index 3bd88d2..b7d090e 100644
--- a/net/socket/socket_test_util.h
+++ b/net/socket/socket_test_util.h
@@ -779,6 +779,9 @@
   const BoundNetLog& NetLog() const override;
   void SetSubresourceSpeculation() override {}
   void SetOmniboxSpeculation() override {}
+  void GetConnectionAttempts(ConnectionAttempts* out) const override;
+  void ClearConnectionAttempts() override {}
+  void AddConnectionAttempts(const ConnectionAttempts& attempts) override {}
 
   // SSLClientSocket implementation.
   void GetSSLCertRequestInfo(SSLCertRequestInfo* cert_request_info) override;
@@ -841,6 +844,9 @@
   bool UsingTCPFastOpen() const override;
   bool WasNpnNegotiated() const override;
   bool GetSSLInfo(SSLInfo* ssl_info) override;
+  void GetConnectionAttempts(ConnectionAttempts* out) const override;
+  void ClearConnectionAttempts() override;
+  void AddConnectionAttempts(const ConnectionAttempts& attempts) override;
 
   // AsyncSocket:
   void OnReadComplete(const MockRead& data) override;
diff --git a/net/socket/socks5_client_socket.cc b/net/socket/socks5_client_socket.cc
index e43cde6..4ac9ca5 100644
--- a/net/socket/socks5_client_socket.cc
+++ b/net/socket/socks5_client_socket.cc
@@ -144,7 +144,10 @@
   }
   NOTREACHED();
   return false;
+}
 
+void SOCKS5ClientSocket::GetConnectionAttempts(ConnectionAttempts* out) const {
+  out->clear();
 }
 
 // Read is called by the transport layer above to read. This can only be done
diff --git a/net/socket/socks5_client_socket.h b/net/socket/socks5_client_socket.h
index 59f481b..d54e790 100644
--- a/net/socket/socks5_client_socket.h
+++ b/net/socket/socks5_client_socket.h
@@ -55,6 +55,9 @@
   bool WasNpnNegotiated() const override;
   NextProto GetNegotiatedProtocol() const override;
   bool GetSSLInfo(SSLInfo* ssl_info) override;
+  void GetConnectionAttempts(ConnectionAttempts* out) const override;
+  void ClearConnectionAttempts() override {}
+  void AddConnectionAttempts(const ConnectionAttempts& attempts) override {}
 
   // Socket implementation.
   int Read(IOBuffer* buf,
diff --git a/net/socket/socks_client_socket.cc b/net/socket/socks_client_socket.cc
index fd16f83..dbdc0251 100644
--- a/net/socket/socks_client_socket.cc
+++ b/net/socket/socks_client_socket.cc
@@ -172,7 +172,10 @@
   }
   NOTREACHED();
   return false;
+}
 
+void SOCKSClientSocket::GetConnectionAttempts(ConnectionAttempts* out) const {
+  out->clear();
 }
 
 // Read is called by the transport layer above to read. This can only be done
diff --git a/net/socket/socks_client_socket.h b/net/socket/socks_client_socket.h
index 66fab45..ee2918a 100644
--- a/net/socket/socks_client_socket.h
+++ b/net/socket/socks_client_socket.h
@@ -52,6 +52,9 @@
   bool WasNpnNegotiated() const override;
   NextProto GetNegotiatedProtocol() const override;
   bool GetSSLInfo(SSLInfo* ssl_info) override;
+  void GetConnectionAttempts(ConnectionAttempts* out) const override;
+  void ClearConnectionAttempts() override {}
+  void AddConnectionAttempts(const ConnectionAttempts& attempts) override {}
 
   // Socket implementation.
   int Read(IOBuffer* buf,
diff --git a/net/socket/ssl_client_socket_nss.cc b/net/socket/ssl_client_socket_nss.cc
index 4d90751..229735b 100644
--- a/net/socket/ssl_client_socket_nss.cc
+++ b/net/socket/ssl_client_socket_nss.cc
@@ -2491,6 +2491,10 @@
   return true;
 }
 
+void SSLClientSocketNSS::GetConnectionAttempts(ConnectionAttempts* out) const {
+  out->clear();
+}
+
 void SSLClientSocketNSS::GetSSLCertRequestInfo(
     SSLCertRequestInfo* cert_request_info) {
   EnterFunction("");
diff --git a/net/socket/ssl_client_socket_nss.h b/net/socket/ssl_client_socket_nss.h
index 97bcb032..72fa9d2 100644
--- a/net/socket/ssl_client_socket_nss.h
+++ b/net/socket/ssl_client_socket_nss.h
@@ -92,6 +92,9 @@
   bool WasEverUsed() const override;
   bool UsingTCPFastOpen() const override;
   bool GetSSLInfo(SSLInfo* ssl_info) override;
+  void GetConnectionAttempts(ConnectionAttempts* out) const override;
+  void ClearConnectionAttempts() override {}
+  void AddConnectionAttempts(const ConnectionAttempts& attempts) override {}
 
   // Socket implementation.
   int Read(IOBuffer* buf,
diff --git a/net/socket/ssl_client_socket_openssl.cc b/net/socket/ssl_client_socket_openssl.cc
index e019b1e..9b1ce38 100644
--- a/net/socket/ssl_client_socket_openssl.cc
+++ b/net/socket/ssl_client_socket_openssl.cc
@@ -621,6 +621,11 @@
   return true;
 }
 
+void SSLClientSocketOpenSSL::GetConnectionAttempts(
+    ConnectionAttempts* out) const {
+  out->clear();
+}
+
 int SSLClientSocketOpenSSL::Read(IOBuffer* buf,
                                  int buf_len,
                                  const CompletionCallback& callback) {
diff --git a/net/socket/ssl_client_socket_openssl.h b/net/socket/ssl_client_socket_openssl.h
index 3f13aa6..c78a815 100644
--- a/net/socket/ssl_client_socket_openssl.h
+++ b/net/socket/ssl_client_socket_openssl.h
@@ -85,6 +85,9 @@
   bool WasEverUsed() const override;
   bool UsingTCPFastOpen() const override;
   bool GetSSLInfo(SSLInfo* ssl_info) override;
+  void GetConnectionAttempts(ConnectionAttempts* out) const override;
+  void ClearConnectionAttempts() override {}
+  void AddConnectionAttempts(const ConnectionAttempts& attempts) override {}
 
   // Socket implementation.
   int Read(IOBuffer* buf,
diff --git a/net/socket/ssl_client_socket_unittest.cc b/net/socket/ssl_client_socket_unittest.cc
index 19e5e8d..0a7b7118 100644
--- a/net/socket/ssl_client_socket_unittest.cc
+++ b/net/socket/ssl_client_socket_unittest.cc
@@ -108,6 +108,15 @@
   bool GetSSLInfo(SSLInfo* ssl_info) override {
     return transport_->GetSSLInfo(ssl_info);
   }
+  void GetConnectionAttempts(ConnectionAttempts* out) const override {
+    transport_->GetConnectionAttempts(out);
+  }
+  void ClearConnectionAttempts() override {
+    transport_->ClearConnectionAttempts();
+  }
+  void AddConnectionAttempts(const ConnectionAttempts& attempts) override {
+    transport_->AddConnectionAttempts(attempts);
+  }
 
   // Socket implementation:
   int Read(IOBuffer* buf,
diff --git a/net/socket/ssl_server_socket_nss.cc b/net/socket/ssl_server_socket_nss.cc
index cdc0e67..6f505aff 100644
--- a/net/socket/ssl_server_socket_nss.cc
+++ b/net/socket/ssl_server_socket_nss.cc
@@ -307,6 +307,10 @@
   return false;
 }
 
+void SSLServerSocketNSS::GetConnectionAttempts(ConnectionAttempts* out) const {
+  out->clear();
+}
+
 int SSLServerSocketNSS::InitializeSSLOptions() {
   // Transport connected, now hook it up to nss
   nss_fd_ = memio_CreateIOLayer(kRecvBufferSize, kSendBufferSize);
diff --git a/net/socket/ssl_server_socket_nss.h b/net/socket/ssl_server_socket_nss.h
index e91514a..d1bcec69 100644
--- a/net/socket/ssl_server_socket_nss.h
+++ b/net/socket/ssl_server_socket_nss.h
@@ -66,6 +66,9 @@
   bool WasNpnNegotiated() const override;
   NextProto GetNegotiatedProtocol() const override;
   bool GetSSLInfo(SSLInfo* ssl_info) override;
+  void GetConnectionAttempts(ConnectionAttempts* out) const override;
+  void ClearConnectionAttempts() override {}
+  void AddConnectionAttempts(const ConnectionAttempts& attempts) override {}
 
  private:
   enum State {
diff --git a/net/socket/ssl_server_socket_openssl.cc b/net/socket/ssl_server_socket_openssl.cc
index 954e616..5a61eeb 100644
--- a/net/socket/ssl_server_socket_openssl.cc
+++ b/net/socket/ssl_server_socket_openssl.cc
@@ -248,6 +248,11 @@
   return false;
 }
 
+void SSLServerSocketOpenSSL::GetConnectionAttempts(
+    ConnectionAttempts* out) const {
+  out->clear();
+}
+
 void SSLServerSocketOpenSSL::OnSendComplete(int result) {
   if (next_handshake_state_ == STATE_HANDSHAKE) {
     // In handshake phase.
diff --git a/net/socket/ssl_server_socket_openssl.h b/net/socket/ssl_server_socket_openssl.h
index 5f5909b..34e8bb0 100644
--- a/net/socket/ssl_server_socket_openssl.h
+++ b/net/socket/ssl_server_socket_openssl.h
@@ -68,6 +68,9 @@
   bool WasNpnNegotiated() const override;
   NextProto GetNegotiatedProtocol() const override;
   bool GetSSLInfo(SSLInfo* ssl_info) override;
+  void GetConnectionAttempts(ConnectionAttempts* out) const override;
+  void ClearConnectionAttempts() override {}
+  void AddConnectionAttempts(const ConnectionAttempts& attempts) override {}
 
  private:
   enum State {
diff --git a/net/socket/ssl_server_socket_unittest.cc b/net/socket/ssl_server_socket_unittest.cc
index 8af2e01..84663eb 100644
--- a/net/socket/ssl_server_socket_unittest.cc
+++ b/net/socket/ssl_server_socket_unittest.cc
@@ -239,6 +239,14 @@
 
   bool GetSSLInfo(SSLInfo* ssl_info) override { return false; }
 
+  void GetConnectionAttempts(ConnectionAttempts* out) const override {
+    out->clear();
+  }
+
+  void ClearConnectionAttempts() override {}
+
+  void AddConnectionAttempts(const ConnectionAttempts& attempts) override {}
+
  private:
   BoundNetLog net_log_;
   FakeDataChannel* incoming_;
diff --git a/net/socket/stream_socket.h b/net/socket/stream_socket.h
index 30a199e2..5669ea3e 100644
--- a/net/socket/stream_socket.h
+++ b/net/socket/stream_socket.h
@@ -6,6 +6,7 @@
 #define NET_SOCKET_STREAM_SOCKET_H_
 
 #include "net/log/net_log.h"
+#include "net/socket/connection_attempts.h"
 #include "net/socket/next_proto.h"
 #include "net/socket/socket.h"
 
@@ -95,6 +96,16 @@
   // SSL was not used by this socket.
   virtual bool GetSSLInfo(SSLInfo* ssl_info) = 0;
 
+  // Overwrites |out| with the connection attempts made in the process of
+  // connecting this socket.
+  virtual void GetConnectionAttempts(ConnectionAttempts* out) const = 0;
+
+  // Clears the socket's list of connection attempts.
+  virtual void ClearConnectionAttempts() = 0;
+
+  // Adds |attempts| to the socket's list of connection attempts.
+  virtual void AddConnectionAttempts(const ConnectionAttempts& attempts) = 0;
+
  protected:
   // The following class is only used to gather statistics about the history of
   // a socket.  It is only instantiated and used in basic sockets, such as
diff --git a/net/socket/tcp_client_socket.cc b/net/socket/tcp_client_socket.cc
index 4a72bdf..a1c1fad 100644
--- a/net/socket/tcp_client_socket.cc
+++ b/net/socket/tcp_client_socket.cc
@@ -124,6 +124,7 @@
 
     if (previously_disconnected_) {
       use_history_.Reset();
+      connection_attempts_.clear();
       previously_disconnected_ = false;
     }
 
@@ -159,6 +160,9 @@
     return OK;  // Done!
   }
 
+  connection_attempts_.push_back(
+      ConnectionAttempt(addresses_[current_address_index_], result));
+
   // Close whatever partially connected socket we currently have.
   DoDisconnect();
 
@@ -296,6 +300,20 @@
   return socket_->SetNoDelay(no_delay);
 }
 
+void TCPClientSocket::GetConnectionAttempts(ConnectionAttempts* out) const {
+  *out = connection_attempts_;
+}
+
+void TCPClientSocket::ClearConnectionAttempts() {
+  connection_attempts_.clear();
+}
+
+void TCPClientSocket::AddConnectionAttempts(
+    const ConnectionAttempts& attempts) {
+  connection_attempts_.insert(connection_attempts_.begin(), attempts.begin(),
+                              attempts.end());
+}
+
 void TCPClientSocket::DidCompleteConnect(int result) {
   DCHECK_EQ(next_connect_state_, CONNECT_STATE_CONNECT_COMPLETE);
   DCHECK_NE(result, ERR_IO_PENDING);
diff --git a/net/socket/tcp_client_socket.h b/net/socket/tcp_client_socket.h
index e1b3fa0..0b8062b 100644
--- a/net/socket/tcp_client_socket.h
+++ b/net/socket/tcp_client_socket.h
@@ -12,6 +12,7 @@
 #include "net/base/completion_callback.h"
 #include "net/base/net_export.h"
 #include "net/log/net_log.h"
+#include "net/socket/connection_attempts.h"
 #include "net/socket/stream_socket.h"
 #include "net/socket/tcp_socket.h"
 
@@ -69,6 +70,10 @@
   virtual bool SetKeepAlive(bool enable, int delay);
   virtual bool SetNoDelay(bool no_delay);
 
+  void GetConnectionAttempts(ConnectionAttempts* out) const override;
+  void ClearConnectionAttempts() override;
+  void AddConnectionAttempts(const ConnectionAttempts& attempts) override;
+
  private:
   // State machine for connecting the socket.
   enum ConnectState {
@@ -116,6 +121,9 @@
   // histograms.
   UseHistory use_history_;
 
+  // Failed connection attempts made while trying to connect this socket.
+  ConnectionAttempts connection_attempts_;
+
   DISALLOW_COPY_AND_ASSIGN(TCPClientSocket);
 };
 
diff --git a/net/socket/transport_client_socket_pool.cc b/net/socket/transport_client_socket_pool.cc
index b728cb9..a5f8adb 100644
--- a/net/socket/transport_client_socket_pool.cc
+++ b/net/socket/transport_client_socket_pool.cc
@@ -208,8 +208,7 @@
                  BoundNetLog::Make(net_log, NetLog::SOURCE_CONNECT_JOB)),
       helper_(params, client_socket_factory, host_resolver, &connect_timing_),
       interval_between_connects_(CONNECT_INTERVAL_GT_20MS),
-      resolve_result_(OK),
-      connect_result_(OK) {
+      resolve_result_(OK) {
   helper_.SetOnIOComplete(this);
 }
 
@@ -235,18 +234,16 @@
 
 void TransportConnectJob::GetAdditionalErrorState(ClientSocketHandle* handle) {
   // If hostname resolution failed, record an empty endpoint and the result.
-  // If the actual socket Connect call failed, record the result and the last
-  // address attempted.
-  // TODO(ttuttle): Plumb into the socket layer and record *all* attempts.
+  // Also record any attempts made on either of the sockets.
   ConnectionAttempts attempts;
   if (resolve_result_ != OK) {
     DCHECK_EQ(0u, helper_.addresses().size());
     attempts.push_back(ConnectionAttempt(IPEndPoint(), resolve_result_));
-  } else if (connect_result_ != OK) {
-    DCHECK_LT(0u, helper_.addresses().size());
-    attempts.push_back(
-        ConnectionAttempt(helper_.addresses().back(), connect_result_));
   }
+  attempts.insert(attempts.begin(), connection_attempts_.begin(),
+                  connection_attempts_.end());
+  attempts.insert(attempts.begin(), fallback_connection_attempts_.begin(),
+                  fallback_connection_attempts_.end());
   handle->set_connection_attempts(attempts);
 }
 
@@ -330,6 +327,16 @@
 
 int TransportConnectJob::DoTransportConnectComplete(int result) {
   if (result == OK) {
+    // Success will be returned via the main socket, so also include connection
+    // attempts made on the fallback socket up to this point. (Unfortunately,
+    // the only simple way to return information in the success case is through
+    // the successfully-connected socket.)
+    if (fallback_transport_socket_) {
+      ConnectionAttempts fallback_attempts;
+      fallback_transport_socket_->GetConnectionAttempts(&fallback_attempts);
+      transport_socket_->AddConnectionAttempts(fallback_attempts);
+    }
+
     bool is_ipv4 =
         helper_.addresses().front().GetFamily() == ADDRESS_FAMILY_IPV4;
     TransportConnectJobHelper::ConnectionLatencyHistogram race_result =
@@ -378,12 +385,17 @@
     SetSocket(transport_socket_.Pass());
     fallback_timer_.Stop();
   } else {
+    // Failure will be returned via |GetAdditionalErrorState|, so save
+    // connection attempts from both sockets for use there.
+    CopyConnectionAttemptsFromSockets();
+
     // Be a bit paranoid and kill off the fallback members to prevent reuse.
     fallback_transport_socket_.reset();
     fallback_addresses_.reset();
   }
 
-  connect_result_ = result;
+  // N.B.: The owner of the ConnectJob will delete it after the callback is
+  // called, so the fallback socket, if any, won't stick around for long.
 
   return result;
 }
@@ -428,6 +440,17 @@
 
   if (result == OK) {
     DCHECK(!fallback_connect_start_time_.is_null());
+
+    // Success will be returned via the fallback socket, so also include
+    // connection attempts made on the main socket up to this point.
+    // (Unfortunately, the only simple way to return information in the success
+    // case is through the successfully-connected socket.)
+    if (transport_socket_) {
+      ConnectionAttempts attempts;
+      transport_socket_->GetConnectionAttempts(&attempts);
+      fallback_transport_socket_->AddConnectionAttempts(attempts);
+    }
+
     connect_timing_.connect_start = fallback_connect_start_time_;
     helper_.HistogramDuration(
         TransportConnectJobHelper::CONNECTION_LATENCY_IPV4_WINS_RACE);
@@ -435,10 +458,18 @@
     helper_.set_next_state(TransportConnectJobHelper::STATE_NONE);
     transport_socket_.reset();
   } else {
+    // Failure will be returned via |GetAdditionalErrorState|, so save
+    // connection attempts from both sockets for use there.
+    CopyConnectionAttemptsFromSockets();
+
     // Be a bit paranoid and kill off the fallback members to prevent reuse.
     fallback_transport_socket_.reset();
     fallback_addresses_.reset();
   }
+
+  // N.B.: The owner of the ConnectJob will delete it after the callback is
+  // called, so the main socket, if any, won't stick around for long.
+
   NotifyDelegateOfCompletion(result);  // Deletes |this|
 }
 
@@ -446,6 +477,15 @@
   return helper_.DoConnectInternal(this);
 }
 
+void TransportConnectJob::CopyConnectionAttemptsFromSockets() {
+  if (transport_socket_)
+    transport_socket_->GetConnectionAttempts(&connection_attempts_);
+  if (fallback_transport_socket_) {
+    fallback_transport_socket_->GetConnectionAttempts(
+        &fallback_connection_attempts_);
+  }
+}
+
 scoped_ptr<ConnectJob>
 TransportClientSocketPool::TransportConnectJobFactory::NewConnectJob(
     const std::string& group_name,
diff --git a/net/socket/transport_client_socket_pool.h b/net/socket/transport_client_socket_pool.h
index de57d18..c2c8ab2 100644
--- a/net/socket/transport_client_socket_pool.h
+++ b/net/socket/transport_client_socket_pool.h
@@ -17,6 +17,7 @@
 #include "net/dns/single_request_host_resolver.h"
 #include "net/socket/client_socket_pool.h"
 #include "net/socket/client_socket_pool_base.h"
+#include "net/socket/connection_attempts.h"
 
 namespace net {
 
@@ -196,6 +197,8 @@
   // Otherwise, it returns a net error code.
   int ConnectInternal() override;
 
+  void CopyConnectionAttemptsFromSockets();
+
   TransportConnectJobHelper helper_;
 
   scoped_ptr<StreamSocket> transport_socket_;
@@ -209,7 +212,14 @@
   ConnectInterval interval_between_connects_;
 
   int resolve_result_;
-  int connect_result_;
+
+  // Used in the failure case to save connection attempts made on the main and
+  // fallback sockets and pass them on in |GetAdditionalErrorState|. (In the
+  // success case, connection attempts are passed through the returned socket;
+  // attempts are copied from the other socket, if one exists, into it before
+  // it is returned.)
+  ConnectionAttempts connection_attempts_;
+  ConnectionAttempts fallback_connection_attempts_;
 
   DISALLOW_COPY_AND_ASSIGN(TransportConnectJob);
 };
diff --git a/net/socket/transport_client_socket_pool_test_util.cc b/net/socket/transport_client_socket_pool_test_util.cc
index 82ed8e6..352fd87 100644
--- a/net/socket/transport_client_socket_pool_test_util.cc
+++ b/net/socket/transport_client_socket_pool_test_util.cc
@@ -69,6 +69,11 @@
   bool WasNpnNegotiated() const override { return false; }
   NextProto GetNegotiatedProtocol() const override { return kProtoUnknown; }
   bool GetSSLInfo(SSLInfo* ssl_info) override { return false; }
+  void GetConnectionAttempts(ConnectionAttempts* out) const override {
+    out->clear();
+  }
+  void ClearConnectionAttempts() override {}
+  void AddConnectionAttempts(const ConnectionAttempts& attempts) override {}
 
   // Socket implementation.
   int Read(IOBuffer* buf,
@@ -125,6 +130,13 @@
   bool WasNpnNegotiated() const override { return false; }
   NextProto GetNegotiatedProtocol() const override { return kProtoUnknown; }
   bool GetSSLInfo(SSLInfo* ssl_info) override { return false; }
+  void GetConnectionAttempts(ConnectionAttempts* out) const override {
+    out->clear();
+    for (const auto& addr : addrlist_)
+      out->push_back(ConnectionAttempt(addr, ERR_CONNECTION_FAILED));
+  }
+  void ClearConnectionAttempts() override {}
+  void AddConnectionAttempts(const ConnectionAttempts& attempts) override {}
 
   // Socket implementation.
   int Read(IOBuffer* buf,
@@ -196,9 +208,16 @@
 
   static scoped_ptr<StreamSocket> MakeMockStalledClientSocket(
       const AddressList& addrlist,
-      net::NetLog* net_log) {
+      net::NetLog* net_log,
+      bool failing) {
     scoped_ptr<MockTriggerableClientSocket> socket(
         new MockTriggerableClientSocket(addrlist, true, net_log));
+    if (failing) {
+      DCHECK_LE(1u, addrlist.size());
+      ConnectionAttempts attempts;
+      attempts.push_back(ConnectionAttempt(addrlist[0], ERR_CONNECTION_FAILED));
+      socket->AddConnectionAttempts(attempts);
+    }
     return socket.Pass();
   }
 
@@ -236,6 +255,14 @@
   bool WasNpnNegotiated() const override { return false; }
   NextProto GetNegotiatedProtocol() const override { return kProtoUnknown; }
   bool GetSSLInfo(SSLInfo* ssl_info) override { return false; }
+  void GetConnectionAttempts(ConnectionAttempts* out) const override {
+    *out = connection_attempts_;
+  }
+  void ClearConnectionAttempts() override { connection_attempts_.clear(); }
+  void AddConnectionAttempts(const ConnectionAttempts& attempts) override {
+    connection_attempts_.insert(connection_attempts_.begin(), attempts.begin(),
+                                attempts.end());
+  }
 
   // Socket implementation.
   int Read(IOBuffer* buf,
@@ -264,6 +291,7 @@
   BoundNetLog net_log_;
   CompletionCallback callback_;
   bool use_tcp_fastopen_;
+  ConnectionAttempts connection_attempts_;
 
   base::WeakPtrFactory<MockTriggerableClientSocket> weak_factory_;
 
@@ -364,8 +392,11 @@
       return MockTriggerableClientSocket::MakeMockDelayedClientSocket(
           addresses, false, delay_, net_log_);
     case MOCK_STALLED_CLIENT_SOCKET:
-      return MockTriggerableClientSocket::MakeMockStalledClientSocket(addresses,
-                                                                      net_log_);
+      return MockTriggerableClientSocket::MakeMockStalledClientSocket(
+          addresses, net_log_, false);
+    case MOCK_STALLED_FAILING_CLIENT_SOCKET:
+      return MockTriggerableClientSocket::MakeMockStalledClientSocket(
+          addresses, net_log_, true);
     case MOCK_TRIGGERABLE_CLIENT_SOCKET: {
       scoped_ptr<MockTriggerableClientSocket> rv(
           new MockTriggerableClientSocket(addresses, true, net_log_));
diff --git a/net/socket/transport_client_socket_pool_test_util.h b/net/socket/transport_client_socket_pool_test_util.h
index 7b0daa3..6e38af66 100644
--- a/net/socket/transport_client_socket_pool_test_util.h
+++ b/net/socket/transport_client_socket_pool_test_util.h
@@ -62,6 +62,9 @@
     MOCK_DELAYED_FAILING_CLIENT_SOCKET,
     // A stalled socket that never connects at all.
     MOCK_STALLED_CLIENT_SOCKET,
+    // A stalled socket that never connects at all, but returns a failing
+    // ConnectionAttempt in |GetConnectionAttempts|.
+    MOCK_STALLED_FAILING_CLIENT_SOCKET,
     // A socket that can be triggered to connect explicitly, asynchronously.
     MOCK_TRIGGERABLE_CLIENT_SOCKET,
   };
diff --git a/net/socket/transport_client_socket_pool_unittest.cc b/net/socket/transport_client_socket_pool_unittest.cc
index dbfc506..1a00d46 100644
--- a/net/socket/transport_client_socket_pool_unittest.cc
+++ b/net/socket/transport_client_socket_pool_unittest.cc
@@ -776,6 +776,8 @@
   EXPECT_EQ(ERR_CONNECTION_FAILED, callback.WaitForResult());
   EXPECT_FALSE(handle.is_initialized());
   EXPECT_FALSE(handle.socket());
+  ASSERT_EQ(1u, handle.connection_attempts().size());
+  EXPECT_EQ(ERR_CONNECTION_FAILED, handle.connection_attempts()[0].result);
   EXPECT_EQ(0, pool_.IdleSocketCount());
   handle.Reset();
 
@@ -824,6 +826,8 @@
   EXPECT_EQ(ERR_CONNECTION_FAILED, callback.WaitForResult());
   EXPECT_FALSE(handle.is_initialized());
   EXPECT_FALSE(handle.socket());
+  ASSERT_EQ(1u, handle.connection_attempts().size());
+  EXPECT_EQ(ERR_CONNECTION_FAILED, handle.connection_attempts()[0].result);
   handle.Reset();
 
   // Reset for the next case.
@@ -842,11 +846,11 @@
                                  NULL);
 
   MockTransportClientSocketFactory::ClientSocketType case_types[] = {
-    // This is the IPv6 socket.
-    MockTransportClientSocketFactory::MOCK_STALLED_CLIENT_SOCKET,
-    // This is the IPv4 socket.
-    MockTransportClientSocketFactory::MOCK_PENDING_CLIENT_SOCKET
-  };
+      // This is the IPv6 socket. It stalls, but presents one failed connection
+      // attempt on GetConnectionAttempts.
+      MockTransportClientSocketFactory::MOCK_STALLED_FAILING_CLIENT_SOCKET,
+      // This is the IPv4 socket.
+      MockTransportClientSocketFactory::MOCK_PENDING_CLIENT_SOCKET};
 
   client_socket_factory_.set_client_socket_types(case_types, 2);
 
@@ -868,6 +872,14 @@
   IPEndPoint endpoint;
   handle.socket()->GetLocalAddress(&endpoint);
   EXPECT_EQ(kIPv4AddressSize, endpoint.address().size());
+
+  // Check that the failed connection attempt on the main socket is collected.
+  ConnectionAttempts attempts;
+  handle.socket()->GetConnectionAttempts(&attempts);
+  ASSERT_EQ(1u, attempts.size());
+  EXPECT_EQ(ERR_CONNECTION_FAILED, attempts[0].result);
+  EXPECT_EQ(kIPv6AddressSize, attempts[0].endpoint.address().size());
+
   EXPECT_EQ(2, client_socket_factory_.allocation_count());
 }
 
@@ -884,11 +896,11 @@
                                  NULL);
 
   MockTransportClientSocketFactory::ClientSocketType case_types[] = {
-    // This is the IPv6 socket.
-    MockTransportClientSocketFactory::MOCK_DELAYED_CLIENT_SOCKET,
-    // This is the IPv4 socket.
-    MockTransportClientSocketFactory::MOCK_STALLED_CLIENT_SOCKET
-  };
+      // This is the IPv6 socket.
+      MockTransportClientSocketFactory::MOCK_DELAYED_CLIENT_SOCKET,
+      // This is the IPv4 socket. It stalls, but presents one failed connection
+      // attempt on GetConnectionATtempts.
+      MockTransportClientSocketFactory::MOCK_STALLED_FAILING_CLIENT_SOCKET};
 
   client_socket_factory_.set_client_socket_types(case_types, 2);
   client_socket_factory_.set_delay(base::TimeDelta::FromMilliseconds(
@@ -912,6 +924,15 @@
   IPEndPoint endpoint;
   handle.socket()->GetLocalAddress(&endpoint);
   EXPECT_EQ(kIPv6AddressSize, endpoint.address().size());
+
+  // Check that the failed connection attempt on the fallback socket is
+  // collected.
+  ConnectionAttempts attempts;
+  handle.socket()->GetConnectionAttempts(&attempts);
+  ASSERT_EQ(1u, attempts.size());
+  EXPECT_EQ(ERR_CONNECTION_FAILED, attempts[0].result);
+  EXPECT_EQ(kIPv4AddressSize, attempts[0].endpoint.address().size());
+
   EXPECT_EQ(2, client_socket_factory_.allocation_count());
 }
 
@@ -945,6 +966,7 @@
   IPEndPoint endpoint;
   handle.socket()->GetLocalAddress(&endpoint);
   EXPECT_EQ(kIPv6AddressSize, endpoint.address().size());
+  EXPECT_EQ(0u, handle.connection_attempts().size());
   EXPECT_EQ(1, client_socket_factory_.allocation_count());
 }
 
@@ -977,6 +999,7 @@
   IPEndPoint endpoint;
   handle.socket()->GetLocalAddress(&endpoint);
   EXPECT_EQ(kIPv4AddressSize, endpoint.address().size());
+  EXPECT_EQ(0u, handle.connection_attempts().size());
   EXPECT_EQ(1, client_socket_factory_.allocation_count());
 }
 
diff --git a/net/socket/unix_domain_client_socket_posix.cc b/net/socket/unix_domain_client_socket_posix.cc
index 70ad42ba..79aa275 100644
--- a/net/socket/unix_domain_client_socket_posix.cc
+++ b/net/socket/unix_domain_client_socket_posix.cc
@@ -150,6 +150,11 @@
   return false;
 }
 
+void UnixDomainClientSocket::GetConnectionAttempts(
+    ConnectionAttempts* out) const {
+  out->clear();
+}
+
 int UnixDomainClientSocket::Read(IOBuffer* buf, int buf_len,
                                  const CompletionCallback& callback) {
   DCHECK(socket_);
diff --git a/net/socket/unix_domain_client_socket_posix.h b/net/socket/unix_domain_client_socket_posix.h
index 0ddb9d9..77ef9d5 100644
--- a/net/socket/unix_domain_client_socket_posix.h
+++ b/net/socket/unix_domain_client_socket_posix.h
@@ -55,6 +55,9 @@
   bool WasNpnNegotiated() const override;
   NextProto GetNegotiatedProtocol() const override;
   bool GetSSLInfo(SSLInfo* ssl_info) override;
+  void GetConnectionAttempts(ConnectionAttempts* out) const override;
+  void ClearConnectionAttempts() override {}
+  void AddConnectionAttempts(const ConnectionAttempts& attempts) override {}
 
   // Socket implementation.
   int Read(IOBuffer* buf,
diff --git a/net/socket/websocket_endpoint_lock_manager_unittest.cc b/net/socket/websocket_endpoint_lock_manager_unittest.cc
index dbe8494f..29fc067b7 100644
--- a/net/socket/websocket_endpoint_lock_manager_unittest.cc
+++ b/net/socket/websocket_endpoint_lock_manager_unittest.cc
@@ -54,6 +54,14 @@
 
   bool GetSSLInfo(SSLInfo* ssl_info) override { return false; }
 
+  void GetConnectionAttempts(ConnectionAttempts* out) const override {
+    out->clear();
+  }
+
+  void ClearConnectionAttempts() override {}
+
+  void AddConnectionAttempts(const ConnectionAttempts& attempts) override {}
+
   // Socket implementation
   int Read(IOBuffer* buf,
            int buf_len,
diff --git a/net/spdy/spdy_proxy_client_socket.cc b/net/spdy/spdy_proxy_client_socket.cc
index 544b9a1..3f351b2 100644
--- a/net/spdy/spdy_proxy_client_socket.cc
+++ b/net/spdy/spdy_proxy_client_socket.cc
@@ -190,6 +190,11 @@
                                   &protocol_negotiated);
 }
 
+void SpdyProxyClientSocket::GetConnectionAttempts(
+    ConnectionAttempts* out) const {
+  out->clear();
+}
+
 int SpdyProxyClientSocket::Read(IOBuffer* buf, int buf_len,
                                 const CompletionCallback& callback) {
   DCHECK(read_callback_.is_null());
diff --git a/net/spdy/spdy_proxy_client_socket.h b/net/spdy/spdy_proxy_client_socket.h
index 371182a..30861ee 100644
--- a/net/spdy/spdy_proxy_client_socket.h
+++ b/net/spdy/spdy_proxy_client_socket.h
@@ -73,6 +73,9 @@
   bool WasNpnNegotiated() const override;
   NextProto GetNegotiatedProtocol() const override;
   bool GetSSLInfo(SSLInfo* ssl_info) override;
+  void GetConnectionAttempts(ConnectionAttempts* out) const override;
+  void ClearConnectionAttempts() override {}
+  void AddConnectionAttempts(const ConnectionAttempts& attempts) override {}
 
   // Socket implementation.
   int Read(IOBuffer* buf,
diff --git a/remoting/protocol/channel_multiplexer.cc b/remoting/protocol/channel_multiplexer.cc
index 832fba3..8cdbf0d 100644
--- a/remoting/protocol/channel_multiplexer.cc
+++ b/remoting/protocol/channel_multiplexer.cc
@@ -165,6 +165,12 @@
     NOTIMPLEMENTED();
     return false;
   }
+  void GetConnectionAttempts(net::ConnectionAttempts* out) const override {
+    out->clear();
+  }
+  void ClearConnectionAttempts() override {}
+  void AddConnectionAttempts(const net::ConnectionAttempts& attempts) override {
+  }
 
  private:
   MuxChannel* channel_;
diff --git a/remoting/protocol/fake_stream_socket.cc b/remoting/protocol/fake_stream_socket.cc
index 7e1519a..fc7f31e5 100644
--- a/remoting/protocol/fake_stream_socket.cc
+++ b/remoting/protocol/fake_stream_socket.cc
@@ -244,6 +244,12 @@
   return false;
 }
 
+void FakeStreamSocket::GetConnectionAttempts(
+    net::ConnectionAttempts* out) const {
+  EXPECT_TRUE(task_runner_->BelongsToCurrentThread());
+  out->clear();
+}
+
 FakeStreamChannelFactory::FakeStreamChannelFactory()
     : task_runner_(base::ThreadTaskRunnerHandle::Get()),
       asynchronous_create_(false),
diff --git a/remoting/protocol/fake_stream_socket.h b/remoting/protocol/fake_stream_socket.h
index ac20e38..6bd1cbe 100644
--- a/remoting/protocol/fake_stream_socket.h
+++ b/remoting/protocol/fake_stream_socket.h
@@ -93,6 +93,10 @@
   bool WasNpnNegotiated() const override;
   net::NextProto GetNegotiatedProtocol() const override;
   bool GetSSLInfo(net::SSLInfo* ssl_info) override;
+  void GetConnectionAttempts(net::ConnectionAttempts* out) const override;
+  void ClearConnectionAttempts() override {}
+  void AddConnectionAttempts(const net::ConnectionAttempts& attempts) override {
+  }
 
  private:
   void DoAsyncWrite(scoped_refptr<net::IOBuffer> buf, int buf_len,