Bug 444328 - Enable TCP Keepalive for short and long-lived HTTP Connections (exc. SPDY, WebSockets) r=mcmanus

This commit is contained in:
Steve Workman 2014-02-06 11:51:38 -08:00
Родитель 9b9ae0b4f1
Коммит 121f9e83ae
5 изменённых файлов: 329 добавлений и 2 удалений

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

@ -1011,6 +1011,16 @@ pref("network.http.pacing.requests.min-parallelism", 6);
pref("network.http.pacing.requests.hz", 100);
pref("network.http.pacing.requests.burst", 32);
// TCP Keepalive config for HTTP connections.
pref("network.http.tcp_keepalive.short_lived_connections", true);
// Max time from initial request during which conns are considered short-lived.
pref("network.http.tcp_keepalive.short_lived_time", 60);
// Idle time of TCP connection until first keepalive probe sent.
pref("network.http.tcp_keepalive.short_lived_idle_time", 10);
pref("network.http.tcp_keepalive.long_lived_connections", true);
pref("network.http.tcp_keepalive.long_lived_idle_time", 600);
// default values for FTP
// in a DSCP environment this should be 40 (0x28, or AF11), per RFC-4594,
// Section 4.8 "High-Throughput Data Service Class", and 80 (0x50, or AF22)

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

