OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
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/proxy_service.h" | |
6 | |
7 #include <algorithm> | |
8 | |
9 #include "base/bind.h" | |
10 #include "base/bind_helpers.h" | |
11 #include "base/compiler_specific.h" | |
12 #include "base/logging.h" | |
13 #include "base/memory/weak_ptr.h" | |
14 #include "base/message_loop/message_loop.h" | |
15 #include "base/message_loop/message_loop_proxy.h" | |
16 #include "base/profiler/scoped_tracker.h" | |
17 #include "base/strings/string_util.h" | |
18 #include "base/thread_task_runner_handle.h" | |
19 #include "base/values.h" | |
20 #include "net/base/completion_callback.h" | |
21 #include "net/base/load_flags.h" | |
22 #include "net/base/net_errors.h" | |
23 #include "net/base/net_log.h" | |
24 #include "net/base/net_util.h" | |
25 #include "net/proxy/dhcp_proxy_script_fetcher.h" | |
26 #include "net/proxy/multi_threaded_proxy_resolver.h" | |
27 #include "net/proxy/network_delegate_error_observer.h" | |
28 #include "net/proxy/proxy_config_service_fixed.h" | |
29 #include "net/proxy/proxy_resolver.h" | |
30 #include "net/proxy/proxy_script_decider.h" | |
31 #include "net/proxy/proxy_script_fetcher.h" | |
32 #include "net/url_request/url_request_context.h" | |
33 #include "url/gurl.h" | |
34 | |
35 #if defined(OS_WIN) | |
36 #include "net/proxy/proxy_config_service_win.h" | |
37 #include "net/proxy/proxy_resolver_winhttp.h" | |
38 #elif defined(OS_IOS) | |
39 #include "net/proxy/proxy_config_service_ios.h" | |
40 #include "net/proxy/proxy_resolver_mac.h" | |
41 #elif defined(OS_MACOSX) | |
42 #include "net/proxy/proxy_config_service_mac.h" | |
43 #include "net/proxy/proxy_resolver_mac.h" | |
44 #elif defined(OS_LINUX) && !defined(OS_CHROMEOS) | |
45 #include "net/proxy/proxy_config_service_linux.h" | |
46 #elif defined(OS_ANDROID) | |
47 #include "net/proxy/proxy_config_service_android.h" | |
48 #endif | |
49 | |
50 using base::TimeDelta; | |
51 using base::TimeTicks; | |
52 | |
53 namespace net { | |
54 | |
55 namespace { | |
56 | |
57 // When the IP address changes we don't immediately re-run proxy auto-config. | |
58 // Instead, we wait for |kDelayAfterNetworkChangesMs| before | |
59 // attempting to re-valuate proxy auto-config. | |
60 // | |
61 // During this time window, any resolve requests sent to the ProxyService will | |
62 // be queued. Once we have waited the required amount of them, the proxy | |
63 // auto-config step will be run, and the queued requests resumed. | |
64 // | |
65 // The reason we play this game is that our signal for detecting network | |
66 // changes (NetworkChangeNotifier) may fire *before* the system's networking | |
67 // dependencies are fully configured. This is a problem since it means if | |
68 // we were to run proxy auto-config right away, it could fail due to spurious | |
69 // DNS failures. (see http://crbug.com/50779 for more details.) | |
70 // | |
71 // By adding the wait window, we give things a better chance to get properly | |
72 // set up. Network failures can happen at any time though, so we additionally | |
73 // poll the PAC script for changes, which will allow us to recover from these | |
74 // sorts of problems. | |
75 const int64 kDelayAfterNetworkChangesMs = 2000; | |
76 | |
77 // This is the default policy for polling the PAC script. | |
78 // | |
79 // In response to a failure, the poll intervals are: | |
80 // 0: 8 seconds (scheduled on timer) | |
81 // 1: 32 seconds | |
82 // 2: 2 minutes | |
83 // 3+: 4 hours | |
84 // | |
85 // In response to a success, the poll intervals are: | |
86 // 0+: 12 hours | |
87 // | |
88 // Only the 8 second poll is scheduled on a timer, the rest happen in response | |
89 // to network activity (and hence will take longer than the written time). | |
90 // | |
91 // Explanation for these values: | |
92 // | |
93 // TODO(eroman): These values are somewhat arbitrary, and need to be tuned | |
94 // using some histograms data. Trying to be conservative so as not to break | |
95 // existing setups when deployed. A simple exponential retry scheme would be | |
96 // more elegant, but places more load on server. | |
97 // | |
98 // The motivation for trying quickly after failures (8 seconds) is to recover | |
99 // from spurious network failures, which are common after the IP address has | |
100 // just changed (like DNS failing to resolve). The next 32 second boundary is | |
101 // to try and catch other VPN weirdness which anecdotally I have seen take | |
102 // 10+ seconds for some users. | |
103 // | |
104 // The motivation for re-trying after a success is to check for possible | |
105 // content changes to the script, or to the WPAD auto-discovery results. We are | |
106 // not very aggressive with these checks so as to minimize the risk of | |
107 // overloading existing PAC setups. Moreover it is unlikely that PAC scripts | |
108 // change very frequently in existing setups. More research is needed to | |
109 // motivate what safe values are here, and what other user agents do. | |
110 // | |
111 // Comparison to other browsers: | |
112 // | |
113 // In Firefox the PAC URL is re-tried on failures according to | |
114 // network.proxy.autoconfig_retry_interval_min and | |
115 // network.proxy.autoconfig_retry_interval_max. The defaults are 5 seconds and | |
116 // 5 minutes respectively. It doubles the interval at each attempt. | |
117 // | |
118 // TODO(eroman): Figure out what Internet Explorer does. | |
119 class DefaultPollPolicy : public ProxyService::PacPollPolicy { | |
120 public: | |
121 DefaultPollPolicy() {} | |
122 | |
123 Mode GetNextDelay(int initial_error, | |
124 TimeDelta current_delay, | |
125 TimeDelta* next_delay) const override { | |
126 if (initial_error != OK) { | |
127 // Re-try policy for failures. | |
128 const int kDelay1Seconds = 8; | |
129 const int kDelay2Seconds = 32; | |
130 const int kDelay3Seconds = 2 * 60; // 2 minutes | |
131 const int kDelay4Seconds = 4 * 60 * 60; // 4 Hours | |
132 | |
133 // Initial poll. | |
134 if (current_delay < TimeDelta()) { | |
135 *next_delay = TimeDelta::FromSeconds(kDelay1Seconds); | |
136 return MODE_USE_TIMER; | |
137 } | |
138 switch (current_delay.InSeconds()) { | |
139 case kDelay1Seconds: | |
140 *next_delay = TimeDelta::FromSeconds(kDelay2Seconds); | |
141 return MODE_START_AFTER_ACTIVITY; | |
142 case kDelay2Seconds: | |
143 *next_delay = TimeDelta::FromSeconds(kDelay3Seconds); | |
144 return MODE_START_AFTER_ACTIVITY; | |
145 default: | |
146 *next_delay = TimeDelta::FromSeconds(kDelay4Seconds); | |
147 return MODE_START_AFTER_ACTIVITY; | |
148 } | |
149 } else { | |
150 // Re-try policy for succeses. | |
151 *next_delay = TimeDelta::FromHours(12); | |
152 return MODE_START_AFTER_ACTIVITY; | |
153 } | |
154 } | |
155 | |
156 private: | |
157 DISALLOW_COPY_AND_ASSIGN(DefaultPollPolicy); | |
158 }; | |
159 | |
160 // Config getter that always returns direct settings. | |
161 class ProxyConfigServiceDirect : public ProxyConfigService { | |
162 public: | |
163 // ProxyConfigService implementation: | |
164 void AddObserver(Observer* observer) override {} | |
165 void RemoveObserver(Observer* observer) override {} | |
166 ConfigAvailability GetLatestProxyConfig(ProxyConfig* config) override { | |
167 *config = ProxyConfig::CreateDirect(); | |
168 config->set_source(PROXY_CONFIG_SOURCE_UNKNOWN); | |
169 return CONFIG_VALID; | |
170 } | |
171 }; | |
172 | |
173 // Proxy resolver that fails every time. | |
174 class ProxyResolverNull : public ProxyResolver { | |
175 public: | |
176 ProxyResolverNull() : ProxyResolver(false /*expects_pac_bytes*/) {} | |
177 | |
178 // ProxyResolver implementation. | |
179 int GetProxyForURL(const GURL& url, | |
180 ProxyInfo* results, | |
181 const CompletionCallback& callback, | |
182 RequestHandle* request, | |
183 const BoundNetLog& net_log) override { | |
184 return ERR_NOT_IMPLEMENTED; | |
185 } | |
186 | |
187 void CancelRequest(RequestHandle request) override { NOTREACHED(); } | |
188 | |
189 LoadState GetLoadState(RequestHandle request) const override { | |
190 NOTREACHED(); | |
191 return LOAD_STATE_IDLE; | |
192 } | |
193 | |
194 void CancelSetPacScript() override { NOTREACHED(); } | |
195 | |
196 int SetPacScript( | |
197 const scoped_refptr<ProxyResolverScriptData>& /*script_data*/, | |
198 const CompletionCallback& /*callback*/) override { | |
199 return ERR_NOT_IMPLEMENTED; | |
200 } | |
201 }; | |
202 | |
203 // ProxyResolver that simulates a PAC script which returns | |
204 // |pac_string| for every single URL. | |
205 class ProxyResolverFromPacString : public ProxyResolver { | |
206 public: | |
207 explicit ProxyResolverFromPacString(const std::string& pac_string) | |
208 : ProxyResolver(false /*expects_pac_bytes*/), | |
209 pac_string_(pac_string) {} | |
210 | |
211 int GetProxyForURL(const GURL& url, | |
212 ProxyInfo* results, | |
213 const CompletionCallback& callback, | |
214 RequestHandle* request, | |
215 const BoundNetLog& net_log) override { | |
216 results->UsePacString(pac_string_); | |
217 return OK; | |
218 } | |
219 | |
220 void CancelRequest(RequestHandle request) override { NOTREACHED(); } | |
221 | |
222 LoadState GetLoadState(RequestHandle request) const override { | |
223 NOTREACHED(); | |
224 return LOAD_STATE_IDLE; | |
225 } | |
226 | |
227 void CancelSetPacScript() override { NOTREACHED(); } | |
228 | |
229 int SetPacScript(const scoped_refptr<ProxyResolverScriptData>& pac_script, | |
230 const CompletionCallback& callback) override { | |
231 return OK; | |
232 } | |
233 | |
234 private: | |
235 const std::string pac_string_; | |
236 }; | |
237 | |
238 // Creates ProxyResolvers using a platform-specific implementation. | |
239 class ProxyResolverFactoryForSystem : public ProxyResolverFactory { | |
240 public: | |
241 ProxyResolverFactoryForSystem() | |
242 : ProxyResolverFactory(false /*expects_pac_bytes*/) {} | |
243 | |
244 ProxyResolver* CreateProxyResolver() override { | |
245 DCHECK(IsSupported()); | |
246 #if defined(OS_WIN) | |
247 return new ProxyResolverWinHttp(); | |
248 #elif defined(OS_MACOSX) | |
249 return new ProxyResolverMac(); | |
250 #else | |
251 NOTREACHED(); | |
252 return NULL; | |
253 #endif | |
254 } | |
255 | |
256 static bool IsSupported() { | |
257 #if defined(OS_WIN) || defined(OS_MACOSX) | |
258 return true; | |
259 #else | |
260 return false; | |
261 #endif | |
262 } | |
263 }; | |
264 | |
265 // Returns NetLog parameters describing a proxy configuration change. | |
266 base::Value* NetLogProxyConfigChangedCallback( | |
267 const ProxyConfig* old_config, | |
268 const ProxyConfig* new_config, | |
269 NetLog::LogLevel /* log_level */) { | |
270 base::DictionaryValue* dict = new base::DictionaryValue(); | |
271 // The "old_config" is optional -- the first notification will not have | |
272 // any "previous" configuration. | |
273 if (old_config->is_valid()) | |
274 dict->Set("old_config", old_config->ToValue()); | |
275 dict->Set("new_config", new_config->ToValue()); | |
276 return dict; | |
277 } | |
278 | |
279 base::Value* NetLogBadProxyListCallback(const ProxyRetryInfoMap* retry_info, | |
280 NetLog::LogLevel /* log_level */) { | |
281 base::DictionaryValue* dict = new base::DictionaryValue(); | |
282 base::ListValue* list = new base::ListValue(); | |
283 | |
284 for (ProxyRetryInfoMap::const_iterator iter = retry_info->begin(); | |
285 iter != retry_info->end(); ++iter) { | |
286 list->Append(new base::StringValue(iter->first)); | |
287 } | |
288 dict->Set("bad_proxy_list", list); | |
289 return dict; | |
290 } | |
291 | |
292 // Returns NetLog parameters on a successfuly proxy resolution. | |
293 base::Value* NetLogFinishedResolvingProxyCallback( | |
294 const ProxyInfo* result, | |
295 NetLog::LogLevel /* log_level */) { | |
296 base::DictionaryValue* dict = new base::DictionaryValue(); | |
297 dict->SetString("pac_string", result->ToPacString()); | |
298 return dict; | |
299 } | |
300 | |
301 #if defined(OS_CHROMEOS) | |
302 class UnsetProxyConfigService : public ProxyConfigService { | |
303 public: | |
304 UnsetProxyConfigService() {} | |
305 ~UnsetProxyConfigService() override {} | |
306 | |
307 void AddObserver(Observer* observer) override {} | |
308 void RemoveObserver(Observer* observer) override {} | |
309 ConfigAvailability GetLatestProxyConfig(ProxyConfig* config) override { | |
310 return CONFIG_UNSET; | |
311 } | |
312 }; | |
313 #endif | |
314 | |
315 } // namespace | |
316 | |
317 // ProxyService::InitProxyResolver -------------------------------------------- | |
318 | |
319 // This glues together two asynchronous steps: | |
320 // (1) ProxyScriptDecider -- try to fetch/validate a sequence of PAC scripts | |
321 // to figure out what we should configure against. | |
322 // (2) Feed the fetched PAC script into the ProxyResolver. | |
323 // | |
324 // InitProxyResolver is a single-use class which encapsulates cancellation as | |
325 // part of its destructor. Start() or StartSkipDecider() should be called just | |
326 // once. The instance can be destroyed at any time, and the request will be | |
327 // cancelled. | |
328 | |
329 class ProxyService::InitProxyResolver { | |
330 public: | |
331 InitProxyResolver() | |
332 : proxy_resolver_(NULL), | |
333 next_state_(STATE_NONE), | |
334 quick_check_enabled_(true) { | |
335 } | |
336 | |
337 ~InitProxyResolver() { | |
338 // Note that the destruction of ProxyScriptDecider will automatically cancel | |
339 // any outstanding work. | |
340 if (next_state_ == STATE_SET_PAC_SCRIPT_COMPLETE) { | |
341 proxy_resolver_->CancelSetPacScript(); | |
342 } | |
343 } | |
344 | |
345 // Begins initializing the proxy resolver; calls |callback| when done. | |
346 int Start(ProxyResolver* proxy_resolver, | |
347 ProxyScriptFetcher* proxy_script_fetcher, | |
348 DhcpProxyScriptFetcher* dhcp_proxy_script_fetcher, | |
349 NetLog* net_log, | |
350 const ProxyConfig& config, | |
351 TimeDelta wait_delay, | |
352 const CompletionCallback& callback) { | |
353 DCHECK_EQ(STATE_NONE, next_state_); | |
354 proxy_resolver_ = proxy_resolver; | |
355 | |
356 decider_.reset(new ProxyScriptDecider( | |
357 proxy_script_fetcher, dhcp_proxy_script_fetcher, net_log)); | |
358 decider_->set_quick_check_enabled(quick_check_enabled_); | |
359 config_ = config; | |
360 wait_delay_ = wait_delay; | |
361 callback_ = callback; | |
362 | |
363 next_state_ = STATE_DECIDE_PROXY_SCRIPT; | |
364 return DoLoop(OK); | |
365 } | |
366 | |
367 // Similar to Start(), however it skips the ProxyScriptDecider stage. Instead | |
368 // |effective_config|, |decider_result| and |script_data| will be used as the | |
369 // inputs for initializing the ProxyResolver. | |
370 int StartSkipDecider(ProxyResolver* proxy_resolver, | |
371 const ProxyConfig& effective_config, | |
372 int decider_result, | |
373 ProxyResolverScriptData* script_data, | |
374 const CompletionCallback& callback) { | |
375 DCHECK_EQ(STATE_NONE, next_state_); | |
376 proxy_resolver_ = proxy_resolver; | |
377 | |
378 effective_config_ = effective_config; | |
379 script_data_ = script_data; | |
380 callback_ = callback; | |
381 | |
382 if (decider_result != OK) | |
383 return decider_result; | |
384 | |
385 next_state_ = STATE_SET_PAC_SCRIPT; | |
386 return DoLoop(OK); | |
387 } | |
388 | |
389 // Returns the proxy configuration that was selected by ProxyScriptDecider. | |
390 // Should only be called upon completion of the initialization. | |
391 const ProxyConfig& effective_config() const { | |
392 DCHECK_EQ(STATE_NONE, next_state_); | |
393 return effective_config_; | |
394 } | |
395 | |
396 // Returns the PAC script data that was selected by ProxyScriptDecider. | |
397 // Should only be called upon completion of the initialization. | |
398 ProxyResolverScriptData* script_data() { | |
399 DCHECK_EQ(STATE_NONE, next_state_); | |
400 return script_data_.get(); | |
401 } | |
402 | |
403 LoadState GetLoadState() const { | |
404 if (next_state_ == STATE_DECIDE_PROXY_SCRIPT_COMPLETE) { | |
405 // In addition to downloading, this state may also include the stall time | |
406 // after network change events (kDelayAfterNetworkChangesMs). | |
407 return LOAD_STATE_DOWNLOADING_PROXY_SCRIPT; | |
408 } | |
409 return LOAD_STATE_RESOLVING_PROXY_FOR_URL; | |
410 } | |
411 | |
412 void set_quick_check_enabled(bool enabled) { quick_check_enabled_ = enabled; } | |
413 bool quick_check_enabled() const { return quick_check_enabled_; } | |
414 | |
415 private: | |
416 enum State { | |
417 STATE_NONE, | |
418 STATE_DECIDE_PROXY_SCRIPT, | |
419 STATE_DECIDE_PROXY_SCRIPT_COMPLETE, | |
420 STATE_SET_PAC_SCRIPT, | |
421 STATE_SET_PAC_SCRIPT_COMPLETE, | |
422 }; | |
423 | |
424 int DoLoop(int result) { | |
425 DCHECK_NE(next_state_, STATE_NONE); | |
426 int rv = result; | |
427 do { | |
428 State state = next_state_; | |
429 next_state_ = STATE_NONE; | |
430 switch (state) { | |
431 case STATE_DECIDE_PROXY_SCRIPT: | |
432 DCHECK_EQ(OK, rv); | |
433 rv = DoDecideProxyScript(); | |
434 break; | |
435 case STATE_DECIDE_PROXY_SCRIPT_COMPLETE: | |
436 rv = DoDecideProxyScriptComplete(rv); | |
437 break; | |
438 case STATE_SET_PAC_SCRIPT: | |
439 DCHECK_EQ(OK, rv); | |
440 rv = DoSetPacScript(); | |
441 break; | |
442 case STATE_SET_PAC_SCRIPT_COMPLETE: | |
443 rv = DoSetPacScriptComplete(rv); | |
444 break; | |
445 default: | |
446 NOTREACHED() << "bad state: " << state; | |
447 rv = ERR_UNEXPECTED; | |
448 break; | |
449 } | |
450 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); | |
451 return rv; | |
452 } | |
453 | |
454 int DoDecideProxyScript() { | |
455 next_state_ = STATE_DECIDE_PROXY_SCRIPT_COMPLETE; | |
456 | |
457 return decider_->Start( | |
458 config_, wait_delay_, proxy_resolver_->expects_pac_bytes(), | |
459 base::Bind(&InitProxyResolver::OnIOCompletion, base::Unretained(this))); | |
460 } | |
461 | |
462 int DoDecideProxyScriptComplete(int result) { | |
463 if (result != OK) | |
464 return result; | |
465 | |
466 effective_config_ = decider_->effective_config(); | |
467 script_data_ = decider_->script_data(); | |
468 | |
469 next_state_ = STATE_SET_PAC_SCRIPT; | |
470 return OK; | |
471 } | |
472 | |
473 int DoSetPacScript() { | |
474 DCHECK(script_data_.get()); | |
475 // TODO(eroman): Should log this latency to the NetLog. | |
476 next_state_ = STATE_SET_PAC_SCRIPT_COMPLETE; | |
477 return proxy_resolver_->SetPacScript( | |
478 script_data_, | |
479 base::Bind(&InitProxyResolver::OnIOCompletion, base::Unretained(this))); | |
480 } | |
481 | |
482 int DoSetPacScriptComplete(int result) { | |
483 return result; | |
484 } | |
485 | |
486 void OnIOCompletion(int result) { | |
487 DCHECK_NE(STATE_NONE, next_state_); | |
488 int rv = DoLoop(result); | |
489 if (rv != ERR_IO_PENDING) | |
490 DoCallback(rv); | |
491 } | |
492 | |
493 void DoCallback(int result) { | |
494 DCHECK_NE(ERR_IO_PENDING, result); | |
495 callback_.Run(result); | |
496 } | |
497 | |
498 ProxyConfig config_; | |
499 ProxyConfig effective_config_; | |
500 scoped_refptr<ProxyResolverScriptData> script_data_; | |
501 TimeDelta wait_delay_; | |
502 scoped_ptr<ProxyScriptDecider> decider_; | |
503 ProxyResolver* proxy_resolver_; | |
504 CompletionCallback callback_; | |
505 State next_state_; | |
506 bool quick_check_enabled_; | |
507 | |
508 DISALLOW_COPY_AND_ASSIGN(InitProxyResolver); | |
509 }; | |
510 | |
511 // ProxyService::ProxyScriptDeciderPoller ------------------------------------- | |
512 | |
513 // This helper class encapsulates the logic to schedule and run periodic | |
514 // background checks to see if the PAC script (or effective proxy configuration) | |
515 // has changed. If a change is detected, then the caller will be notified via | |
516 // the ChangeCallback. | |
517 class ProxyService::ProxyScriptDeciderPoller { | |
518 public: | |
519 typedef base::Callback<void(int, ProxyResolverScriptData*, | |
520 const ProxyConfig&)> ChangeCallback; | |
521 | |
522 // Builds a poller helper, and starts polling for updates. Whenever a change | |
523 // is observed, |callback| will be invoked with the details. | |
524 // | |
525 // |config| specifies the (unresolved) proxy configuration to poll. | |
526 // |proxy_resolver_expects_pac_bytes| the type of proxy resolver we expect | |
527 // to use the resulting script data with | |
528 // (so it can choose the right format). | |
529 // |proxy_script_fetcher| this pointer must remain alive throughout our | |
530 // lifetime. It is the dependency that will be used | |
531 // for downloading proxy scripts. | |
532 // |dhcp_proxy_script_fetcher| similar to |proxy_script_fetcher|, but for | |
533 // the DHCP dependency. | |
534 // |init_net_error| This is the initial network error (possibly success) | |
535 // encountered by the first PAC fetch attempt. We use it | |
536 // to schedule updates more aggressively if the initial | |
537 // fetch resulted in an error. | |
538 // |init_script_data| the initial script data from the PAC fetch attempt. | |
539 // This is the baseline used to determine when the | |
540 // script's contents have changed. | |
541 // |net_log| the NetLog to log progress into. | |
542 ProxyScriptDeciderPoller(ChangeCallback callback, | |
543 const ProxyConfig& config, | |
544 bool proxy_resolver_expects_pac_bytes, | |
545 ProxyScriptFetcher* proxy_script_fetcher, | |
546 DhcpProxyScriptFetcher* dhcp_proxy_script_fetcher, | |
547 int init_net_error, | |
548 ProxyResolverScriptData* init_script_data, | |
549 NetLog* net_log) | |
550 : change_callback_(callback), | |
551 config_(config), | |
552 proxy_resolver_expects_pac_bytes_(proxy_resolver_expects_pac_bytes), | |
553 proxy_script_fetcher_(proxy_script_fetcher), | |
554 dhcp_proxy_script_fetcher_(dhcp_proxy_script_fetcher), | |
555 last_error_(init_net_error), | |
556 last_script_data_(init_script_data), | |
557 last_poll_time_(TimeTicks::Now()), | |
558 weak_factory_(this) { | |
559 // Set the initial poll delay. | |
560 next_poll_mode_ = poll_policy()->GetNextDelay( | |
561 last_error_, TimeDelta::FromSeconds(-1), &next_poll_delay_); | |
562 TryToStartNextPoll(false); | |
563 } | |
564 | |
565 void OnLazyPoll() { | |
566 // We have just been notified of network activity. Use this opportunity to | |
567 // see if we can start our next poll. | |
568 TryToStartNextPoll(true); | |
569 } | |
570 | |
571 static const PacPollPolicy* set_policy(const PacPollPolicy* policy) { | |
572 const PacPollPolicy* prev = poll_policy_; | |
573 poll_policy_ = policy; | |
574 return prev; | |
575 } | |
576 | |
577 void set_quick_check_enabled(bool enabled) { quick_check_enabled_ = enabled; } | |
578 bool quick_check_enabled() const { return quick_check_enabled_; } | |
579 | |
580 private: | |
581 // Returns the effective poll policy (the one injected by unit-tests, or the | |
582 // default). | |
583 const PacPollPolicy* poll_policy() { | |
584 if (poll_policy_) | |
585 return poll_policy_; | |
586 return &default_poll_policy_; | |
587 } | |
588 | |
589 void StartPollTimer() { | |
590 DCHECK(!decider_.get()); | |
591 | |
592 base::MessageLoop::current()->PostDelayedTask( | |
593 FROM_HERE, | |
594 base::Bind(&ProxyScriptDeciderPoller::DoPoll, | |
595 weak_factory_.GetWeakPtr()), | |
596 next_poll_delay_); | |
597 } | |
598 | |
599 void TryToStartNextPoll(bool triggered_by_activity) { | |
600 switch (next_poll_mode_) { | |
601 case PacPollPolicy::MODE_USE_TIMER: | |
602 if (!triggered_by_activity) | |
603 StartPollTimer(); | |
604 break; | |
605 | |
606 case PacPollPolicy::MODE_START_AFTER_ACTIVITY: | |
607 if (triggered_by_activity && !decider_.get()) { | |
608 TimeDelta elapsed_time = TimeTicks::Now() - last_poll_time_; | |
609 if (elapsed_time >= next_poll_delay_) | |
610 DoPoll(); | |
611 } | |
612 break; | |
613 } | |
614 } | |
615 | |
616 void DoPoll() { | |
617 last_poll_time_ = TimeTicks::Now(); | |
618 | |
619 // Start the proxy script decider to see if anything has changed. | |
620 // TODO(eroman): Pass a proper NetLog rather than NULL. | |
621 decider_.reset(new ProxyScriptDecider( | |
622 proxy_script_fetcher_, dhcp_proxy_script_fetcher_, NULL)); | |
623 decider_->set_quick_check_enabled(quick_check_enabled_); | |
624 int result = decider_->Start( | |
625 config_, TimeDelta(), proxy_resolver_expects_pac_bytes_, | |
626 base::Bind(&ProxyScriptDeciderPoller::OnProxyScriptDeciderCompleted, | |
627 base::Unretained(this))); | |
628 | |
629 if (result != ERR_IO_PENDING) | |
630 OnProxyScriptDeciderCompleted(result); | |
631 } | |
632 | |
633 void OnProxyScriptDeciderCompleted(int result) { | |
634 if (HasScriptDataChanged(result, decider_->script_data())) { | |
635 // Something has changed, we must notify the ProxyService so it can | |
636 // re-initialize its ProxyResolver. Note that we post a notification task | |
637 // rather than calling it directly -- this is done to avoid an ugly | |
638 // destruction sequence, since |this| might be destroyed as a result of | |
639 // the notification. | |
640 base::MessageLoop::current()->PostTask( | |
641 FROM_HERE, | |
642 base::Bind(&ProxyScriptDeciderPoller::NotifyProxyServiceOfChange, | |
643 weak_factory_.GetWeakPtr(), | |
644 result, | |
645 make_scoped_refptr(decider_->script_data()), | |
646 decider_->effective_config())); | |
647 return; | |
648 } | |
649 | |
650 decider_.reset(); | |
651 | |
652 // Decide when the next poll should take place, and possibly start the | |
653 // next timer. | |
654 next_poll_mode_ = poll_policy()->GetNextDelay( | |
655 last_error_, next_poll_delay_, &next_poll_delay_); | |
656 TryToStartNextPoll(false); | |
657 } | |
658 | |
659 bool HasScriptDataChanged(int result, ProxyResolverScriptData* script_data) { | |
660 if (result != last_error_) { | |
661 // Something changed -- it was failing before and now it succeeded, or | |
662 // conversely it succeeded before and now it failed. Or it failed in | |
663 // both cases, however the specific failure error codes differ. | |
664 return true; | |
665 } | |
666 | |
667 if (result != OK) { | |
668 // If it failed last time and failed again with the same error code this | |
669 // time, then nothing has actually changed. | |
670 return false; | |
671 } | |
672 | |
673 // Otherwise if it succeeded both this time and last time, we need to look | |
674 // closer and see if we ended up downloading different content for the PAC | |
675 // script. | |
676 return !script_data->Equals(last_script_data_.get()); | |
677 } | |
678 | |
679 void NotifyProxyServiceOfChange( | |
680 int result, | |
681 const scoped_refptr<ProxyResolverScriptData>& script_data, | |
682 const ProxyConfig& effective_config) { | |
683 // Note that |this| may be deleted after calling into the ProxyService. | |
684 change_callback_.Run(result, script_data.get(), effective_config); | |
685 } | |
686 | |
687 ChangeCallback change_callback_; | |
688 ProxyConfig config_; | |
689 bool proxy_resolver_expects_pac_bytes_; | |
690 ProxyScriptFetcher* proxy_script_fetcher_; | |
691 DhcpProxyScriptFetcher* dhcp_proxy_script_fetcher_; | |
692 | |
693 int last_error_; | |
694 scoped_refptr<ProxyResolverScriptData> last_script_data_; | |
695 | |
696 scoped_ptr<ProxyScriptDecider> decider_; | |
697 TimeDelta next_poll_delay_; | |
698 PacPollPolicy::Mode next_poll_mode_; | |
699 | |
700 TimeTicks last_poll_time_; | |
701 | |
702 // Polling policy injected by unit-tests. Otherwise this is NULL and the | |
703 // default policy will be used. | |
704 static const PacPollPolicy* poll_policy_; | |
705 | |
706 const DefaultPollPolicy default_poll_policy_; | |
707 | |
708 bool quick_check_enabled_; | |
709 | |
710 base::WeakPtrFactory<ProxyScriptDeciderPoller> weak_factory_; | |
711 | |
712 DISALLOW_COPY_AND_ASSIGN(ProxyScriptDeciderPoller); | |
713 }; | |
714 | |
715 // static | |
716 const ProxyService::PacPollPolicy* | |
717 ProxyService::ProxyScriptDeciderPoller::poll_policy_ = NULL; | |
718 | |
719 // ProxyService::PacRequest --------------------------------------------------- | |
720 | |
721 class ProxyService::PacRequest | |
722 : public base::RefCounted<ProxyService::PacRequest> { | |
723 public: | |
724 PacRequest(ProxyService* service, | |
725 const GURL& url, | |
726 int load_flags, | |
727 NetworkDelegate* network_delegate, | |
728 ProxyInfo* results, | |
729 const net::CompletionCallback& user_callback, | |
730 const BoundNetLog& net_log) | |
731 : service_(service), | |
732 user_callback_(user_callback), | |
733 results_(results), | |
734 url_(url), | |
735 load_flags_(load_flags), | |
736 network_delegate_(network_delegate), | |
737 resolve_job_(NULL), | |
738 config_id_(ProxyConfig::kInvalidConfigID), | |
739 config_source_(PROXY_CONFIG_SOURCE_UNKNOWN), | |
740 net_log_(net_log) { | |
741 DCHECK(!user_callback.is_null()); | |
742 } | |
743 | |
744 // Starts the resolve proxy request. | |
745 int Start() { | |
746 DCHECK(!was_cancelled()); | |
747 DCHECK(!is_started()); | |
748 | |
749 DCHECK(service_->config_.is_valid()); | |
750 | |
751 config_id_ = service_->config_.id(); | |
752 config_source_ = service_->config_.source(); | |
753 proxy_resolve_start_time_ = TimeTicks::Now(); | |
754 | |
755 return resolver()->GetProxyForURL( | |
756 url_, results_, | |
757 base::Bind(&PacRequest::QueryComplete, base::Unretained(this)), | |
758 &resolve_job_, net_log_); | |
759 } | |
760 | |
761 bool is_started() const { | |
762 // Note that !! casts to bool. (VS gives a warning otherwise). | |
763 return !!resolve_job_; | |
764 } | |
765 | |
766 void StartAndCompleteCheckingForSynchronous() { | |
767 int rv = service_->TryToCompleteSynchronously(url_, load_flags_, | |
768 network_delegate_, results_); | |
769 if (rv == ERR_IO_PENDING) | |
770 rv = Start(); | |
771 if (rv != ERR_IO_PENDING) | |
772 QueryComplete(rv); | |
773 } | |
774 | |
775 void CancelResolveJob() { | |
776 DCHECK(is_started()); | |
777 // The request may already be running in the resolver. | |
778 resolver()->CancelRequest(resolve_job_); | |
779 resolve_job_ = NULL; | |
780 DCHECK(!is_started()); | |
781 } | |
782 | |
783 void Cancel() { | |
784 net_log_.AddEvent(NetLog::TYPE_CANCELLED); | |
785 | |
786 if (is_started()) | |
787 CancelResolveJob(); | |
788 | |
789 // Mark as cancelled, to prevent accessing this again later. | |
790 service_ = NULL; | |
791 user_callback_.Reset(); | |
792 results_ = NULL; | |
793 | |
794 net_log_.EndEvent(NetLog::TYPE_PROXY_SERVICE); | |
795 } | |
796 | |
797 // Returns true if Cancel() has been called. | |
798 bool was_cancelled() const { | |
799 return user_callback_.is_null(); | |
800 } | |
801 | |
802 // Helper to call after ProxyResolver completion (both synchronous and | |
803 // asynchronous). Fixes up the result that is to be returned to user. | |
804 int QueryDidComplete(int result_code) { | |
805 DCHECK(!was_cancelled()); | |
806 | |
807 // Note that DidFinishResolvingProxy might modify |results_|. | |
808 int rv = service_->DidFinishResolvingProxy(url_, load_flags_, | |
809 network_delegate_, results_, | |
810 result_code, net_log_); | |
811 | |
812 // Make a note in the results which configuration was in use at the | |
813 // time of the resolve. | |
814 results_->config_id_ = config_id_; | |
815 results_->config_source_ = config_source_; | |
816 results_->did_use_pac_script_ = true; | |
817 results_->proxy_resolve_start_time_ = proxy_resolve_start_time_; | |
818 results_->proxy_resolve_end_time_ = TimeTicks::Now(); | |
819 | |
820 // Reset the state associated with in-progress-resolve. | |
821 resolve_job_ = NULL; | |
822 config_id_ = ProxyConfig::kInvalidConfigID; | |
823 config_source_ = PROXY_CONFIG_SOURCE_UNKNOWN; | |
824 | |
825 return rv; | |
826 } | |
827 | |
828 BoundNetLog* net_log() { return &net_log_; } | |
829 | |
830 LoadState GetLoadState() const { | |
831 if (is_started()) | |
832 return resolver()->GetLoadState(resolve_job_); | |
833 return LOAD_STATE_RESOLVING_PROXY_FOR_URL; | |
834 } | |
835 | |
836 private: | |
837 friend class base::RefCounted<ProxyService::PacRequest>; | |
838 | |
839 ~PacRequest() {} | |
840 | |
841 // Callback for when the ProxyResolver request has completed. | |
842 void QueryComplete(int result_code) { | |
843 result_code = QueryDidComplete(result_code); | |
844 | |
845 // Remove this completed PacRequest from the service's pending list. | |
846 /// (which will probably cause deletion of |this|). | |
847 if (!user_callback_.is_null()) { | |
848 net::CompletionCallback callback = user_callback_; | |
849 service_->RemovePendingRequest(this); | |
850 callback.Run(result_code); | |
851 } | |
852 } | |
853 | |
854 ProxyResolver* resolver() const { return service_->resolver_.get(); } | |
855 | |
856 // Note that we don't hold a reference to the ProxyService. Outstanding | |
857 // requests are cancelled during ~ProxyService, so this is guaranteed | |
858 // to be valid throughout our lifetime. | |
859 ProxyService* service_; | |
860 net::CompletionCallback user_callback_; | |
861 ProxyInfo* results_; | |
862 GURL url_; | |
863 int load_flags_; | |
864 NetworkDelegate* network_delegate_; | |
865 ProxyResolver::RequestHandle resolve_job_; | |
866 ProxyConfig::ID config_id_; // The config id when the resolve was started. | |
867 ProxyConfigSource config_source_; // The source of proxy settings. | |
868 BoundNetLog net_log_; | |
869 // Time when the PAC is started. Cached here since resetting ProxyInfo also | |
870 // clears the proxy times. | |
871 TimeTicks proxy_resolve_start_time_; | |
872 }; | |
873 | |
874 // ProxyService --------------------------------------------------------------- | |
875 | |
876 ProxyService::ProxyService(ProxyConfigService* config_service, | |
877 ProxyResolver* resolver, | |
878 NetLog* net_log) | |
879 : resolver_(resolver), | |
880 next_config_id_(1), | |
881 current_state_(STATE_NONE), | |
882 net_log_(net_log), | |
883 stall_proxy_auto_config_delay_(TimeDelta::FromMilliseconds( | |
884 kDelayAfterNetworkChangesMs)), | |
885 quick_check_enabled_(true) { | |
886 NetworkChangeNotifier::AddIPAddressObserver(this); | |
887 NetworkChangeNotifier::AddDNSObserver(this); | |
888 ResetConfigService(config_service); | |
889 } | |
890 | |
891 // static | |
892 ProxyService* ProxyService::CreateUsingSystemProxyResolver( | |
893 ProxyConfigService* proxy_config_service, | |
894 size_t num_pac_threads, | |
895 NetLog* net_log) { | |
896 DCHECK(proxy_config_service); | |
897 | |
898 if (!ProxyResolverFactoryForSystem::IsSupported()) { | |
899 LOG(WARNING) << "PAC support disabled because there is no " | |
900 "system implementation"; | |
901 return CreateWithoutProxyResolver(proxy_config_service, net_log); | |
902 } | |
903 | |
904 if (num_pac_threads == 0) | |
905 num_pac_threads = kDefaultNumPacThreads; | |
906 | |
907 ProxyResolver* proxy_resolver = new MultiThreadedProxyResolver( | |
908 new ProxyResolverFactoryForSystem(), num_pac_threads); | |
909 | |
910 return new ProxyService(proxy_config_service, proxy_resolver, net_log); | |
911 } | |
912 | |
913 // static | |
914 ProxyService* ProxyService::CreateWithoutProxyResolver( | |
915 ProxyConfigService* proxy_config_service, | |
916 NetLog* net_log) { | |
917 return new ProxyService(proxy_config_service, | |
918 new ProxyResolverNull(), | |
919 net_log); | |
920 } | |
921 | |
922 // static | |
923 ProxyService* ProxyService::CreateFixed(const ProxyConfig& pc) { | |
924 // TODO(eroman): This isn't quite right, won't work if |pc| specifies | |
925 // a PAC script. | |
926 return CreateUsingSystemProxyResolver(new ProxyConfigServiceFixed(pc), | |
927 0, NULL); | |
928 } | |
929 | |
930 // static | |
931 ProxyService* ProxyService::CreateFixed(const std::string& proxy) { | |
932 net::ProxyConfig proxy_config; | |
933 proxy_config.proxy_rules().ParseFromString(proxy); | |
934 return ProxyService::CreateFixed(proxy_config); | |
935 } | |
936 | |
937 // static | |
938 ProxyService* ProxyService::CreateDirect() { | |
939 return CreateDirectWithNetLog(NULL); | |
940 } | |
941 | |
942 ProxyService* ProxyService::CreateDirectWithNetLog(NetLog* net_log) { | |
943 // Use direct connections. | |
944 return new ProxyService(new ProxyConfigServiceDirect, new ProxyResolverNull, | |
945 net_log); | |
946 } | |
947 | |
948 // static | |
949 ProxyService* ProxyService::CreateFixedFromPacResult( | |
950 const std::string& pac_string) { | |
951 | |
952 // We need the settings to contain an "automatic" setting, otherwise the | |
953 // ProxyResolver dependency we give it will never be used. | |
954 scoped_ptr<ProxyConfigService> proxy_config_service( | |
955 new ProxyConfigServiceFixed(ProxyConfig::CreateAutoDetect())); | |
956 | |
957 scoped_ptr<ProxyResolver> proxy_resolver( | |
958 new ProxyResolverFromPacString(pac_string)); | |
959 | |
960 return new ProxyService(proxy_config_service.release(), | |
961 proxy_resolver.release(), | |
962 NULL); | |
963 } | |
964 | |
965 int ProxyService::ResolveProxy(const GURL& raw_url, | |
966 int load_flags, | |
967 ProxyInfo* result, | |
968 const net::CompletionCallback& callback, | |
969 PacRequest** pac_request, | |
970 NetworkDelegate* network_delegate, | |
971 const BoundNetLog& net_log) { | |
972 DCHECK(!callback.is_null()); | |
973 return ResolveProxyHelper(raw_url, | |
974 load_flags, | |
975 result, | |
976 callback, | |
977 pac_request, | |
978 network_delegate, | |
979 net_log); | |
980 } | |
981 | |
982 int ProxyService::ResolveProxyHelper(const GURL& raw_url, | |
983 int load_flags, | |
984 ProxyInfo* result, | |
985 const net::CompletionCallback& callback, | |
986 PacRequest** pac_request, | |
987 NetworkDelegate* network_delegate, | |
988 const BoundNetLog& net_log) { | |
989 DCHECK(CalledOnValidThread()); | |
990 | |
991 net_log.BeginEvent(NetLog::TYPE_PROXY_SERVICE); | |
992 | |
993 // Notify our polling-based dependencies that a resolve is taking place. | |
994 // This way they can schedule their polls in response to network activity. | |
995 config_service_->OnLazyPoll(); | |
996 if (script_poller_.get()) | |
997 script_poller_->OnLazyPoll(); | |
998 | |
999 if (current_state_ == STATE_NONE) | |
1000 ApplyProxyConfigIfAvailable(); | |
1001 | |
1002 // Strip away any reference fragments and the username/password, as they | |
1003 // are not relevant to proxy resolution. | |
1004 GURL url = SimplifyUrlForRequest(raw_url); | |
1005 | |
1006 // Check if the request can be completed right away. (This is the case when | |
1007 // using a direct connection for example). | |
1008 int rv = TryToCompleteSynchronously(url, load_flags, | |
1009 network_delegate, result); | |
1010 if (rv != ERR_IO_PENDING) | |
1011 return DidFinishResolvingProxy(url, load_flags, network_delegate, | |
1012 result, rv, net_log); | |
1013 | |
1014 if (callback.is_null()) | |
1015 return ERR_IO_PENDING; | |
1016 | |
1017 scoped_refptr<PacRequest> req( | |
1018 new PacRequest(this, url, load_flags, network_delegate, | |
1019 result, callback, net_log)); | |
1020 | |
1021 if (current_state_ == STATE_READY) { | |
1022 // Start the resolve request. | |
1023 rv = req->Start(); | |
1024 if (rv != ERR_IO_PENDING) | |
1025 return req->QueryDidComplete(rv); | |
1026 } else { | |
1027 req->net_log()->BeginEvent(NetLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC); | |
1028 } | |
1029 | |
1030 DCHECK_EQ(ERR_IO_PENDING, rv); | |
1031 DCHECK(!ContainsPendingRequest(req.get())); | |
1032 pending_requests_.push_back(req); | |
1033 | |
1034 // Completion will be notified through |callback|, unless the caller cancels | |
1035 // the request using |pac_request|. | |
1036 if (pac_request) | |
1037 *pac_request = req.get(); | |
1038 return rv; // ERR_IO_PENDING | |
1039 } | |
1040 | |
1041 bool ProxyService:: TryResolveProxySynchronously( | |
1042 const GURL& raw_url, | |
1043 int load_flags, | |
1044 ProxyInfo* result, | |
1045 NetworkDelegate* network_delegate, | |
1046 const BoundNetLog& net_log) { | |
1047 net::CompletionCallback null_callback; | |
1048 return ResolveProxyHelper(raw_url, | |
1049 load_flags, | |
1050 result, | |
1051 null_callback, | |
1052 NULL /* pac_request*/, | |
1053 network_delegate, | |
1054 net_log) == OK; | |
1055 } | |
1056 | |
1057 int ProxyService::TryToCompleteSynchronously(const GURL& url, | |
1058 int load_flags, | |
1059 NetworkDelegate* network_delegate, | |
1060 ProxyInfo* result) { | |
1061 DCHECK_NE(STATE_NONE, current_state_); | |
1062 | |
1063 if (current_state_ != STATE_READY) | |
1064 return ERR_IO_PENDING; // Still initializing. | |
1065 | |
1066 DCHECK_NE(config_.id(), ProxyConfig::kInvalidConfigID); | |
1067 | |
1068 // If it was impossible to fetch or parse the PAC script, we cannot complete | |
1069 // the request here and bail out. | |
1070 if (permanent_error_ != OK) | |
1071 return permanent_error_; | |
1072 | |
1073 if (config_.HasAutomaticSettings()) | |
1074 return ERR_IO_PENDING; // Must submit the request to the proxy resolver. | |
1075 | |
1076 // Use the manual proxy settings. | |
1077 config_.proxy_rules().Apply(url, result); | |
1078 result->config_source_ = config_.source(); | |
1079 result->config_id_ = config_.id(); | |
1080 | |
1081 return OK; | |
1082 } | |
1083 | |
1084 ProxyService::~ProxyService() { | |
1085 NetworkChangeNotifier::RemoveIPAddressObserver(this); | |
1086 NetworkChangeNotifier::RemoveDNSObserver(this); | |
1087 config_service_->RemoveObserver(this); | |
1088 | |
1089 // Cancel any inprogress requests. | |
1090 for (PendingRequests::iterator it = pending_requests_.begin(); | |
1091 it != pending_requests_.end(); | |
1092 ++it) { | |
1093 (*it)->Cancel(); | |
1094 } | |
1095 } | |
1096 | |
1097 void ProxyService::SuspendAllPendingRequests() { | |
1098 for (PendingRequests::iterator it = pending_requests_.begin(); | |
1099 it != pending_requests_.end(); | |
1100 ++it) { | |
1101 PacRequest* req = it->get(); | |
1102 if (req->is_started()) { | |
1103 req->CancelResolveJob(); | |
1104 | |
1105 req->net_log()->BeginEvent( | |
1106 NetLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC); | |
1107 } | |
1108 } | |
1109 } | |
1110 | |
1111 void ProxyService::SetReady() { | |
1112 DCHECK(!init_proxy_resolver_.get()); | |
1113 current_state_ = STATE_READY; | |
1114 | |
1115 // Make a copy in case |this| is deleted during the synchronous completion | |
1116 // of one of the requests. If |this| is deleted then all of the PacRequest | |
1117 // instances will be Cancel()-ed. | |
1118 PendingRequests pending_copy = pending_requests_; | |
1119 | |
1120 for (PendingRequests::iterator it = pending_copy.begin(); | |
1121 it != pending_copy.end(); | |
1122 ++it) { | |
1123 PacRequest* req = it->get(); | |
1124 if (!req->is_started() && !req->was_cancelled()) { | |
1125 req->net_log()->EndEvent(NetLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC); | |
1126 | |
1127 // Note that we re-check for synchronous completion, in case we are | |
1128 // no longer using a ProxyResolver (can happen if we fell-back to manual). | |
1129 req->StartAndCompleteCheckingForSynchronous(); | |
1130 } | |
1131 } | |
1132 } | |
1133 | |
1134 void ProxyService::ApplyProxyConfigIfAvailable() { | |
1135 DCHECK_EQ(STATE_NONE, current_state_); | |
1136 | |
1137 config_service_->OnLazyPoll(); | |
1138 | |
1139 // If we have already fetched the configuration, start applying it. | |
1140 if (fetched_config_.is_valid()) { | |
1141 InitializeUsingLastFetchedConfig(); | |
1142 return; | |
1143 } | |
1144 | |
1145 // Otherwise we need to first fetch the configuration. | |
1146 current_state_ = STATE_WAITING_FOR_PROXY_CONFIG; | |
1147 | |
1148 // Retrieve the current proxy configuration from the ProxyConfigService. | |
1149 // If a configuration is not available yet, we will get called back later | |
1150 // by our ProxyConfigService::Observer once it changes. | |
1151 ProxyConfig config; | |
1152 ProxyConfigService::ConfigAvailability availability = | |
1153 config_service_->GetLatestProxyConfig(&config); | |
1154 if (availability != ProxyConfigService::CONFIG_PENDING) | |
1155 OnProxyConfigChanged(config, availability); | |
1156 } | |
1157 | |
1158 void ProxyService::OnInitProxyResolverComplete(int result) { | |
1159 DCHECK_EQ(STATE_WAITING_FOR_INIT_PROXY_RESOLVER, current_state_); | |
1160 DCHECK(init_proxy_resolver_.get()); | |
1161 DCHECK(fetched_config_.HasAutomaticSettings()); | |
1162 config_ = init_proxy_resolver_->effective_config(); | |
1163 | |
1164 // At this point we have decided which proxy settings to use (i.e. which PAC | |
1165 // script if any). We start up a background poller to periodically revisit | |
1166 // this decision. If the contents of the PAC script change, or if the | |
1167 // result of proxy auto-discovery changes, this poller will notice it and | |
1168 // will trigger a re-initialization using the newly discovered PAC. | |
1169 script_poller_.reset(new ProxyScriptDeciderPoller( | |
1170 base::Bind(&ProxyService::InitializeUsingDecidedConfig, | |
1171 base::Unretained(this)), | |
1172 fetched_config_, | |
1173 resolver_->expects_pac_bytes(), | |
1174 proxy_script_fetcher_.get(), | |
1175 dhcp_proxy_script_fetcher_.get(), | |
1176 result, | |
1177 init_proxy_resolver_->script_data(), | |
1178 NULL)); | |
1179 script_poller_->set_quick_check_enabled(quick_check_enabled_); | |
1180 | |
1181 init_proxy_resolver_.reset(); | |
1182 | |
1183 if (result != OK) { | |
1184 if (fetched_config_.pac_mandatory()) { | |
1185 VLOG(1) << "Failed configuring with mandatory PAC script, blocking all " | |
1186 "traffic."; | |
1187 config_ = fetched_config_; | |
1188 result = ERR_MANDATORY_PROXY_CONFIGURATION_FAILED; | |
1189 } else { | |
1190 VLOG(1) << "Failed configuring with PAC script, falling-back to manual " | |
1191 "proxy servers."; | |
1192 config_ = fetched_config_; | |
1193 config_.ClearAutomaticSettings(); | |
1194 result = OK; | |
1195 } | |
1196 } | |
1197 permanent_error_ = result; | |
1198 | |
1199 // TODO(eroman): Make this ID unique in the case where configuration changed | |
1200 // due to ProxyScriptDeciderPoller. | |
1201 config_.set_id(fetched_config_.id()); | |
1202 config_.set_source(fetched_config_.source()); | |
1203 | |
1204 // Resume any requests which we had to defer until the PAC script was | |
1205 // downloaded. | |
1206 SetReady(); | |
1207 } | |
1208 | |
1209 int ProxyService::ReconsiderProxyAfterError(const GURL& url, | |
1210 int load_flags, | |
1211 int net_error, | |
1212 ProxyInfo* result, | |
1213 const CompletionCallback& callback, | |
1214 PacRequest** pac_request, | |
1215 NetworkDelegate* network_delegate, | |
1216 const BoundNetLog& net_log) { | |
1217 DCHECK(CalledOnValidThread()); | |
1218 | |
1219 // Check to see if we have a new config since ResolveProxy was called. We | |
1220 // want to re-run ResolveProxy in two cases: 1) we have a new config, or 2) a | |
1221 // direct connection failed and we never tried the current config. | |
1222 | |
1223 DCHECK(result); | |
1224 bool re_resolve = result->config_id_ != config_.id(); | |
1225 | |
1226 if (re_resolve) { | |
1227 // If we have a new config or the config was never tried, we delete the | |
1228 // list of bad proxies and we try again. | |
1229 proxy_retry_info_.clear(); | |
1230 return ResolveProxy(url, load_flags, result, callback, pac_request, | |
1231 network_delegate, net_log); | |
1232 } | |
1233 | |
1234 DCHECK(!result->is_empty()); | |
1235 ProxyServer bad_proxy = result->proxy_server(); | |
1236 | |
1237 // We don't have new proxy settings to try, try to fallback to the next proxy | |
1238 // in the list. | |
1239 bool did_fallback = result->Fallback(net_error, net_log); | |
1240 | |
1241 // Return synchronous failure if there is nothing left to fall-back to. | |
1242 // TODO(eroman): This is a yucky API, clean it up. | |
1243 return did_fallback ? OK : ERR_FAILED; | |
1244 } | |
1245 | |
1246 bool ProxyService::MarkProxiesAsBadUntil( | |
1247 const ProxyInfo& result, | |
1248 base::TimeDelta retry_delay, | |
1249 const ProxyServer& another_bad_proxy, | |
1250 const BoundNetLog& net_log) { | |
1251 result.proxy_list_.UpdateRetryInfoOnFallback(&proxy_retry_info_, | |
1252 retry_delay, | |
1253 false, | |
1254 another_bad_proxy, | |
1255 OK, | |
1256 net_log); | |
1257 if (another_bad_proxy.is_valid()) | |
1258 return result.proxy_list_.size() > 2; | |
1259 else | |
1260 return result.proxy_list_.size() > 1; | |
1261 } | |
1262 | |
1263 void ProxyService::ReportSuccess(const ProxyInfo& result, | |
1264 NetworkDelegate* network_delegate) { | |
1265 DCHECK(CalledOnValidThread()); | |
1266 | |
1267 const ProxyRetryInfoMap& new_retry_info = result.proxy_retry_info(); | |
1268 if (new_retry_info.empty()) | |
1269 return; | |
1270 | |
1271 for (ProxyRetryInfoMap::const_iterator iter = new_retry_info.begin(); | |
1272 iter != new_retry_info.end(); ++iter) { | |
1273 ProxyRetryInfoMap::iterator existing = proxy_retry_info_.find(iter->first); | |
1274 if (existing == proxy_retry_info_.end()) { | |
1275 proxy_retry_info_[iter->first] = iter->second; | |
1276 if (network_delegate) { | |
1277 const ProxyServer& bad_proxy = | |
1278 ProxyServer::FromURI(iter->first, ProxyServer::SCHEME_HTTP); | |
1279 const ProxyRetryInfo& proxy_retry_info = iter->second; | |
1280 network_delegate->NotifyProxyFallback(bad_proxy, | |
1281 proxy_retry_info.net_error); | |
1282 } | |
1283 } | |
1284 else if (existing->second.bad_until < iter->second.bad_until) | |
1285 existing->second.bad_until = iter->second.bad_until; | |
1286 } | |
1287 if (net_log_) { | |
1288 net_log_->AddGlobalEntry( | |
1289 NetLog::TYPE_BAD_PROXY_LIST_REPORTED, | |
1290 base::Bind(&NetLogBadProxyListCallback, &new_retry_info)); | |
1291 } | |
1292 } | |
1293 | |
1294 void ProxyService::CancelPacRequest(PacRequest* req) { | |
1295 DCHECK(CalledOnValidThread()); | |
1296 DCHECK(req); | |
1297 req->Cancel(); | |
1298 RemovePendingRequest(req); | |
1299 } | |
1300 | |
1301 LoadState ProxyService::GetLoadState(const PacRequest* req) const { | |
1302 CHECK(req); | |
1303 if (current_state_ == STATE_WAITING_FOR_INIT_PROXY_RESOLVER) | |
1304 return init_proxy_resolver_->GetLoadState(); | |
1305 return req->GetLoadState(); | |
1306 } | |
1307 | |
1308 bool ProxyService::ContainsPendingRequest(PacRequest* req) { | |
1309 PendingRequests::iterator it = std::find( | |
1310 pending_requests_.begin(), pending_requests_.end(), req); | |
1311 return pending_requests_.end() != it; | |
1312 } | |
1313 | |
1314 void ProxyService::RemovePendingRequest(PacRequest* req) { | |
1315 DCHECK(ContainsPendingRequest(req)); | |
1316 PendingRequests::iterator it = std::find( | |
1317 pending_requests_.begin(), pending_requests_.end(), req); | |
1318 pending_requests_.erase(it); | |
1319 } | |
1320 | |
1321 int ProxyService::DidFinishResolvingProxy(const GURL& url, | |
1322 int load_flags, | |
1323 NetworkDelegate* network_delegate, | |
1324 ProxyInfo* result, | |
1325 int result_code, | |
1326 const BoundNetLog& net_log) { | |
1327 // Log the result of the proxy resolution. | |
1328 if (result_code == OK) { | |
1329 // Allow the network delegate to interpose on the resolution decision, | |
1330 // possibly modifying the ProxyInfo. | |
1331 if (network_delegate) | |
1332 network_delegate->NotifyResolveProxy(url, load_flags, *this, result); | |
1333 | |
1334 // When logging all events is enabled, dump the proxy list. | |
1335 if (net_log.IsLogging()) { | |
1336 net_log.AddEvent( | |
1337 NetLog::TYPE_PROXY_SERVICE_RESOLVED_PROXY_LIST, | |
1338 base::Bind(&NetLogFinishedResolvingProxyCallback, result)); | |
1339 } | |
1340 result->DeprioritizeBadProxies(proxy_retry_info_); | |
1341 } else { | |
1342 net_log.AddEventWithNetErrorCode( | |
1343 NetLog::TYPE_PROXY_SERVICE_RESOLVED_PROXY_LIST, result_code); | |
1344 | |
1345 if (!config_.pac_mandatory()) { | |
1346 // Fall-back to direct when the proxy resolver fails. This corresponds | |
1347 // with a javascript runtime error in the PAC script. | |
1348 // | |
1349 // This implicit fall-back to direct matches Firefox 3.5 and | |
1350 // Internet Explorer 8. For more information, see: | |
1351 // | |
1352 // http://www.chromium.org/developers/design-documents/proxy-settings-fall
back | |
1353 result->UseDirect(); | |
1354 result_code = OK; | |
1355 | |
1356 // Allow the network delegate to interpose on the resolution decision, | |
1357 // possibly modifying the ProxyInfo. | |
1358 if (network_delegate) | |
1359 network_delegate->NotifyResolveProxy(url, load_flags, *this, result); | |
1360 } else { | |
1361 result_code = ERR_MANDATORY_PROXY_CONFIGURATION_FAILED; | |
1362 } | |
1363 } | |
1364 | |
1365 net_log.EndEvent(NetLog::TYPE_PROXY_SERVICE); | |
1366 return result_code; | |
1367 } | |
1368 | |
1369 void ProxyService::SetProxyScriptFetchers( | |
1370 ProxyScriptFetcher* proxy_script_fetcher, | |
1371 DhcpProxyScriptFetcher* dhcp_proxy_script_fetcher) { | |
1372 DCHECK(CalledOnValidThread()); | |
1373 State previous_state = ResetProxyConfig(false); | |
1374 proxy_script_fetcher_.reset(proxy_script_fetcher); | |
1375 dhcp_proxy_script_fetcher_.reset(dhcp_proxy_script_fetcher); | |
1376 if (previous_state != STATE_NONE) | |
1377 ApplyProxyConfigIfAvailable(); | |
1378 } | |
1379 | |
1380 ProxyScriptFetcher* ProxyService::GetProxyScriptFetcher() const { | |
1381 DCHECK(CalledOnValidThread()); | |
1382 return proxy_script_fetcher_.get(); | |
1383 } | |
1384 | |
1385 ProxyService::State ProxyService::ResetProxyConfig(bool reset_fetched_config) { | |
1386 DCHECK(CalledOnValidThread()); | |
1387 State previous_state = current_state_; | |
1388 | |
1389 permanent_error_ = OK; | |
1390 proxy_retry_info_.clear(); | |
1391 script_poller_.reset(); | |
1392 init_proxy_resolver_.reset(); | |
1393 SuspendAllPendingRequests(); | |
1394 config_ = ProxyConfig(); | |
1395 if (reset_fetched_config) | |
1396 fetched_config_ = ProxyConfig(); | |
1397 current_state_ = STATE_NONE; | |
1398 | |
1399 return previous_state; | |
1400 } | |
1401 | |
1402 void ProxyService::ResetConfigService( | |
1403 ProxyConfigService* new_proxy_config_service) { | |
1404 DCHECK(CalledOnValidThread()); | |
1405 State previous_state = ResetProxyConfig(true); | |
1406 | |
1407 // Release the old configuration service. | |
1408 if (config_service_.get()) | |
1409 config_service_->RemoveObserver(this); | |
1410 | |
1411 // Set the new configuration service. | |
1412 config_service_.reset(new_proxy_config_service); | |
1413 config_service_->AddObserver(this); | |
1414 | |
1415 if (previous_state != STATE_NONE) | |
1416 ApplyProxyConfigIfAvailable(); | |
1417 } | |
1418 | |
1419 void ProxyService::ForceReloadProxyConfig() { | |
1420 DCHECK(CalledOnValidThread()); | |
1421 ResetProxyConfig(false); | |
1422 ApplyProxyConfigIfAvailable(); | |
1423 } | |
1424 | |
1425 // static | |
1426 ProxyConfigService* ProxyService::CreateSystemProxyConfigService( | |
1427 const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner, | |
1428 const scoped_refptr<base::SingleThreadTaskRunner>& file_task_runner) { | |
1429 #if defined(OS_WIN) | |
1430 return new ProxyConfigServiceWin(); | |
1431 #elif defined(OS_IOS) | |
1432 return new ProxyConfigServiceIOS(); | |
1433 #elif defined(OS_MACOSX) | |
1434 return new ProxyConfigServiceMac(io_task_runner); | |
1435 #elif defined(OS_CHROMEOS) | |
1436 LOG(ERROR) << "ProxyConfigService for ChromeOS should be created in " | |
1437 << "profile_io_data.cc::CreateProxyConfigService and this should " | |
1438 << "be used only for examples."; | |
1439 return new UnsetProxyConfigService; | |
1440 #elif defined(OS_LINUX) | |
1441 ProxyConfigServiceLinux* linux_config_service = | |
1442 new ProxyConfigServiceLinux(); | |
1443 | |
1444 // Assume we got called on the thread that runs the default glib | |
1445 // main loop, so the current thread is where we should be running | |
1446 // gconf calls from. | |
1447 scoped_refptr<base::SingleThreadTaskRunner> glib_thread_task_runner = | |
1448 base::ThreadTaskRunnerHandle::Get(); | |
1449 | |
1450 // Synchronously fetch the current proxy config (since we are running on | |
1451 // glib_default_loop). Additionally register for notifications (delivered in | |
1452 // either |glib_default_loop| or |file_task_runner|) to keep us updated when | |
1453 // the proxy config changes. | |
1454 linux_config_service->SetupAndFetchInitialConfig( | |
1455 glib_thread_task_runner, io_task_runner, file_task_runner); | |
1456 | |
1457 return linux_config_service; | |
1458 #elif defined(OS_ANDROID) | |
1459 return new ProxyConfigServiceAndroid( | |
1460 io_task_runner, base::MessageLoop::current()->message_loop_proxy()); | |
1461 #else | |
1462 LOG(WARNING) << "Failed to choose a system proxy settings fetcher " | |
1463 "for this platform."; | |
1464 return new ProxyConfigServiceDirect(); | |
1465 #endif | |
1466 } | |
1467 | |
1468 // static | |
1469 const ProxyService::PacPollPolicy* ProxyService::set_pac_script_poll_policy( | |
1470 const PacPollPolicy* policy) { | |
1471 return ProxyScriptDeciderPoller::set_policy(policy); | |
1472 } | |
1473 | |
1474 // static | |
1475 scoped_ptr<ProxyService::PacPollPolicy> | |
1476 ProxyService::CreateDefaultPacPollPolicy() { | |
1477 return scoped_ptr<PacPollPolicy>(new DefaultPollPolicy()); | |
1478 } | |
1479 | |
1480 void ProxyService::OnProxyConfigChanged( | |
1481 const ProxyConfig& config, | |
1482 ProxyConfigService::ConfigAvailability availability) { | |
1483 // TODO(pkasting): Remove ScopedTracker below once crbug.com/455942 is fixed. | |
1484 tracked_objects::ScopedTracker tracking_profile( | |
1485 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
1486 "455942 ProxyService::OnProxyConfigChanged")); | |
1487 // Retrieve the current proxy configuration from the ProxyConfigService. | |
1488 // If a configuration is not available yet, we will get called back later | |
1489 // by our ProxyConfigService::Observer once it changes. | |
1490 ProxyConfig effective_config; | |
1491 switch (availability) { | |
1492 case ProxyConfigService::CONFIG_PENDING: | |
1493 // ProxyConfigService implementors should never pass CONFIG_PENDING. | |
1494 NOTREACHED() << "Proxy config change with CONFIG_PENDING availability!"; | |
1495 return; | |
1496 case ProxyConfigService::CONFIG_VALID: | |
1497 effective_config = config; | |
1498 break; | |
1499 case ProxyConfigService::CONFIG_UNSET: | |
1500 effective_config = ProxyConfig::CreateDirect(); | |
1501 break; | |
1502 } | |
1503 | |
1504 // Emit the proxy settings change to the NetLog stream. | |
1505 if (net_log_) { | |
1506 net_log_->AddGlobalEntry( | |
1507 net::NetLog::TYPE_PROXY_CONFIG_CHANGED, | |
1508 base::Bind(&NetLogProxyConfigChangedCallback, | |
1509 &fetched_config_, &effective_config)); | |
1510 } | |
1511 | |
1512 // Set the new configuration as the most recently fetched one. | |
1513 fetched_config_ = effective_config; | |
1514 fetched_config_.set_id(1); // Needed for a later DCHECK of is_valid(). | |
1515 | |
1516 InitializeUsingLastFetchedConfig(); | |
1517 } | |
1518 | |
1519 void ProxyService::InitializeUsingLastFetchedConfig() { | |
1520 ResetProxyConfig(false); | |
1521 | |
1522 DCHECK(fetched_config_.is_valid()); | |
1523 | |
1524 // Increment the ID to reflect that the config has changed. | |
1525 fetched_config_.set_id(next_config_id_++); | |
1526 | |
1527 if (!fetched_config_.HasAutomaticSettings()) { | |
1528 config_ = fetched_config_; | |
1529 SetReady(); | |
1530 return; | |
1531 } | |
1532 | |
1533 // Start downloading + testing the PAC scripts for this new configuration. | |
1534 current_state_ = STATE_WAITING_FOR_INIT_PROXY_RESOLVER; | |
1535 | |
1536 // If we changed networks recently, we should delay running proxy auto-config. | |
1537 TimeDelta wait_delay = | |
1538 stall_proxy_autoconfig_until_ - TimeTicks::Now(); | |
1539 | |
1540 init_proxy_resolver_.reset(new InitProxyResolver()); | |
1541 init_proxy_resolver_->set_quick_check_enabled(quick_check_enabled_); | |
1542 int rv = init_proxy_resolver_->Start( | |
1543 resolver_.get(), | |
1544 proxy_script_fetcher_.get(), | |
1545 dhcp_proxy_script_fetcher_.get(), | |
1546 net_log_, | |
1547 fetched_config_, | |
1548 wait_delay, | |
1549 base::Bind(&ProxyService::OnInitProxyResolverComplete, | |
1550 base::Unretained(this))); | |
1551 | |
1552 if (rv != ERR_IO_PENDING) | |
1553 OnInitProxyResolverComplete(rv); | |
1554 } | |
1555 | |
1556 void ProxyService::InitializeUsingDecidedConfig( | |
1557 int decider_result, | |
1558 ProxyResolverScriptData* script_data, | |
1559 const ProxyConfig& effective_config) { | |
1560 DCHECK(fetched_config_.is_valid()); | |
1561 DCHECK(fetched_config_.HasAutomaticSettings()); | |
1562 | |
1563 ResetProxyConfig(false); | |
1564 | |
1565 current_state_ = STATE_WAITING_FOR_INIT_PROXY_RESOLVER; | |
1566 | |
1567 init_proxy_resolver_.reset(new InitProxyResolver()); | |
1568 int rv = init_proxy_resolver_->StartSkipDecider( | |
1569 resolver_.get(), | |
1570 effective_config, | |
1571 decider_result, | |
1572 script_data, | |
1573 base::Bind(&ProxyService::OnInitProxyResolverComplete, | |
1574 base::Unretained(this))); | |
1575 | |
1576 if (rv != ERR_IO_PENDING) | |
1577 OnInitProxyResolverComplete(rv); | |
1578 } | |
1579 | |
1580 void ProxyService::OnIPAddressChanged() { | |
1581 // See the comment block by |kDelayAfterNetworkChangesMs| for info. | |
1582 stall_proxy_autoconfig_until_ = | |
1583 TimeTicks::Now() + stall_proxy_auto_config_delay_; | |
1584 | |
1585 State previous_state = ResetProxyConfig(false); | |
1586 if (previous_state != STATE_NONE) | |
1587 ApplyProxyConfigIfAvailable(); | |
1588 } | |
1589 | |
1590 void ProxyService::OnDNSChanged() { | |
1591 OnIPAddressChanged(); | |
1592 } | |
1593 | |
1594 } // namespace net | |
OLD | NEW |