910e62b5创建于 1月15日历史提交
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CONTENT_BROWSER_PRELOADING_PREFETCH_PREFETCH_CANARY_CHECKER_H_
#define CONTENT_BROWSER_PRELOADING_PREFETCH_PREFETCH_CANARY_CHECKER_H_

#include <optional>

#include "base/containers/lru_cache.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "content/common/content_export.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "net/base/backoff_entry.h"
#include "services/network/public/mojom/host_resolver.mojom.h"
#include "url/gurl.h"

namespace net {
class AddressList;
}  // namespace net

namespace network {
class NetworkConnectionTracker;
}  // namespace network

namespace content {

class BrowserContext;

// This class makes DNS lookups to a specified host to verify if the user's ISP
// would like Prefetch Proxy users to first probe the prefetched host before
// using a prefetched resource. This allows ISP to perform filtering even if
// a response has been fetched via an encrypted tunnel through the Prefetch
// Proxy.
class CONTENT_EXPORT PrefetchCanaryChecker {
 public:
  // Callers who wish to use this class should add a value to this enum. This
  // enum is mapped to a string value which is then used in histograms and
  // prefs. Be sure to update the |PrefetchProxy.CanaryChecker.Clients|
  // histogram suffix in
  // //tools/metrics/histograms/metadata/prefetch/histograms.xml whenever a
  // change is made to this enum.
  //
  // Please add the header file of the client when new items are added.
  enum class CheckType {
    // content/browser/speculation_rules/prefetch/prefetch_origin_decider.h
    kDNS = 0,
    kTLS = 1,
    kMaxValue = kTLS,
  };

  struct CONTENT_EXPORT RetryPolicy {
    RetryPolicy();
    RetryPolicy(const RetryPolicy& other);
    ~RetryPolicy();

    // The maximum number of retries (not including the original check) to
    // attempt.
    size_t max_retries = 0;

    // Backoff policy to use to compute how long we should wait between the end
    // of last retry and start of next retry.
    net::BackoffEntry::Policy backoff_policy = {
        .num_errors_to_ignore = 0,
        .initial_delay_ms = 100,
        .multiply_factor = 2,
        .jitter_factor = 0.2,
        // No maximum backoff.
        .maximum_backoff_ms = -1,
        .entry_lifetime_ms = -1,
        .always_use_initial_delay = false,
    };
  };

  // Cache entry representing a canary check result.
  struct CacheEntry {
    bool success;
    base::Time last_modified;
  };

  // Creates an instance of |PrefetchCanaryChecker| when given a valid |url|. If
  // |url| is invalid then nullptr is returned.
  static std::unique_ptr<PrefetchCanaryChecker> MakePrefetchCanaryChecker(
      BrowserContext* browser_context,
      CheckType,
      const GURL& url,
      const RetryPolicy& retry_policy,
      const base::TimeDelta check_timeout,
      base::TimeDelta revalidate_cache_after);

  PrefetchCanaryChecker(BrowserContext* browser_context,
                        CheckType name,
                        const GURL& url,
                        const RetryPolicy& retry_policy,
                        const base::TimeDelta check_timeout,
                        base::TimeDelta revalidate_cache_after);
  ~PrefetchCanaryChecker();

  PrefetchCanaryChecker(const PrefetchCanaryChecker&) = delete;
  PrefetchCanaryChecker& operator=(const PrefetchCanaryChecker) = delete;

  base::WeakPtr<PrefetchCanaryChecker> GetWeakPtr();

  // Returns the successfulness of the last canary check, if there was one. If
  // the last status was not cached or was cached and needs to be revalidated,
  // this may trigger new checks. This updates the
  // PrefetchProxy.CanaryChecker.CacheLookupStatus histogram, so avoid calling
  // this method repeatedly when its result can be reused.
  std::optional<bool> CanaryCheckSuccessful();

