Bug 453723: Short-circuit known-clean hosts in the url-classifier. r=tony

This commit is contained in:
Dave Camp 2008-09-29 16:18:21 -07:00
parent 0ca49699b8
commit 670a9b8f96
5 changed files with 476 additions and 91 deletions

View File

@ -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();
@ -1131,11 +1156,14 @@ private:
// Reset the in-progress update
void ResetUpdate();
// Reset the set of clean host keys and cached lookups.
void ResetLookupCache();
// take a lookup string (www.hostname.com/path/to/resource.html) and
// 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 +1175,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 +1284,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 +1339,7 @@ nsUrlClassifierDBServiceWorker::nsUrlClassifierDBServiceWorker()
, mHaveCachedSubChunks(PR_FALSE)
, mUpdateStartTime(0)
, mGethashNoise(0)
, mCleanHostKeysLock(nsnull)
, mPendingLookupLock(nsnull)
{
}
@ -1294,6 +1349,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 +1379,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 +1415,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 +1520,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 +1535,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 +1594,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 +1615,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 +1664,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 +1671,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 +2047,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,
@ -2749,6 +2872,18 @@ nsUrlClassifierDBServiceWorker::ResetUpdate()
mUpdateTables.Clear();
}
void
nsUrlClassifierDBServiceWorker::ResetLookupCache()
{
mCachedHostKey.Truncate();
mCachedEntries.Clear();
mCleanFragments.Clear();
nsAutoLock lock(mCleanHostKeysLock);
mCleanHostKeys.Clear();
}
NS_IMETHODIMP
nsUrlClassifierDBServiceWorker::SetHashCompleter(const nsACString &tableName,
nsIUrlClassifierHashCompleter *completer)
@ -3035,6 +3170,12 @@ nsUrlClassifierDBServiceWorker::ApplyUpdate()
}
}
if (NS_SUCCEEDED(mUpdateStatus)) {
// We have modified the db, we can't trust the set of clean
// fragments or domains anymore.
ResetLookupCache();
}
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
@ -3109,6 +3250,7 @@ nsUrlClassifierDBServiceWorker::ResetDatabase()
ClearCachedChunkLists();
mTableFreshness.Clear();
ResetLookupCache();
nsresult rv = CloseDb();
NS_ENSURE_SUCCESS(rv, rv);
@ -3190,6 +3332,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 +3955,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 +3982,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 +4002,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.

View File

@ -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();

View File

@ -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_

View File

@ -46,6 +46,10 @@ var prefBranch = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefBranch);
prefBranch.setIntPref("urlclassifier.gethashnoise", 0);
// Enable malware/phishign checking for tests
prefBranch.setBoolPref("browser.safebrowsing.malware.enabled", true);
prefBranch.setBoolPref("browser.safebrowsing.enabled", true);
function cleanUp() {
try {
// Delete a previously created sqlite file

View File

@ -0,0 +1,115 @@
// 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();