Bug 1310197 - Implement fast open nspr part. r=mcmanus,bagder
☠☠ backed out by 1e4d5a3704d0 ☠ ☠
authorDragana Damjanovic <dd.mozilla@gmail.com>
Thu, 13 Apr 2017 22:44:54 +0200
changeset 353009 54d50e4a99b24c09ae6671f9cb26625234adc5ba
parent 353008 f101a7b5c1353bf474722c36d1f7b95bc9f8a78f
child 353010 10c1d50e50bda3c9235afd1cdbe54228f90433c1
push id31655
push userihsiao@mozilla.com
push dateFri, 14 Apr 2017 09:07:09 +0000
treeherdermozilla-central@8f806306fb83 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmcmanus, bagder
bugs1310197
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1310197 - Implement fast open nspr part. r=mcmanus,bagder
build/moz.configure/init.configure
config/external/nspr/pr/moz.build
nsprpub/configure
nsprpub/configure.in
nsprpub/pr/include/md/_win95.h
nsprpub/pr/include/private/primpl.h
nsprpub/pr/src/io/prsocket.c
nsprpub/pr/src/md/windows/w95sock.c
nsprpub/pr/src/pthreads/ptio.c
--- a/build/moz.configure/init.configure
+++ b/build/moz.configure/init.configure
@@ -499,16 +499,23 @@ add_old_configure_assignment('CROSS_COMP
 def have_64_bit(target):
     if target.bitness == 64:
         return True
 
 set_config('HAVE_64BIT_BUILD', have_64_bit)
 set_define('HAVE_64BIT_BUILD', have_64_bit)
 add_old_configure_assignment('HAVE_64BIT_BUILD', have_64_bit)
 
+@depends(host)
+def host_os_kernel_major_version(host):
+    versions = host.raw_os.split('.')
+    version = ''.join(x for x in versions[0] if x.isdigit())
+    return version
+
+set_config('HOST_MAJOR_VERSION', host_os_kernel_major_version)
 
 # Autoconf needs these set
 @depends(host)
 def host_for_old_configure(host):
     return '--host=%s' % host.alias
 
 add_old_configure_arg(host_for_old_configure)
 
--- a/config/external/nspr/pr/moz.build
+++ b/config/external/nspr/pr/moz.build
@@ -35,16 +35,20 @@ elif CONFIG['OS_TARGET'] in ('FreeBSD', 
         HAVE_BSD_FLOCK=True,
         HAVE_SOCKLEN_T=True,
         HAVE_POINTER_LOCALTIME_R=True,
     )
     DEFINES[CONFIG['OS_TARGET'].upper()] = True
     SOURCES += ['/nsprpub/pr/src/md/unix/%s.c' % CONFIG['OS_TARGET'].lower()]
 elif CONFIG['OS_TARGET'] == 'Darwin':
     OS_LIBS += ['-framework CoreServices']
+    if CONFIG['HOST_MAJOR_VERSION'] == '15':
+        DEFINES.update(
+            HAS_CONNECTX=True,
+        )
     DEFINES.update(
         DARWIN=True,
         HAVE_BSD_FLOCK=True,
         HAVE_SOCKLEN_T=True,
         HAVE_POINTER_LOCALTIME_R=True,
     )
     SOURCES += [
         '/nsprpub/pr/src/md/unix/darwin.c',
--- a/nsprpub/configure
+++ b/nsprpub/configure
@@ -6555,16 +6555,21 @@ fi
     $as_echo "#define DARWIN 1" >>confdefs.h
 
     $as_echo "#define HAVE_BSD_FLOCK 1" >>confdefs.h
 
     $as_echo "#define HAVE_SOCKLEN_T 1" >>confdefs.h
 
     $as_echo "#define HAVE_POINTER_LOCALTIME_R 1" >>confdefs.h
 
+    HOST_DARWIN_MAJOR=`echo "$build_os" | sed -E -e 's/^darwin([0-9]+).*$/\1/'`
+    if test "$HOST_DARWIN_MAJOR" -ge 15 ; then
+        $as_echo "#define HAS_CONNECTX 1" >>confdefs.h
+    fi
+
     AS='$(CC) -x assembler-with-cpp'
     CFLAGS="$CFLAGS -Wall -fno-common"
     case "${target_cpu}" in
         arm*)
             CPU_ARCH=arm
             ;;
         i*86*|x86_64)
             if test -n "$USE_64"; then
