Dictionary.h (13500B)
1 /* vim: set ts=2 sts=2 et sw=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 #ifndef mozilla_net_Dictionary_h 7 #define mozilla_net_Dictionary_h 8 9 #include "nsCOMPtr.h" 10 #include "nsICacheEntry.h" 11 #include "nsICacheEntryOpenCallback.h" 12 #include "nsICacheStorageService.h" 13 #include "nsICacheStorageVisitor.h" 14 #include "nsICryptoHash.h" 15 #include "nsIInterfaceRequestor.h" 16 #include "nsIObserver.h" 17 #include "nsIStreamListener.h" 18 #include "mozilla/RefPtr.h" 19 #include "mozilla/Vector.h" 20 #include "nsString.h" 21 #include "nsTArray.h" 22 #include "mozilla/dom/RequestBinding.h" 23 #include "mozilla/TimeStamp.h" 24 #include "nsTHashMap.h" 25 #include "nsHashKeys.h" 26 27 class nsICacheStorage; 28 class nsIIOService; 29 class nsILoadContextInfo; 30 31 // Version of metadata entries we expect 32 static const uint32_t METADATA_DICTIONARY_VERSION = 1; 33 #define META_DICTIONARY_PREFIX "dict:"_ns 34 35 namespace mozilla { 36 namespace net { 37 38 class nsHttpChannel; 39 class DictionaryOrigin; 40 41 // Outstanding requests that offer this dictionary will hold a reference to it. 42 // If it's replaced (or removed) during the request, we would a) read the data 43 // into memory* b) unlink this from the origin in the memory cache. 44 // 45 // * or we wait for read-into-memory to finish, if we start reading entries 46 // when we send the request. 47 // 48 // When creating an entry from incoming data, we'll create it with no hash 49 // initially until the full data has arrived, then update the Hash. 50 class DictionaryCacheEntry final : public nsICacheEntryOpenCallback, 51 public nsIStreamListener { 52 friend class DictionaryOrigin; 53 54 private: 55 ~DictionaryCacheEntry(); 56 57 public: 58 NS_DECL_THREADSAFE_ISUPPORTS 59 NS_DECL_NSICACHEENTRYOPENCALLBACK 60 NS_DECL_NSIREQUESTOBSERVER 61 NS_DECL_NSISTREAMLISTENER 62 63 explicit DictionaryCacheEntry(const char* aKey); 64 DictionaryCacheEntry(const nsACString& aKey, const nsACString& aPattern, 65 nsTArray<nsCString>& aMatchDest, const nsACString& aId, 66 uint32_t aExpiration = 0, 67 const Maybe<nsCString>& aHash = Nothing()); 68 69 static void ConvertMatchDestToEnumArray( 70 const nsTArray<nsCString>& aMatchDest, 71 nsTArray<dom::RequestDestination>& aMatchEnums); 72 73 // returns true if the pattern for the dictionary matches the path given 74 bool Match(const nsACString& aFilePath, ExtContentPolicyType aType, 75 uint32_t aNow, uint32_t& aLongest); 76 77 // This will fail if the cache entry is no longer available. 78 // Start reading the cache entry into memory and call completion 79 // function when done 80 nsresult Prefetch(nsILoadContextInfo* aLoadContextInfo, bool& aShouldSuspend, 81 const std::function<void(nsresult)>& aFunc); 82 83 const nsCString& GetHash() const { return mHash; } 84 85 bool HasHash() { 86 // Hard to statically check since we're called from lambdas in 87 // GetDictionaryFor 88 return !mHash.IsEmpty(); 89 } 90 91 void SetHash(const nsACString& aHash) { 92 MOZ_ASSERT(NS_IsMainThread()); 93 mHash = aHash; 94 } 95 96 void WriteOnHash(); 97 98 void SetOrigin(DictionaryOrigin* aOrigin) { mOrigin = aOrigin; } 99 100 const nsCString& GetId() const { return mId; } 101 102 // keep track of requests that may need the data 103 void InUse(); 104 void UseCompleted(); 105 bool IsReading() const { return mUsers > 0 && !mWaitingPrefetch.IsEmpty(); } 106 107 void SetReplacement(DictionaryCacheEntry* aEntry, DictionaryOrigin* aOrigin) { 108 mReplacement = aEntry; 109 mOrigin = aOrigin; 110 if (mReplacement) { 111 mReplacement->mShouldSuspend = true; 112 mReplacement->mBlocked = true; 113 } 114 } 115 116 bool ShouldSuspendUntilCacheRead() const { return mShouldSuspend; } 117 118 // aFunc is called when we have finished reading a dictionary from the 119 // cache, or we have no users waiting for cache data (cancelled, etc) 120 void CallbackOnCacheRead(const std::function<void(nsresult)>& aFunc) { 121 // the reasons to call back are identical to Prefetch() 122 mWaitingPrefetch.AppendElement(aFunc); 123 } 124 125 const nsACString& GetURI() const { return mURI; } 126 127 const Vector<uint8_t>& GetDictionary() const { return mDictionaryData; } 128 129 // Clear dictionary data for testing (forces reload from cache on next 130 // prefetch) 131 void ClearDataForTesting() { 132 mDictionaryData.clear(); 133 mDictionaryDataComplete = false; 134 } 135 136 // Accumulate a hash while saving a file being received to the cache 137 void AccumulateHash(const char* aBuf, int32_t aCount); 138 void FinishHash(); 139 140 // return a pointer to the data and length 141 uint8_t* DictionaryData(size_t* aLength) const { 142 *aLength = mDictionaryData.length(); 143 return (uint8_t*)mDictionaryData.begin(); 144 } 145 146 bool DictionaryReady() const { return mDictionaryDataComplete; } 147 148 size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const { 149 // XXX 150 return mallocSizeOf(this); 151 } 152 153 static nsresult ReadCacheData(nsIInputStream* aInStream, void* aClosure, 154 const char* aFromSegment, uint32_t aToOffset, 155 uint32_t aCount, uint32_t* aWriteCount); 156 157 void MakeMetadataEntry(nsCString& aNewValue); 158 159 nsresult Write(nsICacheEntry* aEntry); 160 161 nsresult RemoveEntry(nsICacheEntry* aCacheEntry); 162 163 // Parse metadata from DictionaryOrigin 164 bool ParseMetadata(const char* aSrc); 165 166 void CopyFrom(DictionaryCacheEntry* aOther) { 167 mURI = aOther->mURI; 168 mPattern = aOther->mPattern; 169 mId = aOther->mId; 170 mMatchDest = aOther->mMatchDest; 171 // XXX mType = aOther->mType; 172 } 173 174 void UnblockAddEntry(DictionaryOrigin* aOrigin); 175 176 const nsCString& GetPattern() const { return mPattern; } 177 void AppendMatchDest(nsACString& aDest) const; 178 179 private: 180 // URI (without ref) for the dictionary 181 nsCString mURI; 182 // Expiration time, or 0 for none (default) 183 uint32_t mExpiration{0}; 184 185 nsCString mPattern; 186 nsCString mId; // max length 1024 187 CopyableTArray<dom::RequestDestination> mMatchDest; 188 // dcb and dcz use type 'raw'. We're allowed to ignore types we don't 189 // understand, so we can fail to record a dictionary with type != 'raw' 190 // nsCString mType; 191 192 // SHA-256 hash value ready to put into a header 193 nsCString mHash; 194 uint32_t mUsers{0}; // active requests using this entry 195 // in-memory copy of the entry to use to decompress incoming data 196 Vector<uint8_t> mDictionaryData; 197 bool mDictionaryDataComplete{false}; 198 199 // for accumulating SHA-256 hash values for dictionaries 200 nsCOMPtr<nsICryptoHash> mCrypto; 201 202 // call these when prefetch is complete 203 nsTArray<std::function<void(nsresult)>> mWaitingPrefetch; 204 205 // If we need to Write() an entry before we know the hash, remember the origin 206 // here (creates a temporary cycle). Clear on StopRequest 207 RefPtr<DictionaryOrigin> mOrigin; 208 // Don't store origin for write if we've already received OnStopRequest 209 bool mStopReceived{false}; 210 211 // If set, a new entry wants to replace us, and we have active decoding users. 212 // When we finish reading data into this entry for decoding, do 2 things: 213 // Remove our entry from origin->mEntries (so no future requests find this, 214 // and un-Suspend the new channel so it can start saving data into the cache. 215 RefPtr<DictionaryCacheEntry> mReplacement; 216 217 // We should suspend until the ond entry has been read 218 bool mShouldSuspend{false}; 219 220 // The cache entry has been removed 221 bool mNotCached{false}; 222 223 // We're blocked from taking over for the old entry for now 224 bool mBlocked{false}; 225 }; 226 227 // XXX Do we want to pre-read dictionaries into RAM at startup (lazily)? 228 // If we have all dictionaries stored in the cache, we don't need to do 229 // lookups to find if an origin has dictionaries or not, and we don't need to 230 // store empty entries (and LRU them). Downside would be if there are a LOT of 231 // origins with dictionaries, which may eventually happen, it would use more 232 // memory for rarely used origins. We could have a limit for dictionaries, and 233 // above that switch to partial caching and empty entries for origins without. 234 235 class DictionaryCache; 236 237 class DictionaryOriginReader final : public nsICacheEntryOpenCallback, 238 public nsIStreamListener { 239 NS_DECL_THREADSAFE_ISUPPORTS 240 NS_DECL_NSICACHEENTRYOPENCALLBACK 241 NS_DECL_NSIREQUESTOBSERVER 242 NS_DECL_NSISTREAMLISTENER 243 244 DictionaryOriginReader() {} 245 246 void Start( 247 bool aCreate, DictionaryOrigin* aOrigin, nsACString& aKey, nsIURI* aURI, 248 ExtContentPolicyType aType, DictionaryCache* aCache, 249 const std::function<nsresult(bool, DictionaryCacheEntry*)>& aCallback); 250 void FinishMatch(); 251 252 private: 253 ~DictionaryOriginReader() {} 254 255 RefPtr<DictionaryOrigin> mOrigin; 256 nsCOMPtr<nsIURI> mURI; 257 ExtContentPolicyType mType; 258 std::function<nsresult(bool, DictionaryCacheEntry*)> mCallback; 259 RefPtr<DictionaryCache> mCache; 260 }; 261 262 // using DictCacheList = AutoCleanLinkedList<RefPtr<DictionaryCacheEntry>>; 263 using DictCacheList = nsTArray<RefPtr<DictionaryCacheEntry>>; 264 265 // XXX if we want to have a parallel LRU list for pushing origins out of memory, 266 // add this: public LinkedListElement<RefPtr<DictionaryOrigin>>, 267 class DictionaryOrigin : public nsICacheEntryMetaDataVisitor { 268 friend class DictionaryCache; 269 friend class DictionaryOriginReader; 270 271 public: 272 NS_DECL_THREADSAFE_ISUPPORTS 273 NS_DECL_NSICACHEENTRYMETADATAVISITOR 274 275 DictionaryOrigin(const nsACString& aOrigin, nsICacheEntry* aEntry) 276 : mOrigin(aOrigin), mEntry(aEntry) {} 277 278 void SetCacheEntry(nsICacheEntry* aEntry); 279 nsresult Write(DictionaryCacheEntry* aDictEntry); 280 already_AddRefed<DictionaryCacheEntry> AddEntry( 281 DictionaryCacheEntry* aDictEntry, bool aNewEntry); 282 nsresult RemoveEntry(const nsACString& aKey); 283 void RemoveEntry(DictionaryCacheEntry* aEntry); 284 DictionaryCacheEntry* Match(const nsACString& path, 285 ExtContentPolicyType aType); 286 void FinishAddEntry(DictionaryCacheEntry* aEntry); 287 void DumpEntries(); 288 void Clear(); 289 bool IsEmpty() const { 290 return mEntries.IsEmpty() && mPendingEntries.IsEmpty() && 291 mPendingRemove.IsEmpty() && mWaitingCacheRead.IsEmpty(); 292 } 293 294 private: 295 virtual ~DictionaryOrigin() {} 296 297 nsCString mOrigin; 298 nsCOMPtr<nsICacheEntry> mEntry; 299 DictCacheList mEntries; 300 // Dictionaries currently being received. Once these get a Hash, move to 301 // mEntries 302 DictCacheList mPendingEntries; 303 // Dictionaries removed from mEntries but waiting to be removed from the 304 // Cache metadata 305 DictCacheList mPendingRemove; 306 // Write out all entries once we have a cacheentry 307 bool mDeferredWrites{false}; 308 309 // readers that are waiting for this origin's metadata to be read 310 nsTArray<RefPtr<DictionaryOriginReader>> mWaitingCacheRead; 311 }; 312 313 // singleton class 314 class DictionaryCache final { 315 private: 316 DictionaryCache() { 317 nsresult rv = Init(); 318 (void)rv; 319 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); 320 } 321 ~DictionaryCache() {} 322 323 friend class DictionaryOriginReader; 324 friend class DictionaryCacheEntry; 325 326 public: 327 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DictionaryCache) 328 329 static already_AddRefed<DictionaryCache> GetInstance(); 330 331 nsresult Init(); 332 static void Shutdown(); 333 334 nsresult AddEntry(nsIURI* aURI, const nsACString& aKey, 335 const nsACString& aPattern, nsTArray<nsCString>& aMatchDest, 336 const nsACString& aId, const Maybe<nsCString>& aHash, 337 bool aNewEntry, uint32_t aExpiration, 338 DictionaryCacheEntry** aDictEntry); 339 340 already_AddRefed<DictionaryCacheEntry> AddEntry( 341 nsIURI* aURI, bool aNewEntry, DictionaryCacheEntry* aDictEntry); 342 343 static void RemoveDictionaryFor(const nsACString& aKey); 344 // remove the entire origin (should be empty!) 345 static void RemoveOriginFor(const nsACString& aKey); 346 347 // Remove a dictionary if it exists for the key given 348 void RemoveDictionary(const nsACString& aKey); 349 // Remove an origin for the origin given 350 void RemoveOrigin(const nsACString& aOrigin); 351 352 nsresult RemoveEntry(nsIURI* aURI, const nsACString& aKey); 353 354 static void RemoveDictionariesForOrigin(nsIURI* aURI); 355 static void RemoveAllDictionaries(); 356 357 // Clears all ports at host 358 void Clear(); 359 360 // Corrupt the hash of a dictionary entry for testing 361 void CorruptHashForTesting(const nsACString& aURI); 362 363 // Clear the dictionary data while keeping the entry (for testing) 364 void ClearDictionaryDataForTesting(const nsACString& aURI); 365 366 // return an entry 367 void GetDictionaryFor( 368 nsIURI* aURI, ExtContentPolicyType aType, nsHttpChannel* aChan, 369 void (*aSuspend)(nsHttpChannel*), 370 const std::function<nsresult(bool, DictionaryCacheEntry*)>& aCallback); 371 372 size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const { 373 // XXX 374 return mallocSizeOf(this); 375 } 376 377 private: 378 void RemoveOriginForInternal(const nsACString& aKey); 379 380 static StaticRefPtr<nsICacheStorage> sCacheStorage; 381 382 // In-memory cache of dictionary entries. HashMap, keyed by origin, of 383 // Linked list (LRU order) of valid dictionaries for the origin. 384 // We keep empty entries in there to avoid hitting the disk cache to find out 385 // if there are dictionaries for an origin. 386 // Static assertions fire if we try to have a LinkedList directly in an 387 // nsTHashMap 388 nsTHashMap<nsCStringHashKey, RefPtr<DictionaryOrigin>> mDictionaryCache; 389 }; 390 391 } // namespace net 392 } // namespace mozilla 393 394 #endif // mozilla_net_Dictionary_h