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