--- a/nsprpub/configure.in
+++ b/nsprpub/configure.in
@@ -1379,16 +1379,21 @@ case "$target" in
     ;;
 
 *-darwin*)
     AC_DEFINE(XP_UNIX)
     AC_DEFINE(DARWIN)
     AC_DEFINE(HAVE_BSD_FLOCK)
     AC_DEFINE(HAVE_SOCKLEN_T)
     AC_DEFINE(HAVE_POINTER_LOCALTIME_R)
+    changequote(,)
+    HOST_DARWIN_MAJOR=`echo "$build_os" | sed -E -e 's/^darwin([0-9]+).*$/\1/'`
+    changequote([,])
+    if test "$HOST_DARWIN_MAJOR" -ge 15 ; then
+        AC_DEFINE(HAS_CONNECTX)
     AS='$(CC) -x assembler-with-cpp'
     CFLAGS="$CFLAGS -Wall -fno-common"
     case "${target_cpu}" in
         arm*)
             CPU_ARCH=arm
             ;;
         i*86*|x86_64)
             if test -n "$USE_64"; then
--- a/nsprpub/pr/include/md/_win95.h
+++ b/nsprpub/pr/include/md/_win95.h
@@ -285,16 +285,19 @@ extern void _MD_MakeNonblock(PRFileDesc 
 #define _MD_MAKE_NONBLOCK             _MD_MakeNonblock
 #define _MD_INIT_FD_INHERITABLE       _PR_MD_INIT_FD_INHERITABLE
 #define _MD_QUERY_FD_INHERITABLE      _PR_MD_QUERY_FD_INHERITABLE
 #define _MD_SHUTDOWN                  _PR_MD_SHUTDOWN
 #define _MD_LISTEN                    _PR_MD_LISTEN
 extern PRInt32 _MD_CloseSocket(PROsfd osfd);
 #define _MD_CLOSE_SOCKET              _MD_CloseSocket
 #define _MD_SENDTO                    _PR_MD_SENDTO
+#ifdef _WIN64
+#define _MD_TCPSENDTO                 _PR_MD_TCPSENDTO
+#endif
 #define _MD_RECVFROM                  _PR_MD_RECVFROM
 #define _MD_SOCKETPAIR(s, type, proto, sv) -1
 #define _MD_GETSOCKNAME               _PR_MD_GETSOCKNAME
 #define _MD_GETPEERNAME               _PR_MD_GETPEERNAME
 #define _MD_GETSOCKOPT                _PR_MD_GETSOCKOPT
 #define _MD_SETSOCKOPT                _PR_MD_SETSOCKOPT
 #define _MD_SET_FD_INHERITABLE        _PR_MD_SET_FD_INHERITABLE
 #define _MD_SELECT                    select
--- a/nsprpub/pr/include/private/primpl.h
+++ b/nsprpub/pr/include/private/primpl.h
@@ -1220,16 +1220,23 @@ extern PRInt32 _PR_MD_RECVFROM(
     PRNetAddr *addr, PRUint32 *addrlen, PRIntervalTime timeout);
 #define    _PR_MD_RECVFROM _MD_RECVFROM
 
 extern PRInt32 _PR_MD_SENDTO(
     PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
     const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout);
 #define    _PR_MD_SENDTO _MD_SENDTO
 
+#if defined(_WIN64) && defined(WIN95)
+extern PRInt32 _PR_MD_TCPSENDTO(
+    PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
+    const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout);
+#define    _PR_MD_TCPSENDTO _MD_TCPSENDTO
+#endif
+
 extern PRInt32 _PR_MD_SOCKETPAIR(int af, int type, int flags, PROsfd *osfd);
 #define    _PR_MD_SOCKETPAIR _MD_SOCKETPAIR
 
 extern PROsfd _PR_MD_SOCKET(int af, int type, int flags);
 #define    _PR_MD_SOCKET _MD_SOCKET
 
 extern PRInt32 _PR_MD_SOCKETAVAILABLE(PRFileDesc *fd);
 #define    _PR_MD_SOCKETAVAILABLE _MD_SOCKETAVAILABLE
@@ -1742,16 +1749,28 @@ struct PRFilePrivate {
                          * append mode.  See Bugzilla 4090, 276330. */
 #endif
     _MDFileDesc md;
 #ifdef _PR_NEED_SECRET_AF
     PRUint16 af;        /* If the platform's implementation of accept()
                          * requires knowing the address family of the 
 			 * socket, we save the address family here. */
 #endif
+
+#if defined(_WIN64)
+    /* This is necessary for TCP Fast Open. TCP Fast Open in windows must
+     * use ConnectEx function which uses OVERLAPPED. TCPSendTo will call
+     * ConnectEx to send fast open data. If ConnectEx returns
+     * ERROR_IO_PENDING we need to save OVERLAPPED structure and we will
+     * use it in ConnectContinue to get the final result of ConnectEx.
+     */
+    PRBool alreadyConnected;
+    PRBool overlappedActive;
+    OVERLAPPED ol;
+#endif
 };
 
 #ifdef _WIN64
 #define PR_PRIdOSFD "lld"       /* for printing PROsfd */
 #define PR_PRIxOSFD "llx"
 #define PR_SCNdOSFD "lld"       /* for scanning PROsfd */
 #define PR_SCNxOSFD "llx"
 #else
--- a/nsprpub/pr/src/io/prsocket.c
+++ b/nsprpub/pr/src/io/prsocket.c
@@ -2,16 +2,22 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "primpl.h"
 
 #include <string.h>
 
+#if defined(_WIN64)
+#ifndef SO_UPDATE_CONNECT_CONTEXT
+#define SO_UPDATE_CONNECT_CONTEXT 0x7010
+#endif
+#endif
+
 /************************************************************************/
 
 /* These two functions are only used in assertions. */
 #if defined(DEBUG)
 
 PRBool IsValidNetAddr(const PRNetAddr *addr)
 {
     if ((addr != NULL)
@@ -281,16 +287,58 @@ static PRStatus PR_CALLBACK SocketConnec
 
     err = _MD_unix_get_nonblocking_connect_error(osfd);
     if (err != 0) {
         _PR_MD_MAP_CONNECT_ERROR(err);
         return PR_FAILURE;
     }
     return PR_SUCCESS;
 
+#elif defined(_WIN64)
+    if ((out_flags & PR_POLL_WRITE) && fd->secret->alreadyConnected) {
+        fd->secret->alreadyConnected = PR_FALSE;
+        return PR_SUCCESS;
+    }
+    // TCP Fast Open on Windows must use ConnectEx, which uses overlapped
+    // input/output.
+    // To get result we need to use GetOverlappedResult.
+    if (fd->secret->overlappedActive) {
+        PR_ASSERT(fd->secret->nonblocking);
+        PRInt32 rvSent;
+        if (GetOverlappedResult(osfd, &fd->secret->ol, &rvSent, FALSE) == TRUE)
+        {
+            fd->secret->overlappedActive = FALSE;
+            PR_LOG(_pr_io_lm, PR_LOG_MIN,
+               ("SocketConnectContinue GetOverlappedResult succeeded\n"));
+            // When ConnectEx is used, all previously set socket options and
+            // property are not enabled and to enable them
+            // SO_UPDATE_CONNECT_CONTEXT option need to be set.
+            if (setsockopt((SOCKET)osfd, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, NULL, 0) != 0) {
+                err = WSAGetLastError();
+                PR_LOG(_pr_io_lm, PR_LOG_MIN,
+                       ("SocketConnectContinue setting SO_UPDATE_CONNECT_CONTEXT failed %d\n", err));
+                _PR_MD_MAP_SETSOCKOPT_ERROR(err);
+                return PR_FAILURE;
+            }
+            return PR_SUCCESS;
+        } else {
+            err = WSAGetLastError();
+            PR_LOG(_pr_io_lm, PR_LOG_MIN,
+               ("SocketConnectContinue GetOverlappedResult failed %d\n", err));
+            if (err != ERROR_IO_PENDING) {
+                _PR_MD_MAP_CONNECT_ERROR(err);
+                fd->secret->overlappedActive = FALSE;
+                return PR_FAILURE;
+            } else {
+                PR_SetError(PR_IN_PROGRESS_ERROR, 0);
+                return PR_FAILURE;
+            }
+        }
+    }
+
 #elif defined(WIN32) || defined(WIN16)
 
     if (out_flags & PR_POLL_EXCEPT) {
         int len = sizeof(err);
         if (getsockopt(osfd, (int)SOL_SOCKET, SO_ERROR, (char *) &err, &len)
                 == SOCKET_ERROR) {
             _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
             return PR_FAILURE;
@@ -763,16 +811,66 @@ static PRInt32 PR_CALLBACK SocketSendTo(
 			break;
 		}
 		buf = (const void*) ((const char*)buf + temp);
 		amount -= temp;
 	} while (amount > 0);
 	return count;
 }
 
+#if defined(_WIN64) && defined(WIN95)
+static PRInt32 PR_CALLBACK SocketTCPSendTo(
+    PRFileDesc *fd, const void *buf, PRInt32 amount,
+    PRIntn flags, const PRNetAddr *addr, PRIntervalTime timeout)
+{
+    PRInt32 temp, count;
+    const PRNetAddr *addrp = addr;
+#if defined(_PR_INET6)
+    PRNetAddr addrCopy;
+#endif
+    PRThread *me = _PR_MD_CURRENT_THREAD();
+
+    if (_PR_PENDING_INTERRUPT(me)) {
+        me->flags &= ~_PR_INTERRUPT;
+        PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+        return -1;
+    }
+    if (_PR_IO_PENDING(me)) {
+        PR_SetError(PR_IO_PENDING_ERROR, 0);
+        return -1;
+    }
+
+    PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE);
+#if defined(_PR_INET6)
+    if (addr->raw.family == PR_AF_INET6) {
+        addrCopy = *addr;
+        addrCopy.raw.family = AF_INET6;
+        addrp = &addrCopy;
+    }
+#endif
+
+    count = 0;
+    while (amount > 0) {
+        temp = _PR_MD_TCPSENDTO(fd, buf, amount, flags,
+                                addrp, PR_NETADDR_SIZE(addr), timeout);
+        if (temp < 0) {
+            count = -1;
+            break;
+        }
+        count += temp;
+        if (fd->secret->nonblocking) {
+            break;
+        }
+        buf = (const void*) ((const char*)buf + temp);
+        amount -= temp;
+    }
+    return count;
+}
+#endif
+
 static PRInt32 PR_CALLBACK SocketRecvFrom(PRFileDesc *fd, void *buf, PRInt32 amount,
 PRIntn flags, PRNetAddr *addr, PRIntervalTime timeout)
 {
 	PRInt32 rv;
 	PRUint32 al;
 	PRThread *me = _PR_MD_CURRENT_THREAD();
 
 	if (_PR_PENDING_INTERRUPT(me)) {
@@ -1061,16 +1159,24 @@ static PRStatus PR_CALLBACK SocketGetPee
 	PR_ASSERT(IsValidNetAddrLen(addr, addrlen) == PR_TRUE);
 	return PR_SUCCESS;
 }
 
 static PRInt16 PR_CALLBACK SocketPoll(
     PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags)
 {
     *out_flags = 0;
+#if defined(_WIN64)
+    if (in_flags & PR_POLL_WRITE) {
+        if (fd->secret->alreadyConnected) {
+            out_flags = PR_POLL_WRITE;
+            return PR_POLL_WRITE;
+        }
+    }
+#endif
     return in_flags;
 }  /* SocketPoll */
 
 static PRIOMethods tcpMethods = {
 	PR_DESC_SOCKET_TCP,
 	SocketClose,
 	SocketRead,
 	SocketWrite,
@@ -1085,17 +1191,21 @@ static PRIOMethods tcpMethods = {
 	SocketConnect,
 	SocketAccept,
 	SocketBind,
 	SocketListen,
 	SocketShutdown,
 	SocketRecv,
 	SocketSend,
 	(PRRecvfromFN)_PR_InvalidInt,
+#if defined(_WIN64) && defined(WIN95)
+	SocketTCPSendTo, // This is for fast open. We imitate Linux interface.
+#else
 	(PRSendtoFN)_PR_InvalidInt,
+#endif
 	SocketPoll,
 	SocketAcceptRead,
 	SocketTransmitFile,
 	SocketGetName,
 	SocketGetPeerName,
 	(PRReservedFN)_PR_InvalidInt,
 	(PRReservedFN)_PR_InvalidInt,
 	_PR_SocketGetSocketOption,
--- a/nsprpub/pr/src/md/windows/w95sock.c
+++ b/nsprpub/pr/src/md/windows/w95sock.c
@@ -325,16 +325,177 @@ PRInt32
             {
                 return -1;
             }
         }
     } while(bytesSent < amount);
     return bytesSent;
 }
 
+#if defined(_WIN64)
+
+static PRCallOnceType _pr_has_connectex_once;
+typedef BOOL (WINAPI *_pr_win_connectex_ptr)(SOCKET, const struct sockaddr *, int, PVOID, DWORD, LPDWORD, LPOVERLAPPED);
+#ifndef WSAID_CONNECTEX
+#define WSAID_CONNECTEX \
+  {0x25a207b9,0xddf3,0x4660,{0x8e,0xe9,0x76,0xe5,0x8c,0x74,0x06,0x3e}}
+#endif
+#ifndef SIO_GET_EXTENSION_FUNCTION_POINTER
+#define SIO_GET_EXTENSION_FUNCTION_POINTER 0xC8000006
+#endif
+#ifndef TCP_FASTOPEN
+#define TCP_FASTOPEN 15
+#endif
+
+#ifndef SO_UPDATE_CONNECT_CONTEXT
+#define SO_UPDATE_CONNECT_CONTEXT 0x7010
+#endif
+
+static _pr_win_connectex_ptr _pr_win_connectex;
+
+static PRStatus PR_CALLBACK _pr_set_connectex(void)
+{
+    _pr_win_connectex = NULL;
+    SOCKET sock;
+    PRInt32 dwBytes;
+    int rc;
+
+    /* Dummy socket needed for WSAIoctl */
+    sock = socket(AF_INET, SOCK_STREAM, 0);
+    if (sock == INVALID_SOCKET)
+        return PR_SUCCESS;
+
+    GUID guid = WSAID_CONNECTEX;
+    rc = WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER,
+                  &guid, sizeof(guid),
+                  &_pr_win_connectex, sizeof(_pr_win_connectex),
+                  &dwBytes, NULL, NULL);
+    if (rc != 0) {
+        _pr_win_connectex = NULL;
+        return PR_SUCCESS;
+    }
+
+    rc = closesocket(sock);
+    return PR_SUCCESS;
+}
+
+PRInt32
+_PR_MD_TCPSENDTO(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
+                 const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout)
+{
+    if (PR_CallOnce(&_pr_has_connectex_once, _pr_set_connectex) != PR_SUCCESS) {
+        PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+        return PR_FAILURE;
+    }
+
+    if (_pr_win_connectex == NULL) {
+        PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+        return PR_FAILURE;
+    }
+
+    PROsfd osfd = fd->secret->md.osfd;
+    PRInt32 rv, err;
+    PRInt32 bytesSent = 0;
+    DWORD rvSent;
+
+    BOOL option = 1;
+    rv = setsockopt((SOCKET)osfd, IPPROTO_TCP, TCP_FASTOPEN, (char*)&option, sizeof(option));
+    if (rv != 0) {
+        err = WSAGetLastError();
+        PR_LOG(_pr_io_lm, PR_LOG_MIN,
+               ("_PR_MD_TCPSENDTO error set opt TCP_FASTOPEN failed %d\n", err));
+        if (err == WSAENOPROTOOPT) {
+            PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+        } else {
+            _PR_MD_MAP_SETSOCKOPT_ERROR(err);
+        }
+        return -1;
+    }
+
+    // ConnectEx requires the socket to be initially bound. We will use INADDR_ANY
+    PRNetAddr bindAddr;
+    memset(&bindAddr, 0, sizeof(bindAddr));
+    if (addr->raw.family == PR_AF_INET) {
+        bindAddr.inet.family = PR_AF_INET;
+    } else if (addr->raw.family == PR_AF_INET6) {
+        bindAddr.ipv6.family = PR_AF_INET6;
+    }
+    rv = bind((SOCKET)osfd, (SOCKADDR*) &bindAddr, sizeof(bindAddr));
+    if (rv != 0) {
+        err = WSAGetLastError();
+        PR_LOG(_pr_io_lm, PR_LOG_MIN,
+               ("_PR_MD_TCPSENDTO error bind failed %d\n", err));
+        _PR_MD_MAP_SETSOCKOPT_ERROR(err);
+        return -1;
+    }
+
+    PR_LOG(_pr_io_lm, PR_LOG_MIN,
+           ("_PR_MD_TCPSENDTO calling _pr_win_connectex  %d %p\n", amount, (char*)buf));
+
+    rvSent = 0;
+    memset(&fd->secret->ol, 0, sizeof(fd->secret->ol));
+    // ConnectEx return TRUE on a success and FALSE on an error.
+    if (_pr_win_connectex( (SOCKET)osfd, (struct sockaddr *) addr,
+                           addrlen, buf, amount,
+                           &rvSent, &fd->secret->ol) == TRUE) {
+        // When ConnectEx is used, all previously set socket options and
+        // property are not enabled and to enable them
+        // SO_UPDATE_CONNECT_CONTEXT option need to be set.
+        rv = setsockopt((SOCKET)osfd, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, NULL, 0);
+        if (rv != 0) {
+            err = WSAGetLastError();
+            PR_LOG(_pr_io_lm, PR_LOG_MIN,
+               ("_PR_MD_TCPSENDTO setting SO_UPDATE_CONNECT_CONTEXT failed %d\n", err));
+            _PR_MD_MAP_SETSOCKOPT_ERROR(err);
+            return -1;
+        }
+        // We imitate Linux here. SendTo will return number of bytes send but
+        // it can not return connection success at the same time, so we return
+        // number of bytes send and "connection success" will be return on the
+        // connectcontinue.
+        fd->secret->alreadyConnected = PR_TRUE;
+        return rvSent;
+    } else {
+        err = WSAGetLastError();
+        PR_LOG(_pr_io_lm, PR_LOG_MIN,
+               ("_PR_MD_TCPSENDTO error _pr_win_connectex failed %d\n", err));
+        if (err != ERROR_IO_PENDING) {
+            _PR_MD_MAP_CONNECT_ERROR(err);
+            return -1;
+        } else if (fd->secret->nonblocking) {
+            // Remember that overlapped structure is set. We will neede to get
+            // the final result of ConnectEx call.
+            fd->secret->overlappedActive = PR_TRUE;
+            _PR_MD_MAP_CONNECT_ERROR(WSAEWOULDBLOCK);
+            // ConnectEx will copy supplied data to a internal buffer and send
+            // them during Fast Open or after connect. Therefore we can assumed
+            // this data already send.
+            return amount;
+        }
+        while (err == ERROR_IO_PENDING) {
+            rv = socket_io_wait(osfd, WRITE_FD, timeout);
+            if ( rv < 0 ) {
+                return -1;
+            }
+            rv = GetOverlappedResult(osfd, &fd->secret->ol, &rvSent, FALSE);
+            if ( rv == TRUE ) {
+                return rvSent;
+            } else {
+                err = WSAGetLastError();
+                if (err != ERROR_IO_PENDING) {
+                    _PR_MD_MAP_CONNECT_ERROR(err);
+                    return -1;
+                }
+            }
+        }
+    }
+    return -1;
+}
+#endif
+
 PRInt32
 _PR_MD_RECVFROM(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
                 PRNetAddr *addr, PRUint32 *addrlen, PRIntervalTime timeout)
 {
     PROsfd osfd = fd->secret->md.osfd;
     PRInt32 rv, err;
 
     while ((rv = recvfrom( osfd, buf, amount, 0, (struct sockaddr *) addr,
--- a/nsprpub/pr/src/pthreads/ptio.c
+++ b/nsprpub/pr/src/pthreads/ptio.c
@@ -159,16 +159,19 @@ static ssize_t (*pt_aix_sendfile_fptr)()
 #include <netinet/tcp.h>  /* TCP_NODELAY, TCP_MAXSEG */
 #endif
 
 #ifdef LINUX
 /* TCP_CORK is not defined in <netinet/tcp.h> on Red Hat Linux 6.0 */
 #ifndef TCP_CORK
 #define TCP_CORK 3
 #endif
+#ifndef MSG_FASTOPEN
+#define MSG_FASTOPEN    0x20000000
+#endif
 #endif
 
 #ifdef _PR_IPV6_V6ONLY_PROBE
 static PRBool _pr_ipv6_v6only_on_by_default;
 #endif
 
 #if (defined(HPUX) && !defined(HPUX10_30) && !defined(HPUX11))
 #define _PRSelectFdSetArg_t int *
@@ -2048,16 +2051,101 @@ static PRInt32 pt_SendTo(
         bytes = pt_Continue(&op);
         syserrno = op.syserrno;
     }
     if (bytes < 0)
         pt_MapError(_PR_MD_MAP_SENDTO_ERROR, syserrno);
     return bytes;
 }  /* pt_SendTo */
 
+// Linux uses SendTo to send data during TCP Fast Open. OSX uses connectx, but
+// we will make it imitate the Linux's interface.
+static PRInt32 pt_TCP_SendTo(
+    PRFileDesc *fd, const void *buf,
+    PRInt32 amount, PRIntn flags, const PRNetAddr *addr,
+    PRIntervalTime timeout)
+{
+#if !defined(DARWIN) || HAS_CONNECTX
+    PRInt32 syserrno, bytes = -1;
+    PRBool fNeedContinue = PR_FALSE;
+    pt_SockLen addr_len;
+    const PRNetAddr *addrp = addr;
+#if defined(_PR_HAVE_SOCKADDR_LEN) || defined(_PR_INET6)
+    PRUint16 md_af = addr->raw.family;
+    PRNetAddr addrCopy;
+#endif
+
+    if (pt_TestAbort()) return bytes;
+
+    PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE);
+#if defined(_PR_INET6)
+    if (addr->raw.family == PR_AF_INET6) {
+        md_af = AF_INET6;
+#ifndef _PR_HAVE_SOCKADDR_LEN
+        addrCopy = *addr;
+        addrCopy.raw.family = AF_INET6;
+        addrp = &addrCopy;
+#endif
+    }
+#endif
+
+    addr_len = PR_NETADDR_SIZE(addr);
+#ifdef _PR_HAVE_SOCKADDR_LEN
+    addrCopy = *addr;
+    ((struct sockaddr*)&addrCopy)->sa_len = addr_len;
+    ((struct sockaddr*)&addrCopy)->sa_family = md_af;
+    addrp = &addrCopy;
+#endif
+
+#ifndef HAS_CONNECTX
+    bytes = sendto(
+        fd->secret->md.osfd, buf, amount, MSG_FASTOPEN,
+        (struct sockaddr*)addrp, addr_len);
+#else
+    sa_endpoints_t endpoints;
+    endpoints.sae_srcif = 0;
+    endpoints.sae_srcaddr = NULL;
+    endpoints.sae_srcaddrlen = 0;
+    endpoints.sae_dstaddr = (struct sockaddr *)addrp;
+    endpoints.sae_dstaddrlen = addr_len;
+    struct iovec iov[1];
+    iov[0].iov_base = buf;
+    iov[0].iov_len = amount;
+    PRInt32 rv = connectx(fd->secret->md.osfd, &endpoints, SAE_ASSOCID_ANY,
+                         CONNECT_DATA_IDEMPOTENT, iov, 1, &bytes, NULL);
+#endif
+    syserrno = errno;
+    if ( (bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN)
+        && (!fd->secret->nonblocking) ) {
+        if (PR_INTERVAL_NO_WAIT == timeout) syserrno = ETIMEDOUT;
+        else fNeedContinue = PR_TRUE;
+    }
+    if (fNeedContinue == PR_TRUE) {
+        pt_Continuation op;
+        op.arg1.osfd = fd->secret->md.osfd;
+        op.arg2.buffer = (void*)buf;
+        op.arg3.amount = amount;
+        op.arg4.flags = flags;
+        op.arg5.addr = (PRNetAddr*)addrp;
+        op.timeout = timeout;
+        op.result.code = 0;  /* initialize the number sent */
+        op.function = pt_sendto_cont;
+        op.event = POLLOUT | POLLPRI;
+        bytes = pt_Continue(&op);
+        syserrno = op.syserrno;
+    }
+    if (bytes < 0)
+        pt_MapError(_PR_MD_MAP_SENDTO_ERROR, syserrno);
+    return bytes;
+#else
+    PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+    return -1;
+#endif
+}  /* pt_TCP_SendTo */
+
 static PRInt32 pt_RecvFrom(PRFileDesc *fd, void *buf, PRInt32 amount,
     PRIntn flags, PRNetAddr *addr, PRIntervalTime timeout)
 {
     PRBool fNeedContinue = PR_FALSE;
     PRInt32 syserrno, bytes = -1;
     pt_SockLen addr_len = sizeof(PRNetAddr);
 
     if (pt_TestAbort()) return bytes;
@@ -3159,17 +3247,17 @@ static PRIOMethods _pr_tcp_methods = {
     pt_Connect,
     pt_Accept,
     pt_Bind,
     pt_Listen,
     pt_Shutdown,
     pt_Recv,
     pt_Send,
     (PRRecvfromFN)_PR_InvalidInt,
-    (PRSendtoFN)_PR_InvalidInt,
+    pt_TCP_SendTo, // Thiss is for TCP Fast Open. Linux uses SendTo function for this. OSX uses connectx, but we imitate Linux.
     pt_Poll,
     pt_AcceptRead,
     pt_TransmitFile,
     pt_GetSockName,
     pt_GetPeerName,
     (PRReservedFN)_PR_InvalidInt,
     (PRReservedFN)_PR_InvalidInt,
     pt_GetSocketOption,