1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489 |
- // Copyright (c) 2012 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 "net/proxy_resolution/configured_proxy_resolution_service.h"
- #include <algorithm>
- #include <cmath>
- #include <memory>
- #include <utility>
- #include "base/bind.h"
- #include "base/callback_helpers.h"
- #include "base/compiler_specific.h"
- #include "base/location.h"
- #include "base/logging.h"
- #include "base/memory/raw_ptr.h"
- #include "base/memory/weak_ptr.h"
- #include "base/metrics/histogram_macros.h"
- #include "base/strings/string_util.h"
- #include "base/task/single_thread_task_runner.h"
- #include "base/threading/thread_task_runner_handle.h"
- #include "base/time/time.h"
- #include "base/values.h"
- #include "build/build_config.h"
- #include "build/chromeos_buildflags.h"
- #include "net/base/net_errors.h"
- #include "net/base/net_info_source_list.h"
- #include "net/base/network_isolation_key.h"
- #include "net/base/proxy_delegate.h"
- #include "net/base/proxy_server.h"
- #include "net/base/proxy_string_util.h"
- #include "net/base/url_util.h"
- #include "net/log/net_log.h"
- #include "net/log/net_log_event_type.h"
- #include "net/log/net_log_util.h"
- #include "net/log/net_log_with_source.h"
- #include "net/proxy_resolution/configured_proxy_resolution_request.h"
- #include "net/proxy_resolution/dhcp_pac_file_fetcher.h"
- #include "net/proxy_resolution/multi_threaded_proxy_resolver.h"
- #include "net/proxy_resolution/pac_file_decider.h"
- #include "net/proxy_resolution/pac_file_fetcher.h"
- #include "net/proxy_resolution/proxy_config_service_fixed.h"
- #include "net/proxy_resolution/proxy_resolver_factory.h"
- #include "net/url_request/url_request_context.h"
- #if BUILDFLAG(IS_WIN)
- #include "net/proxy_resolution/win/proxy_resolver_winhttp.h"
- #elif BUILDFLAG(IS_IOS)
- #include "net/proxy_resolution/proxy_resolver_mac.h"
- #elif BUILDFLAG(IS_MAC)
- #include "net/proxy_resolution/proxy_resolver_mac.h"
- #endif
- using base::TimeTicks;
- namespace net {
- namespace {
- const size_t kDefaultNumPacThreads = 4;
- // When the IP address changes we don't immediately re-run proxy auto-config.
- // Instead, we wait for |kDelayAfterNetworkChangesMs| before
- // attempting to re-valuate proxy auto-config.
- //
- // During this time window, any resolve requests sent to the
- // ConfiguredProxyResolutionService will be queued. Once we have waited the
- // required amount of them, the proxy auto-config step will be run, and the
- // queued requests resumed.
- //
- // The reason we play this game is that our signal for detecting network
- // changes (NetworkChangeNotifier) may fire *before* the system's networking
- // dependencies are fully configured. This is a problem since it means if
- // we were to run proxy auto-config right away, it could fail due to spurious
- // DNS failures. (see http://crbug.com/50779 for more details.)
- //
- // By adding the wait window, we give things a better chance to get properly
- // set up. Network failures can happen at any time though, so we additionally
- // poll the PAC script for changes, which will allow us to recover from these
- // sorts of problems.
- const int64_t kDelayAfterNetworkChangesMs = 2000;
- // This is the default policy for polling the PAC script.
- //
- // In response to a failure, the poll intervals are:
- // 0: 8 seconds (scheduled on timer)
- // 1: 32 seconds
- // 2: 2 minutes
- // 3+: 4 hours
- //
- // In response to a success, the poll intervals are:
- // 0+: 12 hours
- //
- // Only the 8 second poll is scheduled on a timer, the rest happen in response
- // to network activity (and hence will take longer than the written time).
- //
- // Explanation for these values:
- //
- // TODO(eroman): These values are somewhat arbitrary, and need to be tuned
- // using some histograms data. Trying to be conservative so as not to break
- // existing setups when deployed. A simple exponential retry scheme would be
- // more elegant, but places more load on server.
- //
- // The motivation for trying quickly after failures (8 seconds) is to recover
- // from spurious network failures, which are common after the IP address has
- // just changed (like DNS failing to resolve). The next 32 second boundary is
- // to try and catch other VPN weirdness which anecdotally I have seen take
- // 10+ seconds for some users.
- //
- // The motivation for re-trying after a success is to check for possible
- // content changes to the script, or to the WPAD auto-discovery results. We are
- // not very aggressive with these checks so as to minimize the risk of
- // overloading existing PAC setups. Moreover it is unlikely that PAC scripts
- // change very frequently in existing setups. More research is needed to
- // motivate what safe values are here, and what other user agents do.
- //
- // Comparison to other browsers:
- //
- // In Firefox the PAC URL is re-tried on failures according to
- // network.proxy.autoconfig_retry_interval_min and
- // network.proxy.autoconfig_retry_interval_max. The defaults are 5 seconds and
- // 5 minutes respectively. It doubles the interval at each attempt.
- //
- // TODO(eroman): Figure out what Internet Explorer does.
- class DefaultPollPolicy
- : public ConfiguredProxyResolutionService::PacPollPolicy {
- public:
- DefaultPollPolicy() = default;
- DefaultPollPolicy(const DefaultPollPolicy&) = delete;
- DefaultPollPolicy& operator=(const DefaultPollPolicy&) = delete;
- Mode GetNextDelay(int initial_error,
- base::TimeDelta current_delay,
- base::TimeDelta* next_delay) const override {
- if (initial_error != OK) {
- // Re-try policy for failures.
- const int kDelay1Seconds = 8;
- const int kDelay2Seconds = 32;
- const int kDelay3Seconds = 2 * 60; // 2 minutes
- const int kDelay4Seconds = 4 * 60 * 60; // 4 Hours
- // Initial poll.
- if (current_delay.is_negative()) {
- *next_delay = base::Seconds(kDelay1Seconds);
- return MODE_USE_TIMER;
- }
- switch (current_delay.InSeconds()) {
- case kDelay1Seconds:
- *next_delay = base::Seconds(kDelay2Seconds);
- return MODE_START_AFTER_ACTIVITY;
- case kDelay2Seconds:
- *next_delay = base::Seconds(kDelay3Seconds);
- return MODE_START_AFTER_ACTIVITY;
- default:
- *next_delay = base::Seconds(kDelay4Seconds);
- return MODE_START_AFTER_ACTIVITY;
- }
- } else {
- // Re-try policy for succeses.
- *next_delay = base::Hours(12);
- return MODE_START_AFTER_ACTIVITY;
- }
- }
- };
- // Config getter that always returns direct settings.
- class ProxyConfigServiceDirect : public ProxyConfigService {
- public:
- // ProxyConfigService implementation:
- void AddObserver(Observer* observer) override {}
- void RemoveObserver(Observer* observer) override {}
- ConfigAvailability GetLatestProxyConfig(
- ProxyConfigWithAnnotation* config) override {
- *config = ProxyConfigWithAnnotation::CreateDirect();
- return CONFIG_VALID;
- }
- };
- // Proxy resolver that fails every time.
- class ProxyResolverNull : public ProxyResolver {
- public:
- ProxyResolverNull() = default;
- // ProxyResolver implementation.
- int GetProxyForURL(const GURL& url,
- const NetworkIsolationKey& network_isolation_key,
- ProxyInfo* results,
- CompletionOnceCallback callback,
- std::unique_ptr<Request>* request,
- const NetLogWithSource& net_log) override {
- return ERR_NOT_IMPLEMENTED;
- }
- };
- // ProxyResolver that simulates a PAC script which returns
- // |pac_string| for every single URL.
- class ProxyResolverFromPacString : public ProxyResolver {
- public:
- explicit ProxyResolverFromPacString(const std::string& pac_string)
- : pac_string_(pac_string) {}
- int GetProxyForURL(const GURL& url,
- const NetworkIsolationKey& network_isolation_key,
- ProxyInfo* results,
- CompletionOnceCallback callback,
- std::unique_ptr<Request>* request,
- const NetLogWithSource& net_log) override {
- results->UsePacString(pac_string_);
- return OK;
- }
- private:
- const std::string pac_string_;
- };
- // Creates ProxyResolvers using a platform-specific implementation.
- class ProxyResolverFactoryForSystem : public MultiThreadedProxyResolverFactory {
- public:
- explicit ProxyResolverFactoryForSystem(size_t max_num_threads)
- : MultiThreadedProxyResolverFactory(max_num_threads,
- false /*expects_pac_bytes*/) {}
- ProxyResolverFactoryForSystem(const ProxyResolverFactoryForSystem&) = delete;
- ProxyResolverFactoryForSystem& operator=(
- const ProxyResolverFactoryForSystem&) = delete;
- std::unique_ptr<ProxyResolverFactory> CreateProxyResolverFactory() override {
- #if BUILDFLAG(IS_WIN)
- return std::make_unique<ProxyResolverFactoryWinHttp>();
- #elif BUILDFLAG(IS_APPLE)
- return std::make_unique<ProxyResolverFactoryMac>();
- #else
- NOTREACHED();
- return nullptr;
- #endif
- }
- static bool IsSupported() {
- #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE)
- return true;
- #else
- return false;
- #endif
- }
- };
- class ProxyResolverFactoryForNullResolver : public ProxyResolverFactory {
- public:
- ProxyResolverFactoryForNullResolver() : ProxyResolverFactory(false) {}
- ProxyResolverFactoryForNullResolver(
- const ProxyResolverFactoryForNullResolver&) = delete;
- ProxyResolverFactoryForNullResolver& operator=(
- const ProxyResolverFactoryForNullResolver&) = delete;
- // ProxyResolverFactory overrides.
- int CreateProxyResolver(const scoped_refptr<PacFileData>& pac_script,
- std::unique_ptr<ProxyResolver>* resolver,
- CompletionOnceCallback callback,
- std::unique_ptr<Request>* request) override {
- *resolver = std::make_unique<ProxyResolverNull>();
- return OK;
- }
- };
- class ProxyResolverFactoryForPacResult : public ProxyResolverFactory {
- public:
- explicit ProxyResolverFactoryForPacResult(const std::string& pac_string)
- : ProxyResolverFactory(false), pac_string_(pac_string) {}
- ProxyResolverFactoryForPacResult(const ProxyResolverFactoryForPacResult&) =
- delete;
- ProxyResolverFactoryForPacResult& operator=(
- const ProxyResolverFactoryForPacResult&) = delete;
- // ProxyResolverFactory override.
- int CreateProxyResolver(const scoped_refptr<PacFileData>& pac_script,
- std::unique_ptr<ProxyResolver>* resolver,
- CompletionOnceCallback callback,
- std::unique_ptr<Request>* request) override {
- *resolver = std::make_unique<ProxyResolverFromPacString>(pac_string_);
- return OK;
- }
- private:
- const std::string pac_string_;
- };
- // Returns NetLog parameters describing a proxy configuration change.
- base::Value NetLogProxyConfigChangedParams(
- const absl::optional<ProxyConfigWithAnnotation>* old_config,
- const ProxyConfigWithAnnotation* new_config) {
- base::Value::Dict dict;
- // The "old_config" is optional -- the first notification will not have
- // any "previous" configuration.
- if (old_config->has_value())
- dict.Set("old_config", (*old_config)->value().ToValue());
- dict.Set("new_config", new_config->value().ToValue());
- return base::Value(std::move(dict));
- }
- base::Value NetLogBadProxyListParams(const ProxyRetryInfoMap* retry_info) {
- base::Value::Dict dict;
- base::Value list(base::Value::Type::LIST);
- for (const auto& retry_info_pair : *retry_info)
- list.Append(retry_info_pair.first);
- dict.Set("bad_proxy_list", std::move(list));
- return base::Value(std::move(dict));
- }
- // Returns NetLog parameters on a successful proxy resolution.
- base::Value NetLogFinishedResolvingProxyParams(const ProxyInfo* result) {
- base::Value::Dict dict;
- dict.Set("pac_string", result->ToPacString());
- return base::Value(std::move(dict));
- }
- // Returns a sanitized copy of |url| which is safe to pass on to a PAC script.
- //
- // PAC scripts are modelled as being controllable by a network-present
- // attacker (since such an attacker can influence the outcome of proxy
- // auto-discovery, or modify the contents of insecurely delivered PAC scripts).
- //
- // As such, it is important that the full path/query of https:// URLs not be
- // sent to PAC scripts, since that would give an attacker access to data that
- // is ordinarily protected by TLS.
- //
- // Obscuring the path for http:// URLs isn't being done since it doesn't matter
- // for security (attacker can already route traffic through their HTTP proxy
- // and see the full URL for http:// requests).
- //
- // TODO(https://crbug.com/882536): Use the same stripping for insecure URL
- // schemes.
- GURL SanitizeUrl(const GURL& url) {
- DCHECK(url.is_valid());
- GURL::Replacements replacements;
- replacements.ClearUsername();
- replacements.ClearPassword();
- replacements.ClearRef();
- if (url.SchemeIsCryptographic()) {
- replacements.ClearPath();
- replacements.ClearQuery();
- }
- return url.ReplaceComponents(replacements);
- }
- } // namespace
- // ConfiguredProxyResolutionService::InitProxyResolver
- // ----------------------------------
- // This glues together two asynchronous steps:
- // (1) PacFileDecider -- try to fetch/validate a sequence of PAC scripts
- // to figure out what we should configure against.
- // (2) Feed the fetched PAC script into the ProxyResolver.
- //
- // InitProxyResolver is a single-use class which encapsulates cancellation as
- // part of its destructor. Start() or StartSkipDecider() should be called just
- // once. The instance can be destroyed at any time, and the request will be
- // cancelled.
- class ConfiguredProxyResolutionService::InitProxyResolver {
- public:
- InitProxyResolver() = default;
- InitProxyResolver(const InitProxyResolver&) = delete;
- InitProxyResolver& operator=(const InitProxyResolver&) = delete;
- // Note that the destruction of PacFileDecider will automatically cancel
- // any outstanding work.
- ~InitProxyResolver() = default;
- // Begins initializing the proxy resolver; calls |callback| when done. A
- // ProxyResolver instance will be created using |proxy_resolver_factory| and
- // assigned to |*proxy_resolver| if the final result is OK.
- int Start(std::unique_ptr<ProxyResolver>* proxy_resolver,
- ProxyResolverFactory* proxy_resolver_factory,
- PacFileFetcher* pac_file_fetcher,
- DhcpPacFileFetcher* dhcp_pac_file_fetcher,
- NetLog* net_log,
- const ProxyConfigWithAnnotation& config,
- base::TimeDelta wait_delay,
- CompletionOnceCallback callback) {
- DCHECK_EQ(State::kNone, next_state_);
- proxy_resolver_ = proxy_resolver;
- proxy_resolver_factory_ = proxy_resolver_factory;
- decider_ = std::make_unique<PacFileDecider>(pac_file_fetcher,
- dhcp_pac_file_fetcher, net_log);
- decider_->set_quick_check_enabled(quick_check_enabled_);
- config_ = config;
- wait_delay_ = wait_delay;
- callback_ = std::move(callback);
- next_state_ = State::kDecidePacFile;
- return DoLoop(OK);
- }
- // Similar to Start(), however it skips the PacFileDecider stage. Instead
- // |effective_config|, |decider_result| and |script_data| will be used as the
- // inputs for initializing the ProxyResolver. A ProxyResolver instance will
- // be created using |proxy_resolver_factory| and assigned to
- // |*proxy_resolver| if the final result is OK.
- int StartSkipDecider(std::unique_ptr<ProxyResolver>* proxy_resolver,
- ProxyResolverFactory* proxy_resolver_factory,
- const ProxyConfigWithAnnotation& effective_config,
- int decider_result,
- const PacFileDataWithSource& script_data,
- CompletionOnceCallback callback) {
- DCHECK_EQ(State::kNone, next_state_);
- proxy_resolver_ = proxy_resolver;
- proxy_resolver_factory_ = proxy_resolver_factory;
- effective_config_ = effective_config;
- script_data_ = script_data;
- callback_ = std::move(callback);
- if (decider_result != OK)
- return decider_result;
- next_state_ = State::kCreateResolver;
- return DoLoop(OK);
- }
- // Returns the proxy configuration that was selected by PacFileDecider.
- // Should only be called upon completion of the initialization.
- const ProxyConfigWithAnnotation& effective_config() const {
- DCHECK_EQ(State::kNone, next_state_);
- return effective_config_;
- }
- // Returns the PAC script data that was selected by PacFileDecider.
- // Should only be called upon completion of the initialization.
- const PacFileDataWithSource& script_data() {
- DCHECK_EQ(State::kNone, next_state_);
- return script_data_;
- }
- LoadState GetLoadState() const {
- if (next_state_ == State::kDecidePacFileComplete) {
- // In addition to downloading, this state may also include the stall time
- // after network change events (kDelayAfterNetworkChangesMs).
- return LOAD_STATE_DOWNLOADING_PAC_FILE;
- }
- return LOAD_STATE_RESOLVING_PROXY_FOR_URL;
- }
- // This must be called before the HostResolver is torn down.
- void OnShutdown() {
- if (decider_)
- decider_->OnShutdown();
- }
- void set_quick_check_enabled(bool enabled) { quick_check_enabled_ = enabled; }
- bool quick_check_enabled() const { return quick_check_enabled_; }
- private:
- enum class State {
- kNone,
- kDecidePacFile,
- kDecidePacFileComplete,
- kCreateResolver,
- kCreateResolverComplete,
- };
- int DoLoop(int result) {
- DCHECK_NE(next_state_, State::kNone);
- int rv = result;
- do {
- State state = next_state_;
- next_state_ = State::kNone;
- switch (state) {
- case State::kDecidePacFile:
- DCHECK_EQ(OK, rv);
- rv = DoDecidePacFile();
- break;
- case State::kDecidePacFileComplete:
- rv = DoDecidePacFileComplete(rv);
- break;
- case State::kCreateResolver:
- DCHECK_EQ(OK, rv);
- rv = DoCreateResolver();
- break;
- case State::kCreateResolverComplete:
- rv = DoCreateResolverComplete(rv);
- break;
- default:
- NOTREACHED() << "bad state: " << static_cast<int>(state);
- rv = ERR_UNEXPECTED;
- break;
- }
- } while (rv != ERR_IO_PENDING && next_state_ != State::kNone);
- return rv;
- }
- int DoDecidePacFile() {
- next_state_ = State::kDecidePacFileComplete;
- return decider_->Start(config_, wait_delay_,
- proxy_resolver_factory_->expects_pac_bytes(),
- base::BindOnce(&InitProxyResolver::OnIOCompletion,
- base::Unretained(this)));
- }
- int DoDecidePacFileComplete(int result) {
- if (result != OK)
- return result;
- effective_config_ = decider_->effective_config();
- script_data_ = decider_->script_data();
- next_state_ = State::kCreateResolver;
- return OK;
- }
- int DoCreateResolver() {
- DCHECK(script_data_.data);
- // TODO(eroman): Should log this latency to the NetLog.
- next_state_ = State::kCreateResolverComplete;
- return proxy_resolver_factory_->CreateProxyResolver(
- script_data_.data, proxy_resolver_,
- base::BindOnce(&InitProxyResolver::OnIOCompletion,
- base::Unretained(this)),
- &create_resolver_request_);
- }
- int DoCreateResolverComplete(int result) {
- if (result != OK)
- proxy_resolver_->reset();
- return result;
- }
- void OnIOCompletion(int result) {
- DCHECK_NE(State::kNone, next_state_);
- int rv = DoLoop(result);
- if (rv != ERR_IO_PENDING)
- std::move(callback_).Run(result);
- }
- ProxyConfigWithAnnotation config_;
- ProxyConfigWithAnnotation effective_config_;
- PacFileDataWithSource script_data_;
- base::TimeDelta wait_delay_;
- std::unique_ptr<PacFileDecider> decider_;
- raw_ptr<ProxyResolverFactory> proxy_resolver_factory_ = nullptr;
- std::unique_ptr<ProxyResolverFactory::Request> create_resolver_request_;
- raw_ptr<std::unique_ptr<ProxyResolver>> proxy_resolver_ = nullptr;
- CompletionOnceCallback callback_;
- State next_state_ = State::kNone;
- bool quick_check_enabled_ = true;
- };
- // ConfiguredProxyResolutionService::PacFileDeciderPoller
- // ---------------------------
- // This helper class encapsulates the logic to schedule and run periodic
- // background checks to see if the PAC script (or effective proxy configuration)
- // has changed. If a change is detected, then the caller will be notified via
- // the ChangeCallback.
- class ConfiguredProxyResolutionService::PacFileDeciderPoller {
- public:
- typedef base::RepeatingCallback<
- void(int, const PacFileDataWithSource&, const ProxyConfigWithAnnotation&)>
- ChangeCallback;
- // Builds a poller helper, and starts polling for updates. Whenever a change
- // is observed, |callback| will be invoked with the details.
- //
- // |config| specifies the (unresolved) proxy configuration to poll.
- // |proxy_resolver_expects_pac_bytes| the type of proxy resolver we expect
- // to use the resulting script data with
- // (so it can choose the right format).
- // |pac_file_fetcher| this pointer must remain alive throughout our
- // lifetime. It is the dependency that will be used
- // for downloading PAC files.
- // |dhcp_pac_file_fetcher| similar to |pac_file_fetcher|, but for
- // he DHCP dependency.
- // |init_net_error| This is the initial network error (possibly success)
- // encountered by the first PAC fetch attempt. We use it
- // to schedule updates more aggressively if the initial
- // fetch resulted in an error.
- // |init_script_data| the initial script data from the PAC fetch attempt.
- // This is the baseline used to determine when the
- // script's contents have changed.
- // |net_log| the NetLog to log progress into.
- PacFileDeciderPoller(ChangeCallback callback,
- const ProxyConfigWithAnnotation& config,
- bool proxy_resolver_expects_pac_bytes,
- PacFileFetcher* pac_file_fetcher,
- DhcpPacFileFetcher* dhcp_pac_file_fetcher,
- int init_net_error,
- const PacFileDataWithSource& init_script_data,
- NetLog* net_log)
- : change_callback_(callback),
- config_(config),
- proxy_resolver_expects_pac_bytes_(proxy_resolver_expects_pac_bytes),
- pac_file_fetcher_(pac_file_fetcher),
- dhcp_pac_file_fetcher_(dhcp_pac_file_fetcher),
- last_error_(init_net_error),
- last_script_data_(init_script_data),
- last_poll_time_(TimeTicks::Now()),
- net_log_(net_log) {
- // Set the initial poll delay.
- next_poll_mode_ = poll_policy()->GetNextDelay(
- last_error_, base::Seconds(-1), &next_poll_delay_);
- TryToStartNextPoll(false);
- }
- PacFileDeciderPoller(const PacFileDeciderPoller&) = delete;
- PacFileDeciderPoller& operator=(const PacFileDeciderPoller&) = delete;
- void OnLazyPoll() {
- // We have just been notified of network activity. Use this opportunity to
- // see if we can start our next poll.
- TryToStartNextPoll(true);
- }
- static const PacPollPolicy* set_policy(const PacPollPolicy* policy) {
- const PacPollPolicy* prev = poll_policy_;
- poll_policy_ = policy;
- return prev;
- }
- void set_quick_check_enabled(bool enabled) { quick_check_enabled_ = enabled; }
- bool quick_check_enabled() const { return quick_check_enabled_; }
- private:
- // Returns the effective poll policy (the one injected by unit-tests, or the
- // default).
- const PacPollPolicy* poll_policy() {
- if (poll_policy_)
- return poll_policy_;
- return &default_poll_policy_;
- }
- void StartPollTimer() {
- DCHECK(!decider_.get());
- base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE,
- base::BindOnce(&PacFileDeciderPoller::DoPoll,
- weak_factory_.GetWeakPtr()),
- next_poll_delay_);
- }
- void TryToStartNextPoll(bool triggered_by_activity) {
- switch (next_poll_mode_) {
- case PacPollPolicy::MODE_USE_TIMER:
- if (!triggered_by_activity)
- StartPollTimer();
- break;
- case PacPollPolicy::MODE_START_AFTER_ACTIVITY:
- if (triggered_by_activity && !decider_.get()) {
- base::TimeDelta elapsed_time = TimeTicks::Now() - last_poll_time_;
- if (elapsed_time >= next_poll_delay_)
- DoPoll();
- }
- break;
- }
- }
- void DoPoll() {
- last_poll_time_ = TimeTicks::Now();
- // Start the PAC file decider to see if anything has changed.
- decider_ = std::make_unique<PacFileDecider>(
- pac_file_fetcher_, dhcp_pac_file_fetcher_, net_log_);
- decider_->set_quick_check_enabled(quick_check_enabled_);
- int result = decider_->Start(
- config_, base::TimeDelta(), proxy_resolver_expects_pac_bytes_,
- base::BindOnce(&PacFileDeciderPoller::OnPacFileDeciderCompleted,
- base::Unretained(this)));
- if (result != ERR_IO_PENDING)
- OnPacFileDeciderCompleted(result);
- }
- void OnPacFileDeciderCompleted(int result) {
- if (HasScriptDataChanged(result, decider_->script_data())) {
- // Something has changed, we must notify the
- // ConfiguredProxyResolutionService so it can re-initialize its
- // ProxyResolver. Note that we post a notification task rather than
- // calling it directly -- this is done to avoid an ugly destruction
- // sequence, since |this| might be destroyed as a result of the
- // notification.
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::BindOnce(
- &PacFileDeciderPoller::NotifyProxyResolutionServiceOfChange,
- weak_factory_.GetWeakPtr(), result, decider_->script_data(),
- decider_->effective_config()));
- return;
- }
- decider_.reset();
- // Decide when the next poll should take place, and possibly start the
- // next timer.
- next_poll_mode_ = poll_policy()->GetNextDelay(last_error_, next_poll_delay_,
- &next_poll_delay_);
- TryToStartNextPoll(false);
- }
- bool HasScriptDataChanged(int result,
- const PacFileDataWithSource& script_data) {
- if (result != last_error_) {
- // Something changed -- it was failing before and now it succeeded, or
- // conversely it succeeded before and now it failed. Or it failed in
- // both cases, however the specific failure error codes differ.
- return true;
- }
- if (result != OK) {
- // If it failed last time and failed again with the same error code this
- // time, then nothing has actually changed.
- return false;
- }
- // Otherwise if it succeeded both this time and last time, we need to look
- // closer and see if we ended up downloading different content for the PAC
- // script.
- return !script_data.data->Equals(last_script_data_.data.get()) ||
- (script_data.from_auto_detect != last_script_data_.from_auto_detect);
- }
- void NotifyProxyResolutionServiceOfChange(
- int result,
- const PacFileDataWithSource& script_data,
- const ProxyConfigWithAnnotation& effective_config) {
- // Note that |this| may be deleted after calling into the
- // ConfiguredProxyResolutionService.
- change_callback_.Run(result, script_data, effective_config);
- }
- ChangeCallback change_callback_;
- ProxyConfigWithAnnotation config_;
- bool proxy_resolver_expects_pac_bytes_;
- raw_ptr<PacFileFetcher> pac_file_fetcher_;
- raw_ptr<DhcpPacFileFetcher> dhcp_pac_file_fetcher_;
- int last_error_;
- PacFileDataWithSource last_script_data_;
- std::unique_ptr<PacFileDecider> decider_;
- base::TimeDelta next_poll_delay_;
- PacPollPolicy::Mode next_poll_mode_;
- TimeTicks last_poll_time_;
- const raw_ptr<NetLog> net_log_;
- // Polling policy injected by unit-tests. Otherwise this is nullptr and the
- // default policy will be used.
- static const PacPollPolicy* poll_policy_;
- const DefaultPollPolicy default_poll_policy_;
- bool quick_check_enabled_;
- base::WeakPtrFactory<PacFileDeciderPoller> weak_factory_{this};
- };
- // static
- const ConfiguredProxyResolutionService::PacPollPolicy*
- ConfiguredProxyResolutionService::PacFileDeciderPoller::poll_policy_ =
- nullptr;
- // ConfiguredProxyResolutionService
- // -----------------------------------------------------
- ConfiguredProxyResolutionService::ConfiguredProxyResolutionService(
- std::unique_ptr<ProxyConfigService> config_service,
- std::unique_ptr<ProxyResolverFactory> resolver_factory,
- NetLog* net_log,
- bool quick_check_enabled)
- : config_service_(std::move(config_service)),
- resolver_factory_(std::move(resolver_factory)),
- net_log_(net_log),
- stall_proxy_auto_config_delay_(
- base::Milliseconds(kDelayAfterNetworkChangesMs)),
- quick_check_enabled_(quick_check_enabled) {
- NetworkChangeNotifier::AddIPAddressObserver(this);
- NetworkChangeNotifier::AddDNSObserver(this);
- config_service_->AddObserver(this);
- }
- // static
- std::unique_ptr<ConfiguredProxyResolutionService>
- ConfiguredProxyResolutionService::CreateUsingSystemProxyResolver(
- std::unique_ptr<ProxyConfigService> proxy_config_service,
- NetLog* net_log,
- bool quick_check_enabled) {
- DCHECK(proxy_config_service);
- if (!ProxyResolverFactoryForSystem::IsSupported()) {
- VLOG(1) << "PAC support disabled because there is no system implementation";
- return CreateWithoutProxyResolver(std::move(proxy_config_service), net_log);
- }
- std::unique_ptr<ConfiguredProxyResolutionService> proxy_resolution_service =
- std::make_unique<ConfiguredProxyResolutionService>(
- std::move(proxy_config_service),
- std::make_unique<ProxyResolverFactoryForSystem>(
- kDefaultNumPacThreads),
- net_log, quick_check_enabled);
- return proxy_resolution_service;
- }
- // static
- std::unique_ptr<ConfiguredProxyResolutionService>
- ConfiguredProxyResolutionService::CreateWithoutProxyResolver(
- std::unique_ptr<ProxyConfigService> proxy_config_service,
- NetLog* net_log) {
- return std::make_unique<ConfiguredProxyResolutionService>(
- std::move(proxy_config_service),
- std::make_unique<ProxyResolverFactoryForNullResolver>(), net_log,
- /*quick_check_enabled=*/false);
- }
- // static
- std::unique_ptr<ConfiguredProxyResolutionService>
- ConfiguredProxyResolutionService::CreateFixedForTest(
- const ProxyConfigWithAnnotation& pc) {
- // TODO(eroman): This isn't quite right, won't work if |pc| specifies
- // a PAC script.
- return CreateUsingSystemProxyResolver(
- std::make_unique<ProxyConfigServiceFixed>(pc), nullptr,
- /*quick_check_enabled=*/true);
- }
- // static
- std::unique_ptr<ConfiguredProxyResolutionService>
- ConfiguredProxyResolutionService::CreateFixedForTest(
- const std::string& proxy,
- const NetworkTrafficAnnotationTag& traffic_annotation) {
- ProxyConfig proxy_config;
- proxy_config.proxy_rules().ParseFromString(proxy);
- ProxyConfigWithAnnotation annotated_config(proxy_config, traffic_annotation);
- return ConfiguredProxyResolutionService::CreateFixedForTest(annotated_config);
- }
- // static
- std::unique_ptr<ConfiguredProxyResolutionService>
- ConfiguredProxyResolutionService::CreateDirect() {
- // Use direct connections.
- return std::make_unique<ConfiguredProxyResolutionService>(
- std::make_unique<ProxyConfigServiceDirect>(),
- std::make_unique<ProxyResolverFactoryForNullResolver>(), nullptr,
- /*quick_check_enabled=*/true);
- }
- // static
- std::unique_ptr<ConfiguredProxyResolutionService>
- ConfiguredProxyResolutionService::CreateFixedFromPacResultForTest(
- const std::string& pac_string,
- const NetworkTrafficAnnotationTag& traffic_annotation) {
- // We need the settings to contain an "automatic" setting, otherwise the
- // ProxyResolver dependency we give it will never be used.
- auto proxy_config_service = std::make_unique<ProxyConfigServiceFixed>(
- ProxyConfigWithAnnotation(ProxyConfig::CreateFromCustomPacURL(GURL(
- "https://my-pac-script.invalid/wpad.dat")),
- traffic_annotation));
- return std::make_unique<ConfiguredProxyResolutionService>(
- std::move(proxy_config_service),
- std::make_unique<ProxyResolverFactoryForPacResult>(pac_string), nullptr,
- /*quick_check_enabled=*/true);
- }
- // static
- std::unique_ptr<ConfiguredProxyResolutionService>
- ConfiguredProxyResolutionService::CreateFixedFromAutoDetectedPacResultForTest(
- const std::string& pac_string,
- const NetworkTrafficAnnotationTag& traffic_annotation) {
- auto proxy_config_service =
- std::make_unique<ProxyConfigServiceFixed>(ProxyConfigWithAnnotation(
- ProxyConfig::CreateAutoDetect(), traffic_annotation));
- return std::make_unique<ConfiguredProxyResolutionService>(
- std::move(proxy_config_service),
- std::make_unique<ProxyResolverFactoryForPacResult>(pac_string), nullptr,
- /*quick_check_enabled=*/true);
- }
- int ConfiguredProxyResolutionService::ResolveProxy(
- const GURL& raw_url,
- const std::string& method,
- const NetworkIsolationKey& network_isolation_key,
- ProxyInfo* result,
- CompletionOnceCallback callback,
- std::unique_ptr<ProxyResolutionRequest>* out_request,
- const NetLogWithSource& net_log) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DCHECK(!callback.is_null());
- DCHECK(out_request);
- net_log.BeginEvent(NetLogEventType::PROXY_RESOLUTION_SERVICE);
- // Notify our polling-based dependencies that a resolve is taking place.
- // This way they can schedule their polls in response to network activity.
- config_service_->OnLazyPoll();
- if (script_poller_.get())
- script_poller_->OnLazyPoll();
- if (current_state_ == STATE_NONE)
- ApplyProxyConfigIfAvailable();
- // Sanitize the URL before passing it on to the proxy resolver (i.e. PAC
- // script). The goal is to remove sensitive data (like embedded user names
- // and password), and local data (i.e. reference fragment) which does not need
- // to be disclosed to the resolver.
- GURL url = SanitizeUrl(raw_url);
- // Check if the request can be completed right away. (This is the case when
- // using a direct connection for example).
- int rv = TryToCompleteSynchronously(url, result);
- if (rv != ERR_IO_PENDING) {
- rv = DidFinishResolvingProxy(url, method, result, rv, net_log);
- return rv;
- }
- auto req = std::make_unique<ConfiguredProxyResolutionRequest>(
- this, url, method, network_isolation_key, result, std::move(callback),
- net_log);
- if (current_state_ == STATE_READY) {
- // Start the resolve request.
- rv = req->Start();
- if (rv != ERR_IO_PENDING)
- return req->QueryDidCompleteSynchronously(rv);
- } else {
- req->net_log()->BeginEvent(
- NetLogEventType::PROXY_RESOLUTION_SERVICE_WAITING_FOR_INIT_PAC);
- }
- DCHECK_EQ(ERR_IO_PENDING, rv);
- DCHECK(!ContainsPendingRequest(req.get()));
- pending_requests_.insert(req.get());
- // Completion will be notified through |callback|, unless the caller cancels
- // the request using |out_request|.
- *out_request = std::move(req);
- return rv; // ERR_IO_PENDING
- }
- int ConfiguredProxyResolutionService::TryToCompleteSynchronously(
- const GURL& url,
- ProxyInfo* result) {
- DCHECK_NE(STATE_NONE, current_state_);
- if (current_state_ != STATE_READY)
- return ERR_IO_PENDING; // Still initializing.
- DCHECK(config_);
- // If it was impossible to fetch or parse the PAC script, we cannot complete
- // the request here and bail out.
- if (permanent_error_ != OK) {
- // Before returning the permanent error check if the URL would have been
- // implicitly bypassed.
- if (ApplyPacBypassRules(url, result))
- return OK;
- return permanent_error_;
- }
- if (config_->value().HasAutomaticSettings())
- return ERR_IO_PENDING; // Must submit the request to the proxy resolver.
- // Use the manual proxy settings.
- config_->value().proxy_rules().Apply(url, result);
- result->set_traffic_annotation(
- MutableNetworkTrafficAnnotationTag(config_->traffic_annotation()));
- return OK;
- }
- ConfiguredProxyResolutionService::~ConfiguredProxyResolutionService() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- NetworkChangeNotifier::RemoveIPAddressObserver(this);
- NetworkChangeNotifier::RemoveDNSObserver(this);
- config_service_->RemoveObserver(this);
- // Cancel any inprogress requests.
- // This cancels the internal requests, but leaves the responsibility of
- // canceling the high-level Request (by deleting it) to the client.
- // Since |pending_requests_| might be modified in one of the requests'
- // callbacks (if it deletes another request), iterating through the set in a
- // for-loop will not work.
- while (!pending_requests_.empty()) {
- ConfiguredProxyResolutionRequest* req = *pending_requests_.begin();
- req->QueryComplete(ERR_ABORTED);
- pending_requests_.erase(req);
- }
- }
- void ConfiguredProxyResolutionService::SuspendAllPendingRequests() {
- for (ConfiguredProxyResolutionRequest* req : pending_requests_) {
- if (req->is_started()) {
- req->CancelResolveJob();
- req->net_log()->BeginEvent(
- NetLogEventType::PROXY_RESOLUTION_SERVICE_WAITING_FOR_INIT_PAC);
- }
- }
- }
- void ConfiguredProxyResolutionService::SetReady() {
- DCHECK(!init_proxy_resolver_.get());
- current_state_ = STATE_READY;
- // TODO(lilyhoughton): This is necessary because a callback invoked by
- // |StartAndCompleteCheckingForSynchronous()| might delete |this|. A better
- // solution would be to disallow synchronous callbacks altogether.
- base::WeakPtr<ConfiguredProxyResolutionService> weak_this =
- weak_ptr_factory_.GetWeakPtr();
- auto pending_requests_copy = pending_requests_;
- for (auto* req : pending_requests_copy) {
- if (!ContainsPendingRequest(req))
- continue;
- if (!req->is_started()) {
- req->net_log()->EndEvent(
- NetLogEventType::PROXY_RESOLUTION_SERVICE_WAITING_FOR_INIT_PAC);
- // Note that we re-check for synchronous completion, in case we are
- // no longer using a ProxyResolver (can happen if we fell-back to manual.)
- req->StartAndCompleteCheckingForSynchronous();
- if (!weak_this)
- return; // Synchronous callback deleted |this|
- }
- }
- }
- void ConfiguredProxyResolutionService::ApplyProxyConfigIfAvailable() {
- DCHECK_EQ(STATE_NONE, current_state_);
- config_service_->OnLazyPoll();
- // If we have already fetched the configuration, start applying it.
- if (fetched_config_) {
- InitializeUsingLastFetchedConfig();
- return;
- }
- // Otherwise we need to first fetch the configuration.
- current_state_ = STATE_WAITING_FOR_PROXY_CONFIG;
- // Retrieve the current proxy configuration from the ProxyConfigService.
- // If a configuration is not available yet, we will get called back later
- // by our ProxyConfigService::Observer once it changes.
- ProxyConfigWithAnnotation config;
- ProxyConfigService::ConfigAvailability availability =
- config_service_->GetLatestProxyConfig(&config);
- if (availability != ProxyConfigService::CONFIG_PENDING)
- OnProxyConfigChanged(config, availability);
- }
- void ConfiguredProxyResolutionService::OnInitProxyResolverComplete(int result) {
- DCHECK_EQ(STATE_WAITING_FOR_INIT_PROXY_RESOLVER, current_state_);
- DCHECK(init_proxy_resolver_.get());
- DCHECK(fetched_config_);
- DCHECK(fetched_config_->value().HasAutomaticSettings());
- config_ = init_proxy_resolver_->effective_config();
- // At this point we have decided which proxy settings to use (i.e. which PAC
- // script if any). We start up a background poller to periodically revisit
- // this decision. If the contents of the PAC script change, or if the
- // result of proxy auto-discovery changes, this poller will notice it and
- // will trigger a re-initialization using the newly discovered PAC.
- script_poller_ = std::make_unique<PacFileDeciderPoller>(
- base::BindRepeating(
- &ConfiguredProxyResolutionService::InitializeUsingDecidedConfig,
- base::Unretained(this)),
- fetched_config_.value(), resolver_factory_->expects_pac_bytes(),
- pac_file_fetcher_.get(), dhcp_pac_file_fetcher_.get(), result,
- init_proxy_resolver_->script_data(), net_log_);
- script_poller_->set_quick_check_enabled(quick_check_enabled_);
- init_proxy_resolver_.reset();
- if (result != OK) {
- if (fetched_config_->value().pac_mandatory()) {
- VLOG(1) << "Failed configuring with mandatory PAC script, blocking all "
- "traffic.";
- config_ = fetched_config_;
- result = ERR_MANDATORY_PROXY_CONFIGURATION_FAILED;
- } else {
- VLOG(1) << "Failed configuring with PAC script, falling-back to manual "
- "proxy servers.";
- ProxyConfig proxy_config = fetched_config_->value();
- proxy_config.ClearAutomaticSettings();
- config_ = ProxyConfigWithAnnotation(
- proxy_config, fetched_config_->traffic_annotation());
- result = OK;
- }
- }
- permanent_error_ = result;
- // Resume any requests which we had to defer until the PAC script was
- // downloaded.
- SetReady();
- }
- bool ConfiguredProxyResolutionService::MarkProxiesAsBadUntil(
- const ProxyInfo& result,
- base::TimeDelta retry_delay,
- const std::vector<ProxyServer>& additional_bad_proxies,
- const NetLogWithSource& net_log) {
- result.proxy_list().UpdateRetryInfoOnFallback(&proxy_retry_info_, retry_delay,
- false, additional_bad_proxies,
- OK, net_log);
- return result.proxy_list().size() > (additional_bad_proxies.size() + 1);
- }
- void ConfiguredProxyResolutionService::ReportSuccess(const ProxyInfo& result) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- const ProxyRetryInfoMap& new_retry_info = result.proxy_retry_info();
- if (new_retry_info.empty())
- return;
- for (const auto& iter : new_retry_info) {
- auto existing = proxy_retry_info_.find(iter.first);
- if (existing == proxy_retry_info_.end()) {
- proxy_retry_info_[iter.first] = iter.second;
- if (proxy_delegate_) {
- const ProxyServer& bad_proxy =
- ProxyUriToProxyServer(iter.first, ProxyServer::SCHEME_HTTP);
- const ProxyRetryInfo& proxy_retry_info = iter.second;
- proxy_delegate_->OnFallback(bad_proxy, proxy_retry_info.net_error);
- }
- } else if (existing->second.bad_until < iter.second.bad_until) {
- existing->second.bad_until = iter.second.bad_until;
- }
- }
- if (net_log_) {
- net_log_->AddGlobalEntry(NetLogEventType::BAD_PROXY_LIST_REPORTED, [&] {
- return NetLogBadProxyListParams(&new_retry_info);
- });
- }
- }
- bool ConfiguredProxyResolutionService::ContainsPendingRequest(
- ConfiguredProxyResolutionRequest* req) {
- return pending_requests_.count(req) == 1;
- }
- void ConfiguredProxyResolutionService::RemovePendingRequest(
- ConfiguredProxyResolutionRequest* req) {
- DCHECK(ContainsPendingRequest(req));
- pending_requests_.erase(req);
- }
- int ConfiguredProxyResolutionService::DidFinishResolvingProxy(
- const GURL& url,
- const std::string& method,
- ProxyInfo* result,
- int result_code,
- const NetLogWithSource& net_log) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- // Log the result of the proxy resolution.
- if (result_code == OK) {
- // Allow the proxy delegate to interpose on the resolution decision,
- // possibly modifying the ProxyInfo.
- if (proxy_delegate_)
- proxy_delegate_->OnResolveProxy(url, method, proxy_retry_info_, result);
- net_log.AddEvent(
- NetLogEventType::PROXY_RESOLUTION_SERVICE_RESOLVED_PROXY_LIST,
- [&] { return NetLogFinishedResolvingProxyParams(result); });
- // This check is done to only log the NetLog event when necessary, it's
- // not a performance optimization.
- if (!proxy_retry_info_.empty()) {
- result->DeprioritizeBadProxies(proxy_retry_info_);
- net_log.AddEvent(
- NetLogEventType::PROXY_RESOLUTION_SERVICE_DEPRIORITIZED_BAD_PROXIES,
- [&] { return NetLogFinishedResolvingProxyParams(result); });
- }
- } else {
- net_log.AddEventWithNetErrorCode(
- NetLogEventType::PROXY_RESOLUTION_SERVICE_RESOLVED_PROXY_LIST,
- result_code);
- bool reset_config = result_code == ERR_PAC_SCRIPT_TERMINATED;
- if (config_ && !config_->value().pac_mandatory()) {
- // Fall-back to direct when the proxy resolver fails. This corresponds
- // with a javascript runtime error in the PAC script.
- //
- // This implicit fall-back to direct matches Firefox 3.5 and
- // Internet Explorer 8. For more information, see:
- //
- // http://www.chromium.org/developers/design-documents/proxy-settings-fallback
- result->UseDirect();
- result_code = OK;
- // Allow the proxy delegate to interpose on the resolution decision,
- // possibly modifying the ProxyInfo.
- if (proxy_delegate_)
- proxy_delegate_->OnResolveProxy(url, method, proxy_retry_info_, result);
- } else {
- result_code = ERR_MANDATORY_PROXY_CONFIGURATION_FAILED;
- }
- if (reset_config) {
- ResetProxyConfig(false);
- // If the ProxyResolver crashed, force it to be re-initialized for the
- // next request by resetting the proxy config. If there are other pending
- // requests, trigger the recreation immediately so those requests retry.
- if (pending_requests_.size() > 1)
- ApplyProxyConfigIfAvailable();
- }
- }
- net_log.EndEvent(NetLogEventType::PROXY_RESOLUTION_SERVICE);
- return result_code;
- }
- void ConfiguredProxyResolutionService::SetPacFileFetchers(
- std::unique_ptr<PacFileFetcher> pac_file_fetcher,
- std::unique_ptr<DhcpPacFileFetcher> dhcp_pac_file_fetcher) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- State previous_state = ResetProxyConfig(false);
- pac_file_fetcher_ = std::move(pac_file_fetcher);
- dhcp_pac_file_fetcher_ = std::move(dhcp_pac_file_fetcher);
- if (previous_state != STATE_NONE)
- ApplyProxyConfigIfAvailable();
- }
- void ConfiguredProxyResolutionService::SetProxyDelegate(
- ProxyDelegate* delegate) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DCHECK(!proxy_delegate_ || !delegate);
- proxy_delegate_ = delegate;
- }
- void ConfiguredProxyResolutionService::OnShutdown() {
- // Order here does not matter for correctness. |init_proxy_resolver_| is first
- // because shutting it down also cancels its requests using the fetcher.
- if (init_proxy_resolver_)
- init_proxy_resolver_->OnShutdown();
- if (pac_file_fetcher_)
- pac_file_fetcher_->OnShutdown();
- if (dhcp_pac_file_fetcher_)
- dhcp_pac_file_fetcher_->OnShutdown();
- }
- const ProxyRetryInfoMap& ConfiguredProxyResolutionService::proxy_retry_info()
- const {
- return proxy_retry_info_;
- }
- void ConfiguredProxyResolutionService::ClearBadProxiesCache() {
- proxy_retry_info_.clear();
- }
- PacFileFetcher* ConfiguredProxyResolutionService::GetPacFileFetcher() const {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- return pac_file_fetcher_.get();
- }
- bool ConfiguredProxyResolutionService::GetLoadStateIfAvailable(
- LoadState* load_state) const {
- if (current_state_ == STATE_WAITING_FOR_INIT_PROXY_RESOLVER) {
- *load_state = init_proxy_resolver_->GetLoadState();
- return true;
- }
- return false;
- }
- ProxyResolver* ConfiguredProxyResolutionService::GetProxyResolver() const {
- return resolver_.get();
- }
- ConfiguredProxyResolutionService::State
- ConfiguredProxyResolutionService::ResetProxyConfig(bool reset_fetched_config) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- State previous_state = current_state_;
- permanent_error_ = OK;
- proxy_retry_info_.clear();
- script_poller_.reset();
- init_proxy_resolver_.reset();
- SuspendAllPendingRequests();
- resolver_.reset();
- config_ = absl::nullopt;
- if (reset_fetched_config)
- fetched_config_ = absl::nullopt;
- current_state_ = STATE_NONE;
- return previous_state;
- }
- void ConfiguredProxyResolutionService::ForceReloadProxyConfig() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- ResetProxyConfig(false);
- ApplyProxyConfigIfAvailable();
- }
- base::Value::Dict ConfiguredProxyResolutionService::GetProxyNetLogValues() {
- base::Value::Dict net_info_dict;
- // Log Proxy Settings.
- {
- base::Value::Dict dict;
- if (fetched_config_)
- dict.Set("original", fetched_config_->value().ToValue());
- if (config_)
- dict.Set("effective", config_->value().ToValue());
- net_info_dict.Set(kNetInfoProxySettings, std::move(dict));
- }
- // Log Bad Proxies.
- {
- base::Value list(base::Value::Type::LIST);
- for (const auto& it : proxy_retry_info_) {
- const std::string& proxy_uri = it.first;
- const ProxyRetryInfo& retry_info = it.second;
- base::Value::Dict dict;
- dict.Set("proxy_uri", proxy_uri);
- dict.Set("bad_until", NetLog::TickCountToString(retry_info.bad_until));
- list.Append(base::Value(std::move(dict)));
- }
- net_info_dict.Set(kNetInfoBadProxies, std::move(list));
- }
- return net_info_dict;
- }
- bool ConfiguredProxyResolutionService::CastToConfiguredProxyResolutionService(
- ConfiguredProxyResolutionService** configured_proxy_resolution_service) {
- *configured_proxy_resolution_service = this;
- return true;
- }
- // static
- const ConfiguredProxyResolutionService::PacPollPolicy*
- ConfiguredProxyResolutionService::set_pac_script_poll_policy(
- const PacPollPolicy* policy) {
- return PacFileDeciderPoller::set_policy(policy);
- }
- // static
- std::unique_ptr<ConfiguredProxyResolutionService::PacPollPolicy>
- ConfiguredProxyResolutionService::CreateDefaultPacPollPolicy() {
- return std::make_unique<DefaultPollPolicy>();
- }
- void ConfiguredProxyResolutionService::OnProxyConfigChanged(
- const ProxyConfigWithAnnotation& config,
- ProxyConfigService::ConfigAvailability availability) {
- // Retrieve the current proxy configuration from the ProxyConfigService.
- // If a configuration is not available yet, we will get called back later
- // by our ProxyConfigService::Observer once it changes.
- ProxyConfigWithAnnotation effective_config;
- switch (availability) {
- case ProxyConfigService::CONFIG_PENDING:
- // ProxyConfigService implementors should never pass CONFIG_PENDING.
- NOTREACHED() << "Proxy config change with CONFIG_PENDING availability!";
- return;
- case ProxyConfigService::CONFIG_VALID:
- effective_config = config;
- break;
- case ProxyConfigService::CONFIG_UNSET:
- effective_config = ProxyConfigWithAnnotation::CreateDirect();
- break;
- }
- // Emit the proxy settings change to the NetLog stream.
- if (net_log_) {
- net_log_->AddGlobalEntry(NetLogEventType::PROXY_CONFIG_CHANGED, [&] {
- return NetLogProxyConfigChangedParams(&fetched_config_,
- &effective_config);
- });
- }
- // Set the new configuration as the most recently fetched one.
- fetched_config_ = effective_config;
- InitializeUsingLastFetchedConfig();
- }
- bool ConfiguredProxyResolutionService::ApplyPacBypassRules(const GURL& url,
- ProxyInfo* results) {
- DCHECK(config_);
- if (ProxyBypassRules::MatchesImplicitRules(url)) {
- results->UseDirectWithBypassedProxy();
- return true;
- }
- return false;
- }
- void ConfiguredProxyResolutionService::InitializeUsingLastFetchedConfig() {
- ResetProxyConfig(false);
- DCHECK(fetched_config_);
- if (!fetched_config_->value().HasAutomaticSettings()) {
- config_ = fetched_config_;
- SetReady();
- return;
- }
- // Start downloading + testing the PAC scripts for this new configuration.
- current_state_ = STATE_WAITING_FOR_INIT_PROXY_RESOLVER;
- // If we changed networks recently, we should delay running proxy auto-config.
- base::TimeDelta wait_delay = stall_proxy_autoconfig_until_ - TimeTicks::Now();
- init_proxy_resolver_ = std::make_unique<InitProxyResolver>();
- init_proxy_resolver_->set_quick_check_enabled(quick_check_enabled_);
- int rv = init_proxy_resolver_->Start(
- &resolver_, resolver_factory_.get(), pac_file_fetcher_.get(),
- dhcp_pac_file_fetcher_.get(), net_log_, fetched_config_.value(),
- wait_delay,
- base::BindOnce(
- &ConfiguredProxyResolutionService::OnInitProxyResolverComplete,
- base::Unretained(this)));
- if (rv != ERR_IO_PENDING)
- OnInitProxyResolverComplete(rv);
- }
- void ConfiguredProxyResolutionService::InitializeUsingDecidedConfig(
- int decider_result,
- const PacFileDataWithSource& script_data,
- const ProxyConfigWithAnnotation& effective_config) {
- DCHECK(fetched_config_);
- DCHECK(fetched_config_->value().HasAutomaticSettings());
- ResetProxyConfig(false);
- current_state_ = STATE_WAITING_FOR_INIT_PROXY_RESOLVER;
- init_proxy_resolver_ = std::make_unique<InitProxyResolver>();
- int rv = init_proxy_resolver_->StartSkipDecider(
- &resolver_, resolver_factory_.get(), effective_config, decider_result,
- script_data,
- base::BindOnce(
- &ConfiguredProxyResolutionService::OnInitProxyResolverComplete,
- base::Unretained(this)));
- if (rv != ERR_IO_PENDING)
- OnInitProxyResolverComplete(rv);
- }
- void ConfiguredProxyResolutionService::OnIPAddressChanged() {
- // See the comment block by |kDelayAfterNetworkChangesMs| for info.
- stall_proxy_autoconfig_until_ =
- TimeTicks::Now() + stall_proxy_auto_config_delay_;
- // With a new network connection, using the proper proxy configuration for the
- // new connection may be essential for URL requests to work properly. Reset
- // the config to ensure new URL requests are blocked until the potential new
- // proxy configuration is loaded.
- State previous_state = ResetProxyConfig(false);
- if (previous_state != STATE_NONE)
- ApplyProxyConfigIfAvailable();
- }
- void ConfiguredProxyResolutionService::OnDNSChanged() {
- // Do not fully reset proxy config on DNS change notifications. Instead,
- // inform the poller that it would be a good time to check for changes.
- //
- // While a change to DNS servers in use could lead to different WPAD results,
- // and thus a different proxy configuration, it is extremely unlikely to ever
- // be essential for that changed proxy configuration to be picked up
- // immediately. Either URL requests on the connection are generally working
- // fine without the proxy, or requests are already broken, leaving little harm
- // in letting a couple more requests fail until Chrome picks up the new proxy.
- if (script_poller_.get())
- script_poller_->OnLazyPoll();
- }
- } // namespace net
|