@ -13,6 +13,7 @@
#include "nsHttpHandler.h"
#include "nsIOService.h"
#include "nsISocketTransport.h"
#include "nsSocketTransportService2.h"
#include "nsISSLSocketControl.h"
#include "sslt.h"
#include "nsStringStream.h"
@ -71,6 +72,7 @@ nsHttpConnection::nsHttpConnection()
, mLastHttpResponseVersion(NS_HTTP_VERSION_1_1)
, mTransactionCaps(0)
, mResponseTimeoutEnabled(false)
, mTCPKeepaliveConfig(kTCPKeepaliveDisabled)
{
LOG(("Creating nsHttpConnection @%x\n", this));
}
@ -223,6 +225,13 @@ nsHttpConnection::StartSpdy(uint8_t spdyVersion)
}
}
// Disable TCP Keepalives - use SPDY ping instead.
rv = DisableTCPKeepalives();
if (NS_WARN_IF(NS_FAILED(rv))) {
LOG(("nsHttpConnection::StartSpdy [%p] DisableTCPKeepalives failed "
"rv[0x%x]", this, rv));
}
mSupportsPipelining = false; // dont use http/1 pipelines with spdy
mTransaction = mSpdySession;
mIdleTimeout = gHttpHandler->SpdyTimeout();
@ -351,6 +360,15 @@ nsHttpConnection::Activate(nsAHttpTransaction *trans, uint32_t caps, int32_t pri
rv = OnOutputStreamReady(mSocketOut);
if (NS_SUCCEEDED(rv)) {
nsresult rv2 = StartShortLivedTCPKeepalives();
if (NS_WARN_IF(NS_FAILED(rv2))) {
LOG(("nsHttpConnection::Activate [%p] "
"StartShortLivedTCPKeepalives failed rv2[0x%x]",
this, rv));
}
}
failed_activation:
if (NS_FAILED(rv)) {
mTransaction = nullptr;
@ -450,6 +468,12 @@ nsHttpConnection::Close(nsresult reason)
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
// Ensure TCP keepalive timer is stopped.
if (mTCPKeepaliveTransitionTimer) {
mTCPKeepaliveTransitionTimer->Cancel();
mTCPKeepaliveTransitionTimer = nullptr;
}
if (NS_FAILED(reason)) {
if (mIdleMonitoring)
EndIdleMonitoring();
@ -929,6 +953,21 @@ nsHttpConnection::TakeTransport(nsISocketTransport **aTransport,
if (mInputOverflow)
mSocketIn = mInputOverflow.forget();
// Change TCP Keepalive frequency to long-lived if currently short-lived.
if (mTCPKeepaliveConfig == kTCPKeepaliveShortLivedConfig) {
if (mTCPKeepaliveTransitionTimer) {
mTCPKeepaliveTransitionTimer->Cancel();
mTCPKeepaliveTransitionTimer = nullptr;
}
nsresult rv = StartLongLivedTCPKeepalives();
LOG(("nsHttpConnection::TakeTransport [%p] calling "
"StartLongLivedTCPKeepalives", this));
if (NS_WARN_IF(NS_FAILED(rv))) {
LOG(("nsHttpConnection::TakeTransport [%p] "
"StartLongLivedTCPKeepalives failed rv[0x%x]", this, rv));
}
}
NS_IF_ADDREF(*aTransport = mSocketTransport);
NS_IF_ADDREF(*aInputStream = mSocketIn);
NS_IF_ADDREF(*aOutputStream = mSocketOut);
@ -1040,6 +1079,31 @@ nsHttpConnection::ReadTimeoutTick(PRIntervalTime now)
return UINT32_MAX;
}
void
nsHttpConnection::UpdateTCPKeepalive(nsITimer *aTimer, void *aClosure)
{
MOZ_ASSERT(aTimer);
MOZ_ASSERT(aClosure);
nsHttpConnection *self = static_cast<nsHttpConnection*>(aClosure);
if (NS_WARN_IF(self->mUsingSpdyVersion)) {
return;
}
// Do not reduce keepalive probe frequency for idle connections.
if (self->mIdleMonitoring) {
return;
}
nsresult rv = self->StartLongLivedTCPKeepalives();
if (NS_WARN_IF(NS_FAILED(rv))) {
LOG(("nsHttpConnection::UpdateTCPKeepalive [%p] "
"StartLongLivedTCPKeepalives failed rv[0x%x]",
self, rv));
}
}
void
nsHttpConnection::GetSecurityInfo(nsISupports **secinfo)
{
@ -1565,6 +1629,149 @@ nsHttpConnection::ReportDataUsage(bool allowDefer)
mUnreportedBytesRead = mUnreportedBytesWritten = 0;
}
nsresult
nsHttpConnection::StartShortLivedTCPKeepalives()
{
MOZ_ASSERT(!mUsingSpdyVersion, "Don't use TCP Keepalive with SPDY!");
if (NS_WARN_IF(mUsingSpdyVersion)) {
return NS_OK;
}
MOZ_ASSERT(mSocketTransport);
if (!mSocketTransport) {
return NS_ERROR_NOT_INITIALIZED;
}
nsresult rv = NS_OK;
int32_t idleTimeS = -1;
int32_t retryIntervalS = -1;
if (gHttpHandler->TCPKeepaliveEnabledForShortLivedConns()) {
// Set the idle time.
idleTimeS = gHttpHandler->GetTCPKeepaliveShortLivedIdleTime();
LOG(("nsHttpConnection::StartShortLivedTCPKeepalives[%p] "
"idle time[%ds].", this, idleTimeS));
retryIntervalS =
std::max<int32_t>((int32_t)PR_IntervalToSeconds(mRtt), 1);
rv = mSocketTransport->SetKeepaliveVals(idleTimeS, retryIntervalS);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = mSocketTransport->SetKeepaliveEnabled(true);
mTCPKeepaliveConfig = kTCPKeepaliveShortLivedConfig;
} else {
rv = mSocketTransport->SetKeepaliveEnabled(false);
mTCPKeepaliveConfig = kTCPKeepaliveDisabled;
}
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// Start a timer to move to long-lived keepalive config.
if(!mTCPKeepaliveTransitionTimer) {
mTCPKeepaliveTransitionTimer =
do_CreateInstance("@mozilla.org/timer;1");
}
if (mTCPKeepaliveTransitionTimer) {
int32_t time = gHttpHandler->GetTCPKeepaliveShortLivedTime();
// Adjust |time| to ensure a full set of keepalive probes can be sent
// at the end of the short-lived phase.
if (gHttpHandler->TCPKeepaliveEnabledForShortLivedConns()) {
if (NS_WARN_IF(!gSocketTransportService)) {
return NS_ERROR_NOT_INITIALIZED;
}
int32_t probeCount = -1;
rv = gSocketTransportService->GetKeepaliveProbeCount(&probeCount);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (NS_WARN_IF(probeCount <= 0)) {
return NS_ERROR_UNEXPECTED;
}
// Add time for final keepalive probes, and 2 seconds for a buffer.
time += ((probeCount) * retryIntervalS) - (time % idleTimeS) + 2;
}
mTCPKeepaliveTransitionTimer->InitWithFuncCallback(
nsHttpConnection::UpdateTCPKeepalive,
this,
(uint32_t)time*1000,
nsITimer::TYPE_ONE_SHOT);
} else {
NS_WARNING("nsHttpConnection::StartShortLivedTCPKeepalives failed to "
"create timer.");
}
return NS_OK;
}
nsresult
nsHttpConnection::StartLongLivedTCPKeepalives()
{
MOZ_ASSERT(!mUsingSpdyVersion, "Don't use TCP Keepalive with SPDY!");
if (NS_WARN_IF(mUsingSpdyVersion)) {
return NS_OK;
}
MOZ_ASSERT(mSocketTransport);
if (!mSocketTransport) {
return NS_ERROR_NOT_INITIALIZED;
}
nsresult rv = NS_OK;
if (gHttpHandler->TCPKeepaliveEnabledForLongLivedConns()) {
// Increase the idle time.
int32_t idleTimeS = gHttpHandler->GetTCPKeepaliveLongLivedIdleTime();
LOG(("nsHttpConnection::StartLongLivedTCPKeepalives[%p] idle time[%ds]",
this, idleTimeS));
int32_t retryIntervalS =
std::max<int32_t>((int32_t)PR_IntervalToSeconds(mRtt), 1);
rv = mSocketTransport->SetKeepaliveVals(idleTimeS, retryIntervalS);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// Ensure keepalive is enabled, if current status is disabled.
if (mTCPKeepaliveConfig == kTCPKeepaliveDisabled) {
rv = mSocketTransport->SetKeepaliveEnabled(true);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
mTCPKeepaliveConfig = kTCPKeepaliveLongLivedConfig;
} else {
rv = mSocketTransport->SetKeepaliveEnabled(false);
mTCPKeepaliveConfig = kTCPKeepaliveDisabled;
}
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
nsresult
nsHttpConnection::DisableTCPKeepalives()
{
MOZ_ASSERT(mSocketTransport);
if (!mSocketTransport) {
return NS_ERROR_NOT_INITIALIZED;
}
LOG(("nsHttpConnection::DisableTCPKeepalives [%p]", this));
if (mTCPKeepaliveConfig != kTCPKeepaliveDisabled) {
nsresult rv = mSocketTransport->SetKeepaliveEnabled(false);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mTCPKeepaliveConfig = kTCPKeepaliveDisabled;
}
if (mTCPKeepaliveTransitionTimer) {
mTCPKeepaliveTransitionTimer->Cancel();
mTCPKeepaliveTransitionTimer = nullptr;
}
return NS_OK;
}
//-----------------------------------------------------------------------------
// nsHttpConnection::nsISupports
//-----------------------------------------------------------------------------

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

@ -16,6 +16,7 @@
#include "nsIAsyncInputStream.h"
#include "nsIAsyncOutputStream.h"
#include "nsIInterfaceRequestor.h"
#include "nsITimer.h"
class nsISocketTransport;
@ -148,6 +149,11 @@ public:
// other connections.
uint32_t ReadTimeoutTick(PRIntervalTime now);
// For Active and Idle connections, this will be called when
// mTCPKeepaliveTransitionTimer fires, to check if the TCP keepalive config
// should move from short-lived (fast-detect) to long-lived.
static void UpdateTCPKeepalive(nsITimer *aTimer, void *aClosure);
nsAHttpTransaction::Classifier Classification() { return mClassification; }
void Classify(nsAHttpTransaction::Classifier newclass)
{
@ -169,6 +175,13 @@ public:
bool IsExperienced() { return mExperienced; }
private:
// Value (set in mTCPKeepaliveConfig) indicates which set of prefs to use.
enum TCPKeepaliveConfig {
kTCPKeepaliveDisabled = 0,
kTCPKeepaliveShortLivedConfig,
kTCPKeepaliveLongLivedConfig
};
// called to cause the underlying socket to start speaking SSL
nsresult ProxyStartSSL();
@ -196,7 +209,12 @@ private:
// used to inform nsIHttpDataUsage of transfer
void ReportDataUsage(bool);
private:
// Used to set TCP keepalives for fast detection of dead connections during
// an initial period, and slower detection for long-lived connections.
nsresult StartShortLivedTCPKeepalives();
nsresult StartLongLivedTCPKeepalives();
nsresult DisableTCPKeepalives();
nsCOMPtr<nsISocketTransport> mSocketTransport;
nsCOMPtr<nsIAsyncInputStream> mSocketIn;
nsCOMPtr<nsIAsyncOutputStream> mSocketOut;
@ -280,6 +298,10 @@ private:
uint32_t mTransactionCaps;
bool mResponseTimeoutEnabled;
// Flag to indicate connection is in inital keepalive period (fast detect).
uint32_t mTCPKeepaliveConfig;
nsCOMPtr<nsITimer> mTCPKeepaliveTransitionTimer;
};
}} // namespace mozilla::net

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

@ -207,6 +207,11 @@ nsHttpHandler::nsHttpHandler()
, mRequestTokenBucketHz(100)
, mRequestTokenBucketBurst(32)
, mCriticalRequestPrioritization(true)
, mTCPKeepaliveShortLivedEnabled(false)
, mTCPKeepaliveShortLivedTimeS(60)
, mTCPKeepaliveShortLivedIdleTimeS(10)
, mTCPKeepaliveLongLivedEnabled(false)
, mTCPKeepaliveLongLivedIdleTimeS(600)
, mEthernetBytesRead(0)
, mEthernetBytesWritten(0)
, mCellBytesRead(0)
@ -286,7 +291,8 @@ nsHttpHandler::Init()
prefBranch->AddObserver(DONOTTRACK_HEADER_ENABLED, this, true);
prefBranch->AddObserver(DONOTTRACK_HEADER_VALUE, this, true);
prefBranch->AddObserver(TELEMETRY_ENABLED, this, true);
prefBranch->AddObserver(HTTP_PREF("tcp_keepalive.short_lived_connections"), this, true);
prefBranch->AddObserver(HTTP_PREF("tcp_keepalive.long_lived_connections"), this, true);
PrefsChanged(prefBranch, nullptr);
}
@ -1422,6 +1428,47 @@ nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
RequestTokenBucketBurst());
}
// Keepalive values for initial and idle connections.
if (PREF_CHANGED(HTTP_PREF("tcp_keepalive.short_lived_connections"))) {
rv = prefs->GetBoolPref(
HTTP_PREF("tcp_keepalive.short_lived_connections"), &cVar);
if (NS_SUCCEEDED(rv) && cVar != mTCPKeepaliveShortLivedEnabled) {
mTCPKeepaliveShortLivedEnabled = cVar;
}
}
if (PREF_CHANGED(HTTP_PREF("tcp_keepalive.short_lived_time"))) {
rv = prefs->GetIntPref(
HTTP_PREF("tcp_keepalive.short_lived_time"), &val);
if (NS_SUCCEEDED(rv) && val > 0)
mTCPKeepaliveShortLivedTimeS = clamped(val, 1, 300); // Max 5 mins.
}
if (PREF_CHANGED(HTTP_PREF("tcp_keepalive.short_lived_idle_time"))) {
rv = prefs->GetIntPref(
HTTP_PREF("tcp_keepalive.short_lived_idle_time"), &val);
if (NS_SUCCEEDED(rv) && val > 0)
mTCPKeepaliveShortLivedIdleTimeS = clamped(val,
1, kMaxTCPKeepIdle);
}
// Keepalive values for Long-lived Connections.
if (PREF_CHANGED(HTTP_PREF("tcp_keepalive.long_lived_connections"))) {
rv = prefs->GetBoolPref(
HTTP_PREF("tcp_keepalive.long_lived_connections"), &cVar);
if (NS_SUCCEEDED(rv) && cVar != mTCPKeepaliveLongLivedEnabled) {
mTCPKeepaliveLongLivedEnabled = cVar;
}
}
if (PREF_CHANGED(HTTP_PREF("tcp_keepalive.long_lived_idle_time"))) {
rv = prefs->GetIntPref(
HTTP_PREF("tcp_keepalive.long_lived_idle_time"), &val);
if (NS_SUCCEEDED(rv) && val > 0)
mTCPKeepaliveLongLivedIdleTimeS = clamped(val,
1, kMaxTCPKeepIdle);
}
#undef PREF_CHANGED
#undef MULTI_PREF_CHANGED
}

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