  // Triggers new canary checks if there is no cached status or if the cached
  // status is stale. Use this method over CanaryCheckSuccessful if you only
  // want to freshen the cache (as opposed to look up the cached value), as the
  // CanaryCheckSuccessful method updates the CacheLookupStatus histogram, but
  // RunChecksIfNeeded does not.
  void RunChecksIfNeeded();

  // True if checks are being attempted, including retries.
  bool IsActive() const { return time_when_set_active_.has_value(); }

 private:
  void ResetState();
  void StartDNSResolution(const GURL& url);
  void OnDNSResolved(int net_error, const net::AddressList& resolved_addresses);
  void ProcessTimeout();
  void ProcessFailure(int net_error);
  void ProcessSuccess();
  std::string AppendNameToHistogram(const std::string& histogram) const;
  std::optional<bool> LookupAndRunChecksIfNeeded();
  // Sends a check now if the checker is currently inactive. If the check is
  // active (i.e.: there are DNS resolutions in flight), this is a no-op.
  void SendNowIfInactive();

  // This is called whenever the canary check is done. This is caused whenever
  // the check succeeds, fails and there are no more retries, or the delegate
  // stops the probing.
  void OnCheckEnd(bool success);

  // Updates the cache with the given entry and key. The arguments are in an
  // unusual order to make BindOnce calls easier, as this method is used as a
  // callback since generating the cache key happens asynchronously.
  void UpdateCacheEntry(PrefetchCanaryChecker::CacheEntry entry,
                        std::string key);

  // Simply sets |latest_cache_key_| to |key|. This method is used as a
  // callback since generating the cache key happens asynchronously.
  void UpdateCacheKey(std::string key);

  // The current browser context, not owned.
  raw_ptr<BrowserContext> browser_context_;

  // Pipe to allow cancelling an ongoing DNS resolution request. This is set
  // when we fire off a DNS request to the network service. We send the
  // receiving end to the network service as part of the parameters of the
  // ResolveHost call. The network service then listens to this pipe to
  // potentially cancel the request. The pipe is reset as when the request
  // completes (success or failure).
  mojo::Remote<network::mojom::ResolveHostHandle> resolver_control_handle_;

  // The name given to this checker instance. Used in metrics.
  const std::string name_;

  // The URL that will be DNS resolved.
  const GURL url_;

  // The retry policy to use in this checker.
  const RetryPolicy retry_policy_;

  // The exponential backoff state for retries. This gets reset at the end of
  // each check.
  net::BackoffEntry backoff_entry_;

  // How long before we should timeout a DNS check and retry.
  const base::TimeDelta check_timeout_;

  // How long to allow a cached entry to be valid until it is revalidated in the
  // background.
  const base::TimeDelta revalidate_cache_after_;

  // If a retry is being attempted, this will be running until the next attempt.
  std::unique_ptr<base::OneShotTimer> retry_timer_;

  // If a check is being attempted, this will be running until the TTL.
  std::unique_ptr<base::OneShotTimer> timeout_timer_;

  // Remembers the last time the checker became active.
  std::optional<base::Time> time_when_set_active_;

  // This reference is kept around for unregistering |this| as an observer on
  // any thread.
  raw_ptr<network::NetworkConnectionTracker> network_connection_tracker_;

  // Small LRU cache holding the result of canary checks made for different
  // networks. This cache is not persisted across browser restarts.
  base::LRUCache<std::string, PrefetchCanaryChecker::CacheEntry> cache_;

  // Keeps track of that latest key used to cache the canary checks. This key
  // changes if the user's network changes. Evaluating the cache key requires
  // an OS lookup which is slow on android, so we store the latest cache key
  // evaluation (and use this stale cache keys for lookups).
  std::string latest_cache_key_;

  SEQUENCE_CHECKER(sequence_checker_);

  base::WeakPtrFactory<PrefetchCanaryChecker> weak_factory_{this};
};

}  // namespace content

#endif  // CONTENT_BROWSER_PRELOADING_PREFETCH_PREFETCH_CANARY_CHECKER_H_