зеркало из https://github.com/mozilla/gecko-dev.git
Bug 444328 - Add support for TCP keepalive in the Socket Transport Service r=mcmanus
This commit is contained in:
Родитель
230ba19414
Коммит
9b9ae0b4f1
|
@ -4059,6 +4059,22 @@ pref("stagefright.disabled", false);
|
|||
// The default TCP send window on Windows is too small, and autotuning only occurs on receive
|
||||
pref("network.tcp.sendbuffer", 131072);
|
||||
#endif
|
||||
// TCP Keepalive
|
||||
pref("network.tcp.keepalive.enabled", true);
|
||||
// Default idle time before first TCP keepalive probe; same time for interval
|
||||
// between successful probes. Can be overridden in socket transport API.
|
||||
// Win, Linux and Mac.
|
||||
pref("network.tcp.keepalive.idle_time", 600); // seconds; 10 mins
|
||||
// Default timeout for retransmission of unack'd keepalive probes.
|
||||
// Win and Linux only; not configurable on Mac.
|
||||
#if defined(XP_UNIX) && !defined(XP_MACOSX) || defined(XP_WIN)
|
||||
pref("network.tcp.keepalive.retry_interval", 1); // seconds
|
||||
#endif
|
||||
// Default maximum probe retransmissions.
|
||||
// Linux only; not configurable on Win and Mac; fixed at 10 and 8 respectively.
|
||||
#ifdef XP_UNIX && !defined(XP_MACOSX)
|
||||
pref("network.tcp.keepalive.probe_count", 4);
|
||||
#endif
|
||||
|
||||
// Whether to disable acceleration for all widgets.
|
||||
pref("layers.acceleration.disabled", false);
|
||||
|
|
|
@ -81,6 +81,11 @@ public:
|
|||
*aKeepWhenOffline = false;
|
||||
}
|
||||
|
||||
//
|
||||
// called when global pref for keepalive has changed.
|
||||
//
|
||||
virtual void OnKeepaliveEnabledPrefChange(bool aEnabled) { }
|
||||
|
||||
//
|
||||
// returns the number of bytes sent/transmitted over the socket
|
||||
//
|
||||
|
|
|
@ -27,7 +27,7 @@ native NetAddr(mozilla::net::NetAddr);
|
|||
* NOTE: This is a free-threaded interface, meaning that the methods on
|
||||
* this interface may be called from any thread.
|
||||
*/
|
||||
[scriptable, uuid(A8318027-0DDC-4E60-A89B-D81AFE3B5020)]
|
||||
[scriptable, uuid(3F41704C-CD5D-4537-8C4C-7B2EBFC5D972)]
|
||||
interface nsISocketTransport : nsITransport
|
||||
{
|
||||
/**
|
||||
|
@ -198,4 +198,11 @@ interface nsISocketTransport : nsITransport
|
|||
*/
|
||||
attribute unsigned long recvBufferSize;
|
||||
attribute unsigned long sendBufferSize;
|
||||
|
||||
/**
|
||||
* TCP keepalive configuration (support varies by platform).
|
||||
*/
|
||||
attribute boolean keepaliveEnabled;
|
||||
void setKeepaliveVals(in long keepaliveIdleTime,
|
||||
in long keepaliveRetryInterval);
|
||||
};
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
* This is a private interface used by the internals of the networking library.
|
||||
* It will never be frozen. Do not use it in external code.
|
||||
*/
|
||||
[scriptable, uuid(32de7b6e-90c3-11e1-b57e-001fbc092072)]
|
||||
[scriptable, uuid(bc5869e7-53a6-4195-8ab8-32e7116b87dd)]
|
||||
|
||||
interface nsPISocketTransportService : nsISocketTransportService
|
||||
{
|
||||
|
@ -36,6 +36,21 @@ interface nsPISocketTransportService : nsISocketTransportService
|
|||
* Setting it offline will cause non-local socket detachment.
|
||||
*/
|
||||
attribute boolean offline;
|
||||
|
||||
/**
|
||||
* Controls the default timeout (in seconds) for sending keepalive probes.
|
||||
*/
|
||||
readonly attribute long keepaliveIdleTime;
|
||||
|
||||
/**
|
||||
* Controls the default interval (in seconds) between retrying keepalive probes.
|
||||
*/
|
||||
readonly attribute long keepaliveRetryInterval;
|
||||
|
||||
/**
|
||||
* Controls the default retransmission count for keepalive probes.
|
||||
*/
|
||||
readonly attribute long keepaliveProbeCount;
|
||||
};
|
||||
|
||||
%{C++
|
||||
|
|
|
@ -38,10 +38,23 @@
|
|||
#include "nsICancelable.h"
|
||||
#include <algorithm>
|
||||
|
||||
#include "nsPrintfCString.h"
|
||||
|
||||
#if defined(XP_WIN)
|
||||
#include "nsNativeConnectionHelper.h"
|
||||
#endif
|
||||
|
||||
/* Following inclusions required for keepalive config not supported by NSPR. */
|
||||
#include "private/pprio.h"
|
||||
#if defined(XP_WIN)
|
||||
#include <Winsock2.h>
|
||||
#include <Mstcpip.h>
|
||||
#elif defined(XP_UNIX)
|
||||
#include <errno.h>
|
||||
#include <netinet/tcp.h>
|
||||
#endif
|
||||
/* End keepalive config inclusions. */
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::net;
|
||||
|
||||
|
@ -754,6 +767,10 @@ nsSocketTransport::nsSocketTransport()
|
|||
, mInput(MOZ_THIS_IN_INITIALIZER_LIST())
|
||||
, mOutput(MOZ_THIS_IN_INITIALIZER_LIST())
|
||||
, mQoSBits(0x00)
|
||||
, mKeepaliveEnabled(false)
|
||||
, mKeepaliveIdleTimeS(-1)
|
||||
, mKeepaliveRetryIntervalS(-1)
|
||||
, mKeepaliveProbeCount(-1)
|
||||
{
|
||||
SOCKET_LOG(("creating nsSocketTransport @%p\n", this));
|
||||
|
||||
|
@ -1542,6 +1559,14 @@ nsSocketTransport::OnSocketConnected()
|
|||
mFDconnected = true;
|
||||
}
|
||||
|
||||
// Ensure keepalive is configured correctly if previously enabled.
|
||||
if (mKeepaliveEnabled) {
|
||||
nsresult rv = SetKeepaliveEnabledInternal(true);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
SOCKET_LOG((" SetKeepaliveEnabledInternal failed rv[0x%x]", rv));
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_EVENT_TRACER_DONE(this, "net::tcp::connect");
|
||||
|
||||
SendStatus(NS_NET_STATUS_CONNECTED_TO);
|
||||
|
@ -2392,6 +2417,204 @@ nsSocketTransport::SetConnectionFlags(uint32_t value)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsSocketTransport::OnKeepaliveEnabledPrefChange(bool aEnabled)
|
||||
{
|
||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
||||
|
||||
// The global pref toggles keepalive as a system feature; it only affects
|
||||
// an individual socket if keepalive has been specifically enabled for it.
|
||||
// So, ensure keepalive is configured correctly if previously enabled.
|
||||
if (mKeepaliveEnabled) {
|
||||
nsresult rv = SetKeepaliveEnabledInternal(aEnabled);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
SOCKET_LOG((" SetKeepaliveEnabledInternal [%s] failed rv[0x%x]",
|
||||
aEnabled ? "enable" : "disable", rv));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSocketTransport::SetKeepaliveEnabledInternal(bool aEnable)
|
||||
{
|
||||
MOZ_ASSERT(mKeepaliveIdleTimeS > 0 ||
|
||||
mKeepaliveIdleTimeS <= kMaxTCPKeepIdle);
|
||||
MOZ_ASSERT(mKeepaliveRetryIntervalS > 0 ||
|
||||
mKeepaliveRetryIntervalS <= kMaxTCPKeepIntvl);
|
||||
MOZ_ASSERT(mKeepaliveProbeCount > 0 ||
|
||||
mKeepaliveProbeCount <= kMaxTCPKeepCount);
|
||||
|
||||
PRFileDescAutoLock fd(this);
|
||||
if (NS_WARN_IF(!fd.IsInitialized())) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
// Only enable if keepalives are globally enabled, but ensure other
|
||||
// options are set correctly on the fd.
|
||||
bool enable = aEnable && gSocketTransportService->IsKeepaliveEnabled();
|
||||
nsresult rv = fd.SetKeepaliveVals(enable,
|
||||
mKeepaliveIdleTimeS,
|
||||
mKeepaliveRetryIntervalS,
|
||||
mKeepaliveProbeCount);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
SOCKET_LOG((" SetKeepaliveVals failed rv[0x%x]", rv));
|
||||
return rv;
|
||||
}
|
||||
rv = fd.SetKeepaliveEnabled(enable);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
SOCKET_LOG((" SetKeepaliveEnabled failed rv[0x%x]", rv));
|
||||
return rv;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSocketTransport::GetKeepaliveEnabled(bool *aResult)
|
||||
{
|
||||
MOZ_ASSERT(aResult);
|
||||
|
||||
*aResult = mKeepaliveEnabled;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSocketTransport::EnsureKeepaliveValsAreInitialized()
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
int32_t val = -1;
|
||||
if (mKeepaliveIdleTimeS == -1) {
|
||||
rv = gSocketTransportService->GetKeepaliveIdleTime(&val);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
mKeepaliveIdleTimeS = val;
|
||||
}
|
||||
if (mKeepaliveRetryIntervalS == -1) {
|
||||
rv = gSocketTransportService->GetKeepaliveRetryInterval(&val);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
mKeepaliveRetryIntervalS = val;
|
||||
}
|
||||
if (mKeepaliveProbeCount == -1) {
|
||||
rv = gSocketTransportService->GetKeepaliveProbeCount(&val);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
mKeepaliveProbeCount = val;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSocketTransport::SetKeepaliveEnabled(bool aEnable)
|
||||
{
|
||||
#if defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)
|
||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
||||
|
||||
if (aEnable == mKeepaliveEnabled) {
|
||||
SOCKET_LOG(("nsSocketTransport::SetKeepaliveEnabled [%p] already %s.",
|
||||
this, aEnable ? "enabled" : "disabled"));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
if (aEnable) {
|
||||
rv = EnsureKeepaliveValsAreInitialized();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
SOCKET_LOG((" SetKeepaliveEnabled [%p] "
|
||||
"error [0x%x] initializing keepalive vals",
|
||||
this, rv));
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
SOCKET_LOG(("nsSocketTransport::SetKeepaliveEnabled [%p] "
|
||||
"%s, idle time[%ds] retry interval[%ds] packet count[%d]: "
|
||||
"globally %s.",
|
||||
this, aEnable ? "enabled" : "disabled",
|
||||
mKeepaliveIdleTimeS, mKeepaliveRetryIntervalS,
|
||||
mKeepaliveProbeCount,
|
||||
gSocketTransportService->IsKeepaliveEnabled() ?
|
||||
"enabled" : "disabled"));
|
||||
|
||||
// Set mKeepaliveEnabled here so that state is maintained; it is possible
|
||||
// that we're in between fds, e.g. the 1st IP address failed, so we're about
|
||||
// to retry on a 2nd from the DNS record.
|
||||
mKeepaliveEnabled = aEnable;
|
||||
|
||||
rv = SetKeepaliveEnabledInternal(aEnable);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
SOCKET_LOG((" SetKeepaliveEnabledInternal failed rv[0x%x]", rv));
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
#else /* !(defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)) */
|
||||
SOCKET_LOG(("nsSocketTransport::SetKeepaliveEnabled unsupported platform"));
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSocketTransport::SetKeepaliveVals(int32_t aIdleTime,
|
||||
int32_t aRetryInterval)
|
||||
{
|
||||
#if defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)
|
||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
||||
if (NS_WARN_IF(aIdleTime <= 0 || kMaxTCPKeepIdle < aIdleTime)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
if (NS_WARN_IF(aRetryInterval <= 0 ||
|
||||
kMaxTCPKeepIntvl < aRetryInterval)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (aIdleTime == mKeepaliveIdleTimeS &&
|
||||
aRetryInterval == mKeepaliveRetryIntervalS) {
|
||||
SOCKET_LOG(("nsSocketTransport::SetKeepaliveVals [%p] idle time "
|
||||
"already %ds and retry interval already %ds.",
|
||||
this, mKeepaliveIdleTimeS,
|
||||
mKeepaliveRetryIntervalS));
|
||||
return NS_OK;
|
||||
}
|
||||
mKeepaliveIdleTimeS = aIdleTime;
|
||||
mKeepaliveRetryIntervalS = aRetryInterval;
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
if (mKeepaliveProbeCount == -1) {
|
||||
int32_t val = -1;
|
||||
nsresult rv = gSocketTransportService->GetKeepaliveProbeCount(&val);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
mKeepaliveProbeCount = val;
|
||||
}
|
||||
|
||||
SOCKET_LOG(("nsSocketTransport::SetKeepaliveVals [%p] "
|
||||
"keepalive %s, idle time[%ds] retry interval[%ds] "
|
||||
"packet count[%d]",
|
||||
this, mKeepaliveEnabled ? "enabled" : "disabled",
|
||||
mKeepaliveIdleTimeS, mKeepaliveRetryIntervalS,
|
||||
mKeepaliveProbeCount));
|
||||
|
||||
PRFileDescAutoLock fd(this);
|
||||
if (NS_WARN_IF(!fd.IsInitialized())) {
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
}
|
||||
|
||||
rv = fd.SetKeepaliveVals(mKeepaliveEnabled,
|
||||
mKeepaliveIdleTimeS,
|
||||
mKeepaliveRetryIntervalS,
|
||||
mKeepaliveProbeCount);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
return NS_OK;
|
||||
#else
|
||||
SOCKET_LOG(("nsSocketTransport::SetKeepaliveVals unsupported platform"));
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ENABLE_SOCKET_TRACING
|
||||
|
||||
|
@ -2465,3 +2688,169 @@ nsSocketTransport::TraceOutBuf(const char *buf, int32_t n)
|
|||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void LogNSPRError(const char* aPrefix, const void *aObjPtr)
|
||||
{
|
||||
#if defined(PR_LOGGING) && defined(DEBUG)
|
||||
PRErrorCode errCode = PR_GetError();
|
||||
int errLen = PR_GetErrorTextLength();
|
||||
nsAutoCString errStr;
|
||||
if (errLen > 0) {
|
||||
errStr.SetLength(errLen);
|
||||
PR_GetErrorText(errStr.BeginWriting());
|
||||
}
|
||||
NS_WARNING(nsPrintfCString(
|
||||
"%s [%p] NSPR error[0x%x] %s.",
|
||||
aPrefix ? aPrefix : "nsSocketTransport", aObjPtr, errCode,
|
||||
errLen > 0 ? errStr.BeginReading() : "<no error text>").get());
|
||||
#endif
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSocketTransport::PRFileDescAutoLock::SetKeepaliveEnabled(bool aEnable)
|
||||
{
|
||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
||||
MOZ_ASSERT(!(aEnable && !gSocketTransportService->IsKeepaliveEnabled()),
|
||||
"Cannot enable keepalive if global pref is disabled!");
|
||||
if (aEnable && !gSocketTransportService->IsKeepaliveEnabled()) {
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
|
||||
PRSocketOptionData opt;
|
||||
|
||||
opt.option = PR_SockOpt_Keepalive;
|
||||
opt.value.keep_alive = aEnable;
|
||||
PRStatus status = PR_SetSocketOption(mFd, &opt);
|
||||
if (NS_WARN_IF(status != PR_SUCCESS)) {
|
||||
LogNSPRError("nsSocketTransport::PRFileDescAutoLock::SetKeepaliveEnabled",
|
||||
mSocketTransport);
|
||||
return ErrorAccordingToNSPR(PR_GetError());
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static void LogOSError(const char *aPrefix, const void *aObjPtr)
|
||||
{
|
||||
#if defined(PR_LOGGING) && defined(DEBUG)
|
||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
||||
|
||||
#ifdef XP_WIN
|
||||
DWORD errCode = WSAGetLastError();
|
||||
LPVOID errMessage;
|
||||
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL,
|
||||
errCode,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPTSTR) &errMessage,
|
||||
0, NULL);
|
||||
#else
|
||||
int errCode = errno;
|
||||
char *errMessage = strerror(errno);
|
||||
#endif
|
||||
NS_WARNING(nsPrintfCString(
|
||||
"%s [%p] OS error[0x%x] %s",
|
||||
aPrefix ? aPrefix : "nsSocketTransport", aObjPtr, errCode,
|
||||
errMessage ? errMessage : "<no error text>").get());
|
||||
#ifdef XP_WIN
|
||||
LocalFree(errMessage);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
/* XXX PR_SetSockOpt does not support setting keepalive values, so native
|
||||
* handles and platform specific apis (setsockopt, WSAIOCtl) are used in this
|
||||
* file. Requires inclusion of NSPR private/pprio.h, and platform headers.
|
||||
*/
|
||||
|
||||
nsresult
|
||||
nsSocketTransport::PRFileDescAutoLock::SetKeepaliveVals(bool aEnabled,
|
||||
int aIdleTime,
|
||||
int aRetryInterval,
|
||||
int aProbeCount)
|
||||
{
|
||||
#if defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)
|
||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
||||
if (NS_WARN_IF(aIdleTime <= 0 || kMaxTCPKeepIdle < aIdleTime)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
if (NS_WARN_IF(aRetryInterval <= 0 ||
|
||||
kMaxTCPKeepIntvl < aRetryInterval)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
if (NS_WARN_IF(aProbeCount <= 0 || kMaxTCPKeepCount < aProbeCount)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
PROsfd sock = PR_FileDesc2NativeHandle(mFd);
|
||||
if (NS_WARN_IF(sock == -1)) {
|
||||
LogNSPRError("nsSocketTransport::PRFileDescAutoLock::SetKeepaliveVals",
|
||||
mSocketTransport);
|
||||
return ErrorAccordingToNSPR(PR_GetError());
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(XP_WIN)
|
||||
// Windows allows idle time and retry interval to be set; NOT ping count.
|
||||
struct tcp_keepalive keepalive_vals = {
|
||||
(int)aEnabled,
|
||||
// Windows uses msec.
|
||||
aIdleTime * 1000,
|
||||
aRetryInterval * 1000
|
||||
};
|
||||
DWORD bytes_returned;
|
||||
int err = WSAIoctl(sock, SIO_KEEPALIVE_VALS, &keepalive_vals,
|
||||
sizeof(keepalive_vals), NULL, 0, &bytes_returned, NULL,
|
||||
NULL);
|
||||
if (NS_WARN_IF(err)) {
|
||||
LogOSError("nsSocketTransport WSAIoctl failed", mSocketTransport);
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
return NS_OK;
|
||||
|
||||
#elif defined(XP_MACOSX)
|
||||
// OS X uses sec; only supports idle time being set.
|
||||
int err = setsockopt(sock, IPPROTO_TCP, TCP_KEEPALIVE,
|
||||
&aIdleTime, sizeof(aIdleTime));
|
||||
if (NS_WARN_IF(err)) {
|
||||
LogOSError("nsSocketTransport Failed setting TCP_KEEPALIVE",
|
||||
mSocketTransport);
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
return NS_OK;
|
||||
|
||||
#elif defined(XP_UNIX)
|
||||
// Linux uses sec; supports idle time, retry interval and ping count.
|
||||
int err = setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE,
|
||||
&aIdleTime, sizeof(aIdleTime));
|
||||
if (NS_WARN_IF(err)) {
|
||||
LogOSError("nsSocketTransport Failed setting TCP_KEEPIDLE",
|
||||
mSocketTransport);
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
// Linux also has a few extra knobs to tweak
|
||||
err = setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL,
|
||||
&aRetryInterval, sizeof(aRetryInterval));
|
||||
if (NS_WARN_IF(err)) {
|
||||
LogOSError("nsSocketTransport Failed setting TCP_KEEPINTVL",
|
||||
mSocketTransport);
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
err = setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT,
|
||||
&aProbeCount, sizeof(aProbeCount));
|
||||
if (NS_WARN_IF(err)) {
|
||||
LogOSError("nsSocketTransport Failed setting TCP_KEEPCNT",
|
||||
mSocketTransport);
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
#else
|
||||
MOZ_ASSERT(false, "nsSocketTransport::PRFileDescAutoLock::SetKeepaliveVals "
|
||||
"called on unsupported platform!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -139,6 +139,7 @@ public:
|
|||
void OnSocketReady(PRFileDesc *, int16_t outFlags);
|
||||
void OnSocketDetached(PRFileDesc *);
|
||||
void IsLocal(bool *aIsLocal);
|
||||
void OnKeepaliveEnabledPrefChange(bool aEnabled) MOZ_OVERRIDE MOZ_FINAL;
|
||||
|
||||
// called when a socket event is handled
|
||||
void OnSocketEvent(uint32_t type, nsresult status, nsISupports *param);
|
||||
|
@ -205,8 +206,12 @@ private:
|
|||
operator PRFileDesc*() {
|
||||
return mFd;
|
||||
}
|
||||
nsresult SetKeepaliveEnabled(bool aEnable);
|
||||
nsresult SetKeepaliveVals(bool aEnabled, int aIdleTime,
|
||||
int aRetryInterval, int aProbeCount);
|
||||
private:
|
||||
operator PRFileDescAutoLock*() { return nullptr; }
|
||||
|
||||
// Weak ptr to nsSocketTransport since this is a stack class only.
|
||||
nsSocketTransport *mSocketTransport;
|
||||
PRFileDesc *mFd;
|
||||
|
@ -386,6 +391,21 @@ private:
|
|||
void TraceInBuf(const char *buf, int32_t n);
|
||||
void TraceOutBuf(const char *buf, int32_t n);
|
||||
#endif
|
||||
|
||||
// Reads prefs to get default keepalive config.
|
||||
nsresult EnsureKeepaliveValsAreInitialized();
|
||||
|
||||
// Groups calls to fd.SetKeepaliveEnabled and fd.SetKeepaliveVals.
|
||||
nsresult SetKeepaliveEnabledInternal(bool aEnable);
|
||||
|
||||
// True if keepalive has been enabled by the socket owner. Note: Keepalive
|
||||
// must also be enabled globally for it to be enabled in TCP.
|
||||
bool mKeepaliveEnabled;
|
||||
|
||||
// Keepalive config (support varies by platform).
|
||||
int32_t mKeepaliveIdleTimeS;
|
||||
int32_t mKeepaliveRetryIntervalS;
|
||||
int32_t mKeepaliveProbeCount;
|
||||
};
|
||||
|
||||
#endif // !nsSocketTransport_h__
|
||||
|
|
|
@ -35,6 +35,10 @@ nsSocketTransportService *gSocketTransportService = nullptr;
|
|||
PRThread *gSocketThread = nullptr;
|
||||
|
||||
#define SEND_BUFFER_PREF "network.tcp.sendbuffer"
|
||||
#define KEEPALIVE_ENABLED_PREF "network.tcp.keepalive.enabled"
|
||||
#define KEEPALIVE_IDLE_TIME_PREF "network.tcp.keepalive.idle_time"
|
||||
#define KEEPALIVE_RETRY_INTERVAL_PREF "network.tcp.keepalive.retry_interval"
|
||||
#define KEEPALIVE_PROBE_COUNT_PREF "network.tcp.keepalive.probe_count"
|
||||
#define SOCKET_LIMIT_TARGET 550U
|
||||
#define SOCKET_LIMIT_MIN 50U
|
||||
#define BLIP_INTERVAL_PREF "network.activity.blipIntervalMilliseconds"
|
||||
|
@ -61,6 +65,10 @@ nsSocketTransportService::nsSocketTransportService()
|
|||
, mSentBytesCount(0)
|
||||
, mReceivedBytesCount(0)
|
||||
, mSendBufferSize(0)
|
||||
, mKeepaliveIdleTimeS(600)
|
||||
, mKeepaliveRetryIntervalS(1)
|
||||
, mKeepaliveProbeCount(kDefaultTCPKeepCount)
|
||||
, mKeepaliveEnabledPref(false)
|
||||
, mProbedMaxCount(false)
|
||||
{
|
||||
#if defined(PR_LOGGING)
|
||||
|
@ -463,6 +471,10 @@ nsSocketTransportService::Init()
|
|||
nsCOMPtr<nsIPrefBranch> tmpPrefService = do_GetService(NS_PREFSERVICE_CONTRACTID);
|
||||
if (tmpPrefService) {
|
||||
tmpPrefService->AddObserver(SEND_BUFFER_PREF, this, false);
|
||||
tmpPrefService->AddObserver(KEEPALIVE_ENABLED_PREF, this, false);
|
||||
tmpPrefService->AddObserver(KEEPALIVE_IDLE_TIME_PREF, this, false);
|
||||
tmpPrefService->AddObserver(KEEPALIVE_RETRY_INTERVAL_PREF, this, false);
|
||||
tmpPrefService->AddObserver(KEEPALIVE_PROBE_COUNT_PREF, this, false);
|
||||
}
|
||||
UpdatePrefs();
|
||||
|
||||
|
@ -553,6 +565,39 @@ nsSocketTransportService::SetOffline(bool offline)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSocketTransportService::GetKeepaliveIdleTime(int32_t *aKeepaliveIdleTimeS)
|
||||
{
|
||||
MOZ_ASSERT(aKeepaliveIdleTimeS);
|
||||
if (NS_WARN_IF(!aKeepaliveIdleTimeS)) {
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
}
|
||||
*aKeepaliveIdleTimeS = mKeepaliveIdleTimeS;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSocketTransportService::GetKeepaliveRetryInterval(int32_t *aKeepaliveRetryIntervalS)
|
||||
{
|
||||
MOZ_ASSERT(aKeepaliveRetryIntervalS);
|
||||
if (NS_WARN_IF(!aKeepaliveRetryIntervalS)) {
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
}
|
||||
*aKeepaliveRetryIntervalS = mKeepaliveRetryIntervalS;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSocketTransportService::GetKeepaliveProbeCount(int32_t *aKeepaliveProbeCount)
|
||||
{
|
||||
MOZ_ASSERT(aKeepaliveProbeCount);
|
||||
if (NS_WARN_IF(!aKeepaliveProbeCount)) {
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
}
|
||||
*aKeepaliveProbeCount = mKeepaliveProbeCount;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSocketTransportService::CreateTransport(const char **types,
|
||||
uint32_t typeCount,
|
||||
|
@ -909,11 +954,77 @@ nsSocketTransportService::UpdatePrefs()
|
|||
nsresult rv = tmpPrefService->GetIntPref(SEND_BUFFER_PREF, &bufferSize);
|
||||
if (NS_SUCCEEDED(rv) && bufferSize > 0)
|
||||
mSendBufferSize = bufferSize;
|
||||
|
||||
// Default TCP Keepalive Values.
|
||||
int32_t keepaliveIdleTimeS;
|
||||
rv = tmpPrefService->GetIntPref(KEEPALIVE_IDLE_TIME_PREF,
|
||||
&keepaliveIdleTimeS);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
mKeepaliveIdleTimeS = clamped(keepaliveIdleTimeS,
|
||||
1, kMaxTCPKeepIdle);
|
||||
|
||||
int32_t keepaliveRetryIntervalS;
|
||||
rv = tmpPrefService->GetIntPref(KEEPALIVE_RETRY_INTERVAL_PREF,
|
||||
&keepaliveRetryIntervalS);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
mKeepaliveRetryIntervalS = clamped(keepaliveRetryIntervalS,
|
||||
1, kMaxTCPKeepIntvl);
|
||||
|
||||
int32_t keepaliveProbeCount;
|
||||
rv = tmpPrefService->GetIntPref(KEEPALIVE_PROBE_COUNT_PREF,
|
||||
&keepaliveProbeCount);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
mKeepaliveProbeCount = clamped(keepaliveProbeCount,
|
||||
1, kMaxTCPKeepCount);
|
||||
bool keepaliveEnabled = false;
|
||||
rv = tmpPrefService->GetBoolPref(KEEPALIVE_ENABLED_PREF,
|
||||
&keepaliveEnabled);
|
||||
if (NS_SUCCEEDED(rv) && keepaliveEnabled != mKeepaliveEnabledPref) {
|
||||
mKeepaliveEnabledPref = keepaliveEnabled;
|
||||
OnKeepaliveEnabledPrefChange();
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsSocketTransportService::OnKeepaliveEnabledPrefChange()
|
||||
{
|
||||
// Dispatch to socket thread if we're not executing there.
|
||||
if (PR_GetCurrentThread() != gSocketThread) {
|
||||
gSocketTransportService->Dispatch(
|
||||
NS_NewRunnableMethod(
|
||||
this, &nsSocketTransportService::OnKeepaliveEnabledPrefChange),
|
||||
NS_DISPATCH_NORMAL);
|
||||
return;
|
||||
}
|
||||
|
||||
SOCKET_LOG(("nsSocketTransportService::OnKeepaliveEnabledPrefChange %s",
|
||||
mKeepaliveEnabledPref ? "enabled" : "disabled"));
|
||||
|
||||
// Notify each socket that keepalive has been en/disabled globally.
|
||||
for (int32_t i = mActiveCount - 1; i >= 0; --i) {
|
||||
NotifyKeepaliveEnabledPrefChange(&mActiveList[i]);
|
||||
}
|
||||
for (int32_t i = mIdleCount - 1; i >= 0; --i) {
|
||||
NotifyKeepaliveEnabledPrefChange(&mIdleList[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsSocketTransportService::NotifyKeepaliveEnabledPrefChange(SocketContext *sock)
|
||||
{
|
||||
MOZ_ASSERT(sock, "SocketContext cannot be null!");
|
||||
MOZ_ASSERT(sock->mHandler, "SocketContext does not have a handler!");
|
||||
|
||||
if (!sock || !sock->mHandler) {
|
||||
return;
|
||||
}
|
||||
|
||||
sock->mHandler->OnKeepaliveEnabledPrefChange(mKeepaliveEnabledPref);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSocketTransportService::Observe(nsISupports *subject,
|
||||
const char *topic,
|
||||
|
|
|
@ -38,6 +38,25 @@ extern PRLogModuleInfo *gSocketTransportLog;
|
|||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
// These maximums are borrowed from the linux kernel.
|
||||
static const int32_t kMaxTCPKeepIdle = 32767; // ~9 hours.
|
||||
static const int32_t kMaxTCPKeepIntvl = 32767;
|
||||
static const int32_t kMaxTCPKeepCount = 127;
|
||||
static const int32_t kDefaultTCPKeepCount =
|
||||
#if defined (XP_WIN)
|
||||
10; // Hardcoded in Windows.
|
||||
#elif defined (XP_MACOSX)
|
||||
8; // Hardcoded in OSX.
|
||||
#else
|
||||
4; // Specifiable in Linux.
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class nsSocketTransportService : public nsPISocketTransportService
|
||||
, public nsIEventTarget
|
||||
, public nsIThreadObserver
|
||||
|
@ -79,6 +98,9 @@ public:
|
|||
void GetSocketConnections(nsTArray<mozilla::net::SocketInfo> *);
|
||||
uint64_t GetSentBytes() { return mSentBytesCount; }
|
||||
uint64_t GetReceivedBytes() { return mReceivedBytesCount; }
|
||||
|
||||
// Returns true if keepalives are enabled in prefs.
|
||||
bool IsKeepaliveEnabled() { return mKeepaliveEnabledPref; }
|
||||
protected:
|
||||
|
||||
virtual ~nsSocketTransportService();
|
||||
|
@ -183,9 +205,20 @@ private:
|
|||
|
||||
nsEventQueue mPendingSocketQ; // queue of nsIRunnable objects
|
||||
|
||||
// Preference Monitor for SendBufferSize
|
||||
// Preference Monitor for SendBufferSize and Keepalive prefs.
|
||||
nsresult UpdatePrefs();
|
||||
int32_t mSendBufferSize;
|
||||
// Number of seconds of connection is idle before first keepalive ping.
|
||||
int32_t mKeepaliveIdleTimeS;
|
||||
// Number of seconds between retries should keepalive pings fail.
|
||||
int32_t mKeepaliveRetryIntervalS;
|
||||
// Number of keepalive probes to send.
|
||||
int32_t mKeepaliveProbeCount;
|
||||
// True if TCP keepalive is enabled globally.
|
||||
bool mKeepaliveEnabledPref;
|
||||
|
||||
void OnKeepaliveEnabledPrefChange();
|
||||
void NotifyKeepaliveEnabledPrefChange(SocketContext *sock);
|
||||
|
||||
// Socket thread only for dynamically adjusting max socket size
|
||||
#if defined(XP_WIN)
|
||||
|
|
Загрузка…
Ссылка в новой задаче