@ -120,6 +120,33 @@ public:
bool PromptTempRedirect() { return mPromptTempRedirect; }
// TCP Keepalive configuration values.
// Returns true if TCP keepalive should be enabled for short-lived conns.
bool TCPKeepaliveEnabledForShortLivedConns() {
return mTCPKeepaliveShortLivedEnabled;
}
// Return time (secs) that a connection is consider short lived (for TCP
// keepalive purposes). After this time, the connection is long-lived.
int32_t GetTCPKeepaliveShortLivedTime() {
return mTCPKeepaliveShortLivedTimeS;
}
// Returns time (secs) before first TCP keepalive probes should be sent;
// same time used between successful keepalive probes.
int32_t GetTCPKeepaliveShortLivedIdleTime() {
return mTCPKeepaliveShortLivedIdleTimeS;
}
// Returns true if TCP keepalive should be enabled for long-lived conns.
bool TCPKeepaliveEnabledForLongLivedConns() {
return mTCPKeepaliveLongLivedEnabled;
}
// Returns time (secs) before first TCP keepalive probes should be sent;
// same time used between successful keepalive probes.
int32_t GetTCPKeepaliveLongLivedIdleTime() {
return mTCPKeepaliveLongLivedIdleTimeS;
}
nsHttpAuthCache *AuthCache(bool aPrivate) {
return aPrivate ? &mPrivateAuthCache : &mAuthCache;
}
@ -464,6 +491,20 @@ private:
// for 1 minute for most requests.
TimeStamp mCacheSkippedUntil;
// TCP Keepalive configuration values.
// True if TCP keepalive is enabled for short-lived conns.
bool mTCPKeepaliveShortLivedEnabled;
// Time (secs) indicating how long a conn is considered short-lived.
int32_t mTCPKeepaliveShortLivedTimeS;
// Time (secs) before first keepalive probe; between successful probes.
int32_t mTCPKeepaliveShortLivedIdleTimeS;
// True if TCP keepalive is enabled for long-lived conns.
bool mTCPKeepaliveLongLivedEnabled;
// Time (secs) before first keepalive probe; between successful probes.
int32_t mTCPKeepaliveLongLivedIdleTimeS;
private:
// For Rate Pacing Certain Network Events. Only assign this pointer on
// socket thread.