mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 453723: Short circuit known-clean hosts and fragments in the url-classifier. r=tony
This commit is contained in:
parent
e129110ee3
commit
7a5ba0ed93
@ -157,6 +157,10 @@ static const PRLogModuleInfo *gUrlClassifierDbServiceLog = nsnull;
|
||||
#define UPDATE_CACHE_SIZE_PREF "urlclassifier.updatecachemax"
|
||||
#define UPDATE_CACHE_SIZE_DEFAULT -1
|
||||
|
||||
// MRU cache sizes for remembering clean lookups
|
||||
#define CLEAN_HOST_KEYS_SIZE 16
|
||||
#define CLEAN_FRAGMENTS_SIZE 32
|
||||
|
||||
// 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.
|
||||
@ -462,6 +466,10 @@ public:
|
||||
PRUint32 chunkId,
|
||||
nsTArray<nsUrlClassifierEntry>& entry);
|
||||
|
||||
// Read the entries for a given host key from the database.
|
||||
nsresult ReadEntries(const nsUrlClassifierDomainHash& key,
|
||||
nsTArray<nsUrlClassifierEntry>& entry);
|
||||
|
||||
// Read the entry with a given ID from the database
|
||||
nsresult ReadEntry(PRInt64 id, nsUrlClassifierEntry& entry, PRBool *exists);
|
||||
|
||||
@ -769,6 +777,19 @@ nsUrlClassifierStore::ReadEntries(const nsUrlClassifierDomainHash& hash,
|
||||
return ReadEntries(mLookupWithChunkStatement, entries);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsUrlClassifierStore::ReadEntries(const nsUrlClassifierDomainHash& hash,
|
||||
nsTArray<nsUrlClassifierEntry>& entries)
|
||||
{
|
||||
mozStorageStatementScoper scoper(mLookupStatement);
|
||||
|
||||
nsresult rv = mLookupStatement->BindBlobParameter
|
||||
(0, hash.buf, DOMAIN_LENGTH);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return ReadEntries(mLookupStatement, entries);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsUrlClassifierStore::ReadEntry(PRInt64 id,
|
||||
nsUrlClassifierEntry& entry,
|
||||
@ -1017,6 +1038,10 @@ public:
|
||||
nsresult QueueLookup(const nsACString& lookupKey,
|
||||
nsIUrlClassifierLookupCallback* callback);
|
||||
|
||||
// Check if the key is on a known-clean host.
|
||||
nsresult CheckCleanHost(const nsACString &lookupKey,
|
||||
PRBool *clean);
|
||||
|
||||
// Handle any queued-up lookups. We call this function during long-running
|
||||
// update operations to prevent lookups from blocking for too long.
|
||||
nsresult HandlePendingLookups();
|
||||
@ -1135,7 +1160,7 @@ private:
|
||||
// expand it into the set of fragments that should be searched for in an
|
||||
// entry
|
||||
nsresult GetLookupFragments(const nsCSubstring& spec,
|
||||
nsTArray<nsUrlClassifierCompleteHash>& fragments);
|
||||
nsTArray<nsCString>& fragments);
|
||||
|
||||
// Check for a canonicalized IP address.
|
||||
PRBool IsCanonicalizedIP(const nsACString& host);
|
||||
@ -1147,10 +1172,18 @@ private:
|
||||
// www.mail.hostname.com/foo/bar -> mail.hostname.com
|
||||
nsresult GetKey(const nsACString& spec, nsUrlClassifierDomainHash& hash);
|
||||
|
||||
// Similar to GetKey(), but if the domain contains three or more components,
|
||||
// two keys will be returned:
|
||||
// hostname.com/foo/bar -> [hostname.com]
|
||||
// mail.hostname.com/foo/bar -> [hostname.com, mail.hostname.com]
|
||||
// www.mail.hostname.com/foo/bar -> [hostname.com, mail.hostname.com]
|
||||
nsresult GetHostKeys(const nsACString &spec,
|
||||
nsTArray<nsCString> &hostKeys);
|
||||
|
||||
// Look for a given lookup string (www.hostname.com/path/to/resource.html)
|
||||
// in the entries at the given key. Returns a list of entries that match.
|
||||
nsresult CheckKey(const nsCSubstring& spec,
|
||||
const nsUrlClassifierDomainHash& key,
|
||||
const nsACString& key,
|
||||
nsTArray<nsUrlClassifierLookupResult>& results);
|
||||
|
||||
// Perform a classifier lookup for a given url.
|
||||
@ -1248,6 +1281,24 @@ private:
|
||||
// The number of noise entries to add to the set of lookup results.
|
||||
PRInt32 mGethashNoise;
|
||||
|
||||
// We maintain an MRU cache of clean host keys (host keys with no
|
||||
// entry in the db).
|
||||
nsUrlClassifierFragmentSet mCleanHostKeys;
|
||||
|
||||
// The clean-host-key cache is updated in the worker thread, but
|
||||
// checked in the main thread (to avoid posting lookup requests if
|
||||
// not necessary).
|
||||
PRLock* mCleanHostKeysLock;
|
||||
|
||||
// We maintain an MRU cache of clean fragments (fragments with no
|
||||
// entry in the db).
|
||||
nsUrlClassifierFragmentSet mCleanFragments;
|
||||
|
||||
// The host keys from the last host to be checked for malware are
|
||||
// cached for quicker lookup next time through.
|
||||
nsCString mCachedHostKey;
|
||||
nsTArray<nsUrlClassifierEntry> mCachedEntries;
|
||||
|
||||
// Pending lookups are stored in a queue for processing. The queue
|
||||
// is protected by mPendingLookupLock.
|
||||
PRLock* mPendingLookupLock;
|
||||
@ -1285,6 +1336,7 @@ nsUrlClassifierDBServiceWorker::nsUrlClassifierDBServiceWorker()
|
||||
, mHaveCachedSubChunks(PR_FALSE)
|
||||
, mUpdateStartTime(0)
|
||||
, mGethashNoise(0)
|
||||
, mCleanHostKeysLock(nsnull)
|
||||
, mPendingLookupLock(nsnull)
|
||||
{
|
||||
}
|
||||
@ -1294,6 +1346,10 @@ nsUrlClassifierDBServiceWorker::~nsUrlClassifierDBServiceWorker()
|
||||
NS_ASSERTION(!mConnection,
|
||||
"Db connection not closed, leaking memory! Call CloseDb "
|
||||
"to close the connection.");
|
||||
|
||||
if (mCleanHostKeysLock)
|
||||
PR_DestroyLock(mCleanHostKeysLock);
|
||||
|
||||
if (mPendingLookupLock)
|
||||
PR_DestroyLock(mPendingLookupLock);
|
||||
}
|
||||
@ -1320,6 +1376,16 @@ nsUrlClassifierDBServiceWorker::Init(PRInt32 gethashNoise)
|
||||
rv = mDBFile->Append(NS_LITERAL_STRING(DATABASE_FILENAME));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mCleanHostKeysLock = PR_NewLock();
|
||||
if (!mCleanHostKeysLock)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
if (!mCleanHostKeys.Init(CLEAN_HOST_KEYS_SIZE))
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
if (!mCleanFragments.Init(CLEAN_FRAGMENTS_SIZE))
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
mPendingLookupLock = PR_NewLock();
|
||||
if (!mPendingLookupLock)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
@ -1346,9 +1412,30 @@ nsUrlClassifierDBServiceWorker::QueueLookup(const nsACString& spec,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsUrlClassifierDBServiceWorker::CheckCleanHost(const nsACString &spec,
|
||||
PRBool *clean)
|
||||
{
|
||||
nsAutoTArray<nsCString, 2> lookupHosts;
|
||||
nsresult rv = GetHostKeys(spec, lookupHosts);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsAutoLock lock(mCleanHostKeysLock);
|
||||
|
||||
for (PRUint32 i = 0; i < lookupHosts.Length(); i++) {
|
||||
if (!mCleanHostKeys.Has(lookupHosts[i])) {
|
||||
*clean = PR_FALSE;
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
*clean = PR_TRUE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsUrlClassifierDBServiceWorker::GetLookupFragments(const nsACString& spec,
|
||||
nsTArray<nsUrlClassifierCompleteHash>& fragments)
|
||||
nsTArray<nsCString>& fragments)
|
||||
{
|
||||
fragments.Clear();
|
||||
|
||||
@ -1430,15 +1517,13 @@ nsUrlClassifierDBServiceWorker::GetLookupFragments(const nsACString& spec,
|
||||
|
||||
for (int hostIndex = 0; hostIndex < hosts.Count(); hostIndex++) {
|
||||
for (int pathIndex = 0; pathIndex < paths.Count(); pathIndex++) {
|
||||
nsCAutoString key;
|
||||
nsCString key;
|
||||
key.Assign(*hosts[hostIndex]);
|
||||
key.Append('/');
|
||||
key.Append(*paths[pathIndex]);
|
||||
LOG(("Chking %s", key.get()));
|
||||
|
||||
nsUrlClassifierCompleteHash* hash = fragments.AppendElement();
|
||||
if (!hash) return NS_ERROR_OUT_OF_MEMORY;
|
||||
hash->FromPlaintext(key, mCryptoHash);
|
||||
fragments.AppendElement(key);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1447,36 +1532,57 @@ nsUrlClassifierDBServiceWorker::GetLookupFragments(const nsACString& spec,
|
||||
|
||||
nsresult
|
||||
nsUrlClassifierDBServiceWorker::CheckKey(const nsACString& spec,
|
||||
const nsUrlClassifierDomainHash& hash,
|
||||
const nsACString& hostKey,
|
||||
nsTArray<nsUrlClassifierLookupResult>& results)
|
||||
{
|
||||
mozStorageStatementScoper lookupScoper(mMainStore.LookupStatement());
|
||||
// First, if this key has been checked since our last update and had
|
||||
// no entries, we can exit early. We also do this check before
|
||||
// posting the lookup to this thread, but in case multiple lookups
|
||||
// are queued at the same time, it's worth checking again here.
|
||||
{
|
||||
nsAutoLock lock(mCleanHostKeysLock);
|
||||
if (mCleanHostKeys.Has(hostKey))
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult rv = mMainStore.LookupStatement()->BindBlobParameter
|
||||
(0, hash.buf, DOMAIN_LENGTH);
|
||||
// Now read host key entries from the db if necessary.
|
||||
if (hostKey != mCachedHostKey) {
|
||||
mCachedEntries.Clear();
|
||||
nsUrlClassifierDomainHash hostKeyHash;
|
||||
hostKeyHash.FromPlaintext(hostKey, mCryptoHash);
|
||||
mMainStore.ReadEntries(hostKeyHash, mCachedEntries);
|
||||
mCachedHostKey = hostKey;
|
||||
}
|
||||
|
||||
if (mCachedEntries.Length() == 0) {
|
||||
// There were no entries in the db for this host key. Go ahead
|
||||
// and mark the host key as clean to help short-circuit future
|
||||
// lookups.
|
||||
nsAutoLock lock(mCleanHostKeysLock);
|
||||
mCleanHostKeys.Put(hostKey);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Now get the set of fragments to look up.
|
||||
nsTArray<nsCString> fragments;
|
||||
nsresult rv = GetLookupFragments(spec, fragments);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsTArray<nsUrlClassifierCompleteHash> fragments;
|
||||
PRBool haveFragments = PR_FALSE;
|
||||
PRInt64 now = (PR_Now() / PR_USEC_PER_SEC);
|
||||
|
||||
PRBool exists;
|
||||
rv = mMainStore.LookupStatement()->ExecuteStep(&exists);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
while (exists) {
|
||||
if (!haveFragments) {
|
||||
rv = GetLookupFragments(spec, fragments);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
haveFragments = PR_TRUE;
|
||||
}
|
||||
// Now check each lookup fragment against the entries in the DB.
|
||||
for (PRUint32 i = 0; i < fragments.Length(); i++) {
|
||||
// If this fragment has been previously checked, ignore it.
|
||||
if (mCleanFragments.Has(fragments[i]))
|
||||
continue;
|
||||
|
||||
nsUrlClassifierEntry entry;
|
||||
if (!mMainStore.ReadStatement(mMainStore.LookupStatement(), entry))
|
||||
return NS_ERROR_FAILURE;
|
||||
nsUrlClassifierCompleteHash lookupHash;
|
||||
lookupHash.FromPlaintext(fragments[i], mCryptoHash);
|
||||
|
||||
PRInt64 now = (PR_Now() / PR_USEC_PER_SEC);
|
||||
|
||||
for (PRUint32 i = 0; i < fragments.Length(); i++) {
|
||||
if (entry.Match(fragments[i])) {
|
||||
PRBool foundMatch = PR_FALSE;
|
||||
for (PRUint32 j = 0; j < mCachedEntries.Length(); j++) {
|
||||
nsUrlClassifierEntry &entry = mCachedEntries[j];
|
||||
if (entry.Match(lookupHash)) {
|
||||
// If the entry doesn't contain a complete hash, we need to
|
||||
// save it here so that it can be compared against the
|
||||
// complete hash. However, we don't set entry.mHaveComplete
|
||||
@ -1485,7 +1591,7 @@ nsUrlClassifierDBServiceWorker::CheckKey(const nsACString& spec,
|
||||
if (!result)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
result->mLookupFragment = fragments[i];
|
||||
result->mLookupFragment = lookupHash;
|
||||
result->mEntry = entry;
|
||||
|
||||
// Fill in the table name.
|
||||
@ -1506,14 +1612,17 @@ nsUrlClassifierDBServiceWorker::CheckKey(const nsACString& spec,
|
||||
// an up-to-date table.
|
||||
result->mConfirmed = entry.mHaveComplete && fresh;
|
||||
|
||||
foundMatch = PR_TRUE;
|
||||
LOG(("Found a result. complete=%d, fresh=%d",
|
||||
entry.mHaveComplete, fresh));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
rv = mMainStore.LookupStatement()->ExecuteStep(&exists);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (!foundMatch) {
|
||||
// This fragment is clean, we don't need to bother checking it
|
||||
// again until the next update.
|
||||
mCleanFragments.Put(fragments[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
@ -1552,17 +1661,6 @@ nsUrlClassifierDBServiceWorker::DoLookup(const nsACString& spec,
|
||||
}
|
||||
#endif
|
||||
|
||||
nsACString::const_iterator begin, end, iter;
|
||||
spec.BeginReading(begin);
|
||||
spec.EndReading(end);
|
||||
|
||||
iter = begin;
|
||||
if (!FindCharInReadable('/', iter, end)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
const nsCSubstring& host = Substring(begin, iter++);
|
||||
|
||||
nsAutoPtr<nsTArray<nsUrlClassifierLookupResult> > results;
|
||||
results = new nsTArray<nsUrlClassifierLookupResult>();
|
||||
if (!results) {
|
||||
@ -1570,48 +1668,13 @@ nsUrlClassifierDBServiceWorker::DoLookup(const nsACString& spec,
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
nsUrlClassifierDomainHash hash;
|
||||
nsAutoTArray<nsCString, 2> lookupHosts;
|
||||
rv = GetHostKeys(spec, lookupHosts);
|
||||
|
||||
if (IsCanonicalizedIP(host)) {
|
||||
// Don't break up the host into components
|
||||
nsCAutoString lookupHost;
|
||||
lookupHost.Assign(host);
|
||||
lookupHost.Append("/");
|
||||
hash.FromPlaintext(lookupHost, mCryptoHash);
|
||||
CheckKey(spec, hash, *results);
|
||||
} else {
|
||||
nsCStringArray hostComponents;
|
||||
hostComponents.ParseString(PromiseFlatCString(host).get(), ".");
|
||||
|
||||
if (hostComponents.Count() < 2) {
|
||||
// no host or toplevel host, this won't match anything in the db
|
||||
c->LookupComplete(nsnull);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// First check with two domain components
|
||||
PRInt32 last = hostComponents.Count() - 1;
|
||||
nsCAutoString lookupHost;
|
||||
lookupHost.Assign(*hostComponents[last - 1]);
|
||||
lookupHost.Append(".");
|
||||
lookupHost.Append(*hostComponents[last]);
|
||||
lookupHost.Append("/");
|
||||
hash.FromPlaintext(lookupHost, mCryptoHash);
|
||||
|
||||
// we ignore failures from CheckKey because we'd rather try to find
|
||||
// more results than fail.
|
||||
CheckKey(spec, hash, *results);
|
||||
|
||||
// Now check with three domain components
|
||||
if (hostComponents.Count() > 2) {
|
||||
nsCAutoString lookupHost2;
|
||||
lookupHost2.Assign(*hostComponents[last - 2]);
|
||||
lookupHost2.Append(".");
|
||||
lookupHost2.Append(lookupHost);
|
||||
hash.FromPlaintext(lookupHost2, mCryptoHash);
|
||||
|
||||
CheckKey(spec, hash, *results);
|
||||
}
|
||||
for (PRUint32 i = 0; i < lookupHosts.Length(); i++) {
|
||||
// we ignore failures from CheckKey because we'd rather try to
|
||||
// find more results than fail.
|
||||
CheckKey(spec, lookupHosts[i], *results);
|
||||
}
|
||||
|
||||
#if defined(PR_LOGGING)
|
||||
@ -1981,6 +2044,63 @@ nsUrlClassifierDBServiceWorker::GetKey(const nsACString& spec,
|
||||
return hash.FromPlaintext(lookupHost, mCryptoHash);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsUrlClassifierDBServiceWorker::GetHostKeys(const nsACString &spec,
|
||||
nsTArray<nsCString> &hostKeys)
|
||||
{
|
||||
nsACString::const_iterator begin, end, iter;
|
||||
spec.BeginReading(begin);
|
||||
spec.EndReading(end);
|
||||
|
||||
iter = begin;
|
||||
if (!FindCharInReadable('/', iter, end)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
const nsCSubstring& host = Substring(begin, iter);
|
||||
|
||||
if (IsCanonicalizedIP(host)) {
|
||||
nsCString *key = hostKeys.AppendElement();
|
||||
if (!key)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
key->Assign(host);
|
||||
key->Append("/");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCStringArray hostComponents;
|
||||
hostComponents.ParseString(PromiseFlatCString(host).get(), ".");
|
||||
|
||||
if (hostComponents.Count() < 2) {
|
||||
// no host or toplevel host, this won't match anything in the db
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// First check with two domain components
|
||||
PRInt32 last = hostComponents.Count() - 1;
|
||||
nsCString *lookupHost = hostKeys.AppendElement();
|
||||
if (!lookupHost)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
lookupHost->Assign(*hostComponents[last - 1]);
|
||||
lookupHost->Append(".");
|
||||
lookupHost->Append(*hostComponents[last]);
|
||||
lookupHost->Append("/");
|
||||
|
||||
// Now check with three domain components
|
||||
if (hostComponents.Count() > 2) {
|
||||
nsCString *lookupHost2 = hostKeys.AppendElement();
|
||||
if (!lookupHost2)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
lookupHost2->Assign(*hostComponents[last - 2]);
|
||||
lookupHost2->Append(".");
|
||||
lookupHost2->Append(*lookupHost);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsUrlClassifierDBServiceWorker::GetShaEntries(PRUint32 tableId,
|
||||
PRUint32 chunkType,
|
||||
@ -3035,6 +3155,18 @@ nsUrlClassifierDBServiceWorker::ApplyUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(mUpdateStatus)) {
|
||||
// We have modified the db, we can't trust the set of clean
|
||||
// fragments or domains anymore.
|
||||
mCachedHostKey.Truncate();
|
||||
mCachedEntries.Clear();
|
||||
|
||||
mCleanFragments.Clear();
|
||||
|
||||
nsAutoLock lock(mCleanHostKeysLock);
|
||||
mCleanHostKeys.Clear();
|
||||
}
|
||||
|
||||
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
|
||||
@ -3190,6 +3322,11 @@ nsUrlClassifierDBServiceWorker::CacheCompletions(nsTArray<nsUrlClassifierLookupR
|
||||
mMainStore.UpdateEntry(result.mEntry);
|
||||
}
|
||||
|
||||
// Completions change entries in the DB, the cached set of entries is
|
||||
// no longer valid.
|
||||
mCachedHostKey.Truncate();
|
||||
mCachedEntries.Clear();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -3808,15 +3945,14 @@ nsUrlClassifierDBService::Classify(nsIURI *uri,
|
||||
new nsUrlClassifierClassifyCallback(c, mCheckMalware, mCheckPhishing);
|
||||
if (!callback) return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
nsresult rv = LookupURI(uri, callback);
|
||||
nsresult rv = LookupURI(uri, callback, PR_FALSE, result);
|
||||
if (rv == NS_ERROR_MALFORMED_URI) {
|
||||
// The URI had no hostname, don't try to classify it.
|
||||
*result = PR_FALSE;
|
||||
// The URI had no hostname, don't try to classify it.
|
||||
return NS_OK;
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
*result = PR_TRUE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -3836,12 +3972,15 @@ nsUrlClassifierDBService::Lookup(const nsACString& spec,
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return LookupURI(uri, c);
|
||||
PRBool didLookup;
|
||||
return LookupURI(uri, c, PR_TRUE, &didLookup);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsUrlClassifierDBService::LookupURI(nsIURI* uri,
|
||||
nsIUrlClassifierCallback* c)
|
||||
nsIUrlClassifierCallback* c,
|
||||
PRBool forceLookup,
|
||||
PRBool *didLookup)
|
||||
{
|
||||
NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
@ -3853,6 +3992,21 @@ nsUrlClassifierDBService::LookupURI(nsIURI* uri,
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
if (forceLookup) {
|
||||
*didLookup = PR_TRUE;
|
||||
} else {
|
||||
// Check if the URI is on a clean host. If so, we don't need to
|
||||
// bother queueing up a lookup, we can just return.
|
||||
PRBool clean;
|
||||
rv = mWorker->CheckCleanHost(key, &clean);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
*didLookup = !clean;
|
||||
if (clean) {
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// Create an nsUrlClassifierLookupCallback object. This object will
|
||||
// take care of confirming partial hash matches if necessary before
|
||||
// calling the client's callback.
|
||||
|
@ -93,7 +93,8 @@ private:
|
||||
// Disallow copy constructor
|
||||
nsUrlClassifierDBService(nsUrlClassifierDBService&);
|
||||
|
||||
nsresult LookupURI(nsIURI* uri, nsIUrlClassifierCallback* c);
|
||||
nsresult LookupURI(nsIURI* uri, nsIUrlClassifierCallback* c,
|
||||
PRBool forceCheck, PRBool *didCheck);
|
||||
|
||||
// Close db connection and join the background thread if it exists.
|
||||
nsresult Shutdown();
|
||||
|
@ -39,6 +39,8 @@
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsIUrlClassifierUtils.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsDataHashtable.h"
|
||||
|
||||
class nsUrlClassifierUtils : public nsIUrlClassifierUtils
|
||||
{
|
||||
@ -122,4 +124,103 @@ private:
|
||||
nsAutoPtr<Charmap> mEscapeCharmap;
|
||||
};
|
||||
|
||||
// An MRU list of fragments. This is used by the DB service to
|
||||
// keep a set of known-clean fragments that don't need a database
|
||||
// lookup.
|
||||
class nsUrlClassifierFragmentSet
|
||||
{
|
||||
public:
|
||||
nsUrlClassifierFragmentSet() : mFirst(nsnull), mLast(nsnull) {}
|
||||
|
||||
PRBool Init(PRUint32 maxEntries) {
|
||||
if (!mEntryStorage.SetCapacity(maxEntries))
|
||||
return PR_FALSE;
|
||||
|
||||
if (!mEntries.Init())
|
||||
return PR_FALSE;
|
||||
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
PRBool Put(const nsACString &fragment) {
|
||||
Entry *entry;
|
||||
if (mEntries.Get(fragment, &entry)) {
|
||||
// Remove this entry from the list, we'll add it back
|
||||
// to the front.
|
||||
UnlinkEntry(entry);
|
||||
} else {
|
||||
if (mEntryStorage.Length() < mEntryStorage.Capacity()) {
|
||||
entry = mEntryStorage.AppendElement();
|
||||
if (!entry)
|
||||
return PR_FALSE;
|
||||
} else {
|
||||
// Reuse the oldest entry.
|
||||
entry = mLast;
|
||||
UnlinkEntry(entry);
|
||||
mEntries.Remove(entry->mFragment);
|
||||
}
|
||||
entry->mFragment = fragment;
|
||||
mEntries.Put(fragment, entry);
|
||||
}
|
||||
|
||||
// Add the entry to the front of the list
|
||||
entry->mPrev = nsnull;
|
||||
entry->mNext = mFirst;
|
||||
mFirst = entry;
|
||||
if (!mLast) {
|
||||
mLast = entry;
|
||||
}
|
||||
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
PRBool Has(const nsACString &fragment) {
|
||||
return mEntries.Get(fragment, nsnull);
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
mFirst = mLast = nsnull;
|
||||
mEntries.Clear();
|
||||
}
|
||||
|
||||
private:
|
||||
// One entry in the set. We maintain a doubly-linked list, with
|
||||
// the most recently used entry at the front.
|
||||
class Entry {
|
||||
public:
|
||||
Entry() : mNext(nsnull), mPrev(nsnull) {};
|
||||
~Entry() { }
|
||||
|
||||
Entry *mNext;
|
||||
Entry *mPrev;
|
||||
nsCString mFragment;
|
||||
};
|
||||
|
||||
void UnlinkEntry(Entry *entry)
|
||||
{
|
||||
if (entry->mPrev)
|
||||
entry->mPrev->mNext = entry->mNext;
|
||||
else
|
||||
mFirst = entry->mNext;
|
||||
|
||||
if (entry->mNext)
|
||||
entry->mNext->mPrev = entry->mPrev;
|
||||
else
|
||||
mLast = entry->mPrev;
|
||||
|
||||
entry->mPrev = entry->mNext = nsnull;
|
||||
}
|
||||
|
||||
// The newest entry in the cache.
|
||||
Entry *mFirst;
|
||||
// The oldest entry in the cache.
|
||||
Entry *mLast;
|
||||
|
||||
// Storage for the entries in this set.
|
||||
nsTArray<Entry> mEntryStorage;
|
||||
|
||||
// Entry lookup by fragment.
|
||||
nsDataHashtable<nsCStringHashKey, Entry*> mEntries;
|
||||
};
|
||||
|
||||
#endif // nsUrlClassifierUtils_h_
|
||||
|
@ -0,0 +1,117 @@
|
||||
// Test an add of two urls to a fresh database
|
||||
function testCleanHostKeys() {
|
||||
var addUrls = [ "foo.com/a" ];
|
||||
var update = buildPhishingUpdate(
|
||||
[
|
||||
{ "chunkNum" : 1,
|
||||
"urls" : addUrls
|
||||
}]);
|
||||
|
||||
doStreamUpdate(update, function() {
|
||||
var ios = Components.classes["@mozilla.org/network/io-service;1"].
|
||||
getService(Components.interfaces.nsIIOService);
|
||||
|
||||
// Check with a clean host key
|
||||
var uri = ios.newURI("http://bar.com/a", null, null);
|
||||
|
||||
// Use the nsIURIClassifier interface (the
|
||||
// nsIUrlClassifierDBService will always queue a lookup,
|
||||
// nsIURIClassifier won't if the host key is known to be clean.
|
||||
var classifier = dbservice.QueryInterface(Ci.nsIURIClassifier);
|
||||
var result = classifier.classify(uri, function(errorCode) {
|
||||
var result2 = classifier.classify(uri, function() {
|
||||
do_throw("shouldn't get a callback");
|
||||
});
|
||||
// second call shouldn't result in a callback.
|
||||
do_check_eq(result2, false);
|
||||
|
||||
runNextTest();
|
||||
});
|
||||
|
||||
// The first classifier call should result in a callback.
|
||||
do_check_eq(result, true);
|
||||
}, updateError);
|
||||
}
|
||||
|
||||
// Test an add of two urls to a fresh database
|
||||
function testDirtyHostKeys() {
|
||||
var addUrls = [ "foo.com/a" ];
|
||||
var update = buildPhishingUpdate(
|
||||
[
|
||||
{ "chunkNum" : 1,
|
||||
"urls" : addUrls
|
||||
}]);
|
||||
|
||||
doStreamUpdate(update, function() {
|
||||
var ios = Components.classes["@mozilla.org/network/io-service;1"].
|
||||
getService(Components.interfaces.nsIIOService);
|
||||
|
||||
// Check with a dirty host key - both checks should happen.
|
||||
var uri = ios.newURI("http://foo.com/b", null, null);
|
||||
|
||||
// Use the nsIURIClassifier interface (the
|
||||
// nsIUrlClassifierDBService will always queue a lookup,
|
||||
// nsIURIClassifier won't if the host key is known to be clean.
|
||||
var classifier = dbservice.QueryInterface(Ci.nsIURIClassifier);
|
||||
var result = classifier.classify(uri, function(errorCode) {
|
||||
var result2 = classifier.classify(uri, function() {
|
||||
runNextTest();
|
||||
});
|
||||
// second call should result in a callback.
|
||||
do_check_eq(result2, true);
|
||||
});
|
||||
|
||||
// The first classifier call should result in a callback.
|
||||
do_check_eq(result, true);
|
||||
}, updateError);
|
||||
}
|
||||
|
||||
// Make sure that an update properly clears the host key cache
|
||||
function testUpdate() {
|
||||
var ios = Components.classes["@mozilla.org/network/io-service;1"].
|
||||
getService(Components.interfaces.nsIIOService);
|
||||
|
||||
// First lookup should happen...
|
||||
var uri = ios.newURI("http://foo.com/a", null, null);
|
||||
|
||||
// Use the nsIURIClassifier interface (the
|
||||
// nsIUrlClassifierDBService will always queue a lookup,
|
||||
// nsIURIClassifier won't if the host key is known to be clean.
|
||||
var classifier = dbservice.QueryInterface(Ci.nsIURIClassifier);
|
||||
var result = classifier.classify(uri, function(errorCode) {
|
||||
// This check will succeed, which will cause the key to
|
||||
// be put in the clean host key cache...
|
||||
do_check_eq(errorCode, Cr.NS_OK);
|
||||
|
||||
// Now add the url to the db...
|
||||
var addUrls = [ "foo.com/a" ];
|
||||
var update = buildPhishingUpdate(
|
||||
[
|
||||
{ "chunkNum" : 1,
|
||||
"urls" : addUrls
|
||||
}]);
|
||||
doStreamUpdate(update, function() {
|
||||
// The clean key cache should be blown now that we've
|
||||
// added, and this callback should execute.
|
||||
var result2 = classifier.classify(uri, function(errorCode) {
|
||||
do_check_neq(errorCode, Cr.NS_OK);
|
||||
runNextTest();
|
||||
});
|
||||
// second call should result in a callback.
|
||||
do_check_eq(result2, true);
|
||||
}, updateError);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
function run_test()
|
||||
{
|
||||
runTests([
|
||||
testCleanHostKeys,
|
||||
testDirtyHostKeys,
|
||||
testUpdate,
|
||||
]);
|
||||
}
|
||||
|
||||
do_test_pending();
|
Loading…
Reference in New Issue
Block a user