Bug 1310197 - Implement fast open nspr part. r=mcmanus,bagder

This commit is contained in:
Dragana Damjanovic 2017-04-20 14:11:26 +08:00
Родитель d760090512
Коммит ecca1ff420
10 изменённых файлов: 416 добавлений и 1 удалений

Просмотреть файл

@ -504,6 +504,13 @@ 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)

4
config/external/nspr/pr/moz.build поставляемый
Просмотреть файл

@ -40,6 +40,10 @@ elif CONFIG['OS_TARGET'] in ('FreeBSD', 'OpenBSD', 'NetBSD'):
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,

5
nsprpub/configure поставляемый
Просмотреть файл

@ -6560,6 +6560,11 @@ fi
$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

Просмотреть файл

@ -1384,6 +1384,11 @@ case "$target" in
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

Просмотреть файл

@ -290,6 +290,9 @@ extern void _MD_MakeNonblock(PRFileDesc *f);
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

Просмотреть файл

@ -1225,6 +1225,13 @@ extern PRInt32 _PR_MD_SENDTO(
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
@ -1747,6 +1754,18 @@ struct PRFilePrivate {
* 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

Просмотреть файл

@ -119,6 +119,10 @@ PR_IMPLEMENT(PRFileDesc*) PR_AllocFileDesc(
fd->methods = methods;
fd->secret->state = _PR_FILEDESC_OPEN;
fd->secret->md.osfd = osfd;
#if defined(_WIN64)
fd->secret->alreadyConnected = PR_FALSE;
fd->secret->overlappedActive = PR_FALSE;
#endif
_PR_MD_INIT_FILEDESC(fd);
} else {
PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);

Просмотреть файл

@ -7,6 +7,12 @@
#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. */
@ -304,6 +310,48 @@ static PRStatus PR_CALLBACK SocketConnectContinue(
}
PR_ASSERT(out_flags & PR_POLL_WRITE);
#if defined(_WIN64)
if (fd->secret->alreadyConnected) {
fd->secret->alreadyConnected = PR_FALSE;
}
// 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;
}
}
}
#endif
return PR_SUCCESS;
#elif defined(XP_OS2)
@ -768,6 +816,56 @@ static PRInt32 PR_CALLBACK SocketSendTo(
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)
{
@ -1066,6 +1164,15 @@ 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 */
@ -1090,7 +1197,11 @@ static PRIOMethods tcpMethods = {
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,

Просмотреть файл

@ -330,6 +330,167 @@ _PR_MD_SENDTO(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
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)

Просмотреть файл

@ -164,6 +164,9 @@ static ssize_t (*pt_aix_sendfile_fptr)() = NULL;
#ifndef TCP_CORK
#define TCP_CORK 3
#endif
#ifndef MSG_FASTOPEN
#define MSG_FASTOPEN 0x20000000
#endif
#endif
#ifdef _PR_IPV6_V6ONLY_PROBE
@ -2053,6 +2056,99 @@ static PRInt32 pt_SendTo(
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)
PRNetAddr addrCopy;
#endif
#ifdef _PR_HAVE_SOCKADDR_LEN
PRUint16 md_af = addr->raw.family;
#endif
if (pt_TestAbort()) return bytes;
PR_ASSERT(IsValidNetAddr(addr) == PR_TRUE);
addr_len = PR_NETADDR_SIZE(addr);
#if defined(_PR_INET6)
if (addr->raw.family == PR_AF_INET6) {
#ifdef _PR_HAVE_SOCKADDR_LEN
md_af = AF_INET6;
#else
// If _PR_INET6 is defined and it is PR_AF_INET6 we set family
// to AF_INET6.
addrCopy = *addr;
addrCopy.raw.family = AF_INET6;
addrp = &addrCopy;
#endif
}
#endif
#ifdef _PR_HAVE_SOCKADDR_LEN
// if _PR_HAVE_SOCKADDR_LEN is defined and it is PR_AF_INET6 we set family
/ to AF_INET6 and we set address length.
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)
{
@ -3164,7 +3260,7 @@ static PRIOMethods _pr_tcp_methods = {
pt_Recv,
pt_Send,
(PRRecvfromFN)_PR_InvalidInt,
(PRSendtoFN)_PR_InvalidInt,
pt_TCP_SendTo, // This is for TCP Fast Open. Linux uses SendTo function for this. OSX uses connectx, but we imitate Linux.
pt_Poll,
pt_AcceptRead,
pt_TransmitFile,