tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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