• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/proxy_resolution/configured_proxy_resolution_service.h"
6 
7 #include <algorithm>
8 #include <cmath>
9 #include <memory>
10 #include <utility>
11 
12 #include "base/compiler_specific.h"
13 #include "base/functional/bind.h"
14 #include "base/functional/callback_helpers.h"
15 #include "base/location.h"
16 #include "base/logging.h"
17 #include "base/memory/raw_ptr.h"
18 #include "base/memory/weak_ptr.h"
19 #include "base/metrics/histogram_macros.h"
20 #include "base/strings/string_util.h"
21 #include "base/task/single_thread_task_runner.h"
22 #include "base/time/time.h"
23 #include "base/values.h"
24 #include "build/build_config.h"
25 #include "build/chromeos_buildflags.h"
26 #include "net/base/net_errors.h"
27 #include "net/base/net_info_source_list.h"
28 #include "net/base/network_anonymization_key.h"
29 #include "net/base/proxy_delegate.h"
30 #include "net/base/proxy_server.h"
31 #include "net/base/proxy_string_util.h"
32 #include "net/base/url_util.h"
33 #include "net/log/net_log.h"
34 #include "net/log/net_log_event_type.h"
35 #include "net/log/net_log_util.h"
36 #include "net/log/net_log_with_source.h"
37 #include "net/proxy_resolution/configured_proxy_resolution_request.h"
38 #include "net/proxy_resolution/dhcp_pac_file_fetcher.h"
39 #include "net/proxy_resolution/multi_threaded_proxy_resolver.h"
40 #include "net/proxy_resolution/pac_file_decider.h"
41 #include "net/proxy_resolution/pac_file_fetcher.h"
42 #include "net/proxy_resolution/proxy_config_service_fixed.h"
43 #include "net/proxy_resolution/proxy_resolver_factory.h"
44 #include "net/url_request/url_request_context.h"
45 
46 #if BUILDFLAG(IS_WIN)
47 #include "net/proxy_resolution/win/proxy_resolver_winhttp.h"
48 #elif BUILDFLAG(IS_IOS)
49 #include "net/proxy_resolution/proxy_resolver_mac.h"
50 #elif BUILDFLAG(IS_MAC)
51 #include "net/proxy_resolution/proxy_resolver_mac.h"
52 #endif
53 
54 using base::TimeTicks;
55 
56 namespace net {
57 
58 namespace {
59 
60 const size_t kDefaultNumPacThreads = 4;
61 
62 // When the IP address changes we don't immediately re-run proxy auto-config.
63 // Instead, we  wait for |kDelayAfterNetworkChangesMs| before
64 // attempting to re-valuate proxy auto-config.
65 //
66 // During this time window, any resolve requests sent to the
67 // ConfiguredProxyResolutionService will be queued. Once we have waited the
68 // required amount of them, the proxy auto-config step will be run, and the
69 // queued requests resumed.
70 //
71 // The reason we play this game is that our signal for detecting network
72 // changes (NetworkChangeNotifier) may fire *before* the system's networking
73 // dependencies are fully configured. This is a problem since it means if
74 // we were to run proxy auto-config right away, it could fail due to spurious
75 // DNS failures. (see http://crbug.com/50779 for more details.)
76 //
77 // By adding the wait window, we give things a better chance to get properly
78 // set up. Network failures can happen at any time though, so we additionally
79 // poll the PAC script for changes, which will allow us to recover from these
80 // sorts of problems.
81 const int64_t kDelayAfterNetworkChangesMs = 2000;
82 
83 // This is the default policy for polling the PAC script.
84 //
85 // In response to a failure, the poll intervals are:
86 //    0: 8 seconds  (scheduled on timer)
87 //    1: 32 seconds
88 //    2: 2 minutes
89 //    3+: 4 hours
90 //
91 // In response to a success, the poll intervals are:
92 //    0+: 12 hours
93 //
94 // Only the 8 second poll is scheduled on a timer, the rest happen in response
95 // to network activity (and hence will take longer than the written time).
96 //
97 // Explanation for these values:
98 //
99 // TODO(eroman): These values are somewhat arbitrary, and need to be tuned
100 // using some histograms data. Trying to be conservative so as not to break
101 // existing setups when deployed. A simple exponential retry scheme would be
102 // more elegant, but places more load on server.
103 //
104 // The motivation for trying quickly after failures (8 seconds) is to recover
105 // from spurious network failures, which are common after the IP address has
106 // just changed (like DNS failing to resolve). The next 32 second boundary is
107 // to try and catch other VPN weirdness which anecdotally I have seen take
108 // 10+ seconds for some users.
109 //
110 // The motivation for re-trying after a success is to check for possible
111 // content changes to the script, or to the WPAD auto-discovery results. We are
112 // not very aggressive with these checks so as to minimize the risk of
113 // overloading existing PAC setups. Moreover it is unlikely that PAC scripts
114 // change very frequently in existing setups. More research is needed to
115 // motivate what safe values are here, and what other user agents do.
116 //
117 // Comparison to other browsers:
118 //
119 // In Firefox the PAC URL is re-tried on failures according to
120 // network.proxy.autoconfig_retry_interval_min and
121 // network.proxy.autoconfig_retry_interval_max. The defaults are 5 seconds and
122 // 5 minutes respectively. It doubles the interval at each attempt.
123 //
124 // TODO(eroman): Figure out what Internet Explorer does.
125 class DefaultPollPolicy
126     : public ConfiguredProxyResolutionService::PacPollPolicy {
127  public:
128   DefaultPollPolicy() = default;
129 
130   DefaultPollPolicy(const DefaultPollPolicy&) = delete;
131   DefaultPollPolicy& operator=(const DefaultPollPolicy&) = delete;
132 
GetNextDelay(int initial_error,base::TimeDelta current_delay,base::TimeDelta * next_delay) const133   Mode GetNextDelay(int initial_error,
134                     base::TimeDelta current_delay,
135                     base::TimeDelta* next_delay) const override {
136     if (initial_error != OK) {
137       // Re-try policy for failures.
138       const int kDelay1Seconds = 8;
139       const int kDelay2Seconds = 32;
140       const int kDelay3Seconds = 2 * 60;       // 2 minutes
141       const int kDelay4Seconds = 4 * 60 * 60;  // 4 Hours
142 
143       // Initial poll.
144       if (current_delay.is_negative()) {
145         *next_delay = base::Seconds(kDelay1Seconds);
146         return MODE_USE_TIMER;
147       }
148       switch (current_delay.InSeconds()) {
149         case kDelay1Seconds:
150           *next_delay = base::Seconds(kDelay2Seconds);
151           return MODE_START_AFTER_ACTIVITY;
152         case kDelay2Seconds:
153           *next_delay = base::Seconds(kDelay3Seconds);
154           return MODE_START_AFTER_ACTIVITY;
155         default:
156           *next_delay = base::Seconds(kDelay4Seconds);
157           return MODE_START_AFTER_ACTIVITY;
158       }
159     } else {
160       // Re-try policy for succeses.
161       *next_delay = base::Hours(12);
162       return MODE_START_AFTER_ACTIVITY;
163     }
164   }
165 };
166 
167 // Config getter that always returns direct settings.
168 class ProxyConfigServiceDirect : public ProxyConfigService {
169  public:
170   // ProxyConfigService implementation:
AddObserver(Observer * observer)171   void AddObserver(Observer* observer) override {}
RemoveObserver(Observer * observer)172   void RemoveObserver(Observer* observer) override {}
GetLatestProxyConfig(ProxyConfigWithAnnotation * config)173   ConfigAvailability GetLatestProxyConfig(
174       ProxyConfigWithAnnotation* config) override {
175     *config = ProxyConfigWithAnnotation::CreateDirect();
176     return CONFIG_VALID;
177   }
178 };
179 
180 // Proxy resolver that fails every time.
181 class ProxyResolverNull : public ProxyResolver {
182  public:
183   ProxyResolverNull() = default;
184 
185   // ProxyResolver implementation.
GetProxyForURL(const GURL & url,const NetworkAnonymizationKey & network_anonymization_key,ProxyInfo * results,CompletionOnceCallback callback,std::unique_ptr<Request> * request,const NetLogWithSource & net_log)186   int GetProxyForURL(const GURL& url,
187                      const NetworkAnonymizationKey& network_anonymization_key,
188                      ProxyInfo* results,
189                      CompletionOnceCallback callback,
190                      std::unique_ptr<Request>* request,
191                      const NetLogWithSource& net_log) override {
192     return ERR_NOT_IMPLEMENTED;
193   }
194 };
195 
196 // ProxyResolver that simulates a PAC script which returns
197 // |pac_string| for every single URL.
198 class ProxyResolverFromPacString : public ProxyResolver {
199  public:
ProxyResolverFromPacString(const std::string & pac_string)200   explicit ProxyResolverFromPacString(const std::string& pac_string)
201       : pac_string_(pac_string) {}
202 
GetProxyForURL(const GURL & url,const NetworkAnonymizationKey & network_anonymization_key,ProxyInfo * results,CompletionOnceCallback callback,std::unique_ptr<Request> * request,const NetLogWithSource & net_log)203   int GetProxyForURL(const GURL& url,
204                      const NetworkAnonymizationKey& network_anonymization_key,
205                      ProxyInfo* results,
206                      CompletionOnceCallback callback,
207                      std::unique_ptr<Request>* request,
208                      const NetLogWithSource& net_log) override {
209     results->UsePacString(pac_string_);
210     return OK;
211   }
212 
213  private:
214   const std::string pac_string_;
215 };
216 
217 // Creates ProxyResolvers using a platform-specific implementation.
218 class ProxyResolverFactoryForSystem : public MultiThreadedProxyResolverFactory {
219  public:
ProxyResolverFactoryForSystem(size_t max_num_threads)220   explicit ProxyResolverFactoryForSystem(size_t max_num_threads)
221       : MultiThreadedProxyResolverFactory(max_num_threads,
222                                           false /*expects_pac_bytes*/) {}
223 
224   ProxyResolverFactoryForSystem(const ProxyResolverFactoryForSystem&) = delete;
225   ProxyResolverFactoryForSystem& operator=(
226       const ProxyResolverFactoryForSystem&) = delete;
227 
CreateProxyResolverFactory()228   std::unique_ptr<ProxyResolverFactory> CreateProxyResolverFactory() override {
229 #if BUILDFLAG(IS_WIN)
230     return std::make_unique<ProxyResolverFactoryWinHttp>();
231 #elif BUILDFLAG(IS_APPLE)
232     return std::make_unique<ProxyResolverFactoryMac>();
233 #else
234     NOTREACHED();
235     return nullptr;
236 #endif
237   }
238 
IsSupported()239   static bool IsSupported() {
240 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE)
241     return true;
242 #else
243     return false;
244 #endif
245   }
246 };
247 
248 class ProxyResolverFactoryForNullResolver : public ProxyResolverFactory {
249  public:
ProxyResolverFactoryForNullResolver()250   ProxyResolverFactoryForNullResolver() : ProxyResolverFactory(false) {}
251 
252   ProxyResolverFactoryForNullResolver(
253       const ProxyResolverFactoryForNullResolver&) = delete;
254   ProxyResolverFactoryForNullResolver& operator=(
255       const ProxyResolverFactoryForNullResolver&) = delete;
256 
257   // ProxyResolverFactory overrides.
CreateProxyResolver(const scoped_refptr<PacFileData> & pac_script,std::unique_ptr<ProxyResolver> * resolver,CompletionOnceCallback callback,std::unique_ptr<Request> * request)258   int CreateProxyResolver(const scoped_refptr<PacFileData>& pac_script,
259                           std::unique_ptr<ProxyResolver>* resolver,
260                           CompletionOnceCallback callback,
261                           std::unique_ptr<Request>* request) override {
262     *resolver = std::make_unique<ProxyResolverNull>();
263     return OK;
264   }
265 };
266 
267 class ProxyResolverFactoryForPacResult : public ProxyResolverFactory {
268  public:
ProxyResolverFactoryForPacResult(const std::string & pac_string)269   explicit ProxyResolverFactoryForPacResult(const std::string& pac_string)
270       : ProxyResolverFactory(false), pac_string_(pac_string) {}
271 
272   ProxyResolverFactoryForPacResult(const ProxyResolverFactoryForPacResult&) =
273       delete;
274   ProxyResolverFactoryForPacResult& operator=(
275       const ProxyResolverFactoryForPacResult&) = delete;
276 
277   // ProxyResolverFactory override.
CreateProxyResolver(const scoped_refptr<PacFileData> & pac_script,std::unique_ptr<ProxyResolver> * resolver,CompletionOnceCallback callback,std::unique_ptr<Request> * request)278   int CreateProxyResolver(const scoped_refptr<PacFileData>& pac_script,
279                           std::unique_ptr<ProxyResolver>* resolver,
280                           CompletionOnceCallback callback,
281                           std::unique_ptr<Request>* request) override {
282     *resolver = std::make_unique<ProxyResolverFromPacString>(pac_string_);
283     return OK;
284   }
285 
286  private:
287   const std::string pac_string_;
288 };
289 
290 // Returns NetLog parameters describing a proxy configuration change.
NetLogProxyConfigChangedParams(const absl::optional<ProxyConfigWithAnnotation> * old_config,const ProxyConfigWithAnnotation * new_config)291 base::Value::Dict NetLogProxyConfigChangedParams(
292     const absl::optional<ProxyConfigWithAnnotation>* old_config,
293     const ProxyConfigWithAnnotation* new_config) {
294   base::Value::Dict dict;
295   // The "old_config" is optional -- the first notification will not have
296   // any "previous" configuration.
297   if (old_config->has_value())
298     dict.Set("old_config", (*old_config)->value().ToValue());
299   dict.Set("new_config", new_config->value().ToValue());
300   return dict;
301 }
302 
NetLogBadProxyListParams(const ProxyRetryInfoMap * retry_info)303 base::Value::Dict NetLogBadProxyListParams(
304     const ProxyRetryInfoMap* retry_info) {
305   base::Value::Dict dict;
306   base::Value::List list;
307 
308   for (const auto& retry_info_pair : *retry_info)
309     list.Append(retry_info_pair.first.ToDebugString());
310   dict.Set("bad_proxy_list", std::move(list));
311   return dict;
312 }
313 
314 // Returns NetLog parameters on a successful proxy resolution.
NetLogFinishedResolvingProxyParams(const ProxyInfo * result)315 base::Value::Dict NetLogFinishedResolvingProxyParams(const ProxyInfo* result) {
316   base::Value::Dict dict;
317   dict.Set("proxy_info", result->ToDebugString());
318   return dict;
319 }
320 
321 // Returns a sanitized copy of |url| which is safe to pass on to a PAC script.
322 //
323 // PAC scripts are modelled as being controllable by a network-present
324 // attacker (since such an attacker can influence the outcome of proxy
325 // auto-discovery, or modify the contents of insecurely delivered PAC scripts).
326 //
327 // As such, it is important that the full path/query of https:// URLs not be
328 // sent to PAC scripts, since that would give an attacker access to data that
329 // is ordinarily protected by TLS.
330 //
331 // Obscuring the path for http:// URLs isn't being done since it doesn't matter
332 // for security (attacker can already route traffic through their HTTP proxy
333 // and see the full URL for http:// requests).
334 //
335 // TODO(https://crbug.com/882536): Use the same stripping for insecure URL
336 // schemes.
SanitizeUrl(const GURL & url)337 GURL SanitizeUrl(const GURL& url) {
338   DCHECK(url.is_valid());
339 
340   GURL::Replacements replacements;
341   replacements.ClearUsername();
342   replacements.ClearPassword();
343   replacements.ClearRef();
344 
345   if (url.SchemeIsCryptographic()) {
346     replacements.ClearPath();
347     replacements.ClearQuery();
348   }
349 
350   return url.ReplaceComponents(replacements);
351 }
352 
353 }  // namespace
354 
355 // ConfiguredProxyResolutionService::InitProxyResolver
356 // ----------------------------------
357 
358 // This glues together two asynchronous steps:
359 //   (1) PacFileDecider -- try to fetch/validate a sequence of PAC scripts
360 //       to figure out what we should configure against.
361 //   (2) Feed the fetched PAC script into the ProxyResolver.
362 //
363 // InitProxyResolver is a single-use class which encapsulates cancellation as
364 // part of its destructor. Start() or StartSkipDecider() should be called just
365 // once. The instance can be destroyed at any time, and the request will be
366 // cancelled.
367 
368 class ConfiguredProxyResolutionService::InitProxyResolver {
369  public:
370   InitProxyResolver() = default;
371 
372   InitProxyResolver(const InitProxyResolver&) = delete;
373   InitProxyResolver& operator=(const InitProxyResolver&) = delete;
374 
375   // Note that the destruction of PacFileDecider will automatically cancel
376   // any outstanding work.
377   ~InitProxyResolver() = default;
378 
379   // Begins initializing the proxy resolver; calls |callback| when done. A
380   // ProxyResolver instance will be created using |proxy_resolver_factory| and
381   // assigned to |*proxy_resolver| if the final result is OK.
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)382   int Start(std::unique_ptr<ProxyResolver>* proxy_resolver,
383             ProxyResolverFactory* proxy_resolver_factory,
384             PacFileFetcher* pac_file_fetcher,
385             DhcpPacFileFetcher* dhcp_pac_file_fetcher,
386             NetLog* net_log,
387             const ProxyConfigWithAnnotation& config,
388             base::TimeDelta wait_delay,
389             CompletionOnceCallback callback) {
390     DCHECK_EQ(State::kNone, next_state_);
391     proxy_resolver_ = proxy_resolver;
392     proxy_resolver_factory_ = proxy_resolver_factory;
393 
394     decider_ = std::make_unique<PacFileDecider>(pac_file_fetcher,
395                                                 dhcp_pac_file_fetcher, net_log);
396     decider_->set_quick_check_enabled(quick_check_enabled_);
397     config_ = config;
398     wait_delay_ = wait_delay;
399     callback_ = std::move(callback);
400 
401     next_state_ = State::kDecidePacFile;
402     return DoLoop(OK);
403   }
404 
405   // Similar to Start(), however it skips the PacFileDecider stage. Instead
406   // |effective_config|, |decider_result| and |script_data| will be used as the
407   // inputs for initializing the ProxyResolver. A ProxyResolver instance will
408   // be created using |proxy_resolver_factory| and assigned to
409   // |*proxy_resolver| if the final result is OK.
StartSkipDecider(std::unique_ptr<ProxyResolver> * proxy_resolver,ProxyResolverFactory * proxy_resolver_factory,const ProxyConfigWithAnnotation & effective_config,int decider_result,const PacFileDataWithSource & script_data,CompletionOnceCallback callback)410   int StartSkipDecider(std::unique_ptr<ProxyResolver>* proxy_resolver,
411                        ProxyResolverFactory* proxy_resolver_factory,
412                        const ProxyConfigWithAnnotation& effective_config,
413                        int decider_result,
414                        const PacFileDataWithSource& script_data,
415                        CompletionOnceCallback callback) {
416     DCHECK_EQ(State::kNone, next_state_);
417     proxy_resolver_ = proxy_resolver;
418     proxy_resolver_factory_ = proxy_resolver_factory;
419 
420     effective_config_ = effective_config;
421     script_data_ = script_data;
422     callback_ = std::move(callback);
423 
424     if (decider_result != OK)
425       return decider_result;
426 
427     next_state_ = State::kCreateResolver;
428     return DoLoop(OK);
429   }
430 
431   // Returns the proxy configuration that was selected by PacFileDecider.
432   // Should only be called upon completion of the initialization.
effective_config() const433   const ProxyConfigWithAnnotation& effective_config() const {
434     DCHECK_EQ(State::kNone, next_state_);
435     return effective_config_;
436   }
437 
438   // Returns the PAC script data that was selected by PacFileDecider.
439   // Should only be called upon completion of the initialization.
script_data()440   const PacFileDataWithSource& script_data() {
441     DCHECK_EQ(State::kNone, next_state_);
442     return script_data_;
443   }
444 
GetLoadState() const445   LoadState GetLoadState() const {
446     if (next_state_ == State::kDecidePacFileComplete) {
447       // In addition to downloading, this state may also include the stall time
448       // after network change events (kDelayAfterNetworkChangesMs).
449       return LOAD_STATE_DOWNLOADING_PAC_FILE;
450     }
451     return LOAD_STATE_RESOLVING_PROXY_FOR_URL;
452   }
453 
454   // This must be called before the HostResolver is torn down.
OnShutdown()455   void OnShutdown() {
456     if (decider_)
457       decider_->OnShutdown();
458   }
459 
set_quick_check_enabled(bool enabled)460   void set_quick_check_enabled(bool enabled) { quick_check_enabled_ = enabled; }
quick_check_enabled() const461   bool quick_check_enabled() const { return quick_check_enabled_; }
462 
463  private:
464   enum class State {
465     kNone,
466     kDecidePacFile,
467     kDecidePacFileComplete,
468     kCreateResolver,
469     kCreateResolverComplete,
470   };
471 
DoLoop(int result)472   int DoLoop(int result) {
473     DCHECK_NE(next_state_, State::kNone);
474     int rv = result;
475     do {
476       State state = next_state_;
477       next_state_ = State::kNone;
478       switch (state) {
479         case State::kDecidePacFile:
480           DCHECK_EQ(OK, rv);
481           rv = DoDecidePacFile();
482           break;
483         case State::kDecidePacFileComplete:
484           rv = DoDecidePacFileComplete(rv);
485           break;
486         case State::kCreateResolver:
487           DCHECK_EQ(OK, rv);
488           rv = DoCreateResolver();
489           break;
490         case State::kCreateResolverComplete:
491           rv = DoCreateResolverComplete(rv);
492           break;
493         default:
494           NOTREACHED() << "bad state: " << static_cast<int>(state);
495           rv = ERR_UNEXPECTED;
496           break;
497       }
498     } while (rv != ERR_IO_PENDING && next_state_ != State::kNone);
499     return rv;
500   }
501 
DoDecidePacFile()502   int DoDecidePacFile() {
503     next_state_ = State::kDecidePacFileComplete;
504 
505     return decider_->Start(config_, wait_delay_,
506                            proxy_resolver_factory_->expects_pac_bytes(),
507                            base::BindOnce(&InitProxyResolver::OnIOCompletion,
508                                           base::Unretained(this)));
509   }
510 
DoDecidePacFileComplete(int result)511   int DoDecidePacFileComplete(int result) {
512     if (result != OK)
513       return result;
514 
515     effective_config_ = decider_->effective_config();
516     script_data_ = decider_->script_data();
517 
518     next_state_ = State::kCreateResolver;
519     return OK;
520   }
521 
DoCreateResolver()522   int DoCreateResolver() {
523     DCHECK(script_data_.data);
524     // TODO(eroman): Should log this latency to the NetLog.
525     next_state_ = State::kCreateResolverComplete;
526     return proxy_resolver_factory_->CreateProxyResolver(
527         script_data_.data, proxy_resolver_,
528         base::BindOnce(&InitProxyResolver::OnIOCompletion,
529                        base::Unretained(this)),
530         &create_resolver_request_);
531   }
532 
DoCreateResolverComplete(int result)533   int DoCreateResolverComplete(int result) {
534     if (result != OK)
535       proxy_resolver_->reset();
536     return result;
537   }
538 
OnIOCompletion(int result)539   void OnIOCompletion(int result) {
540     DCHECK_NE(State::kNone, next_state_);
541     int rv = DoLoop(result);
542     if (rv != ERR_IO_PENDING)
543       std::move(callback_).Run(result);
544   }
545 
546   ProxyConfigWithAnnotation config_;
547   ProxyConfigWithAnnotation effective_config_;
548   PacFileDataWithSource script_data_;
549   base::TimeDelta wait_delay_;
550   std::unique_ptr<PacFileDecider> decider_;
551   raw_ptr<ProxyResolverFactory> proxy_resolver_factory_ = nullptr;
552   std::unique_ptr<ProxyResolverFactory::Request> create_resolver_request_;
553   raw_ptr<std::unique_ptr<ProxyResolver>> proxy_resolver_ = nullptr;
554   CompletionOnceCallback callback_;
555   State next_state_ = State::kNone;
556   bool quick_check_enabled_ = true;
557 };
558 
559 // ConfiguredProxyResolutionService::PacFileDeciderPoller
560 // ---------------------------
561 
562 // This helper class encapsulates the logic to schedule and run periodic
563 // background checks to see if the PAC script (or effective proxy configuration)
564 // has changed. If a change is detected, then the caller will be notified via
565 // the ChangeCallback.
566 class ConfiguredProxyResolutionService::PacFileDeciderPoller {
567  public:
568   typedef base::RepeatingCallback<
569       void(int, const PacFileDataWithSource&, const ProxyConfigWithAnnotation&)>
570       ChangeCallback;
571 
572   // Builds a poller helper, and starts polling for updates. Whenever a change
573   // is observed, |callback| will be invoked with the details.
574   //
575   //   |config| specifies the (unresolved) proxy configuration to poll.
576   //   |proxy_resolver_expects_pac_bytes| the type of proxy resolver we expect
577   //                                      to use the resulting script data with
578   //                                      (so it can choose the right format).
579   //   |pac_file_fetcher| this pointer must remain alive throughout our
580   //                      lifetime. It is the dependency that will be used
581   //                      for downloading PAC files.
582   //   |dhcp_pac_file_fetcher| similar to |pac_file_fetcher|, but for
583   //                           he DHCP dependency.
584   //   |init_net_error| This is the initial network error (possibly success)
585   //                    encountered by the first PAC fetch attempt. We use it
586   //                    to schedule updates more aggressively if the initial
587   //                    fetch resulted in an error.
588   //   |init_script_data| the initial script data from the PAC fetch attempt.
589   //                      This is the baseline used to determine when the
590   //                      script's contents have changed.
591   //   |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)592   PacFileDeciderPoller(ChangeCallback callback,
593                        const ProxyConfigWithAnnotation& config,
594                        bool proxy_resolver_expects_pac_bytes,
595                        PacFileFetcher* pac_file_fetcher,
596                        DhcpPacFileFetcher* dhcp_pac_file_fetcher,
597                        int init_net_error,
598                        const PacFileDataWithSource& init_script_data,
599                        NetLog* net_log)
600       : change_callback_(callback),
601         config_(config),
602         proxy_resolver_expects_pac_bytes_(proxy_resolver_expects_pac_bytes),
603         pac_file_fetcher_(pac_file_fetcher),
604         dhcp_pac_file_fetcher_(dhcp_pac_file_fetcher),
605         last_error_(init_net_error),
606         last_script_data_(init_script_data),
607         last_poll_time_(TimeTicks::Now()),
608         net_log_(net_log) {
609     // Set the initial poll delay.
610     next_poll_mode_ = poll_policy()->GetNextDelay(
611         last_error_, base::Seconds(-1), &next_poll_delay_);
612     TryToStartNextPoll(false);
613   }
614 
615   PacFileDeciderPoller(const PacFileDeciderPoller&) = delete;
616   PacFileDeciderPoller& operator=(const PacFileDeciderPoller&) = delete;
617 
OnLazyPoll()618   void OnLazyPoll() {
619     // We have just been notified of network activity. Use this opportunity to
620     // see if we can start our next poll.
621     TryToStartNextPoll(true);
622   }
623 
set_policy(const PacPollPolicy * policy)624   static const PacPollPolicy* set_policy(const PacPollPolicy* policy) {
625     const PacPollPolicy* prev = poll_policy_;
626     poll_policy_ = policy;
627     return prev;
628   }
629 
set_quick_check_enabled(bool enabled)630   void set_quick_check_enabled(bool enabled) { quick_check_enabled_ = enabled; }
quick_check_enabled() const631   bool quick_check_enabled() const { return quick_check_enabled_; }
632 
633  private:
634   // Returns the effective poll policy (the one injected by unit-tests, or the
635   // default).
poll_policy()636   const PacPollPolicy* poll_policy() {
637     if (poll_policy_)
638       return poll_policy_;
639     return &default_poll_policy_;
640   }
641 
StartPollTimer()642   void StartPollTimer() {
643     DCHECK(!decider_.get());
644 
645     base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
646         FROM_HERE,
647         base::BindOnce(&PacFileDeciderPoller::DoPoll,
648                        weak_factory_.GetWeakPtr()),
649         next_poll_delay_);
650   }
651 
TryToStartNextPoll(bool triggered_by_activity)652   void TryToStartNextPoll(bool triggered_by_activity) {
653     switch (next_poll_mode_) {
654       case PacPollPolicy::MODE_USE_TIMER:
655         if (!triggered_by_activity)
656           StartPollTimer();
657         break;
658 
659       case PacPollPolicy::MODE_START_AFTER_ACTIVITY:
660         if (triggered_by_activity && !decider_.get()) {
661           base::TimeDelta elapsed_time = TimeTicks::Now() - last_poll_time_;
662           if (elapsed_time >= next_poll_delay_)
663             DoPoll();
664         }
665         break;
666     }
667   }
668 
DoPoll()669   void DoPoll() {
670     last_poll_time_ = TimeTicks::Now();
671 
672     // Start the PAC file decider to see if anything has changed.
673     decider_ = std::make_unique<PacFileDecider>(
674         pac_file_fetcher_, dhcp_pac_file_fetcher_, net_log_);
675     decider_->set_quick_check_enabled(quick_check_enabled_);
676     int result = decider_->Start(
677         config_, base::TimeDelta(), proxy_resolver_expects_pac_bytes_,
678         base::BindOnce(&PacFileDeciderPoller::OnPacFileDeciderCompleted,
679                        base::Unretained(this)));
680 
681     if (result != ERR_IO_PENDING)
682       OnPacFileDeciderCompleted(result);
683   }
684 
OnPacFileDeciderCompleted(int result)685   void OnPacFileDeciderCompleted(int result) {
686     if (HasScriptDataChanged(result, decider_->script_data())) {
687       // Something has changed, we must notify the
688       // ConfiguredProxyResolutionService so it can re-initialize its
689       // ProxyResolver. Note that we post a notification task rather than
690       // calling it directly -- this is done to avoid an ugly destruction
691       // sequence, since |this| might be destroyed as a result of the
692       // notification.
693       base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
694           FROM_HERE,
695           base::BindOnce(
696               &PacFileDeciderPoller::NotifyProxyResolutionServiceOfChange,
697               weak_factory_.GetWeakPtr(), result, decider_->script_data(),
698               decider_->effective_config()));
699       return;
700     }
701 
702     decider_.reset();
703 
704     // Decide when the next poll should take place, and possibly start the
705     // next timer.
706     next_poll_mode_ = poll_policy()->GetNextDelay(last_error_, next_poll_delay_,
707                                                   &next_poll_delay_);
708     TryToStartNextPoll(false);
709   }
710 
HasScriptDataChanged(int result,const PacFileDataWithSource & script_data)711   bool HasScriptDataChanged(int result,
712                             const PacFileDataWithSource& script_data) {
713     if (result != last_error_) {
714       // Something changed -- it was failing before and now it succeeded, or
715       // conversely it succeeded before and now it failed. Or it failed in
716       // both cases, however the specific failure error codes differ.
717       return true;
718     }
719 
720     if (result != OK) {
721       // If it failed last time and failed again with the same error code this
722       // time, then nothing has actually changed.
723       return false;
724     }
725 
726     // Otherwise if it succeeded both this time and last time, we need to look
727     // closer and see if we ended up downloading different content for the PAC
728     // script.
729     return !script_data.data->Equals(last_script_data_.data.get()) ||
730            (script_data.from_auto_detect != last_script_data_.from_auto_detect);
731   }
732 
NotifyProxyResolutionServiceOfChange(int result,const PacFileDataWithSource & script_data,const ProxyConfigWithAnnotation & effective_config)733   void NotifyProxyResolutionServiceOfChange(
734       int result,
735       const PacFileDataWithSource& script_data,
736       const ProxyConfigWithAnnotation& effective_config) {
737     // Note that |this| may be deleted after calling into the
738     // ConfiguredProxyResolutionService.
739     change_callback_.Run(result, script_data, effective_config);
740   }
741 
742   ChangeCallback change_callback_;
743   ProxyConfigWithAnnotation config_;
744   bool proxy_resolver_expects_pac_bytes_;
745   raw_ptr<PacFileFetcher> pac_file_fetcher_;
746   raw_ptr<DhcpPacFileFetcher> dhcp_pac_file_fetcher_;
747 
748   int last_error_;
749   PacFileDataWithSource last_script_data_;
750 
751   std::unique_ptr<PacFileDecider> decider_;
752   base::TimeDelta next_poll_delay_;
753   PacPollPolicy::Mode next_poll_mode_;
754 
755   TimeTicks last_poll_time_;
756 
757   const raw_ptr<NetLog> net_log_;
758 
759   // Polling policy injected by unit-tests. Otherwise this is nullptr and the
760   // default policy will be used.
761   static const PacPollPolicy* poll_policy_;
762 
763   const DefaultPollPolicy default_poll_policy_;
764 
765   bool quick_check_enabled_;
766 
767   base::WeakPtrFactory<PacFileDeciderPoller> weak_factory_{this};
768 };
769 
770 // static
771 const ConfiguredProxyResolutionService::PacPollPolicy*
772     ConfiguredProxyResolutionService::PacFileDeciderPoller::poll_policy_ =
773         nullptr;
774 
775 // ConfiguredProxyResolutionService
776 // -----------------------------------------------------
777 
ConfiguredProxyResolutionService(std::unique_ptr<ProxyConfigService> config_service,std::unique_ptr<ProxyResolverFactory> resolver_factory,NetLog * net_log,bool quick_check_enabled)778 ConfiguredProxyResolutionService::ConfiguredProxyResolutionService(
779     std::unique_ptr<ProxyConfigService> config_service,
780     std::unique_ptr<ProxyResolverFactory> resolver_factory,
781     NetLog* net_log,
782     bool quick_check_enabled)
783     : config_service_(std::move(config_service)),
784       resolver_factory_(std::move(resolver_factory)),
785       net_log_(net_log),
786       stall_proxy_auto_config_delay_(
787           base::Milliseconds(kDelayAfterNetworkChangesMs)),
788       quick_check_enabled_(quick_check_enabled) {
789   NetworkChangeNotifier::AddIPAddressObserver(this);
790   NetworkChangeNotifier::AddDNSObserver(this);
791   config_service_->AddObserver(this);
792 }
793 
794 // static
795 std::unique_ptr<ConfiguredProxyResolutionService>
CreateUsingSystemProxyResolver(std::unique_ptr<ProxyConfigService> proxy_config_service,NetLog * net_log,bool quick_check_enabled)796 ConfiguredProxyResolutionService::CreateUsingSystemProxyResolver(
797     std::unique_ptr<ProxyConfigService> proxy_config_service,
798     NetLog* net_log,
799     bool quick_check_enabled) {
800   DCHECK(proxy_config_service);
801 
802   if (!ProxyResolverFactoryForSystem::IsSupported()) {
803     VLOG(1) << "PAC support disabled because there is no system implementation";
804     return CreateWithoutProxyResolver(std::move(proxy_config_service), net_log);
805   }
806 
807   std::unique_ptr<ConfiguredProxyResolutionService> proxy_resolution_service =
808       std::make_unique<ConfiguredProxyResolutionService>(
809           std::move(proxy_config_service),
810           std::make_unique<ProxyResolverFactoryForSystem>(
811               kDefaultNumPacThreads),
812           net_log, quick_check_enabled);
813   return proxy_resolution_service;
814 }
815 
816 // static
817 std::unique_ptr<ConfiguredProxyResolutionService>
CreateWithoutProxyResolver(std::unique_ptr<ProxyConfigService> proxy_config_service,NetLog * net_log)818 ConfiguredProxyResolutionService::CreateWithoutProxyResolver(
819     std::unique_ptr<ProxyConfigService> proxy_config_service,
820     NetLog* net_log) {
821   return std::make_unique<ConfiguredProxyResolutionService>(
822       std::move(proxy_config_service),
823       std::make_unique<ProxyResolverFactoryForNullResolver>(), net_log,
824       /*quick_check_enabled=*/false);
825 }
826 
827 // static
828 std::unique_ptr<ConfiguredProxyResolutionService>
CreateFixedForTest(const ProxyConfigWithAnnotation & pc)829 ConfiguredProxyResolutionService::CreateFixedForTest(
830     const ProxyConfigWithAnnotation& pc) {
831   // TODO(eroman): This isn't quite right, won't work if |pc| specifies
832   //               a PAC script.
833   return CreateUsingSystemProxyResolver(
834       std::make_unique<ProxyConfigServiceFixed>(pc), nullptr,
835       /*quick_check_enabled=*/true);
836 }
837 
838 // static
839 std::unique_ptr<ConfiguredProxyResolutionService>
CreateFixedForTest(const std::string & proxy,const NetworkTrafficAnnotationTag & traffic_annotation)840 ConfiguredProxyResolutionService::CreateFixedForTest(
841     const std::string& proxy,
842     const NetworkTrafficAnnotationTag& traffic_annotation) {
843   ProxyConfig proxy_config;
844   proxy_config.proxy_rules().ParseFromString(proxy);
845   ProxyConfigWithAnnotation annotated_config(proxy_config, traffic_annotation);
846   return ConfiguredProxyResolutionService::CreateFixedForTest(annotated_config);
847 }
848 
849 // static
850 std::unique_ptr<ConfiguredProxyResolutionService>
CreateDirect()851 ConfiguredProxyResolutionService::CreateDirect() {
852   // Use direct connections.
853   return std::make_unique<ConfiguredProxyResolutionService>(
854       std::make_unique<ProxyConfigServiceDirect>(),
855       std::make_unique<ProxyResolverFactoryForNullResolver>(), nullptr,
856       /*quick_check_enabled=*/true);
857 }
858 
859 // static
860 std::unique_ptr<ConfiguredProxyResolutionService>
CreateFixedFromPacResultForTest(const std::string & pac_string,const NetworkTrafficAnnotationTag & traffic_annotation)861 ConfiguredProxyResolutionService::CreateFixedFromPacResultForTest(
862     const std::string& pac_string,
863     const NetworkTrafficAnnotationTag& traffic_annotation) {
864   // We need the settings to contain an "automatic" setting, otherwise the
865   // ProxyResolver dependency we give it will never be used.
866   auto proxy_config_service = std::make_unique<ProxyConfigServiceFixed>(
867       ProxyConfigWithAnnotation(ProxyConfig::CreateFromCustomPacURL(GURL(
868                                     "https://my-pac-script.invalid/wpad.dat")),
869                                 traffic_annotation));
870 
871   return std::make_unique<ConfiguredProxyResolutionService>(
872       std::move(proxy_config_service),
873       std::make_unique<ProxyResolverFactoryForPacResult>(pac_string), nullptr,
874       /*quick_check_enabled=*/true);
875 }
876 
877 // static
878 std::unique_ptr<ConfiguredProxyResolutionService>
CreateFixedFromAutoDetectedPacResultForTest(const std::string & pac_string,const NetworkTrafficAnnotationTag & traffic_annotation)879 ConfiguredProxyResolutionService::CreateFixedFromAutoDetectedPacResultForTest(
880     const std::string& pac_string,
881     const NetworkTrafficAnnotationTag& traffic_annotation) {
882   auto proxy_config_service =
883       std::make_unique<ProxyConfigServiceFixed>(ProxyConfigWithAnnotation(
884           ProxyConfig::CreateAutoDetect(), traffic_annotation));
885 
886   return std::make_unique<ConfiguredProxyResolutionService>(
887       std::move(proxy_config_service),
888       std::make_unique<ProxyResolverFactoryForPacResult>(pac_string), nullptr,
889       /*quick_check_enabled=*/true);
890 }
891 
ResolveProxy(const GURL & raw_url,const std::string & method,const NetworkAnonymizationKey & network_anonymization_key,ProxyInfo * result,CompletionOnceCallback callback,std::unique_ptr<ProxyResolutionRequest> * out_request,const NetLogWithSource & net_log)892 int ConfiguredProxyResolutionService::ResolveProxy(
893     const GURL& raw_url,
894     const std::string& method,
895     const NetworkAnonymizationKey& network_anonymization_key,
896     ProxyInfo* result,
897     CompletionOnceCallback callback,
898     std::unique_ptr<ProxyResolutionRequest>* out_request,
899     const NetLogWithSource& net_log) {
900   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
901   DCHECK(!callback.is_null());
902   DCHECK(out_request);
903 
904   net_log.BeginEvent(NetLogEventType::PROXY_RESOLUTION_SERVICE);
905 
906   // Notify our polling-based dependencies that a resolve is taking place.
907   // This way they can schedule their polls in response to network activity.
908   config_service_->OnLazyPoll();
909   if (script_poller_.get())
910     script_poller_->OnLazyPoll();
911 
912   if (current_state_ == STATE_NONE)
913     ApplyProxyConfigIfAvailable();
914 
915   // Sanitize the URL before passing it on to the proxy resolver (i.e. PAC
916   // script). The goal is to remove sensitive data (like embedded user names
917   // and password), and local data (i.e. reference fragment) which does not need
918   // to be disclosed to the resolver.
919   GURL url = SanitizeUrl(raw_url);
920 
921   // Check if the request can be completed right away. (This is the case when
922   // using a direct connection for example).
923   int rv = TryToCompleteSynchronously(url, result);
924   if (rv != ERR_IO_PENDING) {
925     rv = DidFinishResolvingProxy(url, network_anonymization_key, method, result,
926                                  rv, net_log);
927     return rv;
928   }
929 
930   auto req = std::make_unique<ConfiguredProxyResolutionRequest>(
931       this, url, method, network_anonymization_key, result, std::move(callback),
932       net_log);
933 
934   if (current_state_ == STATE_READY) {
935     // Start the resolve request.
936     rv = req->Start();
937     if (rv != ERR_IO_PENDING)
938       return req->QueryDidCompleteSynchronously(rv);
939   } else {
940     req->net_log()->BeginEvent(
941         NetLogEventType::PROXY_RESOLUTION_SERVICE_WAITING_FOR_INIT_PAC);
942   }
943 
944   DCHECK_EQ(ERR_IO_PENDING, rv);
945   DCHECK(!ContainsPendingRequest(req.get()));
946   pending_requests_.insert(req.get());
947 
948   // Completion will be notified through |callback|, unless the caller cancels
949   // the request using |out_request|.
950   *out_request = std::move(req);
951   return rv;  // ERR_IO_PENDING
952 }
953 
TryToCompleteSynchronously(const GURL & url,ProxyInfo * result)954 int ConfiguredProxyResolutionService::TryToCompleteSynchronously(
955     const GURL& url,
956     ProxyInfo* result) {
957   DCHECK_NE(STATE_NONE, current_state_);
958 
959   if (current_state_ != STATE_READY)
960     return ERR_IO_PENDING;  // Still initializing.
961 
962   DCHECK(config_);
963   // If it was impossible to fetch or parse the PAC script, we cannot complete
964   // the request here and bail out.
965   if (permanent_error_ != OK) {
966     // Before returning the permanent error check if the URL would have been
967     // implicitly bypassed.
968     if (ApplyPacBypassRules(url, result))
969       return OK;
970     return permanent_error_;
971   }
972 
973   if (config_->value().HasAutomaticSettings())
974     return ERR_IO_PENDING;  // Must submit the request to the proxy resolver.
975 
976   // Use the manual proxy settings.
977   config_->value().proxy_rules().Apply(url, result);
978   result->set_traffic_annotation(
979       MutableNetworkTrafficAnnotationTag(config_->traffic_annotation()));
980 
981   return OK;
982 }
983 
~ConfiguredProxyResolutionService()984 ConfiguredProxyResolutionService::~ConfiguredProxyResolutionService() {
985   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
986   NetworkChangeNotifier::RemoveIPAddressObserver(this);
987   NetworkChangeNotifier::RemoveDNSObserver(this);
988   config_service_->RemoveObserver(this);
989 
990   // Cancel any inprogress requests.
991   // This cancels the internal requests, but leaves the responsibility of
992   // canceling the high-level Request (by deleting it) to the client.
993   // Since |pending_requests_| might be modified in one of the requests'
994   // callbacks (if it deletes another request), iterating through the set in a
995   // for-loop will not work.
996   while (!pending_requests_.empty()) {
997     ConfiguredProxyResolutionRequest* req = *pending_requests_.begin();
998     req->QueryComplete(ERR_ABORTED);
999     pending_requests_.erase(req);
1000   }
1001 }
1002 
SuspendAllPendingRequests()1003 void ConfiguredProxyResolutionService::SuspendAllPendingRequests() {
1004   for (ConfiguredProxyResolutionRequest* req : pending_requests_) {
1005     if (req->is_started()) {
1006       req->CancelResolveJob();
1007 
1008       req->net_log()->BeginEvent(
1009           NetLogEventType::PROXY_RESOLUTION_SERVICE_WAITING_FOR_INIT_PAC);
1010     }
1011   }
1012 }
1013 
SetReady()1014 void ConfiguredProxyResolutionService::SetReady() {
1015   DCHECK(!init_proxy_resolver_.get());
1016   current_state_ = STATE_READY;
1017 
1018   // TODO(lilyhoughton): This is necessary because a callback invoked by
1019   // |StartAndCompleteCheckingForSynchronous()| might delete |this|.  A better
1020   // solution would be to disallow synchronous callbacks altogether.
1021   base::WeakPtr<ConfiguredProxyResolutionService> weak_this =
1022       weak_ptr_factory_.GetWeakPtr();
1023 
1024   auto pending_requests_copy = pending_requests_;
1025   for (auto* req : pending_requests_copy) {
1026     if (!ContainsPendingRequest(req))
1027       continue;
1028 
1029     if (!req->is_started()) {
1030       req->net_log()->EndEvent(
1031           NetLogEventType::PROXY_RESOLUTION_SERVICE_WAITING_FOR_INIT_PAC);
1032 
1033       // Note that we re-check for synchronous completion, in case we are
1034       // no longer using a ProxyResolver (can happen if we fell-back to manual.)
1035       req->StartAndCompleteCheckingForSynchronous();
1036       if (!weak_this)
1037         return;  // Synchronous callback deleted |this|
1038     }
1039   }
1040 }
1041 
ApplyProxyConfigIfAvailable()1042 void ConfiguredProxyResolutionService::ApplyProxyConfigIfAvailable() {
1043   DCHECK_EQ(STATE_NONE, current_state_);
1044 
1045   config_service_->OnLazyPoll();
1046 
1047   // If we have already fetched the configuration, start applying it.
1048   if (fetched_config_) {
1049     InitializeUsingLastFetchedConfig();
1050     return;
1051   }
1052 
1053   // Otherwise we need to first fetch the configuration.
1054   current_state_ = STATE_WAITING_FOR_PROXY_CONFIG;
1055 
1056   // Retrieve the current proxy configuration from the ProxyConfigService.
1057   // If a configuration is not available yet, we will get called back later
1058   // by our ProxyConfigService::Observer once it changes.
1059   ProxyConfigWithAnnotation config;
1060   ProxyConfigService::ConfigAvailability availability =
1061       config_service_->GetLatestProxyConfig(&config);
1062   if (availability != ProxyConfigService::CONFIG_PENDING)
1063     OnProxyConfigChanged(config, availability);
1064 }
1065 
OnInitProxyResolverComplete(int result)1066 void ConfiguredProxyResolutionService::OnInitProxyResolverComplete(int result) {
1067   DCHECK_EQ(STATE_WAITING_FOR_INIT_PROXY_RESOLVER, current_state_);
1068   DCHECK(init_proxy_resolver_.get());
1069   DCHECK(fetched_config_);
1070   DCHECK(fetched_config_->value().HasAutomaticSettings());
1071   config_ = init_proxy_resolver_->effective_config();
1072 
1073   // At this point we have decided which proxy settings to use (i.e. which PAC
1074   // script if any). We start up a background poller to periodically revisit
1075   // this decision. If the contents of the PAC script change, or if the
1076   // result of proxy auto-discovery changes, this poller will notice it and
1077   // will trigger a re-initialization using the newly discovered PAC.
1078   script_poller_ = std::make_unique<PacFileDeciderPoller>(
1079       base::BindRepeating(
1080           &ConfiguredProxyResolutionService::InitializeUsingDecidedConfig,
1081           base::Unretained(this)),
1082       fetched_config_.value(), resolver_factory_->expects_pac_bytes(),
1083       pac_file_fetcher_.get(), dhcp_pac_file_fetcher_.get(), result,
1084       init_proxy_resolver_->script_data(), net_log_);
1085   script_poller_->set_quick_check_enabled(quick_check_enabled_);
1086 
1087   init_proxy_resolver_.reset();
1088 
1089   if (result != OK) {
1090     if (fetched_config_->value().pac_mandatory()) {
1091       VLOG(1) << "Failed configuring with mandatory PAC script, blocking all "
1092                  "traffic.";
1093       config_ = fetched_config_;
1094       result = ERR_MANDATORY_PROXY_CONFIGURATION_FAILED;
1095     } else {
1096       VLOG(1) << "Failed configuring with PAC script, falling-back to manual "
1097                  "proxy servers.";
1098       ProxyConfig proxy_config = fetched_config_->value();
1099       proxy_config.ClearAutomaticSettings();
1100       config_ = ProxyConfigWithAnnotation(
1101           proxy_config, fetched_config_->traffic_annotation());
1102       result = OK;
1103     }
1104   }
1105   permanent_error_ = result;
1106 
1107   // Resume any requests which we had to defer until the PAC script was
1108   // downloaded.
1109   SetReady();
1110 }
1111 
MarkProxiesAsBadUntil(const ProxyInfo & result,base::TimeDelta retry_delay,const std::vector<ProxyChain> & additional_bad_proxies,const NetLogWithSource & net_log)1112 bool ConfiguredProxyResolutionService::MarkProxiesAsBadUntil(
1113     const ProxyInfo& result,
1114     base::TimeDelta retry_delay,
1115     const std::vector<ProxyChain>& additional_bad_proxies,
1116     const NetLogWithSource& net_log) {
1117   result.proxy_list().UpdateRetryInfoOnFallback(&proxy_retry_info_, retry_delay,
1118                                                 false, additional_bad_proxies,
1119                                                 OK, net_log);
1120   return result.proxy_list().size() > (additional_bad_proxies.size() + 1);
1121 }
1122 
ReportSuccess(const ProxyInfo & result)1123 void ConfiguredProxyResolutionService::ReportSuccess(const ProxyInfo& result) {
1124   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1125 
1126   const ProxyRetryInfoMap& new_retry_info = result.proxy_retry_info();
1127   if (new_retry_info.empty())
1128     return;
1129 
1130   for (const auto& iter : new_retry_info) {
1131     auto existing = proxy_retry_info_.find(iter.first);
1132     if (existing == proxy_retry_info_.end()) {
1133       proxy_retry_info_[iter.first] = iter.second;
1134       if (proxy_delegate_) {
1135         const ProxyChain& bad_proxy = iter.first;
1136         DCHECK(!bad_proxy.is_direct());
1137         const ProxyRetryInfo& proxy_retry_info = iter.second;
1138         proxy_delegate_->OnFallback(bad_proxy, proxy_retry_info.net_error);
1139       }
1140     } else if (existing->second.bad_until < iter.second.bad_until) {
1141       existing->second.bad_until = iter.second.bad_until;
1142     }
1143   }
1144   if (net_log_) {
1145     net_log_->AddGlobalEntry(NetLogEventType::BAD_PROXY_LIST_REPORTED, [&] {
1146       return NetLogBadProxyListParams(&new_retry_info);
1147     });
1148   }
1149 }
1150 
ContainsPendingRequest(ConfiguredProxyResolutionRequest * req)1151 bool ConfiguredProxyResolutionService::ContainsPendingRequest(
1152     ConfiguredProxyResolutionRequest* req) {
1153   return pending_requests_.count(req) == 1;
1154 }
1155 
RemovePendingRequest(ConfiguredProxyResolutionRequest * req)1156 void ConfiguredProxyResolutionService::RemovePendingRequest(
1157     ConfiguredProxyResolutionRequest* req) {
1158   DCHECK(ContainsPendingRequest(req));
1159   pending_requests_.erase(req);
1160 }
1161 
DidFinishResolvingProxy(const GURL & url,const NetworkAnonymizationKey & network_anonymization_key,const std::string & method,ProxyInfo * result,int result_code,const NetLogWithSource & net_log)1162 int ConfiguredProxyResolutionService::DidFinishResolvingProxy(
1163     const GURL& url,
1164     const NetworkAnonymizationKey& network_anonymization_key,
1165     const std::string& method,
1166     ProxyInfo* result,
1167     int result_code,
1168     const NetLogWithSource& net_log) {
1169   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1170 
1171   // Log the result of the proxy resolution.
1172   if (result_code == OK) {
1173     // Allow the proxy delegate to interpose on the resolution decision,
1174     // possibly modifying the ProxyInfo.
1175     if (proxy_delegate_)
1176       proxy_delegate_->OnResolveProxy(url, network_anonymization_key, method,
1177                                       proxy_retry_info_, result);
1178 
1179     net_log.AddEvent(
1180         NetLogEventType::PROXY_RESOLUTION_SERVICE_RESOLVED_PROXY_LIST,
1181         [&] { return NetLogFinishedResolvingProxyParams(result); });
1182 
1183     // This check is done to only log the NetLog event when necessary, it's
1184     // not a performance optimization.
1185     if (!proxy_retry_info_.empty()) {
1186       result->DeprioritizeBadProxyChains(proxy_retry_info_);
1187       net_log.AddEvent(
1188           NetLogEventType::PROXY_RESOLUTION_SERVICE_DEPRIORITIZED_BAD_PROXIES,
1189           [&] { return NetLogFinishedResolvingProxyParams(result); });
1190     }
1191   } else {
1192     net_log.AddEventWithNetErrorCode(
1193         NetLogEventType::PROXY_RESOLUTION_SERVICE_RESOLVED_PROXY_LIST,
1194         result_code);
1195 
1196     bool reset_config = result_code == ERR_PAC_SCRIPT_TERMINATED;
1197     if (config_ && !config_->value().pac_mandatory()) {
1198       // Fall-back to direct when the proxy resolver fails. This corresponds
1199       // with a javascript runtime error in the PAC script.
1200       //
1201       // This implicit fall-back to direct matches Firefox 3.5 and
1202       // Internet Explorer 8. For more information, see:
1203       //
1204       // http://www.chromium.org/developers/design-documents/proxy-settings-fallback
1205       result->UseDirect();
1206       result_code = OK;
1207 
1208       // Allow the proxy delegate to interpose on the resolution decision,
1209       // possibly modifying the ProxyInfo.
1210       if (proxy_delegate_)
1211         proxy_delegate_->OnResolveProxy(url, network_anonymization_key, method,
1212                                         proxy_retry_info_, result);
1213     } else {
1214       result_code = ERR_MANDATORY_PROXY_CONFIGURATION_FAILED;
1215     }
1216     if (reset_config) {
1217       ResetProxyConfig(false);
1218       // If the ProxyResolver crashed, force it to be re-initialized for the
1219       // next request by resetting the proxy config. If there are other pending
1220       // requests, trigger the recreation immediately so those requests retry.
1221       if (pending_requests_.size() > 1)
1222         ApplyProxyConfigIfAvailable();
1223     }
1224   }
1225 
1226   net_log.EndEvent(NetLogEventType::PROXY_RESOLUTION_SERVICE);
1227   return result_code;
1228 }
1229 
SetPacFileFetchers(std::unique_ptr<PacFileFetcher> pac_file_fetcher,std::unique_ptr<DhcpPacFileFetcher> dhcp_pac_file_fetcher)1230 void ConfiguredProxyResolutionService::SetPacFileFetchers(
1231     std::unique_ptr<PacFileFetcher> pac_file_fetcher,
1232     std::unique_ptr<DhcpPacFileFetcher> dhcp_pac_file_fetcher) {
1233   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1234   State previous_state = ResetProxyConfig(false);
1235   pac_file_fetcher_ = std::move(pac_file_fetcher);
1236   dhcp_pac_file_fetcher_ = std::move(dhcp_pac_file_fetcher);
1237   if (previous_state != STATE_NONE)
1238     ApplyProxyConfigIfAvailable();
1239 }
1240 
SetProxyDelegate(ProxyDelegate * delegate)1241 void ConfiguredProxyResolutionService::SetProxyDelegate(
1242     ProxyDelegate* delegate) {
1243   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1244   DCHECK(!proxy_delegate_ || !delegate);
1245   proxy_delegate_ = delegate;
1246 }
1247 
OnShutdown()1248 void ConfiguredProxyResolutionService::OnShutdown() {
1249   // Order here does not matter for correctness. |init_proxy_resolver_| is first
1250   // because shutting it down also cancels its requests using the fetcher.
1251   if (init_proxy_resolver_)
1252     init_proxy_resolver_->OnShutdown();
1253   if (pac_file_fetcher_)
1254     pac_file_fetcher_->OnShutdown();
1255   if (dhcp_pac_file_fetcher_)
1256     dhcp_pac_file_fetcher_->OnShutdown();
1257 }
1258 
proxy_retry_info() const1259 const ProxyRetryInfoMap& ConfiguredProxyResolutionService::proxy_retry_info()
1260     const {
1261   return proxy_retry_info_;
1262 }
1263 
ClearBadProxiesCache()1264 void ConfiguredProxyResolutionService::ClearBadProxiesCache() {
1265   proxy_retry_info_.clear();
1266 }
1267 
GetPacFileFetcher() const1268 PacFileFetcher* ConfiguredProxyResolutionService::GetPacFileFetcher() const {
1269   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1270   return pac_file_fetcher_.get();
1271 }
1272 
GetLoadStateIfAvailable(LoadState * load_state) const1273 bool ConfiguredProxyResolutionService::GetLoadStateIfAvailable(
1274     LoadState* load_state) const {
1275   if (current_state_ == STATE_WAITING_FOR_INIT_PROXY_RESOLVER) {
1276     *load_state = init_proxy_resolver_->GetLoadState();
1277     return true;
1278   }
1279 
1280   return false;
1281 }
1282 
GetProxyResolver() const1283 ProxyResolver* ConfiguredProxyResolutionService::GetProxyResolver() const {
1284   return resolver_.get();
1285 }
1286 
1287 ConfiguredProxyResolutionService::State
ResetProxyConfig(bool reset_fetched_config)1288 ConfiguredProxyResolutionService::ResetProxyConfig(bool reset_fetched_config) {
1289   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1290   State previous_state = current_state_;
1291 
1292   permanent_error_ = OK;
1293   proxy_retry_info_.clear();
1294   script_poller_.reset();
1295   init_proxy_resolver_.reset();
1296   SuspendAllPendingRequests();
1297   resolver_.reset();
1298   config_ = absl::nullopt;
1299   if (reset_fetched_config)
1300     fetched_config_ = absl::nullopt;
1301   current_state_ = STATE_NONE;
1302 
1303   return previous_state;
1304 }
1305 
ForceReloadProxyConfig()1306 void ConfiguredProxyResolutionService::ForceReloadProxyConfig() {
1307   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1308   ResetProxyConfig(false);
1309   ApplyProxyConfigIfAvailable();
1310 }
1311 
GetProxyNetLogValues()1312 base::Value::Dict ConfiguredProxyResolutionService::GetProxyNetLogValues() {
1313   base::Value::Dict net_info_dict;
1314 
1315   // Log Proxy Settings.
1316   {
1317     base::Value::Dict dict;
1318     if (fetched_config_)
1319       dict.Set("original", fetched_config_->value().ToValue());
1320     if (config_)
1321       dict.Set("effective", config_->value().ToValue());
1322 
1323     net_info_dict.Set(kNetInfoProxySettings, std::move(dict));
1324   }
1325 
1326   // Log Bad Proxies.
1327   {
1328     base::Value::List list;
1329 
1330     for (const auto& it : proxy_retry_info_) {
1331       const std::string& proxy_chain_uri = it.first.ToDebugString();
1332       const ProxyRetryInfo& retry_info = it.second;
1333 
1334       base::Value::Dict dict;
1335       dict.Set("proxy_chain_uri", proxy_chain_uri);
1336       dict.Set("bad_until", NetLog::TickCountToString(retry_info.bad_until));
1337 
1338       list.Append(base::Value(std::move(dict)));
1339     }
1340 
1341     net_info_dict.Set(kNetInfoBadProxies, std::move(list));
1342   }
1343 
1344   return net_info_dict;
1345 }
1346 
CastToConfiguredProxyResolutionService(ConfiguredProxyResolutionService ** configured_proxy_resolution_service)1347 bool ConfiguredProxyResolutionService::CastToConfiguredProxyResolutionService(
1348     ConfiguredProxyResolutionService** configured_proxy_resolution_service) {
1349   *configured_proxy_resolution_service = this;
1350   return true;
1351 }
1352 
1353 // static
1354 const ConfiguredProxyResolutionService::PacPollPolicy*
set_pac_script_poll_policy(const PacPollPolicy * policy)1355 ConfiguredProxyResolutionService::set_pac_script_poll_policy(
1356     const PacPollPolicy* policy) {
1357   return PacFileDeciderPoller::set_policy(policy);
1358 }
1359 
1360 // static
1361 std::unique_ptr<ConfiguredProxyResolutionService::PacPollPolicy>
CreateDefaultPacPollPolicy()1362 ConfiguredProxyResolutionService::CreateDefaultPacPollPolicy() {
1363   return std::make_unique<DefaultPollPolicy>();
1364 }
1365 
OnProxyConfigChanged(const ProxyConfigWithAnnotation & config,ProxyConfigService::ConfigAvailability availability)1366 void ConfiguredProxyResolutionService::OnProxyConfigChanged(
1367     const ProxyConfigWithAnnotation& config,
1368     ProxyConfigService::ConfigAvailability availability) {
1369   // Retrieve the current proxy configuration from the ProxyConfigService.
1370   // If a configuration is not available yet, we will get called back later
1371   // by our ProxyConfigService::Observer once it changes.
1372   ProxyConfigWithAnnotation effective_config;
1373   switch (availability) {
1374     case ProxyConfigService::CONFIG_PENDING:
1375       // ProxyConfigService implementors should never pass CONFIG_PENDING.
1376       NOTREACHED() << "Proxy config change with CONFIG_PENDING availability!";
1377       return;
1378     case ProxyConfigService::CONFIG_VALID:
1379       effective_config = config;
1380       break;
1381     case ProxyConfigService::CONFIG_UNSET:
1382       effective_config = ProxyConfigWithAnnotation::CreateDirect();
1383       break;
1384   }
1385 
1386   // Emit the proxy settings change to the NetLog stream.
1387   if (net_log_) {
1388     net_log_->AddGlobalEntry(NetLogEventType::PROXY_CONFIG_CHANGED, [&] {
1389       return NetLogProxyConfigChangedParams(&fetched_config_,
1390                                             &effective_config);
1391     });
1392   }
1393 
1394   // Set the new configuration as the most recently fetched one.
1395   fetched_config_ = effective_config;
1396 
1397   InitializeUsingLastFetchedConfig();
1398 }
1399 
ApplyPacBypassRules(const GURL & url,ProxyInfo * results)1400 bool ConfiguredProxyResolutionService::ApplyPacBypassRules(const GURL& url,
1401                                                            ProxyInfo* results) {
1402   DCHECK(config_);
1403 
1404   if (ProxyBypassRules::MatchesImplicitRules(url)) {
1405     results->UseDirectWithBypassedProxy();
1406     return true;
1407   }
1408 
1409   return false;
1410 }
1411 
InitializeUsingLastFetchedConfig()1412 void ConfiguredProxyResolutionService::InitializeUsingLastFetchedConfig() {
1413   ResetProxyConfig(false);
1414 
1415   DCHECK(fetched_config_);
1416   if (!fetched_config_->value().HasAutomaticSettings()) {
1417     config_ = fetched_config_;
1418     SetReady();
1419     return;
1420   }
1421 
1422   // Start downloading + testing the PAC scripts for this new configuration.
1423   current_state_ = STATE_WAITING_FOR_INIT_PROXY_RESOLVER;
1424 
1425   // If we changed networks recently, we should delay running proxy auto-config.
1426   base::TimeDelta wait_delay = stall_proxy_autoconfig_until_ - TimeTicks::Now();
1427 
1428   init_proxy_resolver_ = std::make_unique<InitProxyResolver>();
1429   init_proxy_resolver_->set_quick_check_enabled(quick_check_enabled_);
1430   int rv = init_proxy_resolver_->Start(
1431       &resolver_, resolver_factory_.get(), pac_file_fetcher_.get(),
1432       dhcp_pac_file_fetcher_.get(), net_log_, fetched_config_.value(),
1433       wait_delay,
1434       base::BindOnce(
1435           &ConfiguredProxyResolutionService::OnInitProxyResolverComplete,
1436           base::Unretained(this)));
1437 
1438   if (rv != ERR_IO_PENDING)
1439     OnInitProxyResolverComplete(rv);
1440 }
1441 
InitializeUsingDecidedConfig(int decider_result,const PacFileDataWithSource & script_data,const ProxyConfigWithAnnotation & effective_config)1442 void ConfiguredProxyResolutionService::InitializeUsingDecidedConfig(
1443     int decider_result,
1444     const PacFileDataWithSource& script_data,
1445     const ProxyConfigWithAnnotation& effective_config) {
1446   DCHECK(fetched_config_);
1447   DCHECK(fetched_config_->value().HasAutomaticSettings());
1448 
1449   ResetProxyConfig(false);
1450 
1451   current_state_ = STATE_WAITING_FOR_INIT_PROXY_RESOLVER;
1452 
1453   init_proxy_resolver_ = std::make_unique<InitProxyResolver>();
1454   int rv = init_proxy_resolver_->StartSkipDecider(
1455       &resolver_, resolver_factory_.get(), effective_config, decider_result,
1456       script_data,
1457       base::BindOnce(
1458           &ConfiguredProxyResolutionService::OnInitProxyResolverComplete,
1459           base::Unretained(this)));
1460 
1461   if (rv != ERR_IO_PENDING)
1462     OnInitProxyResolverComplete(rv);
1463 }
1464 
OnIPAddressChanged()1465 void ConfiguredProxyResolutionService::OnIPAddressChanged() {
1466   // See the comment block by |kDelayAfterNetworkChangesMs| for info.
1467   stall_proxy_autoconfig_until_ =
1468       TimeTicks::Now() + stall_proxy_auto_config_delay_;
1469 
1470   // With a new network connection, using the proper proxy configuration for the
1471   // new connection may be essential for URL requests to work properly. Reset
1472   // the config to ensure new URL requests are blocked until the potential new
1473   // proxy configuration is loaded.
1474   State previous_state = ResetProxyConfig(false);
1475   if (previous_state != STATE_NONE)
1476     ApplyProxyConfigIfAvailable();
1477 }
1478 
OnDNSChanged()1479 void ConfiguredProxyResolutionService::OnDNSChanged() {
1480   // Do not fully reset proxy config on DNS change notifications. Instead,
1481   // inform the poller that it would be a good time to check for changes.
1482   //
1483   // While a change to DNS servers in use could lead to different WPAD results,
1484   // and thus a different proxy configuration, it is extremely unlikely to ever
1485   // be essential for that changed proxy configuration to be picked up
1486   // immediately. Either URL requests on the connection are generally working
1487   // fine without the proxy, or requests are already broken, leaving little harm
1488   // in letting a couple more requests fail until Chrome picks up the new proxy.
1489   if (script_poller_.get())
1490     script_poller_->OnLazyPoll();
1491 }
1492 
1493 }  // namespace net
1494