blob: 5ff5eb627f783b0fc490313fbc1c8577942136ad [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <list>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "base/containers/id_map.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/predictors/proxy_lookup_client_impl.h"
#include "chrome/browser/predictors/resolve_host_client_impl.h"
#include "chrome/browser/predictors/resource_prefetch_predictor.h"
#include "url/gurl.h"
class Profile;
namespace network {
namespace mojom {
class NetworkContext;
} // namespace network
namespace predictors {
struct PreconnectRequest;
struct PreconnectedRequestStats {
PreconnectedRequestStats(const GURL& origin,
bool was_preconnected);
PreconnectedRequestStats(const PreconnectedRequestStats& other);
GURL origin;
bool was_preconnected;
struct PreconnectStats {
explicit PreconnectStats(const GURL& url);
GURL url;
base::TimeTicks start_time;
std::vector<PreconnectedRequestStats> requests_stats;
// Stats must be moved only.
// Stores the status of all preconnects associated with a given |url|.
struct PreresolveInfo {
PreresolveInfo(const GURL& url, size_t count);
bool is_done() const { return queued_count == 0 && inflight_count == 0; }
GURL url;
size_t queued_count;
size_t inflight_count = 0;
bool was_canceled = false;
std::unique_ptr<PreconnectStats> stats;
// Stores all data need for running a preresolve and a subsequent optional
// preconnect for a |url|.
struct PreresolveJob {
PreresolveJob(const GURL& url,
int num_sockets,
bool allow_credentials,
PreresolveInfo* info);
PreresolveJob(PreresolveJob&& other);
bool need_preconnect() const {
return num_sockets > 0 && !(info && info->was_canceled);
GURL url;
int num_sockets;
bool allow_credentials;
// Raw pointer usage is fine here because even though PreresolveJob can
// outlive PreresolveInfo it's only accessed on PreconnectManager class
// context and PreresolveInfo lifetime is tied to PreconnectManager.
// May be equal to nullptr in case of detached job.
PreresolveInfo* info;
std::unique_ptr<ResolveHostClientImpl> resolve_host_client;
std::unique_ptr<ProxyLookupClientImpl> proxy_lookup_client;
// PreconnectManager is responsible for preresolving and preconnecting to
// origins based on the input list of URLs.
// - The input list of URLs is associated with a main frame url that can be
// used for cancelling.
// - Limits the total number of preresolves in flight.
// - Preresolves an URL before preconnecting to it to have a better control on
// number of speculative dns requests in flight.
// - When stopped, waits for the pending preresolve requests to finish without
// issuing preconnects for them.
// - All methods of the class must be called on the UI thread.
class PreconnectManager {
class Delegate {
virtual ~Delegate() {}
// Called when all preresolve jobs for the |stats->url| are finished. Note
// that some preconnect jobs can be still in progress, because they are
// fire-and-forget.
// Is called on the UI thread.
virtual void PreconnectFinished(std::unique_ptr<PreconnectStats> stats) = 0;
// An observer for testing.
class Observer {
virtual ~Observer() {}
virtual void OnPreconnectUrl(const GURL& url,
int num_sockets,
bool allow_credentials) {}
virtual void OnPreresolveFinished(const GURL& url, bool success) {}
virtual void OnProxyLookupFinished(const GURL& url, bool success) {}
static const size_t kMaxInflightPreresolves = 3;
PreconnectManager(base::WeakPtr<Delegate> delegate, Profile* profile);
virtual ~PreconnectManager();
// Starts preconnect and preresolve jobs keyed by |url|.
virtual void Start(const GURL& url, std::vector<PreconnectRequest> requests);
// Starts special preconnect and preresolve jobs that are not cancellable and
// don't report about their completion. They are considered more important
// than trackable requests thus they are put in the front of the jobs queue.
virtual void StartPreresolveHost(const GURL& url);
virtual void StartPreresolveHosts(const std::vector<std::string>& hostnames);
virtual void StartPreconnectUrl(const GURL& url, bool allow_credentials);
// No additional jobs keyed by the |url| will be queued after this.
virtual void Stop(const GURL& url);
base::WeakPtr<PreconnectManager> GetWeakPtr() {
return weak_factory_.GetWeakPtr();
void SetNetworkContextForTesting(
network::mojom::NetworkContext* network_context) {
network_context_ = network_context;
void SetObserverForTesting(Observer* observer) { observer_ = observer; }
using PreresolveJobMap = base::IDMap<std::unique_ptr<PreresolveJob>>;
using PreresolveJobId = PreresolveJobMap::KeyType;
friend class PreconnectManagerTest;
void PreconnectUrl(const GURL& url,
int num_sockets,
bool allow_credentials) const;
std::unique_ptr<ResolveHostClientImpl> PreresolveUrl(
const GURL& url,
ResolveHostCallback callback) const;
std::unique_ptr<ProxyLookupClientImpl> LookupProxyForUrl(
const GURL& url,
ProxyLookupCallback callback) const;
void TryToLaunchPreresolveJobs();
void OnPreresolveFinished(PreresolveJobId job_id, bool success);
void OnProxyLookupFinished(PreresolveJobId job_id, bool success);
void FinishPreresolveJob(PreresolveJobId job_id, bool success);
void AllPreresolvesForUrlFinished(PreresolveInfo* info);
network::mojom::NetworkContext* GetNetworkContext() const;
base::WeakPtr<Delegate> delegate_;
Profile* const profile_;
std::list<PreresolveJobId> queued_jobs_;
PreresolveJobMap preresolve_jobs_;
std::map<std::string, std::unique_ptr<PreresolveInfo>> preresolve_info_;
size_t inflight_preresolves_count_ = 0;
// Only used in tests.
network::mojom::NetworkContext* network_context_ = nullptr;
Observer* observer_ = nullptr;
base::WeakPtrFactory<PreconnectManager> weak_factory_;
} // namespace predictors