mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 10:44:56 +00:00
Bug 1365307 - Throttling of HTTP transactions. r=mcmanus
This commit is contained in:
parent
10539cc2c6
commit
e98f5af878
@ -2086,11 +2086,15 @@ pref("network.auth.subresource-img-cross-origin-http-auth-allow", true);
|
||||
// in that case default credentials will always be used.
|
||||
pref("network.auth.private-browsing-sso", false);
|
||||
|
||||
// Control how the throttling service works - number of ms that each
|
||||
// Control how throttling of http responses works - number of ms that each
|
||||
// suspend and resume period lasts (prefs named appropriately)
|
||||
pref("network.throttle.suspend-for", 3000);
|
||||
pref("network.throttle.resume-for", 200);
|
||||
pref("network.throttle.enable", true);
|
||||
pref("network.http.throttle.enable", true);
|
||||
pref("network.http.throttle.suspend-for", 3000);
|
||||
pref("network.http.throttle.resume-for", 200);
|
||||
// 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", 400);
|
||||
|
||||
pref("permissions.default.image", 1); // 1-Accept, 2-Deny, 3-dontAcceptForeign
|
||||
|
||||
|
@ -404,6 +404,7 @@ Http2Session::AddStream(nsAHttpTransaction *aHttpTransaction,
|
||||
}
|
||||
|
||||
aHttpTransaction->SetConnection(this);
|
||||
aHttpTransaction->OnActivated(true);
|
||||
|
||||
if (aUseTunnel) {
|
||||
LOG3(("Http2Session::AddStream session=%p trans=%p OnTunnel",
|
||||
|
@ -45,6 +45,10 @@ public:
|
||||
// called by the connection when it takes ownership of the transaction.
|
||||
virtual void SetConnection(nsAHttpConnection *) = 0;
|
||||
|
||||
// called by the connection after a successfull activation of this transaction
|
||||
// in other words, tells the transaction it transitioned to the "active" state.
|
||||
virtual void OnActivated(bool h2) {}
|
||||
|
||||
// used to obtain the connection associated with this transaction
|
||||
virtual nsAHttpConnection *Connection() = 0;
|
||||
|
||||
|
@ -711,10 +711,6 @@ nsHttpChannel::ContinueConnect()
|
||||
while (suspendCount--)
|
||||
mTransactionPump->Suspend();
|
||||
|
||||
if (mClassOfService & nsIClassOfService::Throttleable) {
|
||||
gHttpHandler->ThrottleTransaction(mTransaction, true);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -6622,10 +6618,8 @@ nsHttpChannel::ContinueBeginConnect()
|
||||
void
|
||||
nsHttpChannel::OnClassOfServiceUpdated()
|
||||
{
|
||||
bool throttleable = !!(mClassOfService & nsIClassOfService::Throttleable);
|
||||
|
||||
if (mTransaction) {
|
||||
gHttpHandler->ThrottleTransaction(mTransaction, throttleable);
|
||||
gHttpHandler->UpdateClassOfServiceOnTransaction(mTransaction, mClassOfService);
|
||||
}
|
||||
}
|
||||
|
||||
@ -8009,10 +8003,6 @@ nsHttpChannel::DoAuthRetry(nsAHttpConnection *conn)
|
||||
while (suspendCount--)
|
||||
mTransactionPump->Suspend();
|
||||
|
||||
if (mSuspendCount && mClassOfService & nsIClassOfService::Throttleable) {
|
||||
gHttpHandler->ThrottleTransaction(mTransaction, true);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -8719,10 +8709,6 @@ nsHttpChannel::SuspendInternal()
|
||||
|
||||
if (mSuspendCount == 1) {
|
||||
mSuspendTimestamp = TimeStamp::NowLoRes();
|
||||
|
||||
if (mClassOfService & nsIClassOfService::Throttleable && mTransaction) {
|
||||
gHttpHandler->ThrottleTransaction(mTransaction, true);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult rvTransaction = NS_OK;
|
||||
@ -8748,10 +8734,6 @@ nsHttpChannel::ResumeInternal()
|
||||
mSuspendTotalTime += (TimeStamp::NowLoRes() - mSuspendTimestamp).
|
||||
ToMilliseconds();
|
||||
|
||||
if (mClassOfService & nsIClassOfService::Throttleable && mTransaction) {
|
||||
gHttpHandler->ThrottleTransaction(mTransaction, false);
|
||||
}
|
||||
|
||||
if (mCallOnResume) {
|
||||
nsresult rv = AsyncCall(mCallOnResume);
|
||||
mCallOnResume = nullptr;
|
||||
|
@ -664,6 +664,8 @@ nsHttpConnection::Activate(nsAHttpTransaction *trans, uint32_t caps, int32_t pri
|
||||
mTransaction = mTLSFilter;
|
||||
}
|
||||
|
||||
trans->OnActivated(false);
|
||||
|
||||
rv = OnOutputStreamReady(mSocketOut);
|
||||
|
||||
failed_activation:
|
||||
|
@ -115,6 +115,11 @@ nsHttpConnectionMgr::nsHttpConnectionMgr()
|
||||
, mMaxConns(0)
|
||||
, mMaxPersistConnsPerHost(0)
|
||||
, mMaxPersistConnsPerProxy(0)
|
||||
, mMaxRequestDelay(0)
|
||||
, mThrottleEnabled(false)
|
||||
, mThrottleSuspendFor(0)
|
||||
, mThrottleResumeFor(0)
|
||||
, mThrottleResumeIn(0)
|
||||
, mIsShuttingDown(false)
|
||||
, mNumActiveConns(0)
|
||||
, mNumIdleConns(0)
|
||||
@ -125,6 +130,9 @@ nsHttpConnectionMgr::nsHttpConnectionMgr()
|
||||
, mTimeoutTickArmed(false)
|
||||
, mTimeoutTickNext(1)
|
||||
, mCurrentTopLevelOuterContentWindowId(0)
|
||||
, mThrottlingInhibitsReading(false)
|
||||
, mActiveTabTransactionsExist(false)
|
||||
, mActiveTabUnthrottledTransactionsExist(false)
|
||||
{
|
||||
LOG(("Creating nsHttpConnectionMgr @%p\n", this));
|
||||
}
|
||||
@ -164,7 +172,11 @@ nsHttpConnectionMgr::Init(uint16_t maxUrgentExcessiveConns,
|
||||
uint16_t maxConns,
|
||||
uint16_t maxPersistConnsPerHost,
|
||||
uint16_t maxPersistConnsPerProxy,
|
||||
uint16_t maxRequestDelay)
|
||||
uint16_t maxRequestDelay,
|
||||
bool throttleEnabled,
|
||||
uint32_t throttleSuspendFor,
|
||||
uint32_t throttleResumeFor,
|
||||
uint32_t throttleResumeIn)
|
||||
{
|
||||
LOG(("nsHttpConnectionMgr::Init\n"));
|
||||
|
||||
@ -177,6 +189,11 @@ nsHttpConnectionMgr::Init(uint16_t maxUrgentExcessiveConns,
|
||||
mMaxPersistConnsPerProxy = maxPersistConnsPerProxy;
|
||||
mMaxRequestDelay = maxRequestDelay;
|
||||
|
||||
mThrottleEnabled = throttleEnabled;
|
||||
mThrottleSuspendFor = throttleSuspendFor;
|
||||
mThrottleResumeFor = throttleResumeFor;
|
||||
mThrottleResumeIn = throttleResumeIn;
|
||||
|
||||
mIsShuttingDown = false;
|
||||
}
|
||||
|
||||
@ -344,13 +361,15 @@ nsHttpConnectionMgr::Observe(nsISupports *subject,
|
||||
nsCOMPtr<nsITimer> timer = do_QueryInterface(subject);
|
||||
if (timer == mTimer) {
|
||||
Unused << PruneDeadConnections();
|
||||
}
|
||||
else if (timer == mTimeoutTick) {
|
||||
} else if (timer == mTimeoutTick) {
|
||||
TimeoutTick();
|
||||
} else if (timer == mTrafficTimer) {
|
||||
Unused << PruneNoTraffic();
|
||||
}
|
||||
else {
|
||||
} else if (timer == mThrottleTicker) {
|
||||
ThrottlerTick();
|
||||
} else if (timer == mDelayedResumeReadTimer) {
|
||||
ResumeBackgroundThrottledTransactions();
|
||||
} else {
|
||||
MOZ_ASSERT(false, "unexpected timer-callback");
|
||||
LOG(("Unexpected timer object\n"));
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
@ -378,12 +397,12 @@ nsHttpConnectionMgr::RescheduleTransaction(nsHttpTransaction *trans, int32_t pri
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpConnectionMgr::ThrottleTransaction(nsHttpTransaction *trans, bool throttle)
|
||||
nsHttpConnectionMgr::UpdateClassOfServiceOnTransaction(nsHttpTransaction *trans, uint32_t classOfService)
|
||||
{
|
||||
LOG(("nsHttpConnectionMgr::ThrottleTransaction [trans=%p throttle=%" PRIx32 "]\n",
|
||||
trans, static_cast<uint32_t>(throttle)));
|
||||
Unused << PostEvent(&nsHttpConnectionMgr::OnMsgThrottleTransaction,
|
||||
static_cast<int32_t>(throttle), trans);
|
||||
LOG(("nsHttpConnectionMgr::UpdateClassOfServiceOnTransaction [trans=%p classOfService=%" PRIu32 "]\n",
|
||||
trans, static_cast<uint32_t>(classOfService)));
|
||||
Unused << PostEvent(&nsHttpConnectionMgr::OnMsgUpdateClassOfServiceOnTransaction,
|
||||
static_cast<int32_t>(classOfService), trans);
|
||||
}
|
||||
|
||||
nsresult
|
||||
@ -2157,6 +2176,10 @@ nsHttpConnectionMgr::OnMsgShutdown(int32_t, ARefBase *param)
|
||||
mTrafficTimer->Cancel();
|
||||
mTrafficTimer = nullptr;
|
||||
}
|
||||
DestroyThrottleTicker();
|
||||
mActiveTransactions[false].Clear();
|
||||
mActiveTransactions[true].Clear();
|
||||
|
||||
mCoalescingHash.Clear();
|
||||
|
||||
// signal shutdown complete
|
||||
@ -2224,15 +2247,15 @@ nsHttpConnectionMgr::OnMsgReschedTransaction(int32_t priority, ARefBase *param)
|
||||
}
|
||||
}
|
||||
|
||||
void nsHttpConnectionMgr::OnMsgThrottleTransaction(int32_t arg, ARefBase *param)
|
||||
void nsHttpConnectionMgr::OnMsgUpdateClassOfServiceOnTransaction(int32_t arg, ARefBase *param)
|
||||
{
|
||||
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
|
||||
LOG(("nsHttpConnectionMgr::OnMsgThrottleTransaction [trans=%p]\n", param));
|
||||
LOG(("nsHttpConnectionMgr::OnMsgUpdateClassOfServiceOnTransaction [trans=%p]\n", param));
|
||||
|
||||
bool throttle = static_cast<bool>(arg);
|
||||
uint32_t cos = static_cast<uint32_t>(arg);
|
||||
nsHttpTransaction *trans = static_cast<nsHttpTransaction *>(param);
|
||||
|
||||
trans->ThrottleResponse(throttle);
|
||||
trans->SetClassOfService(cos);
|
||||
}
|
||||
|
||||
void
|
||||
@ -2730,6 +2753,18 @@ nsHttpConnectionMgr::OnMsgUpdateParam(int32_t inParam, ARefBase *)
|
||||
case MAX_REQUEST_DELAY:
|
||||
mMaxRequestDelay = value;
|
||||
break;
|
||||
case THROTTLING_ENABLED:
|
||||
SetThrottlingEnabled(!!value);
|
||||
break;
|
||||
case THROTTLING_SUSPEND_FOR:
|
||||
mThrottleSuspendFor = value;
|
||||
break;
|
||||
case THROTTLING_RESUME_FOR:
|
||||
mThrottleResumeFor = value;
|
||||
break;
|
||||
case THROTTLING_RESUME_IN:
|
||||
mThrottleResumeIn = value;
|
||||
break;
|
||||
default:
|
||||
NS_NOTREACHED("unexpected parameter name");
|
||||
}
|
||||
@ -2803,16 +2838,480 @@ nsHttpConnectionMgr::UpdateCurrentTopLevelOuterContentWindowId(
|
||||
windowIdWrapper);
|
||||
}
|
||||
|
||||
void nsHttpConnectionMgr::SetThrottlingEnabled(bool aEnable)
|
||||
{
|
||||
LOG(("nsHttpConnectionMgr::SetThrottlingEnabled enable=%d", aEnable));
|
||||
|
||||
mThrottleEnabled = aEnable;
|
||||
|
||||
if (mThrottleEnabled) {
|
||||
EnsureThrottleTickerIfNeeded();
|
||||
} else {
|
||||
DestroyThrottleTicker();
|
||||
ResumeReadOf(mActiveTransactions[false]);
|
||||
ResumeReadOf(mActiveTransactions[true]);
|
||||
}
|
||||
}
|
||||
|
||||
void nsHttpConnectionMgr::LogActiveTransactions(char operation)
|
||||
{
|
||||
if (!LOG_ENABLED()) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsTArray<RefPtr<nsHttpTransaction>> *trs = nullptr;
|
||||
uint32_t au, at, bu = 0, bt = 0;
|
||||
|
||||
trs = mActiveTransactions[false].Get(mCurrentTopLevelOuterContentWindowId);
|
||||
au = trs ? trs->Length() : 0;
|
||||
trs = mActiveTransactions[true].Get(mCurrentTopLevelOuterContentWindowId);
|
||||
at = trs ? trs->Length() : 0;
|
||||
|
||||
for (auto iter = mActiveTransactions[false].Iter(); !iter.Done(); iter.Next()) {
|
||||
bu += iter.UserData()->Length();
|
||||
}
|
||||
bu -= au;
|
||||
for (auto iter = mActiveTransactions[true].Iter(); !iter.Done(); iter.Next()) {
|
||||
bt += iter.UserData()->Length();
|
||||
}
|
||||
bt -= at;
|
||||
|
||||
// Shows counts of:
|
||||
// - unthrottled transaction for the active tab
|
||||
// - throttled transaction for the active tab
|
||||
// - unthrottled transaction for background tabs
|
||||
// - throttled transaction for background tabs
|
||||
LOG(("Active transactions %c[%u,%u,%u,%u]", operation, au, at, bu, bt));
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpConnectionMgr::OnMsgUpdateCurrentTopLevelOuterContentWindowId(
|
||||
int32_t, ARefBase *param)
|
||||
nsHttpConnectionMgr::AddActiveTransaction(nsHttpTransaction * aTrans, bool aThrottled)
|
||||
{
|
||||
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
|
||||
mCurrentTopLevelOuterContentWindowId =
|
||||
static_cast<UINT64Wrapper*>(param)->GetValue();
|
||||
|
||||
uint64_t tabId = aTrans->TopLevelOuterContentWindowId();
|
||||
|
||||
nsTArray<RefPtr<nsHttpTransaction>> *transactions =
|
||||
mActiveTransactions[aThrottled].LookupOrAdd(tabId);
|
||||
|
||||
MOZ_ASSERT(!transactions->Contains(aTrans));
|
||||
|
||||
transactions->AppendElement(aTrans);
|
||||
|
||||
LOG(("nsHttpConnectionMgr::AddActiveTransaction t=%p tabid=%" PRIx64 "(%d) thr=%d",
|
||||
aTrans, tabId, tabId == mCurrentTopLevelOuterContentWindowId, aThrottled));
|
||||
LogActiveTransactions('+');
|
||||
|
||||
if (tabId == mCurrentTopLevelOuterContentWindowId) {
|
||||
mActiveTabTransactionsExist = true;
|
||||
if (!aThrottled) {
|
||||
mActiveTabUnthrottledTransactionsExist = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mThrottleEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureThrottleTickerIfNeeded();
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpConnectionMgr::RemoveActiveTransaction(nsHttpTransaction * aTrans, bool aThrottled)
|
||||
{
|
||||
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
|
||||
|
||||
uint64_t tabId = aTrans->TopLevelOuterContentWindowId();
|
||||
bool forActiveTab = tabId == mCurrentTopLevelOuterContentWindowId;
|
||||
|
||||
nsTArray<RefPtr<nsHttpTransaction>> *transactions =
|
||||
mActiveTransactions[aThrottled].Get(tabId);
|
||||
|
||||
if (!transactions || !transactions->RemoveElement(aTrans)) {
|
||||
// Was not tracked as active, probably just ignore.
|
||||
return;
|
||||
}
|
||||
|
||||
LOG(("nsHttpConnectionMgr::RemoveActiveTransaction t=%p tabid=%" PRIx64 "(%d) thr=%d",
|
||||
aTrans, tabId, forActiveTab, aThrottled));
|
||||
|
||||
if (!transactions->IsEmpty()) {
|
||||
// There are still transactions of the type, hence nothing in the throttling conditions
|
||||
// has changed and we don't need to update "Exists" caches nor we need to wake any now
|
||||
// throttled transactions.
|
||||
LogActiveTransactions('-');
|
||||
return;
|
||||
}
|
||||
|
||||
// To optimize the following logic, always remove the entry when the array is empty.
|
||||
mActiveTransactions[aThrottled].Remove(tabId);
|
||||
LogActiveTransactions('-');
|
||||
|
||||
if (forActiveTab) {
|
||||
// Update caches of the active tab transaction existence, since it's now affected
|
||||
if (!aThrottled) {
|
||||
mActiveTabUnthrottledTransactionsExist = false;
|
||||
}
|
||||
if (mActiveTabTransactionsExist) {
|
||||
mActiveTabTransactionsExist = mActiveTransactions[!aThrottled].Contains(tabId);
|
||||
}
|
||||
}
|
||||
|
||||
if (!mThrottleEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool unthrottledExist = !mActiveTransactions[false].IsEmpty();
|
||||
bool throttledExist = !mActiveTransactions[true].IsEmpty();
|
||||
|
||||
if (!unthrottledExist && !throttledExist) {
|
||||
// Nothing active globally, just get rid of the timer completely and we are done.
|
||||
MOZ_ASSERT(!mActiveTabUnthrottledTransactionsExist);
|
||||
MOZ_ASSERT(!mActiveTabTransactionsExist);
|
||||
|
||||
DestroyThrottleTicker();
|
||||
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 (mActiveTabUnthrottledTransactionsExist) {
|
||||
// There are still unthrottled transactions for the active tab, hence the state
|
||||
// is unaffected and we don't need to do anything (nothing to wake).
|
||||
LOG((" there are unthrottled for the active tab"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (mActiveTabTransactionsExist) {
|
||||
// There are only trottled transactions for the active tab.
|
||||
// If the last transaction we just removed was a non-throttled for the active tab
|
||||
// we can wake the throttled transactions for the active tab.
|
||||
if (forActiveTab && !aThrottled) {
|
||||
LOG((" resuming throttled for active tab"));
|
||||
ResumeReadOf(mActiveTransactions[true].Get(mCurrentTopLevelOuterContentWindowId));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!unthrottledExist) {
|
||||
// There are no unthrottled transactions for any tab. Resume all throttled,
|
||||
// all are only for background tabs.
|
||||
LOG((" delay resuming throttled for background tabs"));
|
||||
DelayedResumeBackgroundThrottledTransactions();
|
||||
return;
|
||||
}
|
||||
|
||||
if (forActiveTab) {
|
||||
// Removing the last transaction for the active tab frees up the unthrottled
|
||||
// background tabs transactions.
|
||||
LOG((" resuming unthrottled for background tabs"));
|
||||
ResumeReadOf(mActiveTransactions[false]);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG((" not resuming anything"));
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpConnectionMgr::MoveActiveTransaction(nsHttpTransaction * aTrans, bool aThrottled)
|
||||
{
|
||||
LOG(("nsHttpConnectionMgr::MoveActiveTransaction ENTER t=%p", aTrans));
|
||||
AddActiveTransaction(aTrans, aThrottled);
|
||||
RemoveActiveTransaction(aTrans, !aThrottled);
|
||||
LOG(("nsHttpConnectionMgr::MoveActiveTransaction EXIT t=%p", aTrans));
|
||||
}
|
||||
|
||||
bool
|
||||
nsHttpConnectionMgr::ShouldStopReading(nsHttpTransaction * aTrans, bool aThrottled)
|
||||
{
|
||||
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
|
||||
|
||||
if (!mThrottlingInhibitsReading || !mThrottleEnabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t tabId = aTrans->TopLevelOuterContentWindowId();
|
||||
|
||||
if (mActiveTabTransactionsExist) {
|
||||
if (!tabId) {
|
||||
// Chrome initiated and unidentified transactions just respect
|
||||
// their throttle flag, when something for the active tab is happening.
|
||||
return aThrottled;
|
||||
}
|
||||
if (tabId != mCurrentTopLevelOuterContentWindowId) {
|
||||
// This is a background tab request, we want them to always throttle.
|
||||
return true;
|
||||
}
|
||||
if (mActiveTabUnthrottledTransactionsExist) {
|
||||
// Unthrottled transactions for the active tab take precedence
|
||||
return aThrottled;
|
||||
}
|
||||
// This is a throttled transaction for the active tab and there are no
|
||||
// unthrottled for the active tab, just let go on full fuel.
|
||||
return false;
|
||||
}
|
||||
|
||||
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.)
|
||||
return aThrottled;
|
||||
}
|
||||
|
||||
// There are only unthrottled transactions for background tabs: don't throttle.
|
||||
return false;
|
||||
}
|
||||
|
||||
bool nsHttpConnectionMgr::IsThrottleTickerNeeded()
|
||||
{
|
||||
LOG(("nsHttpConnectionMgr::IsThrottleTickerNeeded"));
|
||||
|
||||
if (mActiveTabUnthrottledTransactionsExist &&
|
||||
mActiveTransactions[false].Count() > 1) {
|
||||
LOG((" there are unthrottled transactions for both active and bck"));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mActiveTabTransactionsExist &&
|
||||
mActiveTransactions[true].Count() > 1) {
|
||||
LOG((" there are throttled transactions for both active and bck"));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!mActiveTransactions[true].IsEmpty() &&
|
||||
!mActiveTransactions[false].IsEmpty()) {
|
||||
LOG((" there are both throttled and unthrottled transactions"));
|
||||
return true;
|
||||
}
|
||||
|
||||
LOG((" nothing to throttle"));
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpConnectionMgr::EnsureThrottleTickerIfNeeded()
|
||||
{
|
||||
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
|
||||
|
||||
LOG(("nsHttpConnectionMgr::EnsureThrottleTickerIfNeeded"));
|
||||
if (!IsThrottleTickerNeeded()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// There is a new demand to throttle, hence unschedule delayed resume
|
||||
// of background throttled transastions.
|
||||
CancelDelayedResumeBackgroundThrottledTransactions();
|
||||
|
||||
if (mThrottleTicker) {
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!mThrottlingInhibitsReading);
|
||||
|
||||
mThrottleTicker = do_CreateInstance("@mozilla.org/timer;1");
|
||||
if (mThrottleTicker) {
|
||||
mThrottleTicker->Init(this, mThrottleSuspendFor, nsITimer::TYPE_ONE_SHOT);
|
||||
mThrottlingInhibitsReading = true;
|
||||
}
|
||||
|
||||
LogActiveTransactions('^');
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpConnectionMgr::DestroyThrottleTicker()
|
||||
{
|
||||
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
|
||||
|
||||
// Nothing to throttle, hence no need for this timer anymore.
|
||||
CancelDelayedResumeBackgroundThrottledTransactions();
|
||||
|
||||
MOZ_ASSERT(!mThrottleEnabled || !IsThrottleTickerNeeded());
|
||||
|
||||
if (!mThrottleTicker) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG(("nsHttpConnectionMgr::DestroyThrottleTicker"));
|
||||
mThrottleTicker->Cancel();
|
||||
mThrottleTicker = nullptr;
|
||||
mThrottlingInhibitsReading = false;
|
||||
|
||||
LogActiveTransactions('v');
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpConnectionMgr::ThrottlerTick()
|
||||
{
|
||||
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
|
||||
|
||||
mThrottlingInhibitsReading = !mThrottlingInhibitsReading;
|
||||
|
||||
LOG(("nsHttpConnectionMgr::ThrottlerTick inhibit=%d", mThrottlingInhibitsReading));
|
||||
|
||||
if (!mThrottlingInhibitsReading && !IsThrottleTickerNeeded()) {
|
||||
mThrottleTicker = nullptr;
|
||||
} else {
|
||||
mThrottleTicker = do_CreateInstance("@mozilla.org/timer;1");
|
||||
}
|
||||
|
||||
if (mThrottlingInhibitsReading) {
|
||||
if (mThrottleTicker) {
|
||||
mThrottleTicker->Init(this, mThrottleSuspendFor, nsITimer::TYPE_ONE_SHOT);
|
||||
}
|
||||
} else {
|
||||
// Resume by the ticker happens sooner than delayed resume, no need
|
||||
// for the delayed resume timer
|
||||
CancelDelayedResumeBackgroundThrottledTransactions();
|
||||
|
||||
if (mThrottleTicker) {
|
||||
mThrottleTicker->Init(this, mThrottleResumeFor, nsITimer::TYPE_ONE_SHOT);
|
||||
}
|
||||
|
||||
ResumeReadOf(mActiveTransactions[false], true);
|
||||
ResumeReadOf(mActiveTransactions[true]);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpConnectionMgr::DelayedResumeBackgroundThrottledTransactions()
|
||||
{
|
||||
if (mDelayedResumeReadTimer) {
|
||||
return;
|
||||
}
|
||||
|
||||
mDelayedResumeReadTimer = do_CreateInstance("@mozilla.org/timer;1");
|
||||
if (!mDelayedResumeReadTimer) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG(("nsHttpConnectionMgr::DelayedResumeBackgroundThrottledTransactions"));
|
||||
mDelayedResumeReadTimer->Init(this, mThrottleResumeIn, nsITimer::TYPE_ONE_SHOT);
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpConnectionMgr::CancelDelayedResumeBackgroundThrottledTransactions()
|
||||
{
|
||||
if (!mDelayedResumeReadTimer) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG(("nsHttpConnectionMgr::CancelDelayedResumeBackgroundThrottledTransactions"));
|
||||
mDelayedResumeReadTimer->Cancel();
|
||||
mDelayedResumeReadTimer = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpConnectionMgr::ResumeBackgroundThrottledTransactions()
|
||||
{
|
||||
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
|
||||
|
||||
LOG(("nsHttpConnectionMgr::ResumeBackgroundThrottledTransactions"));
|
||||
mDelayedResumeReadTimer = nullptr;
|
||||
|
||||
// We can destroy the ticker, since only transactions to resume now
|
||||
// are background throttled. If 'higher class' transactions have
|
||||
// been added, we don't get here - the ticker has been scheduled
|
||||
// and hence the delayed resume timer canceled.
|
||||
MOZ_ASSERT(mActiveTransactions[false].IsEmpty() &&
|
||||
!mActiveTabTransactionsExist);
|
||||
|
||||
DestroyThrottleTicker();
|
||||
ResumeReadOf(mActiveTransactions[true], true);
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpConnectionMgr::ResumeReadOf(
|
||||
nsClassHashtable<nsUint64HashKey, nsTArray<RefPtr<nsHttpTransaction>>>& hashtable,
|
||||
bool excludeForActiveTab)
|
||||
{
|
||||
for (auto iter = hashtable.Iter(); !iter.Done(); iter.Next()) {
|
||||
if (excludeForActiveTab && iter.Key() == mCurrentTopLevelOuterContentWindowId) {
|
||||
// These have never been throttled (never stopped reading)
|
||||
continue;
|
||||
}
|
||||
ResumeReadOf(iter.UserData());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpConnectionMgr::ResumeReadOf(nsTArray<RefPtr<nsHttpTransaction>>* transactions)
|
||||
{
|
||||
MOZ_ASSERT(transactions);
|
||||
|
||||
for (auto trans : *transactions) {
|
||||
trans->ResumeReading();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpConnectionMgr::OnMsgUpdateCurrentTopLevelOuterContentWindowId(
|
||||
int32_t aLoading, ARefBase *param)
|
||||
{
|
||||
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
|
||||
|
||||
uint64_t winId = static_cast<UINT64Wrapper*>(param)->GetValue();
|
||||
|
||||
if (mCurrentTopLevelOuterContentWindowId == winId) {
|
||||
// duplicate notification
|
||||
return;
|
||||
}
|
||||
|
||||
bool activeTabWasLoading = mActiveTabTransactionsExist;
|
||||
bool activeTabIdChanged = mCurrentTopLevelOuterContentWindowId != winId;
|
||||
|
||||
mCurrentTopLevelOuterContentWindowId = winId;
|
||||
|
||||
LOG(("nsHttpConnectionMgr::OnMsgUpdateCurrentTopLevelOuterContentWindowId"
|
||||
" id=%" PRIu64 "\n",
|
||||
" id=%" PRIx64 "\n",
|
||||
mCurrentTopLevelOuterContentWindowId));
|
||||
|
||||
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;
|
||||
|
||||
if (!mActiveTabUnthrottledTransactionsExist) {
|
||||
transactions = mActiveTransactions[true].Get(mCurrentTopLevelOuterContentWindowId);
|
||||
}
|
||||
mActiveTabTransactionsExist = !!transactions;
|
||||
}
|
||||
|
||||
if (transactions) {
|
||||
// This means there are some transactions for this newly activated tab, resume them
|
||||
// but anything else.
|
||||
LOG((" resuming newly activated tab transactions"));
|
||||
ResumeReadOf(transactions);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!activeTabWasLoading) {
|
||||
// There were no transactions for the previously active tab, hence
|
||||
// all remaning transactions, if there were, were all unthrottled,
|
||||
// no need to wake them.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mActiveTransactions[false].IsEmpty()) {
|
||||
LOG((" resuming unthrottled background transactions"));
|
||||
ResumeReadOf(mActiveTransactions[false]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mActiveTransactions[true].IsEmpty()) {
|
||||
LOG((" delayed resuming throttled background transactions"));
|
||||
DelayedResumeBackgroundThrottledTransactions();
|
||||
return;
|
||||
}
|
||||
|
||||
DestroyThrottleTicker();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -55,7 +55,11 @@ public:
|
||||
MAX_CONNECTIONS,
|
||||
MAX_PERSISTENT_CONNECTIONS_PER_HOST,
|
||||
MAX_PERSISTENT_CONNECTIONS_PER_PROXY,
|
||||
MAX_REQUEST_DELAY
|
||||
MAX_REQUEST_DELAY,
|
||||
THROTTLING_ENABLED,
|
||||
THROTTLING_SUSPEND_FOR,
|
||||
THROTTLING_RESUME_FOR,
|
||||
THROTTLING_RESUME_IN
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
@ -68,7 +72,11 @@ public:
|
||||
uint16_t maxConnections,
|
||||
uint16_t maxPersistentConnectionsPerHost,
|
||||
uint16_t maxPersistentConnectionsPerProxy,
|
||||
uint16_t maxRequestDelay);
|
||||
uint16_t maxRequestDelay,
|
||||
bool throttleEnabled,
|
||||
uint32_t throttleSuspendFor,
|
||||
uint32_t throttleResumeFor,
|
||||
uint32_t throttleResumeIn);
|
||||
MOZ_MUST_USE nsresult Shutdown();
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
@ -95,13 +103,9 @@ public:
|
||||
MOZ_MUST_USE nsresult RescheduleTransaction(nsHttpTransaction *,
|
||||
int32_t priority);
|
||||
|
||||
// tells the transaction to stop receiving the response when |throttle|
|
||||
// is true. to start receiving again, this must be called with |throttle|
|
||||
// set to false. calling multiple times with the same value of |throttle|
|
||||
// has no effect. called by nsHttpChannels with the Throttleable class set
|
||||
// and controlled by net::ThrottlingService.
|
||||
// there is nothing to do when this fails, hence the void result.
|
||||
void ThrottleTransaction(nsHttpTransaction *, bool throttle);
|
||||
// TOOD
|
||||
void UpdateClassOfServiceOnTransaction(nsHttpTransaction *,
|
||||
uint32_t classOfService);
|
||||
|
||||
// cancels a transaction w/ the given reason.
|
||||
MOZ_MUST_USE nsresult CancelTransaction(nsHttpTransaction *,
|
||||
@ -211,6 +215,17 @@ public:
|
||||
|
||||
nsresult UpdateCurrentTopLevelOuterContentWindowId(uint64_t aWindowId);
|
||||
|
||||
// tracks and untracks active transactions according their throttle status
|
||||
void AddActiveTransaction(nsHttpTransaction* aTrans, bool aThrottled);
|
||||
void RemoveActiveTransaction(nsHttpTransaction* aTrans, bool aThrottled);
|
||||
void MoveActiveTransaction(nsHttpTransaction* aTrans, bool aThrottled);
|
||||
|
||||
// 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, bool aThrottled);
|
||||
|
||||
private:
|
||||
virtual ~nsHttpConnectionMgr();
|
||||
|
||||
@ -486,6 +501,10 @@ private:
|
||||
uint16_t mMaxPersistConnsPerHost;
|
||||
uint16_t mMaxPersistConnsPerProxy;
|
||||
uint16_t mMaxRequestDelay; // in seconds
|
||||
bool mThrottleEnabled;
|
||||
uint32_t mThrottleSuspendFor;
|
||||
uint32_t mThrottleResumeFor;
|
||||
uint32_t mThrottleResumeIn;
|
||||
Atomic<bool, mozilla::Relaxed> mIsShuttingDown;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
@ -579,7 +598,7 @@ private:
|
||||
void OnMsgShutdownConfirm (int32_t, ARefBase *);
|
||||
void OnMsgNewTransaction (int32_t, ARefBase *);
|
||||
void OnMsgReschedTransaction (int32_t, ARefBase *);
|
||||
void OnMsgThrottleTransaction (int32_t, ARefBase *);
|
||||
void OnMsgUpdateClassOfServiceOnTransaction (int32_t, ARefBase *);
|
||||
void OnMsgCancelTransaction (int32_t, ARefBase *);
|
||||
void OnMsgCancelTransactions (int32_t, ARefBase *);
|
||||
void OnMsgProcessPendingQ (int32_t, ARefBase *);
|
||||
@ -640,6 +659,58 @@ private:
|
||||
|
||||
nsCString mLogData;
|
||||
uint64_t mCurrentTopLevelOuterContentWindowId;
|
||||
|
||||
// Called on a pref change
|
||||
void SetThrottlingEnabled(bool aEnable);
|
||||
|
||||
// Two hashtalbes keeping track of active transactions regarding window id and throttling.
|
||||
// Used by the throttling algorithm to obtain number of transactions for the active tab
|
||||
// and for inactive tabs according their throttle status.
|
||||
// mActiveTransactions[0] are all unthrottled transactions, mActiveTransactions[1] throttled.
|
||||
nsClassHashtable<nsUint64HashKey, nsTArray<RefPtr<nsHttpTransaction>>> mActiveTransactions[2];
|
||||
|
||||
// Whether we are inside the "stop reading" interval, altered by the throttle ticker
|
||||
bool mThrottlingInhibitsReading;
|
||||
|
||||
// ticker for the 'stop reading'/'resume reading' signal
|
||||
nsCOMPtr<nsITimer> mThrottleTicker;
|
||||
// Checks if the combination of active transactions requires the ticker.
|
||||
bool IsThrottleTickerNeeded();
|
||||
// The method also unschedules the delayed resume of background tabs timer
|
||||
// if the ticker was about to be scheduled.
|
||||
void EnsureThrottleTickerIfNeeded();
|
||||
// Drops also the mThrottlingInhibitsReading flag. Immediate or delayed resume
|
||||
// of currently throttled transactions is not affected by this method.
|
||||
void DestroyThrottleTicker();
|
||||
// Handler for the ticker: alters the mThrottlingInhibitsReading flag.
|
||||
void ThrottlerTick();
|
||||
|
||||
// mechanism to delay immediate resume of background tabs and chrome initiated
|
||||
// throttled transactions after the last transaction blocking their unthrottle
|
||||
// has been removed. Needs to be delayed because during a page load there is
|
||||
// a number of intervals when there is no transaction that would cause throttling.
|
||||
// Hence, throttling of long standing responses, like downloads, would be mostly
|
||||
// ineffective if resumed during every such interval.
|
||||
nsCOMPtr<nsITimer> mDelayedResumeReadTimer;
|
||||
// Schedule the resume
|
||||
void DelayedResumeBackgroundThrottledTransactions();
|
||||
// Simply destroys the timer
|
||||
void CancelDelayedResumeBackgroundThrottledTransactions();
|
||||
// Handler for the timer: resumes all background throttled transactions
|
||||
void ResumeBackgroundThrottledTransactions();
|
||||
|
||||
// Simple helpers, iterates the given hash/array and resume.
|
||||
// @param excludeActive: skip active tabid transactions.
|
||||
void ResumeReadOf(nsClassHashtable<nsUint64HashKey, nsTArray<RefPtr<nsHttpTransaction>>>&,
|
||||
bool excludeActive = false);
|
||||
void ResumeReadOf(nsTArray<RefPtr<nsHttpTransaction>>*);
|
||||
|
||||
// Cached status of the active tab active transactions existence,
|
||||
// saves a lot of hashtable lookups
|
||||
bool mActiveTabTransactionsExist;
|
||||
bool mActiveTabUnthrottledTransactionsExist;
|
||||
|
||||
void LogActiveTransactions(char);
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsHttpConnectionMgr::nsHalfOpenSocket, NS_HALFOPENSOCKET_IID)
|
||||
|
@ -191,6 +191,10 @@ nsHttpHandler::nsHttpHandler()
|
||||
, mMaxConnections(24)
|
||||
, mMaxPersistentConnectionsPerServer(2)
|
||||
, mMaxPersistentConnectionsPerProxy(4)
|
||||
, mThrottleEnabled(true)
|
||||
, mThrottleSuspendFor(3000)
|
||||
, mThrottleResumeFor(200)
|
||||
, mThrottleResumeIn(400)
|
||||
, mRedirectionLimit(10)
|
||||
, mPhishyUserPassLength(1)
|
||||
, mQoSBits(0x00)
|
||||
@ -549,7 +553,11 @@ nsHttpHandler::InitConnectionMgr()
|
||||
mMaxConnections,
|
||||
mMaxPersistentConnectionsPerServer,
|
||||
mMaxPersistentConnectionsPerProxy,
|
||||
mMaxRequestDelay);
|
||||
mMaxRequestDelay,
|
||||
mThrottleEnabled,
|
||||
mThrottleSuspendFor,
|
||||
mThrottleResumeFor,
|
||||
mThrottleResumeIn);
|
||||
return rv;
|
||||
}
|
||||
|
||||
@ -1546,6 +1554,41 @@ nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
|
||||
}
|
||||
}
|
||||
|
||||
if (PREF_CHANGED("network.http.throttle.enable")) {
|
||||
rv = prefs->GetBoolPref("network.http.throttle.enable", &mThrottleEnabled);
|
||||
if (NS_SUCCEEDED(rv) && mConnMgr) {
|
||||
Unused << mConnMgr->UpdateParam(nsHttpConnectionMgr::THROTTLING_ENABLED,
|
||||
static_cast<int32_t>(mThrottleEnabled));
|
||||
}
|
||||
}
|
||||
|
||||
if (PREF_CHANGED("network.http.throttle.suspend-for")) {
|
||||
rv = prefs->GetIntPref("network.http.throttle.suspend-for", &val);
|
||||
mThrottleSuspendFor = (uint32_t)clamped(val, 0, 120000);
|
||||
if (NS_SUCCEEDED(rv) && mConnMgr) {
|
||||
Unused << mConnMgr->UpdateParam(nsHttpConnectionMgr::THROTTLING_SUSPEND_FOR,
|
||||
mThrottleSuspendFor);
|
||||
}
|
||||
}
|
||||
|
||||
if (PREF_CHANGED("network.http.throttle.resume-for")) {
|
||||
rv = prefs->GetIntPref("network.http.throttle.resume-for", &val);
|
||||
mThrottleResumeFor = (uint32_t)clamped(val, 0, 120000);
|
||||
if (NS_SUCCEEDED(rv) && mConnMgr) {
|
||||
Unused << mConnMgr->UpdateParam(nsHttpConnectionMgr::THROTTLING_RESUME_FOR,
|
||||
mThrottleResumeFor);
|
||||
}
|
||||
}
|
||||
|
||||
if (PREF_CHANGED("network.http.throttle.resume-background-in")) {
|
||||
rv = prefs->GetIntPref("network.http.throttle.resume-background-in", &val);
|
||||
mThrottleResumeIn = (uint32_t)clamped(val, 0, 120000);
|
||||
if (NS_SUCCEEDED(rv) && mConnMgr) {
|
||||
Unused << mConnMgr->UpdateParam(nsHttpConnectionMgr::THROTTLING_RESUME_IN,
|
||||
mThrottleResumeIn);
|
||||
}
|
||||
}
|
||||
|
||||
if (PREF_CHANGED(HTTP_PREF("focused_window_transaction_ratio"))) {
|
||||
float ratio = 0;
|
||||
rv = prefs->GetFloatPref(HTTP_PREF("focused_window_transaction_ratio"), &ratio);
|
||||
|
@ -224,10 +224,10 @@ public:
|
||||
return mConnMgr->RescheduleTransaction(trans, priority);
|
||||
}
|
||||
|
||||
void ThrottleTransaction(nsHttpTransaction *trans,
|
||||
bool throttle)
|
||||
void UpdateClassOfServiceOnTransaction(nsHttpTransaction *trans,
|
||||
uint32_t classOfService)
|
||||
{
|
||||
mConnMgr->ThrottleTransaction(trans, throttle);
|
||||
mConnMgr->UpdateClassOfServiceOnTransaction(trans, classOfService);
|
||||
}
|
||||
|
||||
// Called to cancel a transaction, which may or may not be assigned to
|
||||
@ -448,6 +448,11 @@ private:
|
||||
uint8_t mMaxPersistentConnectionsPerServer;
|
||||
uint8_t mMaxPersistentConnectionsPerProxy;
|
||||
|
||||
bool mThrottleEnabled;
|
||||
uint32_t mThrottleSuspendFor;
|
||||
uint32_t mThrottleResumeFor;
|
||||
uint32_t mThrottleResumeIn;
|
||||
|
||||
uint8_t mRedirectionLimit;
|
||||
|
||||
// we'll warn the user if we load an URL containing a userpass field
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "nsIHttpActivityObserver.h"
|
||||
#include "nsSocketTransportService2.h"
|
||||
#include "nsICancelable.h"
|
||||
#include "nsIClassOfService.h"
|
||||
#include "nsIEventTarget.h"
|
||||
#include "nsIHttpChannelInternal.h"
|
||||
#include "nsIInputStream.h"
|
||||
@ -105,9 +106,11 @@ nsHttpTransaction::nsHttpTransaction()
|
||||
, mCurrentHttpResponseHeaderSize(0)
|
||||
, mCapsToClear(0)
|
||||
, mResponseIsComplete(false)
|
||||
, mThrottleResponse(false)
|
||||
, mReadingStopped(false)
|
||||
, mClosed(false)
|
||||
, mConnected(false)
|
||||
, mActivated(false)
|
||||
, mActivatedAsH2(false)
|
||||
, mHaveStatusLine(false)
|
||||
, mHaveAllHeaders(false)
|
||||
, mTransactionDone(false)
|
||||
@ -151,11 +154,43 @@ nsHttpTransaction::nsHttpTransaction()
|
||||
mPeerAddr.raw.family = PR_AF_UNSPEC;
|
||||
}
|
||||
|
||||
void nsHttpTransaction::ThrottleResponse(bool aThrottle)
|
||||
void nsHttpTransaction::ResumeReading()
|
||||
{
|
||||
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
|
||||
|
||||
mThrottleResponse = aThrottle;
|
||||
if (!mReadingStopped) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG(("nsHttpTransaction::ResumeReading %p", this));
|
||||
|
||||
mReadingStopped = false;
|
||||
if (mConnection) {
|
||||
nsresult rv = mConnection->ResumeRecv();
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG((" resume failed with rv=%" PRIx32, static_cast<uint32_t>(rv)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void nsHttpTransaction::SetClassOfService(uint32_t cos)
|
||||
{
|
||||
bool wasThrottling = mClassOfService & nsIClassOfService::Throttleable;
|
||||
|
||||
mClassOfService = cos;
|
||||
|
||||
bool isThrottling = mClassOfService & nsIClassOfService::Throttleable;
|
||||
|
||||
if (mConnection && wasThrottling != isThrottling) {
|
||||
// Do nothing until we are actually activated. For now
|
||||
// only remember the throttle flag. Call to MoveActiveTransaction
|
||||
// would add this transaction to the list too early.
|
||||
gHttpHandler->ConnMgr()->MoveActiveTransaction(this, isThrottling);
|
||||
|
||||
if (mReadingStopped && !isThrottling) {
|
||||
ResumeReading();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsHttpTransaction::~nsHttpTransaction()
|
||||
@ -472,6 +507,21 @@ nsHttpTransaction::SetConnection(nsAHttpConnection *conn)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpTransaction::OnActivated(bool h2)
|
||||
{
|
||||
MOZ_ASSERT(OnSocketThread());
|
||||
|
||||
mActivatedAsH2 = h2;
|
||||
if (mActivated) {
|
||||
return;
|
||||
}
|
||||
|
||||
mActivated = true;
|
||||
gHttpHandler->ConnMgr()->AddActiveTransaction(
|
||||
this, mClassOfService & nsIClassOfService::Throttleable);
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpTransaction::GetSecurityCallbacks(nsIInterfaceRequestor **cb)
|
||||
{
|
||||
@ -774,18 +824,30 @@ nsresult
|
||||
nsHttpTransaction::WriteSegments(nsAHttpSegmentWriter *writer,
|
||||
uint32_t count, uint32_t *countWritten)
|
||||
{
|
||||
static bool reentrantFlag = false;
|
||||
LOG(("nsHttpTransaction::WriteSegments %p reentrantFlag=%d",
|
||||
this, reentrantFlag));
|
||||
MOZ_DIAGNOSTIC_ASSERT(!reentrantFlag);
|
||||
reentrantFlag = true;
|
||||
LOG(("nsHttpTransaction::WriteSegments %p", this));
|
||||
|
||||
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
|
||||
|
||||
if (mTransactionDone) {
|
||||
reentrantFlag = false;
|
||||
return NS_SUCCEEDED(mStatus) ? NS_BASE_STREAM_CLOSED : mStatus;
|
||||
}
|
||||
|
||||
// Throttling feature is now disabled for http/2 transactions
|
||||
// because of bug 1367861. The logic around mActivatedAsH2
|
||||
// will be removed when that is fixed
|
||||
if (!mActivatedAsH2 &&
|
||||
gHttpHandler->ConnMgr()->ShouldStopReading(this,
|
||||
mClassOfService & nsIClassOfService::Throttleable)) {
|
||||
LOG(("nsHttpTransaction::WriteSegments %p response throttled", this));
|
||||
// Must remember that we have to call ResumeRecv() on our connection when
|
||||
// called back by the conn manager to resume reading.
|
||||
mReadingStopped = true;
|
||||
// This makes the underlaying connection or stream wait for explicit resume.
|
||||
// For h1 this means we stop reading from the socket.
|
||||
// For h2 this means we stop updating recv window for the stream.
|
||||
return NS_BASE_STREAM_WOULD_BLOCK;
|
||||
}
|
||||
|
||||
mWriter = writer;
|
||||
|
||||
#ifdef WIN32 // bug 1153929
|
||||
@ -795,7 +857,6 @@ nsHttpTransaction::WriteSegments(nsAHttpSegmentWriter *writer,
|
||||
#endif // WIN32
|
||||
|
||||
if (!mPipeOut) {
|
||||
reentrantFlag = false;
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
@ -827,7 +888,6 @@ nsHttpTransaction::WriteSegments(nsAHttpSegmentWriter *writer,
|
||||
}
|
||||
}
|
||||
|
||||
reentrantFlag = false;
|
||||
return rv;
|
||||
}
|
||||
|
||||
@ -837,6 +897,12 @@ nsHttpTransaction::Close(nsresult reason)
|
||||
LOG(("nsHttpTransaction::Close [this=%p reason=%" PRIx32 "]\n",
|
||||
this, static_cast<uint32_t>(reason)));
|
||||
|
||||
if (!mClosed) {
|
||||
gHttpHandler->ConnMgr()->RemoveActiveTransaction(
|
||||
this, mClassOfService & nsIClassOfService::Throttleable);
|
||||
mActivated = false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
|
||||
if (reason == NS_BINDING_RETARGETED) {
|
||||
LOG((" close %p skipped due to ERETARGETED\n", this));
|
||||
|
@ -89,6 +89,8 @@ public:
|
||||
uint64_t topLevelOuterContentWindowId,
|
||||
nsIAsyncInputStream **responseBody);
|
||||
|
||||
void OnActivated(bool h2) override;
|
||||
|
||||
// attributes
|
||||
nsHttpResponseHead *ResponseHead() { return mHaveAllHeaders ? mResponseHead : nullptr; }
|
||||
nsISupports *SecurityInfo() { return mSecurityInfo; }
|
||||
@ -307,14 +309,17 @@ private:
|
||||
Atomic<uint32_t> mCapsToClear;
|
||||
Atomic<bool, ReleaseAcquire> mResponseIsComplete;
|
||||
|
||||
// If true, this transaction was asked to stop receiving the response.
|
||||
// NOTE: this flag is currently unused. A useful remnant of an old throttling algorithm.
|
||||
bool mThrottleResponse;
|
||||
// True iff WriteSegments was called while this transaction should be throttled (stop reading)
|
||||
// Used to resume read on unblock of reading. Conn manager is responsible for calling back
|
||||
// to resume reading.
|
||||
bool mReadingStopped;
|
||||
|
||||
// state flags, all logically boolean, but not packed together into a
|
||||
// bitfield so as to avoid bitfield-induced races. See bug 560579.
|
||||
bool mClosed;
|
||||
bool mConnected;
|
||||
bool mActivated;
|
||||
bool mActivatedAsH2;
|
||||
bool mHaveStatusLine;
|
||||
bool mHaveAllHeaders;
|
||||
bool mTransactionDone;
|
||||
@ -373,9 +378,9 @@ public:
|
||||
// but later can be dispatched via spdy (not subject to rate pacing).
|
||||
void CancelPacing(nsresult reason);
|
||||
|
||||
// Called only on the socket thread. Updates the flag whether the transaction
|
||||
// should make the underlying connection or session stop reading from the socket.
|
||||
void ThrottleResponse(bool aThrottle);
|
||||
// Called by the connetion manager on the socket thread when reading for this
|
||||
// previously throttled transaction has to be resumed.
|
||||
void ResumeReading();
|
||||
|
||||
private:
|
||||
bool mSubmittedRatePacing;
|
||||
@ -383,7 +388,7 @@ private:
|
||||
bool mSynchronousRatePaceRequest;
|
||||
nsCOMPtr<nsICancelable> mTokenBucketCancel;
|
||||
public:
|
||||
void SetClassOfService(uint32_t cos) { mClassOfService = cos; }
|
||||
void SetClassOfService(uint32_t cos);
|
||||
uint32_t ClassOfService() { return mClassOfService; }
|
||||
private:
|
||||
uint32_t mClassOfService;
|
||||
|
Loading…
Reference in New Issue
Block a user