1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include "Common.h"
7
8 #define EXPIRED_TIME_SEC (PR_Now() / PR_USEC_PER_SEC - 3600)
9 #define NOTEXPIRED_TIME_SEC (PR_Now() / PR_USEC_PER_SEC + 3600)
10
11 #define CACHED_URL "cache.com/"_ns
12 #define NEG_CACHE_EXPIRED_URL "cache.negExpired.com/"_ns
13 #define POS_CACHE_EXPIRED_URL "cache.posExpired.com/"_ns
14 #define BOTH_CACHE_EXPIRED_URL "cache.negAndposExpired.com/"_ns
15
SetupCacheEntry(LookupCacheV2 * aLookupCache,const nsCString & aCompletion,bool aNegExpired=false,bool aPosExpired=false)16 static void SetupCacheEntry(LookupCacheV2* aLookupCache,
17 const nsCString& aCompletion,
18 bool aNegExpired = false,
19 bool aPosExpired = false) {
20 AddCompleteArray completes;
21 AddCompleteArray emptyCompletes;
22 MissPrefixArray misses;
23 MissPrefixArray emptyMisses;
24
25 AddComplete* add = completes.AppendElement(fallible);
26 add->complete.FromPlaintext(aCompletion);
27
28 Prefix* prefix = misses.AppendElement(fallible);
29 prefix->FromPlaintext(aCompletion);
30
31 // Setup positive cache first otherwise negative cache expiry will be
32 // overwritten.
33 int64_t posExpirySec = aPosExpired ? EXPIRED_TIME_SEC : NOTEXPIRED_TIME_SEC;
34 aLookupCache->AddGethashResultToCache(completes, emptyMisses, posExpirySec);
35
36 int64_t negExpirySec = aNegExpired ? EXPIRED_TIME_SEC : NOTEXPIRED_TIME_SEC;
37 aLookupCache->AddGethashResultToCache(emptyCompletes, misses, negExpirySec);
38 }
39
SetupCacheEntry(LookupCacheV4 * aLookupCache,const nsCString & aCompletion,bool aNegExpired=false,bool aPosExpired=false)40 static void SetupCacheEntry(LookupCacheV4* aLookupCache,
41 const nsCString& aCompletion,
42 bool aNegExpired = false,
43 bool aPosExpired = false) {
44 FullHashResponseMap map;
45
46 Prefix prefix;
47 prefix.FromPlaintext(aCompletion);
48
49 CachedFullHashResponse* response = map.GetOrInsertNew(prefix.ToUint32());
50
51 response->negativeCacheExpirySec =
52 aNegExpired ? EXPIRED_TIME_SEC : NOTEXPIRED_TIME_SEC;
53 response->fullHashes.InsertOrUpdate(
54 CreatePrefixFromURL(aCompletion, COMPLETE_SIZE),
55 aPosExpired ? EXPIRED_TIME_SEC : NOTEXPIRED_TIME_SEC);
56
57 aLookupCache->AddFullHashResponseToCache(map);
58 }
59
60 template <typename T>
TestCache(const Completion aCompletion,bool aExpectedHas,bool aExpectedConfirmed,bool aExpectedInCache,T * aCache=nullptr)61 static void TestCache(const Completion aCompletion, bool aExpectedHas,
62 bool aExpectedConfirmed, bool aExpectedInCache,
63 T* aCache = nullptr) {
64 bool has, inCache, confirmed;
65 uint32_t matchLength;
66
67 if (aCache) {
68 aCache->Has(aCompletion, &has, &matchLength, &confirmed);
69 inCache = aCache->IsInCache(aCompletion.ToUint32());
70 } else {
71 _PrefixArray array = {CreatePrefixFromURL("cache.notexpired.com/", 10),
72 CreatePrefixFromURL("cache.expired.com/", 8),
73 CreatePrefixFromURL("gound.com/", 5),
74 CreatePrefixFromURL("small.com/", 4)};
75
76 RefPtr<T> cache = SetupLookupCache<T>(array);
77
78 // Create an expired entry and a non-expired entry
79 SetupCacheEntry(cache, "cache.notexpired.com/"_ns);
80 SetupCacheEntry(cache, "cache.expired.com/"_ns, true, true);
81
82 cache->Has(aCompletion, &has, &matchLength, &confirmed);
83 inCache = cache->IsInCache(aCompletion.ToUint32());
84 }
85
86 EXPECT_EQ(has, aExpectedHas);
87 EXPECT_EQ(confirmed, aExpectedConfirmed);
88 EXPECT_EQ(inCache, aExpectedInCache);
89 }
90
91 template <typename T>
TestCache(const nsCString & aURL,bool aExpectedHas,bool aExpectedConfirmed,bool aExpectedInCache,T * aCache=nullptr)92 static void TestCache(const nsCString& aURL, bool aExpectedHas,
93 bool aExpectedConfirmed, bool aExpectedInCache,
94 T* aCache = nullptr) {
95 Completion lookupHash;
96 lookupHash.FromPlaintext(aURL);
97
98 TestCache<T>(lookupHash, aExpectedHas, aExpectedConfirmed, aExpectedInCache,
99 aCache);
100 }
101
102 // This testcase check the returned result of |Has| API if fullhash cannot match
103 // any prefix in the local database.
TEST(UrlClassifierCaching,NotFound)104 TEST(UrlClassifierCaching, NotFound)
105 {
106 TestCache<LookupCacheV2>("nomatch.com/"_ns, false, false, false);
107 TestCache<LookupCacheV4>("nomatch.com/"_ns, false, false, false);
108 }
109
110 // This testcase check the returned result of |Has| API if fullhash find a match
111 // in the local database but not in the cache.
TEST(UrlClassifierCaching,NotInCache)112 TEST(UrlClassifierCaching, NotInCache)
113 {
114 TestCache<LookupCacheV2>("gound.com/"_ns, true, false, false);
115 TestCache<LookupCacheV4>("gound.com/"_ns, true, false, false);
116 }
117
118 // This testcase check the returned result of |Has| API if fullhash matches
119 // a cache entry in positive cache.
TEST(UrlClassifierCaching,InPositiveCacheNotExpired)120 TEST(UrlClassifierCaching, InPositiveCacheNotExpired)
121 {
122 TestCache<LookupCacheV2>("cache.notexpired.com/"_ns, true, true, true);
123 TestCache<LookupCacheV4>("cache.notexpired.com/"_ns, true, true, true);
124 }
125
126 // This testcase check the returned result of |Has| API if fullhash matches
127 // a cache entry in positive cache but that it is expired.
TEST(UrlClassifierCaching,InPositiveCacheExpired)128 TEST(UrlClassifierCaching, InPositiveCacheExpired)
129 {
130 TestCache<LookupCacheV2>("cache.expired.com/"_ns, true, false, true);
131 TestCache<LookupCacheV4>("cache.expired.com/"_ns, true, false, true);
132 }
133
134 // This testcase check the returned result of |Has| API if fullhash matches
135 // a cache entry in negative cache.
TEST(UrlClassifierCaching,InNegativeCacheNotExpired)136 TEST(UrlClassifierCaching, InNegativeCacheNotExpired)
137 {
138 // Create a fullhash whose prefix matches the prefix in negative cache
139 // but completion doesn't match any fullhash in positive cache.
140
141 Completion prefix;
142 prefix.FromPlaintext("cache.notexpired.com/"_ns);
143
144 Completion fullhash;
145 fullhash.FromPlaintext("firefox.com/"_ns);
146
147 // Overwrite the 4-byte prefix of `fullhash` so that it conflicts with
148 // `prefix`. Since "cache.notexpired.com" is added to database in TestCache as
149 // a 10-byte prefix, we should copy more than 10 bytes to fullhash to ensure
150 // it can match the prefix in database.
151 memcpy(fullhash.buf, prefix.buf, 10);
152
153 TestCache<LookupCacheV2>(fullhash, false, false, true);
154 TestCache<LookupCacheV4>(fullhash, false, false, true);
155 }
156
157 // This testcase check the returned result of |Has| API if fullhash matches
158 // a cache entry in negative cache but that entry is expired.
TEST(UrlClassifierCaching,InNegativeCacheExpired)159 TEST(UrlClassifierCaching, InNegativeCacheExpired)
160 {
161 // Create a fullhash whose prefix is in the cache.
162
163 Completion prefix;
164 prefix.FromPlaintext("cache.expired.com/"_ns);
165
166 Completion fullhash;
167 fullhash.FromPlaintext("firefox.com/"_ns);
168
169 memcpy(fullhash.buf, prefix.buf, 10);
170
171 TestCache<LookupCacheV2>(fullhash, true, false, true);
172 TestCache<LookupCacheV4>(fullhash, true, false, true);
173 }
174
175 // This testcase create 4 cache entries.
176 // 1. unexpired entry.
177 // 2. an entry whose negative cache time is expired but whose positive cache
178 // is not expired.
179 // 3. an entry whose positive cache time is expired
180 // 4. an entry whose negative cache time and positive cache time are expired
181 // After calling |InvalidateExpiredCacheEntry| API, entries with expired
182 // negative time should be removed from cache(2 & 4)
183 template <typename T>
TestInvalidateExpiredCacheEntry()184 void TestInvalidateExpiredCacheEntry() {
185 _PrefixArray array = {CreatePrefixFromURL(CACHED_URL, 10),
186 CreatePrefixFromURL(NEG_CACHE_EXPIRED_URL, 8),
187 CreatePrefixFromURL(POS_CACHE_EXPIRED_URL, 5),
188 CreatePrefixFromURL(BOTH_CACHE_EXPIRED_URL, 4)};
189 RefPtr<T> cache = SetupLookupCache<T>(array);
190
191 SetupCacheEntry(cache, CACHED_URL, false, false);
192 SetupCacheEntry(cache, NEG_CACHE_EXPIRED_URL, true, false);
193 SetupCacheEntry(cache, POS_CACHE_EXPIRED_URL, false, true);
194 SetupCacheEntry(cache, BOTH_CACHE_EXPIRED_URL, true, true);
195
196 // Before invalidate
197 TestCache<T>(CACHED_URL, true, true, true, cache.get());
198 TestCache<T>(NEG_CACHE_EXPIRED_URL, true, true, true, cache.get());
199 TestCache<T>(POS_CACHE_EXPIRED_URL, true, false, true, cache.get());
200 TestCache<T>(BOTH_CACHE_EXPIRED_URL, true, false, true, cache.get());
201
202 // Call InvalidateExpiredCacheEntry to remove cache entries whose negative
203 // cache time is expired
204 cache->InvalidateExpiredCacheEntries();
205
206 // After invalidate, NEG_CACHE_EXPIRED_URL & BOTH_CACHE_EXPIRED_URL should
207 // not be found in cache.
208 TestCache<T>(NEG_CACHE_EXPIRED_URL, true, false, false, cache.get());
209 TestCache<T>(BOTH_CACHE_EXPIRED_URL, true, false, false, cache.get());
210
211 // Other entries should remain the same result.
212 TestCache<T>(CACHED_URL, true, true, true, cache.get());
213 TestCache<T>(POS_CACHE_EXPIRED_URL, true, false, true, cache.get());
214 }
215
TEST(UrlClassifierCaching,InvalidateExpiredCacheEntryV2)216 TEST(UrlClassifierCaching, InvalidateExpiredCacheEntryV2)
217 { TestInvalidateExpiredCacheEntry<LookupCacheV2>(); }
218
TEST(UrlClassifierCaching,InvalidateExpiredCacheEntryV4)219 TEST(UrlClassifierCaching, InvalidateExpiredCacheEntryV4)
220 { TestInvalidateExpiredCacheEntry<LookupCacheV4>(); }
221
222 // This testcase check if an cache entry whose negative cache time is expired
223 // and it doesn't have any postive cache entries in it, it should be removed
224 // from cache after calling |Has|.
TEST(UrlClassifierCaching,NegativeCacheExpireV2)225 TEST(UrlClassifierCaching, NegativeCacheExpireV2)
226 {
227 _PrefixArray array = {CreatePrefixFromURL(NEG_CACHE_EXPIRED_URL, 8)};
228 RefPtr<LookupCacheV2> cache = SetupLookupCache<LookupCacheV2>(array);
229
230 nsCOMPtr<nsICryptoHash> cryptoHash =
231 do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID);
232
233 MissPrefixArray misses;
234 Prefix* prefix = misses.AppendElement(fallible);
235 prefix->FromPlaintext(NEG_CACHE_EXPIRED_URL);
236
237 AddCompleteArray dummy;
238 cache->AddGethashResultToCache(dummy, misses, EXPIRED_TIME_SEC);
239
240 // Ensure it is in cache in the first place.
241 EXPECT_EQ(cache->IsInCache(prefix->ToUint32()), true);
242
243 // It should be removed after calling Has API.
244 TestCache<LookupCacheV2>(NEG_CACHE_EXPIRED_URL, true, false, false,
245 cache.get());
246 }
247
TEST(UrlClassifierCaching,NegativeCacheExpireV4)248 TEST(UrlClassifierCaching, NegativeCacheExpireV4)
249 {
250 _PrefixArray array = {CreatePrefixFromURL(NEG_CACHE_EXPIRED_URL, 8)};
251 RefPtr<LookupCacheV4> cache = SetupLookupCache<LookupCacheV4>(array);
252
253 FullHashResponseMap map;
254 Prefix prefix;
255 nsCOMPtr<nsICryptoHash> cryptoHash =
256 do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID);
257 prefix.FromPlaintext(NEG_CACHE_EXPIRED_URL);
258 CachedFullHashResponse* response = map.GetOrInsertNew(prefix.ToUint32());
259
260 response->negativeCacheExpirySec = EXPIRED_TIME_SEC;
261
262 cache->AddFullHashResponseToCache(map);
263
264 // Ensure it is in cache in the first place.
265 EXPECT_EQ(cache->IsInCache(prefix.ToUint32()), true);
266
267 // It should be removed after calling Has API.
268 TestCache<LookupCacheV4>(NEG_CACHE_EXPIRED_URL, true, false, false,
269 cache.get());
270 }
271