mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 432492: rate limit long-running safebrowsing updates, patch by Dave Camp <dcamp@mozilla.com>, r=tony, a=beltzner
This commit is contained in:
parent
844a9ac9ed
commit
7d64b6cbe7
@ -57,7 +57,7 @@ interface nsIUrlClassifierCallback : nsISupports {
|
||||
* clients streaming updates to the url-classifier (usually
|
||||
* nsUrlClassifierStreamUpdater.
|
||||
*/
|
||||
[scriptable, uuid(1c9bd1c2-f6fe-43a8-a2b9-48359eb4a9b1)]
|
||||
[scriptable, uuid(bbb33c65-e783-476c-8db0-6ddb91826c07)]
|
||||
interface nsIUrlClassifierUpdateObserver : nsISupports {
|
||||
/**
|
||||
* The update requested a new URL whose contents should be downloaded
|
||||
@ -83,8 +83,10 @@ interface nsIUrlClassifierUpdateObserver : nsISupports {
|
||||
* A stream update has completed.
|
||||
*
|
||||
* @param status The state of the update process.
|
||||
* @param delay The amount of time the updater should wait to fetch the
|
||||
* next URL in ms.
|
||||
*/
|
||||
void streamFinished(in nsresult status);
|
||||
void streamFinished(in nsresult status, in unsigned long delay);
|
||||
|
||||
/* The update has encountered an error and should be cancelled */
|
||||
void updateError(in nsresult error);
|
||||
|
@ -154,6 +154,17 @@ static const PRLogModuleInfo *gUrlClassifierDbServiceLog = nsnull;
|
||||
#define UPDATE_CACHE_SIZE_PREF "urlclassifier.updatecachemax"
|
||||
#define UPDATE_CACHE_SIZE_DEFAULT -1
|
||||
|
||||
// Amount of time to spend updating before committing and delaying, in
|
||||
// seconds. This is checked after each update stream, so the actual
|
||||
// time spent can be higher than this, depending on update stream size.
|
||||
#define UPDATE_WORKING_TIME "urlclassifier.workingtime"
|
||||
#define UPDATE_WORKING_TIME_DEFAULT 5
|
||||
|
||||
// The amount of time to delay after hitting UPDATE_WORKING_TIME, in
|
||||
// seconds.
|
||||
#define UPDATE_DELAY_TIME "urlclassifier.updatetime"
|
||||
#define UPDATE_DELAY_TIME_DEFAULT 60
|
||||
|
||||
#define PAGE_SIZE 4096
|
||||
|
||||
class nsUrlClassifierDBServiceWorker;
|
||||
@ -172,6 +183,9 @@ static PRInt32 gFreshnessGuarantee = CONFIRM_AGE_DEFAULT_SEC;
|
||||
|
||||
static PRInt32 gUpdateCacheSize = UPDATE_CACHE_SIZE_DEFAULT;
|
||||
|
||||
static PRInt32 gWorkingTimeThreshold = UPDATE_WORKING_TIME_DEFAULT;
|
||||
static PRInt32 gDelayTime = UPDATE_DELAY_TIME_DEFAULT;
|
||||
|
||||
static void
|
||||
SplitTables(const nsACString& str, nsTArray<nsCString>& tables)
|
||||
{
|
||||
@ -1102,6 +1116,12 @@ private:
|
||||
// Handle chunk data from a stream update
|
||||
nsresult ProcessChunk(PRBool* done);
|
||||
|
||||
// Sets up a transaction and begins counting update time.
|
||||
nsresult SetupUpdate();
|
||||
|
||||
// Applies the current transaction and resets the update/working times.
|
||||
nsresult ApplyUpdate();
|
||||
|
||||
// Reset the in-progress update stream
|
||||
void ResetStream();
|
||||
|
||||
@ -1217,6 +1237,10 @@ private:
|
||||
// The MAC stated by the server.
|
||||
nsCString mServerMAC;
|
||||
|
||||
// Start time of the current update interval. This will be reset
|
||||
// every time we apply the update.
|
||||
PRIntervalTime mUpdateStartTime;
|
||||
|
||||
nsCOMPtr<nsICryptoHMAC> mHMAC;
|
||||
// The number of noise entries to add to the set of lookup results.
|
||||
PRInt32 mGethashNoise;
|
||||
@ -1256,6 +1280,7 @@ nsUrlClassifierDBServiceWorker::nsUrlClassifierDBServiceWorker()
|
||||
, mCachedListsTable(PR_UINT32_MAX)
|
||||
, mHaveCachedAddChunks(PR_FALSE)
|
||||
, mHaveCachedSubChunks(PR_FALSE)
|
||||
, mUpdateStartTime(0)
|
||||
, mGethashNoise(0)
|
||||
, mPendingLookupLock(nsnull)
|
||||
{
|
||||
@ -2757,19 +2782,7 @@ nsUrlClassifierDBServiceWorker::BeginUpdate(nsIUrlClassifierUpdateObserver *obse
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (gUpdateCacheSize > 0) {
|
||||
PRUint32 cachePages = gUpdateCacheSize / PAGE_SIZE;
|
||||
nsCAutoString cacheSizePragma("PRAGMA cache_size=");
|
||||
cacheSizePragma.AppendInt(cachePages);
|
||||
rv = mConnection->ExecuteSimpleSQL(cacheSizePragma);
|
||||
if (NS_FAILED(rv)) {
|
||||
mUpdateStatus = rv;
|
||||
return rv;
|
||||
}
|
||||
mGrewCache = PR_TRUE;
|
||||
}
|
||||
|
||||
rv = mConnection->BeginTransaction();
|
||||
rv = SetupUpdate();
|
||||
if (NS_FAILED(rv)) {
|
||||
mUpdateStatus = rv;
|
||||
return rv;
|
||||
@ -2801,9 +2814,15 @@ nsUrlClassifierDBServiceWorker::BeginStream(const nsACString &table,
|
||||
NS_ENSURE_STATE(mUpdateObserver);
|
||||
NS_ENSURE_STATE(!mInStream);
|
||||
|
||||
mInStream = PR_TRUE;
|
||||
// We may have committed the update in FinishStream, if so set it up
|
||||
// again here.
|
||||
nsresult rv = SetupUpdate();
|
||||
if (NS_FAILED(rv)) {
|
||||
mUpdateStatus = rv;
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
mInStream = PR_TRUE;
|
||||
|
||||
// If we're expecting a MAC, create the nsICryptoHMAC component now.
|
||||
if (!mUpdateClientKey.IsEmpty()) {
|
||||
@ -2941,6 +2960,8 @@ nsUrlClassifierDBServiceWorker::FinishStream()
|
||||
NS_ENSURE_STATE(mInStream);
|
||||
NS_ENSURE_STATE(mUpdateObserver);
|
||||
|
||||
PRInt32 nextStreamDelay = 0;
|
||||
|
||||
if (NS_SUCCEEDED(mUpdateStatus) && mHMAC) {
|
||||
nsCAutoString clientMAC;
|
||||
mHMAC->Finish(PR_TRUE, clientMAC);
|
||||
@ -2951,34 +2972,91 @@ nsUrlClassifierDBServiceWorker::FinishStream()
|
||||
mServerMAC.get(), clientMAC.get()));
|
||||
mUpdateStatus = NS_ERROR_FAILURE;
|
||||
}
|
||||
PRIntervalTime updateTime = PR_IntervalNow() - mUpdateStartTime;
|
||||
if (PR_IntervalToSeconds(updateTime) >=
|
||||
static_cast<PRUint32>(gWorkingTimeThreshold)) {
|
||||
// We've spent long enough working that we should commit what we
|
||||
// have and hold off for a bit.
|
||||
ApplyUpdate();
|
||||
|
||||
nextStreamDelay = gDelayTime * 1000;
|
||||
}
|
||||
}
|
||||
|
||||
mUpdateObserver->StreamFinished(mUpdateStatus);
|
||||
mUpdateObserver->StreamFinished(mUpdateStatus,
|
||||
static_cast<PRUint32>(nextStreamDelay));
|
||||
|
||||
ResetStream();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsUrlClassifierDBServiceWorker::SetupUpdate()
|
||||
{
|
||||
LOG(("nsUrlClassifierDBServiceWorker::SetupUpdate"));
|
||||
PRBool inProgress;
|
||||
nsresult rv = mConnection->GetTransactionInProgress(&inProgress);
|
||||
if (inProgress) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mUpdateStartTime = PR_IntervalNow();
|
||||
|
||||
rv = mConnection->BeginTransaction();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (gUpdateCacheSize > 0) {
|
||||
PRUint32 cachePages = gUpdateCacheSize / PAGE_SIZE;
|
||||
nsCAutoString cacheSizePragma("PRAGMA cache_size=");
|
||||
cacheSizePragma.AppendInt(cachePages);
|
||||
rv = mConnection->ExecuteSimpleSQL(cacheSizePragma);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
mGrewCache = PR_TRUE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsUrlClassifierDBServiceWorker::ApplyUpdate()
|
||||
{
|
||||
LOG(("nsUrlClassifierDBServiceWorker::ApplyUpdate"));
|
||||
|
||||
if (NS_FAILED(mUpdateStatus)) {
|
||||
mConnection->RollbackTransaction();
|
||||
} else {
|
||||
mUpdateStatus = FlushChunkLists();
|
||||
if (NS_SUCCEEDED(mUpdateStatus)) {
|
||||
mUpdateStatus = mConnection->CommitTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
if (mGrewCache) {
|
||||
// During the update we increased the page cache to bigger than we
|
||||
// want to keep around. At the moment, the only reliable way to make
|
||||
// sure that the page cache is freed is to reopen the connection.
|
||||
mGrewCache = PR_FALSE;
|
||||
CloseDb();
|
||||
OpenDb();
|
||||
}
|
||||
|
||||
mUpdateStartTime = 0;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierDBServiceWorker::FinishUpdate()
|
||||
{
|
||||
LOG(("nsUrlClassifierDBServiceWorker::FinishUpdate()"));
|
||||
if (gShuttingDownThread)
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
NS_ENSURE_STATE(!mInStream);
|
||||
NS_ENSURE_STATE(mUpdateObserver);
|
||||
|
||||
if (NS_SUCCEEDED(mUpdateStatus)) {
|
||||
mUpdateStatus = FlushChunkLists();
|
||||
}
|
||||
|
||||
nsCAutoString arg;
|
||||
if (NS_SUCCEEDED(mUpdateStatus)) {
|
||||
mUpdateStatus = mConnection->CommitTransaction();
|
||||
} else {
|
||||
mConnection->RollbackTransaction();
|
||||
}
|
||||
ApplyUpdate();
|
||||
|
||||
if (NS_SUCCEEDED(mUpdateStatus)) {
|
||||
mUpdateObserver->UpdateSuccess(mUpdateWait);
|
||||
@ -3011,13 +3089,6 @@ nsUrlClassifierDBServiceWorker::FinishUpdate()
|
||||
// database reset.
|
||||
if (NS_SUCCEEDED(mUpdateStatus) && resetRequested) {
|
||||
ResetDatabase();
|
||||
} else if (mGrewCache) {
|
||||
// During the update we increased the page cache to bigger than we
|
||||
// want to keep around. At the moment, the only reliable way to make
|
||||
// sure that the page cache is freed is to reopen the connection.
|
||||
mGrewCache = PR_FALSE;
|
||||
CloseDb();
|
||||
OpenDb();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
@ -3071,8 +3142,6 @@ NS_IMETHODIMP
|
||||
nsUrlClassifierDBServiceWorker::CloseDb()
|
||||
{
|
||||
if (mConnection) {
|
||||
CancelUpdate();
|
||||
|
||||
mMainStore.Close();
|
||||
mPendingSubStore.Close();
|
||||
|
||||
@ -3668,6 +3737,14 @@ nsUrlClassifierDBService::Init()
|
||||
|
||||
rv = prefs->GetIntPref(UPDATE_CACHE_SIZE_PREF, &tmpint);
|
||||
PR_AtomicSet(&gUpdateCacheSize, NS_SUCCEEDED(rv) ? tmpint : UPDATE_CACHE_SIZE_DEFAULT);
|
||||
|
||||
rv = prefs->GetIntPref(UPDATE_WORKING_TIME, &tmpint);
|
||||
PR_AtomicSet(&gWorkingTimeThreshold,
|
||||
NS_SUCCEEDED(rv) ? tmpint : UPDATE_WORKING_TIME_DEFAULT);
|
||||
|
||||
rv = prefs->GetIntPref(UPDATE_DELAY_TIME, &tmpint);
|
||||
PR_AtomicSet(&gDelayTime,
|
||||
NS_SUCCEEDED(rv) ? tmpint : UPDATE_DELAY_TIME_DEFAULT);
|
||||
}
|
||||
|
||||
// Start the background thread.
|
||||
@ -3960,6 +4037,16 @@ nsUrlClassifierDBService::Observe(nsISupports *aSubject, const char *aTopic,
|
||||
PRInt32 tmpint;
|
||||
rv = prefs->GetIntPref(UPDATE_CACHE_SIZE_PREF, &tmpint);
|
||||
PR_AtomicSet(&gUpdateCacheSize, NS_SUCCEEDED(rv) ? tmpint : UPDATE_CACHE_SIZE_DEFAULT);
|
||||
} else if (NS_LITERAL_STRING(UPDATE_WORKING_TIME).Equals(aData)) {
|
||||
PRInt32 tmpint;
|
||||
rv = prefs->GetIntPref(UPDATE_WORKING_TIME, &tmpint);
|
||||
PR_AtomicSet(&gWorkingTimeThreshold,
|
||||
NS_SUCCEEDED(rv) ? tmpint : UPDATE_WORKING_TIME_DEFAULT);
|
||||
} else if (NS_LITERAL_STRING(UPDATE_DELAY_TIME).Equals(aData)) {
|
||||
PRInt32 tmpint;
|
||||
rv = prefs->GetIntPref(UPDATE_DELAY_TIME, &tmpint);
|
||||
PR_AtomicSet(&gDelayTime,
|
||||
NS_SUCCEEDED(rv) ? tmpint : UPDATE_DELAY_TIME_DEFAULT);
|
||||
}
|
||||
} else if (!strcmp(aTopic, "profile-before-change") ||
|
||||
!strcmp(aTopic, "xpcom-shutdown-threads")) {
|
||||
@ -3993,6 +4080,8 @@ nsUrlClassifierDBService::Shutdown()
|
||||
nsresult rv;
|
||||
// First close the db connection.
|
||||
if (mWorker) {
|
||||
rv = mWorkerProxy->CancelUpdate();
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to post cancel udpate event");
|
||||
rv = mWorkerProxy->CloseDb();
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to post close db event");
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ nsUrlClassifierStreamUpdater::nsUrlClassifierStreamUpdater()
|
||||
|
||||
}
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS8(nsUrlClassifierStreamUpdater,
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS9(nsUrlClassifierStreamUpdater,
|
||||
nsIUrlClassifierStreamUpdater,
|
||||
nsIUrlClassifierUpdateObserver,
|
||||
nsIRequestObserver,
|
||||
@ -83,7 +83,8 @@ NS_IMPL_THREADSAFE_ISUPPORTS8(nsUrlClassifierStreamUpdater,
|
||||
nsIObserver,
|
||||
nsIBadCertListener2,
|
||||
nsISSLErrorListener,
|
||||
nsIInterfaceRequestor)
|
||||
nsIInterfaceRequestor,
|
||||
nsITimerCallback)
|
||||
|
||||
/**
|
||||
* Clear out the update.
|
||||
@ -271,29 +272,54 @@ nsUrlClassifierStreamUpdater::RekeyRequested()
|
||||
nsnull);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierStreamUpdater::StreamFinished(nsresult status)
|
||||
nsresult
|
||||
nsUrlClassifierStreamUpdater::FetchNext()
|
||||
{
|
||||
nsresult rv;
|
||||
if (mPendingUpdates.Length() == 0) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Pop off a pending URL and update it.
|
||||
if (NS_SUCCEEDED(status) && mPendingUpdates.Length() > 0) {
|
||||
PendingUpdate &update = mPendingUpdates[0];
|
||||
rv = FetchUpdate(update.mUrl, EmptyCString(),
|
||||
update.mTable, update.mServerMAC);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(("Error fetching update url: %s\n", update.mUrl.get()));
|
||||
// We can commit the urls that we've applied so far. This is
|
||||
// probably a transient server problem, so trigger backoff.
|
||||
mDownloadErrorCallback->HandleEvent(EmptyCString());
|
||||
mDownloadError = PR_TRUE;
|
||||
mDBService->FinishUpdate();
|
||||
return rv;
|
||||
}
|
||||
|
||||
mPendingUpdates.RemoveElementAt(0);
|
||||
} else {
|
||||
PendingUpdate &update = mPendingUpdates[0];
|
||||
LOG(("Fetching update url: %s\n", update.mUrl.get()));
|
||||
nsresult rv = FetchUpdate(update.mUrl, EmptyCString(),
|
||||
update.mTable, update.mServerMAC);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(("Error fetching update url: %s\n", update.mUrl.get()));
|
||||
// We can commit the urls that we've applied so far. This is
|
||||
// probably a transient server problem, so trigger backoff.
|
||||
mDownloadErrorCallback->HandleEvent(EmptyCString());
|
||||
mDownloadError = PR_TRUE;
|
||||
mDBService->FinishUpdate();
|
||||
return rv;
|
||||
}
|
||||
|
||||
mPendingUpdates.RemoveElementAt(0);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierStreamUpdater::StreamFinished(nsresult status,
|
||||
PRUint32 requestedDelay)
|
||||
{
|
||||
LOG(("nsUrlClassifierStreamUpdater::StreamFinished [%x, %d]", status, requestedDelay));
|
||||
if (NS_FAILED(status) || mPendingUpdates.Length() == 0) {
|
||||
// We're done.
|
||||
mDBService->FinishUpdate();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Wait the requested amount of time before starting a new stream.
|
||||
nsresult rv;
|
||||
mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = mTimer->InitWithCallback(this, requestedDelay,
|
||||
nsITimer::TYPE_ONE_SHOT);
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Unable to initialize timer, fetching next safebrowsing item immediately");
|
||||
return FetchNext();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
@ -500,6 +526,10 @@ nsUrlClassifierStreamUpdater::Observe(nsISupports *aSubject, const char *aTopic,
|
||||
mIsUpdating = PR_FALSE;
|
||||
mChannel = nsnull;
|
||||
}
|
||||
if (mTimer) {
|
||||
mTimer->Cancel();
|
||||
mTimer = nsnull;
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
@ -538,3 +568,20 @@ nsUrlClassifierStreamUpdater::GetInterface(const nsIID & eventSinkIID, void* *_r
|
||||
{
|
||||
return QueryInterface(eventSinkIID, _retval);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// nsITimerCallback implementation
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierStreamUpdater::Notify(nsITimer *timer)
|
||||
{
|
||||
LOG(("nsUrlClassifierStreamUpdater::Notify [%p]", this));
|
||||
|
||||
mTimer = nsnull;
|
||||
|
||||
// Start the update process up again.
|
||||
FetchNext();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -49,6 +49,7 @@
|
||||
#include "nsTArray.h"
|
||||
#include "nsIBadCertListener2.h"
|
||||
#include "nsISSLErrorListener.h"
|
||||
#include "nsITimer.h"
|
||||
|
||||
// Forward declare pointers
|
||||
class nsIURI;
|
||||
@ -59,7 +60,8 @@ class nsUrlClassifierStreamUpdater : public nsIUrlClassifierStreamUpdater,
|
||||
public nsIObserver,
|
||||
public nsIBadCertListener2,
|
||||
public nsISSLErrorListener,
|
||||
public nsIInterfaceRequestor
|
||||
public nsIInterfaceRequestor,
|
||||
public nsITimerCallback
|
||||
{
|
||||
public:
|
||||
nsUrlClassifierStreamUpdater();
|
||||
@ -73,6 +75,7 @@ public:
|
||||
NS_DECL_NSIBADCERTLISTENER2
|
||||
NS_DECL_NSISSLERRORLISTENER
|
||||
NS_DECL_NSIOBSERVER
|
||||
NS_DECL_NSITIMERCALLBACK
|
||||
|
||||
private:
|
||||
// No subclassing
|
||||
@ -96,6 +99,8 @@ private:
|
||||
const nsACString &aTable,
|
||||
const nsACString &aServerMAC);
|
||||
|
||||
nsresult FetchNext();
|
||||
|
||||
PRBool mIsUpdating;
|
||||
PRBool mInitialized;
|
||||
PRBool mDownloadError;
|
||||
@ -105,6 +110,7 @@ private:
|
||||
nsCString mServerMAC;
|
||||
nsCOMPtr<nsIChannel> mChannel;
|
||||
nsCOMPtr<nsIUrlClassifierDBService> mDBService;
|
||||
nsCOMPtr<nsITimer> mTimer;
|
||||
|
||||
struct PendingUpdate {
|
||||
nsCString mUrl;
|
||||
|
Loading…
Reference in New Issue
Block a user