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