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