зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1386746 - Throttle HTTP response by allowing only small amount of data to read periodically, r=mcmanus
This commit is contained in:
Родитель
04a25ecb14
Коммит
1ee6257bc3
|
@ -2205,16 +2205,25 @@ pref("network.auth.private-browsing-sso", false);
|
|||
// Control how throttling of http responses works - number of ms that each
|
||||
// suspend and resume period lasts (prefs named appropriately)
|
||||
pref("network.http.throttle.enable", true);
|
||||
pref("network.http.throttle.version", 1);
|
||||
|
||||
// V1 prefs
|
||||
pref("network.http.throttle.suspend-for", 900);
|
||||
pref("network.http.throttle.resume-for", 100);
|
||||
|
||||
// V2 prefs
|
||||
pref("network.http.throttle.read-limit-bytes", 8000);
|
||||
pref("network.http.throttle.read-interval-ms", 500);
|
||||
|
||||
// Common prefs
|
||||
// Delay we resume throttled background responses after the last unthrottled
|
||||
// response has finished. Prevents resuming too soon during an active page load
|
||||
// at which sub-resource reqeusts quickly come and go.
|
||||
pref("network.http.throttle.resume-background-in", 1000);
|
||||
pref("network.http.throttle.hold-time-ms", 800);
|
||||
// After the last transaction activation or last data chunk response we only
|
||||
// throttle for this period of time. This prevents comet and unresponsive
|
||||
// http requests to engage long-standing throttling.
|
||||
pref("network.http.throttle.time-window", 3000);
|
||||
pref("network.http.throttle.max-time-ms", 500);
|
||||
|
||||
// Give higher priority to requests resulting from a user interaction event
|
||||
// like click-to-play, image fancy-box zoom, navigation.
|
||||
|
|
|
@ -116,10 +116,13 @@ nsHttpConnectionMgr::nsHttpConnectionMgr()
|
|||
, mMaxPersistConnsPerProxy(0)
|
||||
, mMaxRequestDelay(0)
|
||||
, mThrottleEnabled(false)
|
||||
, mThrottleVersion(2)
|
||||
, mThrottleSuspendFor(0)
|
||||
, mThrottleResumeFor(0)
|
||||
, mThrottleResumeIn(0)
|
||||
, mThrottleTimeWindow(0)
|
||||
, mThrottleReadLimit(0)
|
||||
, mThrottleReadInterval(0)
|
||||
, mThrottleHoldTime(0)
|
||||
, mThrottleMaxTime(0)
|
||||
, mIsShuttingDown(false)
|
||||
, mNumActiveConns(0)
|
||||
, mNumIdleConns(0)
|
||||
|
@ -174,10 +177,13 @@ nsHttpConnectionMgr::Init(uint16_t maxUrgentExcessiveConns,
|
|||
uint16_t maxPersistConnsPerProxy,
|
||||
uint16_t maxRequestDelay,
|
||||
bool throttleEnabled,
|
||||
uint32_t throttleVersion,
|
||||
uint32_t throttleSuspendFor,
|
||||
uint32_t throttleResumeFor,
|
||||
uint32_t throttleResumeIn,
|
||||
uint32_t throttleTimeWindow)
|
||||
uint32_t throttleReadLimit,
|
||||
uint32_t throttleReadInterval,
|
||||
uint32_t throttleHoldTime,
|
||||
uint32_t throttleMaxTime)
|
||||
{
|
||||
LOG(("nsHttpConnectionMgr::Init\n"));
|
||||
|
||||
|
@ -191,10 +197,13 @@ nsHttpConnectionMgr::Init(uint16_t maxUrgentExcessiveConns,
|
|||
mMaxRequestDelay = maxRequestDelay;
|
||||
|
||||
mThrottleEnabled = throttleEnabled;
|
||||
mThrottleVersion = throttleVersion;
|
||||
mThrottleSuspendFor = throttleSuspendFor;
|
||||
mThrottleResumeFor = throttleResumeFor;
|
||||
mThrottleResumeIn = throttleResumeIn;
|
||||
mThrottleTimeWindow = TimeDuration::FromMilliseconds(throttleTimeWindow);
|
||||
mThrottleReadLimit = throttleReadLimit;
|
||||
mThrottleReadInterval = throttleReadInterval;
|
||||
mThrottleHoldTime = throttleHoldTime;
|
||||
mThrottleMaxTime = TimeDuration::FromMilliseconds(throttleMaxTime);
|
||||
|
||||
mIsShuttingDown = false;
|
||||
}
|
||||
|
@ -2832,11 +2841,17 @@ nsHttpConnectionMgr::OnMsgUpdateParam(int32_t inParam, ARefBase *)
|
|||
case THROTTLING_RESUME_FOR:
|
||||
mThrottleResumeFor = value;
|
||||
break;
|
||||
case THROTTLING_RESUME_IN:
|
||||
mThrottleResumeIn = value;
|
||||
case THROTTLING_READ_LIMIT:
|
||||
mThrottleReadLimit = value;
|
||||
break;
|
||||
case THROTTLING_TIME_WINDOW:
|
||||
mThrottleTimeWindow = TimeDuration::FromMilliseconds(value);
|
||||
case THROTTLING_READ_INTERVAL:
|
||||
mThrottleReadInterval = value;
|
||||
break;
|
||||
case THROTTLING_HOLD_TIME:
|
||||
mThrottleHoldTime = value;
|
||||
break;
|
||||
case THROTTLING_MAX_TIME:
|
||||
mThrottleMaxTime = TimeDuration::FromMilliseconds(value);
|
||||
break;
|
||||
default:
|
||||
NS_NOTREACHED("unexpected parameter name");
|
||||
|
@ -2952,7 +2967,7 @@ void nsHttpConnectionMgr::TouchThrottlingTimeWindow(bool aEnsureTicker)
|
|||
|
||||
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
|
||||
|
||||
mThrottlingWindowEndsAt = TimeStamp::NowLoRes() + mThrottleTimeWindow;
|
||||
mThrottlingWindowEndsAt = TimeStamp::NowLoRes() + mThrottleMaxTime;
|
||||
|
||||
if (!mThrottleTicker &&
|
||||
MOZ_LIKELY(aEnsureTicker) && MOZ_LIKELY(mThrottleEnabled)) {
|
||||
|
@ -3091,11 +3106,13 @@ nsHttpConnectionMgr::RemoveActiveTransaction(nsHttpTransaction * aTrans,
|
|||
return;
|
||||
}
|
||||
|
||||
if (!mThrottlingInhibitsReading) {
|
||||
// There is then nothing to wake up. Affected transactions will not be put
|
||||
// to sleep automatically on next tick.
|
||||
LOG((" reading not currently inhibited"));
|
||||
return;
|
||||
if (mThrottleVersion == 1) {
|
||||
if (!mThrottlingInhibitsReading) {
|
||||
// There is then nothing to wake up. Affected transactions will not be put
|
||||
// to sleep automatically on next tick.
|
||||
LOG((" reading not currently inhibited"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (mActiveTabUnthrottledTransactionsExist) {
|
||||
|
@ -3140,22 +3157,36 @@ nsHttpConnectionMgr::UpdateActiveTransaction(nsHttpTransaction * aTrans)
|
|||
{
|
||||
LOG(("nsHttpConnectionMgr::UpdateActiveTransaction ENTER t=%p", aTrans));
|
||||
|
||||
AddActiveTransaction(aTrans);
|
||||
// First remove then add. In case of a download that is the only active
|
||||
// transaction and has just been marked as download (goes unthrottled to
|
||||
// throttled), adding first would cause it to be throttled for first few
|
||||
// milliseconds - becuause it would appear as if there were both throttled
|
||||
// and unthrottled transactions at the time.
|
||||
|
||||
Maybe<bool> reversed;
|
||||
reversed.emplace(!aTrans->EligibleForThrottling());
|
||||
RemoveActiveTransaction(aTrans, reversed);
|
||||
|
||||
AddActiveTransaction(aTrans);
|
||||
|
||||
LOG(("nsHttpConnectionMgr::UpdateActiveTransaction EXIT t=%p", aTrans));
|
||||
}
|
||||
|
||||
bool
|
||||
nsHttpConnectionMgr::ShouldStopReading(nsHttpTransaction * aTrans)
|
||||
nsHttpConnectionMgr::ShouldThrottle(nsHttpTransaction * aTrans)
|
||||
{
|
||||
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
|
||||
|
||||
if (!mThrottlingInhibitsReading || !mThrottleEnabled) {
|
||||
return false;
|
||||
LOG(("nsHttpConnectionMgr::ShouldThrottle trans=%p", aTrans));
|
||||
|
||||
if (mThrottleVersion == 1) {
|
||||
if (!mThrottlingInhibitsReading || !mThrottleEnabled) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!mThrottleEnabled) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t tabId = aTrans->TopLevelOuterContentWindowId();
|
||||
|
@ -3168,54 +3199,66 @@ nsHttpConnectionMgr::ShouldStopReading(nsHttpTransaction * aTrans)
|
|||
// Chrome initiated and unidentified transactions just respect
|
||||
// their throttle flag, when something for the active tab is happening.
|
||||
// This also includes downloads.
|
||||
LOG((" active tab loads, trans is tab-less, throttled=%d", throttled));
|
||||
return throttled;
|
||||
}
|
||||
if (!forActiveTab) {
|
||||
// This is a background tab request, we want them to always throttle
|
||||
// when there are transactions running for the ative tab.
|
||||
LOG((" active tab loads, trans not of the active tab"));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mActiveTabUnthrottledTransactionsExist) {
|
||||
// Unthrottled transactions for the active tab take precedence
|
||||
LOG((" active tab loads unthrottled, trans throttled=%d", throttled));
|
||||
return throttled;
|
||||
}
|
||||
|
||||
LOG((" trans for active tab, don't throttle"));
|
||||
return false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!forActiveTab);
|
||||
|
||||
if (mDelayedResumeReadTimer) {
|
||||
// If this timer exists, background transactions are scheduled to be woken
|
||||
// after a delay, hence leave them asleep.
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!mActiveTransactions[false].IsEmpty()) {
|
||||
// This means there are unthrottled active transactions for background tabs.
|
||||
// If we are here, there can't be any transactions for the active tab.
|
||||
// (If there is no transaction for a tab id, there is no entry for it in
|
||||
// the hashtable.)
|
||||
LOG((" backround tab(s) load unthrottled, trans throttled=%d", throttled));
|
||||
return throttled;
|
||||
}
|
||||
|
||||
// There are only unthrottled transactions for background tabs: don't throttle.
|
||||
LOG((" backround tab(s) load throttled, don't throttle"));
|
||||
return false;
|
||||
}();
|
||||
|
||||
if (forActiveTab && !stop) {
|
||||
// This active-tab transaction is allowed to read even though
|
||||
// we are in the middle of "stop reading" interval. Hence,
|
||||
// This is an active-tab transaction and is allowed to read. Hence,
|
||||
// prolong the throttle time window to make sure all 'lower-decks'
|
||||
// transactions will actually throttle.
|
||||
TouchThrottlingTimeWindow();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only stop reading when in the 3 seconds throttle time window.
|
||||
// This window is prolonged (restarted) by a call to TouchThrottlingTimeWindow.
|
||||
return stop && InThrottlingTimeWindow();
|
||||
// Only stop reading when in the configured throttle max-time (aka time window).
|
||||
// This window is prolonged (restarted) by a call to TouchThrottlingTimeWindow
|
||||
// called on new transaction activation or on receive of response bytes of an
|
||||
// active tab transaction.
|
||||
bool inWindow = InThrottlingTimeWindow();
|
||||
|
||||
LOG((" stop=%d, in-window=%d, delayed-bck-timer=%d",
|
||||
stop, inWindow, !!mDelayedResumeReadTimer));
|
||||
|
||||
if (!forActiveTab) {
|
||||
// If the delayed background resume timer exists, background transactions are
|
||||
// scheduled to be woken after a delay, hence leave them throttled.
|
||||
inWindow = inWindow || mDelayedResumeReadTimer;
|
||||
}
|
||||
|
||||
return stop && inWindow;
|
||||
}
|
||||
|
||||
bool nsHttpConnectionMgr::IsConnEntryUnderPressure(nsHttpConnectionInfo *connInfo)
|
||||
|
@ -3276,12 +3319,16 @@ nsHttpConnectionMgr::EnsureThrottleTickerIfNeeded()
|
|||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!mThrottlingInhibitsReading);
|
||||
|
||||
mThrottleTicker = NS_NewTimer();
|
||||
if (mThrottleTicker) {
|
||||
mThrottleTicker->Init(this, mThrottleSuspendFor, nsITimer::TYPE_ONE_SHOT);
|
||||
mThrottlingInhibitsReading = true;
|
||||
if (mThrottleVersion == 1) {
|
||||
MOZ_ASSERT(!mThrottlingInhibitsReading);
|
||||
|
||||
mThrottleTicker->Init(this, mThrottleSuspendFor, nsITimer::TYPE_ONE_SHOT);
|
||||
mThrottlingInhibitsReading = true;
|
||||
} else {
|
||||
mThrottleTicker->Init(this, mThrottleReadInterval, nsITimer::TYPE_ONE_SHOT);
|
||||
}
|
||||
}
|
||||
|
||||
LogActiveTransactions('^');
|
||||
|
@ -3304,7 +3351,10 @@ nsHttpConnectionMgr::DestroyThrottleTicker()
|
|||
LOG(("nsHttpConnectionMgr::DestroyThrottleTicker"));
|
||||
mThrottleTicker->Cancel();
|
||||
mThrottleTicker = nullptr;
|
||||
mThrottlingInhibitsReading = false;
|
||||
|
||||
if (mThrottleVersion == 1) {
|
||||
mThrottlingInhibitsReading = false;
|
||||
}
|
||||
|
||||
LogActiveTransactions('v');
|
||||
}
|
||||
|
@ -3314,27 +3364,46 @@ nsHttpConnectionMgr::ThrottlerTick()
|
|||
{
|
||||
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
|
||||
|
||||
mThrottlingInhibitsReading = !mThrottlingInhibitsReading;
|
||||
if (mThrottleVersion == 1) {
|
||||
mThrottlingInhibitsReading = !mThrottlingInhibitsReading;
|
||||
|
||||
LOG(("nsHttpConnectionMgr::ThrottlerTick inhibit=%d", mThrottlingInhibitsReading));
|
||||
LOG(("nsHttpConnectionMgr::ThrottlerTick inhibit=%d", mThrottlingInhibitsReading));
|
||||
|
||||
// If there are only background transactions to be woken after a delay, keep
|
||||
// the ticker so that we woke them only for the resume-for interval and then
|
||||
// throttle them again until the background-resume delay passes.
|
||||
if (!mThrottlingInhibitsReading &&
|
||||
!mDelayedResumeReadTimer &&
|
||||
(!IsThrottleTickerNeeded() || !InThrottlingTimeWindow())) {
|
||||
LOG((" last tick"));
|
||||
mThrottleTicker = nullptr;
|
||||
}
|
||||
// If there are only background transactions to be woken after a delay, keep
|
||||
// the ticker so that we woke them only for the resume-for interval and then
|
||||
// throttle them again until the background-resume delay passes.
|
||||
if (!mThrottlingInhibitsReading &&
|
||||
!mDelayedResumeReadTimer &&
|
||||
(!IsThrottleTickerNeeded() || !InThrottlingTimeWindow())) {
|
||||
LOG((" last tick"));
|
||||
mThrottleTicker = nullptr;
|
||||
}
|
||||
|
||||
if (mThrottlingInhibitsReading) {
|
||||
if (mThrottleTicker) {
|
||||
mThrottleTicker->Init(this, mThrottleSuspendFor, nsITimer::TYPE_ONE_SHOT);
|
||||
if (mThrottlingInhibitsReading) {
|
||||
if (mThrottleTicker) {
|
||||
mThrottleTicker->Init(this, mThrottleSuspendFor, nsITimer::TYPE_ONE_SHOT);
|
||||
}
|
||||
} else {
|
||||
if (mThrottleTicker) {
|
||||
mThrottleTicker->Init(this, mThrottleResumeFor, nsITimer::TYPE_ONE_SHOT);
|
||||
}
|
||||
|
||||
ResumeReadOf(mActiveTransactions[false], true);
|
||||
ResumeReadOf(mActiveTransactions[true]);
|
||||
}
|
||||
} else {
|
||||
LOG(("nsHttpConnectionMgr::ThrottlerTick"));
|
||||
|
||||
// If there are only background transactions to be woken after a delay, keep
|
||||
// the ticker so that we still keep the low read limit for that time.
|
||||
if (!mDelayedResumeReadTimer &&
|
||||
(!IsThrottleTickerNeeded() || !InThrottlingTimeWindow())) {
|
||||
LOG((" last tick"));
|
||||
mThrottleTicker = nullptr;
|
||||
}
|
||||
|
||||
if (mThrottleTicker) {
|
||||
mThrottleTicker->Init(this, mThrottleResumeFor, nsITimer::TYPE_ONE_SHOT);
|
||||
mThrottleTicker->Init(this, mThrottleReadInterval, nsITimer::TYPE_ONE_SHOT);
|
||||
}
|
||||
|
||||
ResumeReadOf(mActiveTransactions[false], true);
|
||||
|
@ -3345,13 +3414,25 @@ nsHttpConnectionMgr::ThrottlerTick()
|
|||
void
|
||||
nsHttpConnectionMgr::DelayedResumeBackgroundThrottledTransactions()
|
||||
{
|
||||
if (mDelayedResumeReadTimer) {
|
||||
return;
|
||||
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
|
||||
|
||||
if (mThrottleVersion == 1) {
|
||||
if (mDelayedResumeReadTimer) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// If the mThrottleTicker doesn't exist, there is nothing currently
|
||||
// being throttled. Hence, don't invoke the hold time interval.
|
||||
// This is called also when a single download transaction becomes
|
||||
// marked as throttleable. We would otherwise block it unnecessarily.
|
||||
if (mDelayedResumeReadTimer || !mThrottleTicker) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
LOG(("nsHttpConnectionMgr::DelayedResumeBackgroundThrottledTransactions"));
|
||||
NS_NewTimerWithObserver(getter_AddRefs(mDelayedResumeReadTimer),
|
||||
this, mThrottleResumeIn, nsITimer::TYPE_ONE_SHOT);
|
||||
this, mThrottleHoldTime, nsITimer::TYPE_ONE_SHOT);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -3464,7 +3545,6 @@ nsHttpConnectionMgr::OnMsgUpdateCurrentTopLevelOuterContentWindowId(
|
|||
}
|
||||
|
||||
bool activeTabWasLoading = mActiveTabTransactionsExist;
|
||||
bool activeTabIdChanged = mCurrentTopLevelOuterContentWindowId != winId;
|
||||
|
||||
uint64_t previousWindowId = mCurrentTopLevelOuterContentWindowId;
|
||||
mCurrentTopLevelOuterContentWindowId = winId;
|
||||
|
@ -3479,17 +3559,15 @@ nsHttpConnectionMgr::OnMsgUpdateCurrentTopLevelOuterContentWindowId(
|
|||
|
||||
nsTArray<RefPtr<nsHttpTransaction>> *transactions = nullptr;
|
||||
|
||||
if (activeTabIdChanged) {
|
||||
// Update the "Exists" caches and resume any transactions that now deserve it,
|
||||
// changing the active tab changes the conditions for throttling.
|
||||
transactions = mActiveTransactions[false].Get(mCurrentTopLevelOuterContentWindowId);
|
||||
mActiveTabUnthrottledTransactionsExist = !!transactions;
|
||||
// Update the "Exists" caches and resume any transactions that now deserve it,
|
||||
// changing the active tab changes the conditions for throttling.
|
||||
transactions = mActiveTransactions[false].Get(mCurrentTopLevelOuterContentWindowId);
|
||||
mActiveTabUnthrottledTransactionsExist = !!transactions;
|
||||
|
||||
if (!mActiveTabUnthrottledTransactionsExist) {
|
||||
transactions = mActiveTransactions[true].Get(mCurrentTopLevelOuterContentWindowId);
|
||||
}
|
||||
mActiveTabTransactionsExist = !!transactions;
|
||||
if (!mActiveTabUnthrottledTransactionsExist) {
|
||||
transactions = mActiveTransactions[true].Get(mCurrentTopLevelOuterContentWindowId);
|
||||
}
|
||||
mActiveTabTransactionsExist = !!transactions;
|
||||
|
||||
if (transactions) {
|
||||
// This means there are some transactions for this newly activated tab, resume them
|
||||
|
@ -3513,8 +3591,8 @@ nsHttpConnectionMgr::OnMsgUpdateCurrentTopLevelOuterContentWindowId(
|
|||
}
|
||||
|
||||
if (!mActiveTransactions[true].IsEmpty()) {
|
||||
LOG((" delayed resuming throttled background transactions"));
|
||||
DelayedResumeBackgroundThrottledTransactions();
|
||||
LOG((" resuming throttled background transactions"));
|
||||
ResumeReadOf(mActiveTransactions[true]);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -60,8 +60,10 @@ public:
|
|||
THROTTLING_ENABLED,
|
||||
THROTTLING_SUSPEND_FOR,
|
||||
THROTTLING_RESUME_FOR,
|
||||
THROTTLING_RESUME_IN,
|
||||
THROTTLING_TIME_WINDOW
|
||||
THROTTLING_READ_LIMIT,
|
||||
THROTTLING_READ_INTERVAL,
|
||||
THROTTLING_HOLD_TIME,
|
||||
THROTTLING_MAX_TIME
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
@ -76,10 +78,13 @@ public:
|
|||
uint16_t maxPersistentConnectionsPerProxy,
|
||||
uint16_t maxRequestDelay,
|
||||
bool throttleEnabled,
|
||||
uint32_t throttleVersion,
|
||||
uint32_t throttleSuspendFor,
|
||||
uint32_t throttleResumeFor,
|
||||
uint32_t throttleResumeIn,
|
||||
uint32_t throttleTimeWindow);
|
||||
uint32_t throttleReadLimit,
|
||||
uint32_t throttleReadInterval,
|
||||
uint32_t throttleHoldTime,
|
||||
uint32_t throttleMaxTime);
|
||||
MOZ_MUST_USE nsresult Shutdown();
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
@ -225,19 +230,19 @@ public:
|
|||
void UpdateActiveTransaction(nsHttpTransaction* aTrans);
|
||||
|
||||
// called by nsHttpTransaction::WriteSegments. decides whether the transaction
|
||||
// should stop reading data based on: the throttling ticker status, overall
|
||||
// status of all active transactions regarding active tab and respective
|
||||
// throttling state.
|
||||
bool ShouldStopReading(nsHttpTransaction* aTrans);
|
||||
// should limit reading its reponse data. There are various conditions this
|
||||
// methods evaluates. If called by an active-tab non-throttled transaction,
|
||||
// the throttling window time will be prolonged.
|
||||
bool ShouldThrottle(nsHttpTransaction* aTrans);
|
||||
|
||||
// prolongs the throttling time window to now + the window preferred size
|
||||
// prolongs the throttling time window to now + the window preferred delay.
|
||||
// called when:
|
||||
// - any transaction is activated
|
||||
// - or when a currently unthrottled transaction for the active window receives data
|
||||
void TouchThrottlingTimeWindow(bool aEnsureTicker = true);
|
||||
|
||||
// return true iff the connection has pending transactions for the active tab.
|
||||
// it's mainly used to disallow throttling (stop reading) of a response
|
||||
// it's mainly used to disallow throttling (limit reading) of a response
|
||||
// belonging to the same conn info to free up a connection ASAP.
|
||||
// NOTE: relatively expensive to call, there are two hashtable lookups.
|
||||
bool IsConnEntryUnderPressure(nsHttpConnectionInfo*);
|
||||
|
@ -531,10 +536,13 @@ private:
|
|||
uint16_t mMaxPersistConnsPerProxy;
|
||||
uint16_t mMaxRequestDelay; // in seconds
|
||||
bool mThrottleEnabled;
|
||||
uint32_t mThrottleVersion;
|
||||
uint32_t mThrottleSuspendFor;
|
||||
uint32_t mThrottleResumeFor;
|
||||
uint32_t mThrottleResumeIn;
|
||||
TimeDuration mThrottleTimeWindow;
|
||||
uint32_t mThrottleReadLimit;
|
||||
uint32_t mThrottleReadInterval;
|
||||
uint32_t mThrottleHoldTime;
|
||||
TimeDuration mThrottleMaxTime;
|
||||
Atomic<bool, mozilla::Relaxed> mIsShuttingDown;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
@ -717,6 +725,7 @@ private:
|
|||
// mActiveTransactions[0] are all unthrottled transactions, mActiveTransactions[1] throttled.
|
||||
nsClassHashtable<nsUint64HashKey, nsTArray<RefPtr<nsHttpTransaction>>> mActiveTransactions[2];
|
||||
|
||||
// V1 specific
|
||||
// Whether we are inside the "stop reading" interval, altered by the throttle ticker
|
||||
bool mThrottlingInhibitsReading;
|
||||
|
||||
|
@ -729,10 +738,17 @@ private:
|
|||
// The method also unschedules the delayed resume of background tabs timer
|
||||
// if the ticker was about to be scheduled.
|
||||
void EnsureThrottleTickerIfNeeded();
|
||||
// V1:
|
||||
// Drops also the mThrottlingInhibitsReading flag. Immediate or delayed resume
|
||||
// of currently throttled transactions is not affected by this method.
|
||||
// V2:
|
||||
// Immediate or delayed resume of currently throttled transactions is not
|
||||
// affected by this method.
|
||||
void DestroyThrottleTicker();
|
||||
// V1:
|
||||
// Handler for the ticker: alters the mThrottlingInhibitsReading flag.
|
||||
// V2:
|
||||
// Handler for the ticker: calls ResumeReading() for all throttled transactions.
|
||||
void ThrottlerTick();
|
||||
|
||||
// mechanism to delay immediate resume of background tabs and chrome initiated
|
||||
|
|
|
@ -211,10 +211,13 @@ nsHttpHandler::nsHttpHandler()
|
|||
, mMaxPersistentConnectionsPerServer(2)
|
||||
, mMaxPersistentConnectionsPerProxy(4)
|
||||
, mThrottleEnabled(true)
|
||||
, mThrottleVersion(2)
|
||||
, mThrottleSuspendFor(3000)
|
||||
, mThrottleResumeFor(200)
|
||||
, mThrottleResumeIn(400)
|
||||
, mThrottleTimeWindow(3000)
|
||||
, mThrottleReadLimit(8000)
|
||||
, mThrottleReadInterval(500)
|
||||
, mThrottleHoldTime(600)
|
||||
, mThrottleMaxTime(3000)
|
||||
, mUrgentStartEnabled(true)
|
||||
, mTailBlockingEnabled(true)
|
||||
, mTailDelayQuantum(600)
|
||||
|
@ -613,10 +616,13 @@ nsHttpHandler::InitConnectionMgr()
|
|||
mMaxPersistentConnectionsPerProxy,
|
||||
mMaxRequestDelay,
|
||||
mThrottleEnabled,
|
||||
mThrottleVersion,
|
||||
mThrottleSuspendFor,
|
||||
mThrottleResumeFor,
|
||||
mThrottleResumeIn,
|
||||
mThrottleTimeWindow);
|
||||
mThrottleReadLimit,
|
||||
mThrottleReadInterval,
|
||||
mThrottleHoldTime,
|
||||
mThrottleMaxTime);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -1639,6 +1645,11 @@ nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
|
|||
}
|
||||
}
|
||||
|
||||
if (PREF_CHANGED(HTTP_PREF("throttle.version"))) {
|
||||
rv = prefs->GetIntPref(HTTP_PREF("throttle.version"), &val);
|
||||
mThrottleVersion = (uint32_t)clamped(val, 1, 2);
|
||||
}
|
||||
|
||||
if (PREF_CHANGED(HTTP_PREF("throttle.suspend-for"))) {
|
||||
rv = prefs->GetIntPref(HTTP_PREF("throttle.suspend-for"), &val);
|
||||
mThrottleSuspendFor = (uint32_t)clamped(val, 0, 120000);
|
||||
|
@ -1657,21 +1668,39 @@ nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
|
|||
}
|
||||
}
|
||||
|
||||
if (PREF_CHANGED(HTTP_PREF("throttle.resume-background-in"))) {
|
||||
rv = prefs->GetIntPref(HTTP_PREF("throttle.resume-background-in"), &val);
|
||||
mThrottleResumeIn = (uint32_t)clamped(val, 0, 120000);
|
||||
if (PREF_CHANGED(HTTP_PREF("throttle.read-limit-bytes"))) {
|
||||
rv = prefs->GetIntPref(HTTP_PREF("throttle.read-limit-bytes"), &val);
|
||||
mThrottleReadLimit = (uint32_t)clamped(val, 0, 500000);
|
||||
if (NS_SUCCEEDED(rv) && mConnMgr) {
|
||||
Unused << mConnMgr->UpdateParam(nsHttpConnectionMgr::THROTTLING_RESUME_IN,
|
||||
mThrottleResumeIn);
|
||||
Unused << mConnMgr->UpdateParam(nsHttpConnectionMgr::THROTTLING_READ_LIMIT,
|
||||
mThrottleReadLimit);
|
||||
}
|
||||
}
|
||||
|
||||
if (PREF_CHANGED(HTTP_PREF("throttle.time-window"))) {
|
||||
rv = prefs->GetIntPref(HTTP_PREF("throttle.time-window"), &val);
|
||||
mThrottleTimeWindow = (uint32_t)clamped(val, 0, 120000);
|
||||
if (PREF_CHANGED(HTTP_PREF("throttle.read-interval-ms"))) {
|
||||
rv = prefs->GetIntPref(HTTP_PREF("throttle.read-interval-ms"), &val);
|
||||
mThrottleReadInterval = (uint32_t)clamped(val, 0, 120000);
|
||||
if (NS_SUCCEEDED(rv) && mConnMgr) {
|
||||
Unused << mConnMgr->UpdateParam(nsHttpConnectionMgr::THROTTLING_READ_INTERVAL,
|
||||
mThrottleReadInterval);
|
||||
}
|
||||
}
|
||||
|
||||
if (PREF_CHANGED(HTTP_PREF("throttle.hold-time-ms"))) {
|
||||
rv = prefs->GetIntPref(HTTP_PREF("throttle.hold-time-ms"), &val);
|
||||
mThrottleHoldTime = (uint32_t)clamped(val, 0, 120000);
|
||||
if (NS_SUCCEEDED(rv) && mConnMgr) {
|
||||
Unused << mConnMgr->UpdateParam(nsHttpConnectionMgr::THROTTLING_HOLD_TIME,
|
||||
mThrottleHoldTime);
|
||||
}
|
||||
}
|
||||
|
||||
if (PREF_CHANGED(HTTP_PREF("throttle.max-time-ms"))) {
|
||||
rv = prefs->GetIntPref(HTTP_PREF("throttle.max-time-ms"), &val);
|
||||
mThrottleMaxTime = (uint32_t)clamped(val, 0, 120000);
|
||||
if (NS_SUCCEEDED(rv) && mConnMgr) {
|
||||
Unused << mConnMgr->UpdateParam(nsHttpConnectionMgr::THROTTLING_TIME_WINDOW,
|
||||
mThrottleTimeWindow);
|
||||
Unused << mConnMgr->UpdateParam(nsHttpConnectionMgr::THROTTLING_MAX_TIME,
|
||||
mThrottleMaxTime);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -145,6 +145,8 @@ public:
|
|||
}
|
||||
uint32_t TailBlockingDelayMax() { return mTailDelayMax; }
|
||||
|
||||
uint32_t ThrottlingReadLimit() { return mThrottleVersion == 1 ? 0 : mThrottleReadLimit; }
|
||||
|
||||
// TCP Keepalive configuration values.
|
||||
|
||||
// Returns true if TCP keepalive should be enabled for short-lived conns.
|
||||
|
@ -484,10 +486,13 @@ private:
|
|||
uint8_t mMaxPersistentConnectionsPerProxy;
|
||||
|
||||
bool mThrottleEnabled;
|
||||
uint32_t mThrottleVersion;
|
||||
uint32_t mThrottleSuspendFor;
|
||||
uint32_t mThrottleResumeFor;
|
||||
uint32_t mThrottleResumeIn;
|
||||
uint32_t mThrottleTimeWindow;
|
||||
uint32_t mThrottleReadLimit;
|
||||
uint32_t mThrottleReadInterval;
|
||||
uint32_t mThrottleHoldTime;
|
||||
uint32_t mThrottleMaxTime;
|
||||
|
||||
bool mUrgentStartEnabled;
|
||||
bool mTailBlockingEnabled;
|
||||
|
|
|
@ -104,6 +104,7 @@ nsHttpTransaction::nsHttpTransaction()
|
|||
, mHttpVersion(NS_HTTP_VERSION_UNKNOWN)
|
||||
, mHttpResponseCode(0)
|
||||
, mCurrentHttpResponseHeaderSize(0)
|
||||
, mThrottlingReadAllowance(THROTTLE_NO_LIMIT)
|
||||
, mCapsToClear(0)
|
||||
, mResponseIsComplete(false)
|
||||
, mReadingStopped(false)
|
||||
|
@ -166,6 +167,11 @@ void nsHttpTransaction::ResumeReading()
|
|||
LOG(("nsHttpTransaction::ResumeReading %p", this));
|
||||
|
||||
mReadingStopped = false;
|
||||
|
||||
// This with either reengage the limit when still throttled in WriteSegments or
|
||||
// simply reset to allow unlimeted reading again.
|
||||
mThrottlingReadAllowance = THROTTLE_NO_LIMIT;
|
||||
|
||||
if (mConnection) {
|
||||
nsresult rv = mConnection->ResumeRecv();
|
||||
if (NS_FAILED(rv)) {
|
||||
|
@ -853,16 +859,16 @@ nsHttpTransaction::WritePipeSegment(nsIOutputStream *stream,
|
|||
return rv; // failure code only stops WriteSegments; it is not propagated.
|
||||
}
|
||||
|
||||
bool nsHttpTransaction::ShouldStopReading()
|
||||
bool nsHttpTransaction::ShouldThrottle()
|
||||
{
|
||||
if (mActivatedAsH2) {
|
||||
// Throttling feature is now disabled for http/2 transactions
|
||||
// because of bug 1367861. The logic around mActivatedAsH2
|
||||
// will be removed when that is fixed.
|
||||
//
|
||||
// Calling ShouldStopReading on the manager just to make sure
|
||||
// Calling ShouldThrottle on the manager just to make sure
|
||||
// the throttling time window is correctly updated by this transaction.
|
||||
Unused << gHttpHandler->ConnMgr()->ShouldStopReading(this);
|
||||
Unused << gHttpHandler->ConnMgr()->ShouldThrottle(this);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -876,21 +882,25 @@ bool nsHttpTransaction::ShouldStopReading()
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!gHttpHandler->ConnMgr()->ShouldStopReading(this)) {
|
||||
if (!gHttpHandler->ConnMgr()->ShouldThrottle(this)) {
|
||||
// We are not obligated to throttle
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mContentRead < 16000) {
|
||||
// Let the first bytes go, it may also well be all the content we get
|
||||
LOG(("nsHttpTransaction::ShouldStopReading too few content (%" PRIi64 ") this=%p", mContentRead, this));
|
||||
LOG(("nsHttpTransaction::ShouldThrottle too few content (%" PRIi64 ") this=%p", mContentRead, this));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gHttpHandler->ConnMgr()->IsConnEntryUnderPressure(mConnInfo)) {
|
||||
LOG(("nsHttpTransaction::ShouldStopReading entry pressure this=%p", this));
|
||||
if (!(mClassOfService & nsIClassOfService::Throttleable) &&
|
||||
gHttpHandler->ConnMgr()->IsConnEntryUnderPressure(mConnInfo)) {
|
||||
LOG(("nsHttpTransaction::ShouldThrottle entry pressure this=%p", this));
|
||||
// This is expensive to check (two hashtable lookups) but may help
|
||||
// freeing connections for active tab transactions.
|
||||
// Checking this only for transactions that are not explicitly marked
|
||||
// as throttleable because trackers and (specially) downloads should
|
||||
// keep throttling even under pressure.
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -909,7 +919,16 @@ nsHttpTransaction::WriteSegments(nsAHttpSegmentWriter *writer,
|
|||
return NS_SUCCEEDED(mStatus) ? NS_BASE_STREAM_CLOSED : mStatus;
|
||||
}
|
||||
|
||||
if (ShouldStopReading()) {
|
||||
if (ShouldThrottle()) {
|
||||
if (mThrottlingReadAllowance == THROTTLE_NO_LIMIT) { // no limit set
|
||||
// V1: ThrottlingReadLimit() returns 0
|
||||
mThrottlingReadAllowance = gHttpHandler->ThrottlingReadLimit();
|
||||
}
|
||||
} else {
|
||||
mThrottlingReadAllowance = THROTTLE_NO_LIMIT; // don't limit
|
||||
}
|
||||
|
||||
if (mThrottlingReadAllowance == 0) { // depleted
|
||||
if (gHttpHandler->ConnMgr()->CurrentTopLevelOuterContentWindowId() !=
|
||||
mTopLevelOuterContentWindowId) {
|
||||
nsHttp::NotifyActiveTabLoadOptimization();
|
||||
|
@ -937,6 +956,12 @@ nsHttpTransaction::WriteSegments(nsAHttpSegmentWriter *writer,
|
|||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
if (mThrottlingReadAllowance > 0) {
|
||||
LOG(("nsHttpTransaction::WriteSegments %p limiting read from %u to %d",
|
||||
this, count, mThrottlingReadAllowance));
|
||||
count = std::min(count, static_cast<uint32_t>(mThrottlingReadAllowance));
|
||||
}
|
||||
|
||||
nsresult rv = mPipeOut->WriteSegments(WritePipeSegment, this, count, countWritten);
|
||||
|
||||
mWriter = nullptr;
|
||||
|
@ -963,6 +988,9 @@ nsHttpTransaction::WriteSegments(nsAHttpSegmentWriter *writer,
|
|||
NS_ERROR("no socket thread event target");
|
||||
rv = NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
} else if (mThrottlingReadAllowance > 0 && NS_SUCCEEDED(rv)) {
|
||||
MOZ_ASSERT(count >= *countWritten);
|
||||
mThrottlingReadAllowance -= *countWritten;
|
||||
}
|
||||
|
||||
return rv;
|
||||
|
|
|
@ -229,7 +229,7 @@ private:
|
|||
|
||||
// Called from WriteSegments. Checks for conditions whether to throttle reading
|
||||
// the content. When this returns true, WriteSegments returns WOULD_BLOCK.
|
||||
bool ShouldStopReading();
|
||||
bool ShouldThrottle();
|
||||
|
||||
private:
|
||||
class UpdateSecurityCallbacks : public Runnable
|
||||
|
@ -311,6 +311,19 @@ private:
|
|||
|
||||
uint32_t mCurrentHttpResponseHeaderSize;
|
||||
|
||||
int32_t const THROTTLE_NO_LIMIT = -1;
|
||||
// This can have 3 possible values:
|
||||
// * THROTTLE_NO_LIMIT - this means the transaction is not in any way limited
|
||||
// to read the response, this is the default
|
||||
// * a positive number - a limit is set because the transaction is obligated
|
||||
// to throttle the response read, this is decresed with
|
||||
// every piece of data the transaction receives
|
||||
// * zero - when the transaction depletes the limit for reading, this makes it
|
||||
// stop reading and return WOULD_BLOCK from WriteSegments; transaction
|
||||
// then waits for a call of ResumeReading that resets this member back
|
||||
// to THROTTLE_NO_LIMIT
|
||||
int32_t mThrottlingReadAllowance;
|
||||
|
||||
// mCapsToClear holds flags that should be cleared in mCaps, e.g. unset
|
||||
// NS_HTTP_REFRESH_DNS when DNS refresh request has completed to avoid
|
||||
// redundant requests on the network. The member itself is atomic, but
|
||||
|
|
Загрузка…
Ссылка в новой задаче