summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authormbelshe@chromium.org <mbelshe@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-10-26 18:29:29 +0000
committermbelshe@chromium.org <mbelshe@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-10-26 18:29:29 +0000
commit7f7e9239652bbb552bd5ea62d9702edfcf86528c (patch)
tree1b64094581e7cf5f084bc456acf933b871eea4b6 /net
parent35011c75172bd9085890021da04027925b09addd (diff)
downloadchromium_src-7f7e9239652bbb552bd5ea62d9702edfcf86528c.zip
chromium_src-7f7e9239652bbb552bd5ea62d9702edfcf86528c.tar.gz
chromium_src-7f7e9239652bbb552bd5ea62d9702edfcf86528c.tar.bz2
Add experimental option for TCP FastOpen.
Use chrome.exe --enable-tcp-fastopen BUG=none TEST=none yet Review URL: http://codereview.chromium.org/4039003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@63913 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r--net/http/http_proxy_client_socket.cc8
-rw-r--r--net/http/http_proxy_client_socket.h1
-rw-r--r--net/net.gyp1
-rw-r--r--net/socket/client_socket.h4
-rw-r--r--net/socket/client_socket_pool_base_unittest.cc1
-rw-r--r--net/socket/socket_test_util.cc4
-rw-r--r--net/socket/socket_test_util.h3
-rw-r--r--net/socket/socks5_client_socket.cc8
-rw-r--r--net/socket/socks5_client_socket.h1
-rw-r--r--net/socket/socks_client_socket.cc9
-rw-r--r--net/socket/socks_client_socket.h1
-rw-r--r--net/socket/ssl_client_socket_mac.cc8
-rw-r--r--net/socket/ssl_client_socket_mac.h1
-rw-r--r--net/socket/ssl_client_socket_nss.cc83
-rw-r--r--net/socket/ssl_client_socket_nss.h7
-rw-r--r--net/socket/ssl_client_socket_openssl.cc8
-rw-r--r--net/socket/ssl_client_socket_openssl.h1
-rw-r--r--net/socket/ssl_client_socket_win.cc8
-rw-r--r--net/socket/ssl_client_socket_win.h1
-rw-r--r--net/socket/tcp_client_socket.cc19
-rw-r--r--net/socket/tcp_client_socket.h7
-rw-r--r--net/socket/tcp_client_socket_libevent.cc59
-rw-r--r--net/socket/tcp_client_socket_libevent.h10
-rw-r--r--net/socket/tcp_client_socket_pool_unittest.cc3
-rw-r--r--net/socket/tcp_client_socket_win.cc5
-rw-r--r--net/socket/tcp_client_socket_win.h1
-rw-r--r--net/spdy/spdy_proxy_client_socket.cc4
-rw-r--r--net/spdy/spdy_proxy_client_socket.h1
28 files changed, 235 insertions, 32 deletions
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: