diff options
36 files changed, 257 insertions, 32 deletions
diff --git a/chrome/browser/browser_main.cc b/chrome/browser/browser_main.cc index 6e14230..9b0a628 100644 --- a/chrome/browser/browser_main.cc +++ b/chrome/browser/browser_main.cc @@ -93,6 +93,7 @@ #include "net/http/http_stream_factory.h" #include "net/socket/client_socket_pool_base.h" #include "net/socket/client_socket_pool_manager.h" +#include "net/socket/tcp_client_socket.h" #include "net/spdy/spdy_session.h" #include "net/spdy/spdy_session_pool.h" @@ -196,6 +197,9 @@ void BrowserMainParts::EarlyInitialization() { if (parsed_command_line().HasSwitch(switches::kEnableSnapStart)) net::SSLConfigService::EnableSnapStart(); + if (parsed_command_line().HasSwitch(switches::kEnableTcpFastOpen)) + net::set_tcp_fastopen_enabled(true); + PostEarlyInitialization(); } diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index 9b891e0..11383e1 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc @@ -501,6 +501,10 @@ const char kEnableSyncTypedUrls[] = "enable-sync-typed-urls"; // Enable tabbed options, ie: dom-ui version of options window. const char kEnableTabbedOptions[] = "enable-tabbed-options"; +// Enable use of experimental TCP sockets API for sending data in the +// SYN packet. +const char kEnableTcpFastOpen[] = "enable-tcp-fastopen"; + // Enables TopSites. const char kEnableTopSites[] = "enable-top-sites"; diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h index 62995a2..22ed845 100644 --- a/chrome/common/chrome_switches.h +++ b/chrome/common/chrome_switches.h @@ -155,6 +155,8 @@ extern const char kEnableSyncPreferences[]; extern const char kEnableSyncSessions[]; extern const char kEnableSyncTypedUrls[]; extern const char kEnableTabbedOptions[]; +extern const char kEnableTcpFastOpen[]; + extern const char kEnableTopSites[]; extern const char kEnableTouch[]; extern const char kEnableVerticalTabs[]; diff --git a/jingle/notifier/base/fake_ssl_client_socket.cc b/jingle/notifier/base/fake_ssl_client_socket.cc index cd5d5f5..2cc643d 100644 --- a/jingle/notifier/base/fake_ssl_client_socket.cc +++ b/jingle/notifier/base/fake_ssl_client_socket.cc @@ -328,4 +328,8 @@ bool FakeSSLClientSocket::WasEverUsed() const { return transport_socket_->WasEverUsed(); } +bool FakeSSLClientSocket::UsingTCPFastOpen() const { + return transport_socket_->UsingTCPFastOpen(); +} + } // namespace notifier diff --git a/jingle/notifier/base/fake_ssl_client_socket.h b/jingle/notifier/base/fake_ssl_client_socket.h index d70d4d3..8105002 100644 --- a/jingle/notifier/base/fake_ssl_client_socket.h +++ b/jingle/notifier/base/fake_ssl_client_socket.h @@ -60,6 +60,7 @@ class FakeSSLClientSocket : public net::ClientSocket { virtual void SetSubresourceSpeculation(); virtual void SetOmniboxSpeculation(); virtual bool WasEverUsed() const; + virtual bool UsingTCPFastOpen() const; private: enum HandshakeState { diff --git a/jingle/notifier/base/fake_ssl_client_socket_unittest.cc b/jingle/notifier/base/fake_ssl_client_socket_unittest.cc index 3b7aa50..bbc8fd8 100644 --- a/jingle/notifier/base/fake_ssl_client_socket_unittest.cc +++ b/jingle/notifier/base/fake_ssl_client_socket_unittest.cc @@ -60,6 +60,7 @@ class MockClientSocket : public net::ClientSocket { MOCK_METHOD0(SetSubresourceSpeculation, void()); MOCK_METHOD0(SetOmniboxSpeculation, void()); MOCK_CONST_METHOD0(WasEverUsed, bool()); + MOCK_CONST_METHOD0(UsingTCPFastOpen, bool()); }; // Break up |data| into a bunch of chunked MockReads/Writes and push diff --git a/net/http/http_proxy_client_socket.cc b/net/http/http_proxy_client_socket.cc index b5ede3d..8c1548c 100644 --- a/net/http/http_proxy_client_socket.cc +++ b/net/http/http_proxy_client_socket.cc @@ -185,6 +185,14 @@ bool HttpProxyClientSocket::WasEverUsed() const { return false; } +bool HttpProxyClientSocket::UsingTCPFastOpen() const { + if (transport_.get() && transport_->socket()) { + return transport_->socket()->UsingTCPFastOpen(); + } + NOTREACHED(); + return false; +} + int HttpProxyClientSocket::Read(IOBuffer* buf, int buf_len, CompletionCallback* callback) { DCHECK(!user_callback_); diff --git a/net/http/http_proxy_client_socket.h b/net/http/http_proxy_client_socket.h index 6530285..b42c78b 100644 --- a/net/http/http_proxy_client_socket.h +++ b/net/http/http_proxy_client_socket.h @@ -78,6 +78,7 @@ class HttpProxyClientSocket : public ClientSocket { virtual void SetSubresourceSpeculation(); virtual void SetOmniboxSpeculation(); virtual bool WasEverUsed() const; + virtual bool UsingTCPFastOpen() const; // Socket methods: virtual int Read(IOBuffer* buf, int buf_len, CompletionCallback* callback); diff --git a/net/net.gyp b/net/net.gyp index 6e7ef98..330881d 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -608,6 +608,7 @@ 'socket/ssl_client_socket_pool.h', 'socket/ssl_client_socket_win.cc', 'socket/ssl_client_socket_win.h', + 'socket/tcp_client_socket.cc', 'socket/tcp_client_socket.h', 'socket/tcp_client_socket_libevent.cc', 'socket/tcp_client_socket_libevent.h', diff --git a/net/socket/client_socket.h b/net/socket/client_socket.h index 78b2f16..358716c 100644 --- a/net/socket/client_socket.h +++ b/net/socket/client_socket.h @@ -69,6 +69,10 @@ class ClientSocket : public Socket { // this call to the transport socket. virtual bool WasEverUsed() const = 0; + // Returns true if the underlying transport socket is using TCP FastOpen. + // TCP FastOpen is an experiment with sending data in the TCP SYN packet. + virtual bool UsingTCPFastOpen() const = 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/client_socket_pool_base_unittest.cc b/net/socket/client_socket_pool_base_unittest.cc index 820b030..f8d2d1f 100644 --- a/net/socket/client_socket_pool_base_unittest.cc +++ b/net/socket/client_socket_pool_base_unittest.cc @@ -81,6 +81,7 @@ class MockClientSocket : public ClientSocket { virtual void SetSubresourceSpeculation() {} virtual void SetOmniboxSpeculation() {} virtual bool WasEverUsed() const { return was_used_to_convey_data_; } + virtual bool UsingTCPFastOpen() const { return false; } private: bool connected_; diff --git a/net/socket/socket_test_util.cc b/net/socket/socket_test_util.cc index 53bcf89..afffe26 100644 --- a/net/socket/socket_test_util.cc +++ b/net/socket/socket_test_util.cc @@ -513,6 +513,10 @@ bool MockSSLClientSocket::WasEverUsed() const { return transport_->socket()->WasEverUsed(); } +bool MockSSLClientSocket::UsingTCPFastOpen() const { + return transport_->socket()->UsingTCPFastOpen(); +} + int MockSSLClientSocket::Read(net::IOBuffer* buf, int buf_len, net::CompletionCallback* callback) { return transport_->socket()->Read(buf, buf_len, callback); diff --git a/net/socket/socket_test_util.h b/net/socket/socket_test_util.h index a8e4537..d96087c 100644 --- a/net/socket/socket_test_util.h +++ b/net/socket/socket_test_util.h @@ -609,6 +609,7 @@ class MockTCPClientSocket : public MockClientSocket { virtual bool IsConnected() const; virtual bool IsConnectedAndIdle() const { return IsConnected(); } virtual bool WasEverUsed() const { return was_used_to_convey_data_; } + virtual bool UsingTCPFastOpen() const { return false; } // Socket methods: virtual int Read(net::IOBuffer* buf, int buf_len, @@ -654,6 +655,7 @@ class DeterministicMockTCPClientSocket : public MockClientSocket, virtual bool IsConnected() const; virtual bool IsConnectedAndIdle() const { return IsConnected(); } virtual bool WasEverUsed() const { return was_used_to_convey_data_; } + virtual bool UsingTCPFastOpen() const { return false; } // Socket methods: virtual int Write(net::IOBuffer* buf, int buf_len, @@ -698,6 +700,7 @@ class MockSSLClientSocket : public MockClientSocket { virtual void Disconnect(); virtual bool IsConnected() const; virtual bool WasEverUsed() const; + virtual bool UsingTCPFastOpen() const; // Socket methods: virtual int Read(net::IOBuffer* buf, int buf_len, diff --git a/net/socket/socks5_client_socket.cc b/net/socket/socks5_client_socket.cc index ef929fd..6e28a490 100644 --- a/net/socket/socks5_client_socket.cc +++ b/net/socket/socks5_client_socket.cc @@ -130,6 +130,14 @@ bool SOCKS5ClientSocket::WasEverUsed() const { return false; } +bool SOCKS5ClientSocket::UsingTCPFastOpen() const { + if (transport_.get() && transport_->socket()) { + return transport_->socket()->UsingTCPFastOpen(); + } + NOTREACHED(); + return false; +} + // Read is called by the transport layer above to read. This can only be done // if the SOCKS handshake is complete. int SOCKS5ClientSocket::Read(IOBuffer* buf, int buf_len, diff --git a/net/socket/socks5_client_socket.h b/net/socket/socks5_client_socket.h index 72e27db..5a918cc 100644 --- a/net/socket/socks5_client_socket.h +++ b/net/socket/socks5_client_socket.h @@ -59,6 +59,7 @@ class SOCKS5ClientSocket : public ClientSocket { virtual void SetSubresourceSpeculation(); virtual void SetOmniboxSpeculation(); virtual bool WasEverUsed() const; + virtual bool UsingTCPFastOpen() const; // Socket methods: virtual int Read(IOBuffer* buf, int buf_len, CompletionCallback* callback); diff --git a/net/socket/socks_client_socket.cc b/net/socket/socks_client_socket.cc index 93b41a5..a4a7391 100644 --- a/net/socket/socks_client_socket.cc +++ b/net/socket/socks_client_socket.cc @@ -164,6 +164,15 @@ bool SOCKSClientSocket::WasEverUsed() const { return false; } +bool SOCKSClientSocket::UsingTCPFastOpen() const { + if (transport_.get() && transport_->socket()) { + return transport_->socket()->UsingTCPFastOpen(); + } + NOTREACHED(); + return false; +} + + // Read is called by the transport layer above to read. This can only be done // if the SOCKS handshake is complete. int SOCKSClientSocket::Read(IOBuffer* buf, int buf_len, diff --git a/net/socket/socks_client_socket.h b/net/socket/socks_client_socket.h index e9e9695..86511cf 100644 --- a/net/socket/socks_client_socket.h +++ b/net/socket/socks_client_socket.h @@ -56,6 +56,7 @@ class SOCKSClientSocket : public ClientSocket { virtual void SetSubresourceSpeculation(); virtual void SetOmniboxSpeculation(); virtual bool WasEverUsed() const; + virtual bool UsingTCPFastOpen() const; // Socket methods: virtual int Read(IOBuffer* buf, int buf_len, CompletionCallback* callback); diff --git a/net/socket/ssl_client_socket_mac.cc b/net/socket/ssl_client_socket_mac.cc index 6a8270e..02c2097 100644 --- a/net/socket/ssl_client_socket_mac.cc +++ b/net/socket/ssl_client_socket_mac.cc @@ -614,6 +614,14 @@ bool SSLClientSocketMac::WasEverUsed() const { return false; } +bool SSLClientSocketMac::UsingTCPFastOpen() const { + if (transport_.get() && transport_->socket()) { + return transport_->socket()->UsingTCPFastOpen(); + } + NOTREACHED(); + return false; +} + int SSLClientSocketMac::Read(IOBuffer* buf, int buf_len, CompletionCallback* callback) { DCHECK(completed_handshake()); diff --git a/net/socket/ssl_client_socket_mac.h b/net/socket/ssl_client_socket_mac.h index 00438fc..0763fd3 100644 --- a/net/socket/ssl_client_socket_mac.h +++ b/net/socket/ssl_client_socket_mac.h @@ -50,6 +50,7 @@ class SSLClientSocketMac : public SSLClientSocket { virtual void SetSubresourceSpeculation(); virtual void SetOmniboxSpeculation(); virtual bool WasEverUsed() const; + virtual bool UsingTCPFastOpen() const; // Socket methods: virtual int Read(IOBuffer* buf, int buf_len, CompletionCallback* callback); diff --git a/net/socket/ssl_client_socket_nss.cc b/net/socket/ssl_client_socket_nss.cc index fc9a360..6d3f54f 100644 --- a/net/socket/ssl_client_socket_nss.cc +++ b/net/socket/ssl_client_socket_nss.cc @@ -387,6 +387,7 @@ SSLClientSocketNSS::SSLClientSocketNSS(ClientSocketHandle* transport_socket, pseudo_connected_(false), eset_mitm_detected_(false), netnanny_mitm_detected_(false), + peername_initialized_(false), dnssec_provider_(NULL), next_handshake_state_(STATE_NONE), nss_fd_(NULL), @@ -589,6 +590,16 @@ int SSLClientSocketNSS::Connect(CompletionCallback* callback) { return rv; } + // Attempt to initialize the peer name. In the case of TCP FastOpen, + // we don't have the peer yet. + if (!UsingTCPFastOpen()) { + rv = InitializeSSLPeerName(); + if (rv != OK) { + net_log_.EndEvent(NetLog::TYPE_SSL_CONNECT, NULL); + return rv; + } + } + if (ssl_config_.snap_start_enabled && ssl_host_info_.get()) { GotoState(STATE_SNAP_START_LOAD_INFO); } else { @@ -619,28 +630,6 @@ int SSLClientSocketNSS::InitializeSSLOptions() { return ERR_OUT_OF_MEMORY; // TODO(port): map NSPR error code. } - // Tell NSS who we're connected to - AddressList peer_address; - int err = transport_->socket()->GetPeerAddress(&peer_address); - if (err != OK) - return err; - - const struct addrinfo* ai = peer_address.head(); - - PRNetAddr peername; - memset(&peername, 0, sizeof(peername)); - DCHECK_LE(ai->ai_addrlen, sizeof(peername)); - size_t len = std::min(static_cast<size_t>(ai->ai_addrlen), sizeof(peername)); - memcpy(&peername, ai->ai_addr, len); - - // Adjust the address family field for BSD, whose sockaddr - // structure has a one-byte length and one-byte address family - // field at the beginning. PRNetAddr has a two-byte address - // family field at the beginning. - peername.raw.family = ai->ai_addr->sa_family; - - memio_SetPeerName(nss_fd_, &peername); - // Grab pointer to buffers nss_bufs_ = memio_GetSecret(nss_fd_); @@ -795,6 +784,36 @@ int SSLClientSocketNSS::InitializeSSLOptions() { // Tell SSL the hostname we're trying to connect to. SSL_SetURL(nss_fd_, hostname_.c_str()); + // Tell SSL we're a client; needed if not letting NSPR do socket I/O + SSL_ResetHandshake(nss_fd_, 0); + + return OK; +} + +int SSLClientSocketNSS::InitializeSSLPeerName() { + // Tell NSS who we're connected to + AddressList peer_address; + int err = transport_->socket()->GetPeerAddress(&peer_address); + if (err != OK) + return err; + + const struct addrinfo* ai = peer_address.head(); + + PRNetAddr peername; + memset(&peername, 0, sizeof(peername)); + DCHECK_LE(ai->ai_addrlen, sizeof(peername)); + size_t len = std::min(static_cast<size_t>(ai->ai_addrlen), + sizeof(peername)); + memcpy(&peername, ai->ai_addr, len); + + // Adjust the address family field for BSD, whose sockaddr + // structure has a one-byte length and one-byte address family + // field at the beginning. PRNetAddr has a two-byte address + // family field at the beginning. + peername.raw.family = ai->ai_addr->sa_family; + + memio_SetPeerName(nss_fd_, &peername); + // Set the peer ID for session reuse. This is necessary when we create an // SSL tunnel through a proxy -- GetPeerName returns the proxy's address // rather than the destination server's address in that case. @@ -802,13 +821,11 @@ int SSLClientSocketNSS::InitializeSSLOptions() { // used. std::string peer_id = base::StringPrintf("%s:%d", hostname_.c_str(), peer_address.GetPort()); - rv = SSL_SetSockPeerID(nss_fd_, const_cast<char*>(peer_id.c_str())); + SECStatus rv = SSL_SetSockPeerID(nss_fd_, const_cast<char*>(peer_id.c_str())); if (rv != SECSuccess) LogFailedNSSFunction(net_log_, "SSL_SetSockPeerID", peer_id.c_str()); - // Tell SSL we're a client; needed if not letting NSPR do socket I/O - SSL_ResetHandshake(nss_fd_, 0); - + peername_initialized_ = true; return OK; } @@ -854,6 +871,7 @@ void SSLClientSocketNSS::Disconnect() { pseudo_connected_ = false; eset_mitm_detected_ = false; netnanny_mitm_detected_= false; + peername_initialized_ = false; nss_bufs_ = NULL; client_certs_.clear(); client_auth_cert_needed_ = false; @@ -919,6 +937,14 @@ bool SSLClientSocketNSS::WasEverUsed() const { return false; } +bool SSLClientSocketNSS::UsingTCPFastOpen() const { + if (transport_.get() && transport_->socket()) { + return transport_->socket()->UsingTCPFastOpen(); + } + NOTREACHED(); + return false; +} + int SSLClientSocketNSS::Read(IOBuffer* buf, int buf_len, CompletionCallback* callback) { EnterFunction(buf_len); @@ -1373,6 +1399,11 @@ int SSLClientSocketNSS::BufferSend(void) { void SSLClientSocketNSS::BufferSendComplete(int result) { EnterFunction(result); + + // In the case of TCP FastOpen, connect is now finished. + if (!peername_initialized_ && UsingTCPFastOpen()) + InitializeSSLPeerName(); + memio_PutWriteResult(nss_bufs_, MapErrorToNSS(result)); transport_send_busy_ = false; OnSendComplete(result); diff --git a/net/socket/ssl_client_socket_nss.h b/net/socket/ssl_client_socket_nss.h index 098ef75..ce5fef8 100644 --- a/net/socket/ssl_client_socket_nss.h +++ b/net/socket/ssl_client_socket_nss.h @@ -62,6 +62,7 @@ class SSLClientSocketNSS : public SSLClientSocket { virtual void SetSubresourceSpeculation(); virtual void SetOmniboxSpeculation(); virtual bool WasEverUsed() const; + virtual bool UsingTCPFastOpen() const; // Socket methods: virtual int Read(IOBuffer* buf, int buf_len, CompletionCallback* callback); @@ -73,6 +74,9 @@ class SSLClientSocketNSS : public SSLClientSocket { // Initializes NSS SSL options. Returns a net error code. int InitializeSSLOptions(); + // Initializes the socket peer name in SSL. Returns a net error code. + int InitializeSSLPeerName(); + void InvalidateSessionIfBadCertificate(); #if defined(OS_MACOSX) || defined(OS_WIN) // Creates an OS certificate from a DER-encoded certificate. @@ -188,6 +192,9 @@ class SSLClientSocketNSS : public SSLClientSocket { // connections. bool netnanny_mitm_detected_; + // True if the peer name has been initialized. + bool peername_initialized_; + // This pointer is owned by the caller of UseDNSSEC. DNSSECProvider* dnssec_provider_; // The time when we started waiting for DNSSEC records. diff --git a/net/socket/ssl_client_socket_openssl.cc b/net/socket/ssl_client_socket_openssl.cc index bbe3ee4..3aae457 100644 --- a/net/socket/ssl_client_socket_openssl.cc +++ b/net/socket/ssl_client_socket_openssl.cc @@ -631,6 +631,14 @@ bool SSLClientSocketOpenSSL::WasEverUsed() const { return false; } +bool SSLClientSocketOpenSSL::UsingTCPFastOpen() const { + if (transport_.get() && transport_->socket()) + return transport_->socket()->UsingTCPFastOpen(); + + NOTREACHED(); + return false; +} + // Socket methods int SSLClientSocketOpenSSL::Read(IOBuffer* buf, diff --git a/net/socket/ssl_client_socket_openssl.h b/net/socket/ssl_client_socket_openssl.h index 20f321e..31d5c1c 100644 --- a/net/socket/ssl_client_socket_openssl.h +++ b/net/socket/ssl_client_socket_openssl.h @@ -51,6 +51,7 @@ class SSLClientSocketOpenSSL : public SSLClientSocket { virtual void SetSubresourceSpeculation(); virtual void SetOmniboxSpeculation(); virtual bool WasEverUsed() const; + virtual bool UsingTCPFastOpen() const; // Socket methods: virtual int Read(IOBuffer* buf, int buf_len, CompletionCallback* callback); diff --git a/net/socket/ssl_client_socket_win.cc b/net/socket/ssl_client_socket_win.cc index 7e8c29e..eead7ed 100644 --- a/net/socket/ssl_client_socket_win.cc +++ b/net/socket/ssl_client_socket_win.cc @@ -705,6 +705,14 @@ bool SSLClientSocketWin::WasEverUsed() const { return false; } +bool SSLClientSocketWin::UsingTCPFastOpen() const { + if (transport_.get() && transport_->socket()) { + return transport_->socket()->UsingTCPFastOpen(); + } + NOTREACHED(); + return false; +} + int SSLClientSocketWin::Read(IOBuffer* buf, int buf_len, CompletionCallback* callback) { DCHECK(completed_handshake()); diff --git a/net/socket/ssl_client_socket_win.h b/net/socket/ssl_client_socket_win.h index 38cdb32..4f96e80 100644 --- a/net/socket/ssl_client_socket_win.h +++ b/net/socket/ssl_client_socket_win.h @@ -54,6 +54,7 @@ class SSLClientSocketWin : public SSLClientSocket { virtual void SetSubresourceSpeculation(); virtual void SetOmniboxSpeculation(); virtual bool WasEverUsed() const; + virtual bool UsingTCPFastOpen() const; // Socket methods: virtual int Read(IOBuffer* buf, int buf_len, CompletionCallback* callback); diff --git a/net/socket/tcp_client_socket.cc b/net/socket/tcp_client_socket.cc new file mode 100644 index 0000000..d3d4e20 --- /dev/null +++ b/net/socket/tcp_client_socket.cc @@ -0,0 +1,19 @@ +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/socket/tcp_client_socket.h" + +namespace net { + +static bool g_tcp_fastopen_enabled = false; + +void set_tcp_fastopen_enabled(bool value) { + g_tcp_fastopen_enabled = value; +} + +bool is_tcp_fastopen_enabled() { + return g_tcp_fastopen_enabled; +} + +} // namespace net diff --git a/net/socket/tcp_client_socket.h b/net/socket/tcp_client_socket.h index 6139b5b..13d2dfb 100644 --- a/net/socket/tcp_client_socket.h +++ b/net/socket/tcp_client_socket.h @@ -23,6 +23,13 @@ typedef TCPClientSocketWin TCPClientSocket; typedef TCPClientSocketLibevent TCPClientSocket; #endif +// Enable/disable experimental TCP FastOpen option. +// Not thread safe. Must be called during initialization/startup only. +void set_tcp_fastopen_enabled(bool value); + +// Check if the TCP FastOpen option is enabled. +bool is_tcp_fastopen_enabled(); + } // namespace net #endif // NET_SOCKET_TCP_CLIENT_SOCKET_H_ diff --git a/net/socket/tcp_client_socket_libevent.cc b/net/socket/tcp_client_socket_libevent.cc index cda0955..30f7bc2 100644 --- a/net/socket/tcp_client_socket_libevent.cc +++ b/net/socket/tcp_client_socket_libevent.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "net/socket/tcp_client_socket_libevent.h" +#include "net/socket/tcp_client_socket.h" #include <errno.h> #include <fcntl.h> @@ -121,11 +121,16 @@ TCPClientSocketLibevent::TCPClientSocketLibevent( next_connect_state_(CONNECT_STATE_NONE), connect_os_error_(0), net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_SOCKET)), - previously_disconnected_(false) { + previously_disconnected_(false), + use_tcp_fastopen_(false), + tcp_fastopen_connected_(false) { scoped_refptr<NetLog::EventParameters> params; if (source.is_valid()) params = new NetLogSourceParameter("source_dependency", source); net_log_.BeginEvent(NetLog::TYPE_SOCKET_ALIVE, params); + + if (is_tcp_fastopen_enabled()) + use_tcp_fastopen_ = true; } TCPClientSocketLibevent::~TCPClientSocketLibevent() { @@ -212,9 +217,15 @@ int TCPClientSocketLibevent::DoConnect() { return MapPosixError(connect_os_error_); // Connect the socket. - if (!HANDLE_EINTR(connect(socket_, current_ai_->ai_addr, - static_cast<int>(current_ai_->ai_addrlen)))) { - // Connected without waiting! + if (!use_tcp_fastopen_) { + if (!HANDLE_EINTR(connect(socket_, current_ai_->ai_addr, + static_cast<int>(current_ai_->ai_addrlen)))) { + // Connected without waiting! + return OK; + } + } else { + // With TCP FastOpen, we pretend that the socket is connected. + DCHECK(!tcp_fastopen_connected_); return OK; } @@ -372,7 +383,7 @@ int TCPClientSocketLibevent::Write(IOBuffer* buf, DCHECK(callback); DCHECK_GT(buf_len, 0); - int nwrite = HANDLE_EINTR(write(socket_, buf->data(), buf_len)); + int nwrite = InternalWrite(buf, buf_len); if (nwrite >= 0) { static base::StatsCounter write_bytes("tcp.write_bytes"); write_bytes.Add(nwrite); @@ -398,6 +409,38 @@ int TCPClientSocketLibevent::Write(IOBuffer* buf, return ERR_IO_PENDING; } +int TCPClientSocketLibevent::InternalWrite(IOBuffer* buf, int buf_len) { + int nwrite; + if (use_tcp_fastopen_ && !tcp_fastopen_connected_) { + // We have a limited amount of data to send in the SYN packet. + int kMaxFastOpenSendLength = 1420; + + buf_len = std::min(kMaxFastOpenSendLength, buf_len); + + int flags = 0x20000000; // Magic flag to enable TCP_FASTOPEN + nwrite = HANDLE_EINTR(sendto(socket_, + buf->data(), + buf_len, + flags, + current_ai_->ai_addr, + static_cast<int>(current_ai_->ai_addrlen))); + tcp_fastopen_connected_ = true; + + if (nwrite < 0) { + // Non-blocking mode is returning EINPROGRESS rather than EAGAIN. + if (errno == EINPROGRESS) + errno = EAGAIN; + + // Unlike "normal" nonblocking sockets, the data is already queued, + // so tell the app that we've consumed it. + return buf_len; + } + } else { + nwrite = HANDLE_EINTR(write(socket_, buf->data(), buf_len)); + } + return nwrite; +} + bool TCPClientSocketLibevent::SetReceiveBufferSize(int32 size) { DCHECK(CalledOnValidThread()); int rv = setsockopt(socket_, SOL_SOCKET, SO_RCVBUF, @@ -560,4 +603,8 @@ bool TCPClientSocketLibevent::WasEverUsed() const { return use_history_.was_used_to_convey_data(); } +bool TCPClientSocketLibevent::UsingTCPFastOpen() const { + return use_tcp_fastopen_; +} + } // namespace net diff --git a/net/socket/tcp_client_socket_libevent.h b/net/socket/tcp_client_socket_libevent.h index 72f7d2d..890d0c2 100644 --- a/net/socket/tcp_client_socket_libevent.h +++ b/net/socket/tcp_client_socket_libevent.h @@ -43,6 +43,7 @@ class TCPClientSocketLibevent : public ClientSocket, NonThreadSafe { virtual void SetSubresourceSpeculation(); virtual void SetOmniboxSpeculation(); virtual bool WasEverUsed() const; + virtual bool UsingTCPFastOpen() const; // Socket methods: // Multiple outstanding requests are not supported. @@ -127,6 +128,9 @@ class TCPClientSocketLibevent : public ClientSocket, NonThreadSafe { // Helper to add a TCP_CONNECT (end) event to the NetLog. void LogConnectCompletion(int net_error); + // Internal function to write to a socket. + int InternalWrite(IOBuffer* buf, int buf_len); + int socket_; // The list of addresses we should try in order to establish a connection. @@ -172,6 +176,12 @@ class TCPClientSocketLibevent : public ClientSocket, NonThreadSafe { // histograms. UseHistory use_history_; + // Enables experimental TCP FastOpen option. + bool use_tcp_fastopen_; + + // True when TCP FastOpen is in use and we have done the connect. + bool tcp_fastopen_connected_; + DISALLOW_COPY_AND_ASSIGN(TCPClientSocketLibevent); }; diff --git a/net/socket/tcp_client_socket_pool_unittest.cc b/net/socket/tcp_client_socket_pool_unittest.cc index 80de0aa..ffb530b 100644 --- a/net/socket/tcp_client_socket_pool_unittest.cc +++ b/net/socket/tcp_client_socket_pool_unittest.cc @@ -54,6 +54,7 @@ class MockClientSocket : public ClientSocket { virtual void SetSubresourceSpeculation() {} virtual void SetOmniboxSpeculation() {} virtual bool WasEverUsed() const { return false; } + virtual bool UsingTCPFastOpen() const { return false; } // Socket methods: virtual int Read(IOBuffer* buf, int buf_len, @@ -99,6 +100,7 @@ class MockFailingClientSocket : public ClientSocket { virtual void SetSubresourceSpeculation() {} virtual void SetOmniboxSpeculation() {} virtual bool WasEverUsed() const { return false; } + virtual bool UsingTCPFastOpen() const { return false; } // Socket methods: virtual int Read(IOBuffer* buf, int buf_len, @@ -157,6 +159,7 @@ class MockPendingClientSocket : public ClientSocket { virtual void SetSubresourceSpeculation() {} virtual void SetOmniboxSpeculation() {} virtual bool WasEverUsed() const { return false; } + virtual bool UsingTCPFastOpen() const { return false; } // Socket methods: virtual int Read(IOBuffer* buf, int buf_len, diff --git a/net/socket/tcp_client_socket_win.cc b/net/socket/tcp_client_socket_win.cc index f70d61b..740b249 100644 --- a/net/socket/tcp_client_socket_win.cc +++ b/net/socket/tcp_client_socket_win.cc @@ -543,6 +543,11 @@ bool TCPClientSocketWin::WasEverUsed() const { return use_history_.was_used_to_convey_data(); } +bool TCPClientSocketWin::UsingTCPFastOpen() const { + // Not supported on windows. + return false; +} + int TCPClientSocketWin::Read(IOBuffer* buf, int buf_len, CompletionCallback* callback) { diff --git a/net/socket/tcp_client_socket_win.h b/net/socket/tcp_client_socket_win.h index 5d560d6..cd6b066 100644 --- a/net/socket/tcp_client_socket_win.h +++ b/net/socket/tcp_client_socket_win.h @@ -40,6 +40,7 @@ class TCPClientSocketWin : public ClientSocket, NonThreadSafe { virtual void SetSubresourceSpeculation(); virtual void SetOmniboxSpeculation(); virtual bool WasEverUsed() const; + virtual bool UsingTCPFastOpen() const; // Socket methods: // Multiple outstanding requests are not supported. diff --git a/net/spdy/spdy_proxy_client_socket.cc b/net/spdy/spdy_proxy_client_socket.cc index 53bcb0d..91936a0 100644 --- a/net/spdy/spdy_proxy_client_socket.cc +++ b/net/spdy/spdy_proxy_client_socket.cc @@ -120,6 +120,10 @@ bool SpdyProxyClientSocket::WasEverUsed() const { return was_ever_used_ || (spdy_stream_ && spdy_stream_->WasEverUsed()); } +bool SpdyProxyClientSocket::UsingTCPFastOpen() const { + return false; +} + int SpdyProxyClientSocket::Read(IOBuffer* buf, int buf_len, CompletionCallback* callback) { DCHECK(!read_callback_); diff --git a/net/spdy/spdy_proxy_client_socket.h b/net/spdy/spdy_proxy_client_socket.h index b993b5e..4a0747e 100644 --- a/net/spdy/spdy_proxy_client_socket.h +++ b/net/spdy/spdy_proxy_client_socket.h @@ -71,6 +71,7 @@ class SpdyProxyClientSocket : public ClientSocket, public SpdyStream::Delegate { virtual void SetSubresourceSpeculation(); virtual void SetOmniboxSpeculation(); virtual bool WasEverUsed() const; + virtual bool UsingTCPFastOpen() const; // Socket methods: diff --git a/remoting/jingle_glue/ssl_socket_adapter.cc b/remoting/jingle_glue/ssl_socket_adapter.cc index 8936963..3bdde0e 100644 --- a/remoting/jingle_glue/ssl_socket_adapter.cc +++ b/remoting/jingle_glue/ssl_socket_adapter.cc @@ -247,6 +247,11 @@ bool TransportSocket::WasEverUsed() const { return was_used_to_convey_data_; } +bool TransportSocket::UsingTCPFastOpen() const { + NOTREACHED(); + return false; +} + int TransportSocket::Read(net::IOBuffer* buf, int buf_len, net::CompletionCallback* callback) { DCHECK(buf); diff --git a/remoting/jingle_glue/ssl_socket_adapter.h b/remoting/jingle_glue/ssl_socket_adapter.h index a22e2b7..9191fa0 100644 --- a/remoting/jingle_glue/ssl_socket_adapter.h +++ b/remoting/jingle_glue/ssl_socket_adapter.h @@ -46,6 +46,7 @@ class TransportSocket : public net::ClientSocket, public sigslot::has_slots<> { virtual void SetSubresourceSpeculation(); virtual void SetOmniboxSpeculation(); virtual bool WasEverUsed() const; + virtual bool UsingTCPFastOpen() const; // net::Socket implementation |