1 // Copyright (c) 2011 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/compiler_specific.h"
10 #include "base/logging.h"
11 #include "base/message_loop.h"
12 #include "base/string_util.h"
13 #include "base/values.h"
14 #include "googleurl/src/gurl.h"
15 #include "net/base/net_errors.h"
16 #include "net/base/net_log.h"
17 #include "net/base/net_util.h"
18 #include "net/proxy/init_proxy_resolver.h"
19 #include "net/proxy/multi_threaded_proxy_resolver.h"
20 #include "net/proxy/proxy_config_service_fixed.h"
21 #include "net/proxy/proxy_resolver.h"
22 #include "net/proxy/proxy_resolver_js_bindings.h"
23 #ifndef ANDROID
24 #include "net/proxy/proxy_resolver_v8.h"
25 #endif
26 #include "net/proxy/proxy_script_fetcher.h"
27 #include "net/proxy/sync_host_resolver_bridge.h"
28 #include "net/url_request/url_request_context.h"
29
30 #if defined(OS_WIN)
31 #include "net/proxy/proxy_config_service_win.h"
32 #include "net/proxy/proxy_resolver_winhttp.h"
33 #elif defined(OS_MACOSX)
34 #include "net/proxy/proxy_config_service_mac.h"
35 #include "net/proxy/proxy_resolver_mac.h"
36 #elif defined(OS_LINUX) && !defined(OS_CHROMEOS)
37 #include "net/proxy/proxy_config_service_linux.h"
38 #endif
39
40 using base::TimeDelta;
41 using base::TimeTicks;
42
43 namespace net {
44
45 namespace {
46
47 const size_t kMaxNumNetLogEntries = 100;
48 const size_t kDefaultNumPacThreads = 4;
49
50 // When the IP address changes we don't immediately re-run proxy auto-config.
51 // Instead, we wait for |kNumMillisToStallAfterNetworkChanges| before
52 // attempting to re-valuate proxy auto-config.
53 //
54 // During this time window, any resolve requests sent to the ProxyService will
55 // be queued. Once we have waited the required amount of them, the proxy
56 // auto-config step will be run, and the queued requests resumed.
57 //
58 // The reason we play this game is that our signal for detecting network
59 // changes (NetworkChangeNotifier) may fire *before* the system's networking
60 // dependencies are fully configured. This is a problem since it means if
61 // we were to run proxy auto-config right away, it could fail due to spurious
62 // DNS failures. (see http://crbug.com/50779 for more details.)
63 //
64 // By adding the wait window, we give things a chance to get properly set up.
65 // Now by the time we run the proxy-autoconfig there is a lower chance of
66 // getting transient DNS / connect failures.
67 //
68 // Admitedly this is a hack. Ideally we would have NetworkChangeNotifier
69 // deliver a reliable signal indicating that the network has changed AND is
70 // ready for action... But until then, we can reduce the likelihood of users
71 // getting wedged because of proxy detection failures on network switch.
72 //
73 // The obvious downside to this strategy is it introduces an additional
74 // latency when switching networks. This delay shouldn't be too disruptive
75 // assuming network switches are infrequent and user initiated. However if
76 // NetworkChangeNotifier delivers network changes more frequently this could
77 // cause jankiness. (NetworkChangeNotifier broadcasts a change event when ANY
78 // interface goes up/down. So in theory if the non-primary interface were
79 // hopping on and off wireless networks our constant delayed reconfiguration
80 // could add noticeable jank.)
81 //
82 // The specific hard-coded wait time below is arbitrary.
83 // Basically I ran some experiments switching between wireless networks on
84 // a Linux Ubuntu (Lucid) laptop, and experimentally found this timeout fixes
85 // things. It is entirely possible that the value is insuficient for other
86 // setups.
87 const int64 kNumMillisToStallAfterNetworkChanges = 2000;
88
89 // Config getter that always returns direct settings.
90 class ProxyConfigServiceDirect : public ProxyConfigService {
91 public:
92 // ProxyConfigService implementation:
AddObserver(Observer * observer)93 virtual void AddObserver(Observer* observer) {}
RemoveObserver(Observer * observer)94 virtual void RemoveObserver(Observer* observer) {}
GetLatestProxyConfig(ProxyConfig * config)95 virtual ConfigAvailability GetLatestProxyConfig(ProxyConfig* config) {
96 *config = ProxyConfig::CreateDirect();
97 return CONFIG_VALID;
98 }
99 };
100
101 // Proxy resolver that fails every time.
102 class ProxyResolverNull : public ProxyResolver {
103 public:
ProxyResolverNull()104 ProxyResolverNull() : ProxyResolver(false /*expects_pac_bytes*/) {}
105
106 // ProxyResolver implementation:
GetProxyForURL(const GURL & url,ProxyInfo * results,CompletionCallback * callback,RequestHandle * request,const BoundNetLog & net_log)107 virtual int GetProxyForURL(const GURL& url,
108 ProxyInfo* results,
109 CompletionCallback* callback,
110 RequestHandle* request,
111 const BoundNetLog& net_log) {
112 return ERR_NOT_IMPLEMENTED;
113 }
114
CancelRequest(RequestHandle request)115 virtual void CancelRequest(RequestHandle request) {
116 NOTREACHED();
117 }
118
CancelSetPacScript()119 virtual void CancelSetPacScript() {
120 NOTREACHED();
121 }
122
SetPacScript(const scoped_refptr<ProxyResolverScriptData> &,CompletionCallback *)123 virtual int SetPacScript(
124 const scoped_refptr<ProxyResolverScriptData>& /*script_data*/,
125 CompletionCallback* /*callback*/) {
126 return ERR_NOT_IMPLEMENTED;
127 }
128 };
129
130 // ProxyResolver that simulates a PAC script which returns
131 // |pac_string| for every single URL.
132 class ProxyResolverFromPacString : public ProxyResolver {
133 public:
ProxyResolverFromPacString(const std::string & pac_string)134 ProxyResolverFromPacString(const std::string& pac_string)
135 : ProxyResolver(false /*expects_pac_bytes*/),
136 pac_string_(pac_string) {}
137
GetProxyForURL(const GURL & url,ProxyInfo * results,CompletionCallback * callback,RequestHandle * request,const BoundNetLog & net_log)138 virtual int GetProxyForURL(const GURL& url,
139 ProxyInfo* results,
140 CompletionCallback* callback,
141 RequestHandle* request,
142 const BoundNetLog& net_log) {
143 results->UsePacString(pac_string_);
144 return OK;
145 }
146
CancelRequest(RequestHandle request)147 virtual void CancelRequest(RequestHandle request) {
148 NOTREACHED();
149 }
150
CancelSetPacScript()151 virtual void CancelSetPacScript() {
152 NOTREACHED();
153 }
154
SetPacScript(const scoped_refptr<ProxyResolverScriptData> & pac_script,CompletionCallback * callback)155 virtual int SetPacScript(
156 const scoped_refptr<ProxyResolverScriptData>& pac_script,
157 CompletionCallback* callback) {
158 return OK;
159 }
160
161 private:
162 const std::string pac_string_;
163 };
164
165 #ifndef ANDROID
166 // This factory creates V8ProxyResolvers with appropriate javascript bindings.
167 class ProxyResolverFactoryForV8 : public ProxyResolverFactory {
168 public:
169 // |async_host_resolver|, |io_loop| and |net_log| must remain
170 // valid for the duration of our lifetime.
171 // |async_host_resolver| will only be operated on |io_loop|.
ProxyResolverFactoryForV8(HostResolver * async_host_resolver,MessageLoop * io_loop,NetLog * net_log)172 ProxyResolverFactoryForV8(HostResolver* async_host_resolver,
173 MessageLoop* io_loop,
174 NetLog* net_log)
175 : ProxyResolverFactory(true /*expects_pac_bytes*/),
176 async_host_resolver_(async_host_resolver),
177 io_loop_(io_loop),
178 net_log_(net_log) {
179 }
180
CreateProxyResolver()181 virtual ProxyResolver* CreateProxyResolver() {
182 // Create a synchronous host resolver wrapper that operates
183 // |async_host_resolver_| on |io_loop_|.
184 SyncHostResolverBridge* sync_host_resolver =
185 new SyncHostResolverBridge(async_host_resolver_, io_loop_);
186
187 ProxyResolverJSBindings* js_bindings =
188 ProxyResolverJSBindings::CreateDefault(sync_host_resolver, net_log_);
189
190 // ProxyResolverV8 takes ownership of |js_bindings|.
191 return new ProxyResolverV8(js_bindings);
192 }
193
194 private:
195 HostResolver* const async_host_resolver_;
196 MessageLoop* io_loop_;
197 NetLog* net_log_;
198 };
199 #endif
200
201 // Creates ProxyResolvers using a platform-specific implementation.
202 class ProxyResolverFactoryForSystem : public ProxyResolverFactory {
203 public:
ProxyResolverFactoryForSystem()204 ProxyResolverFactoryForSystem()
205 : ProxyResolverFactory(false /*expects_pac_bytes*/) {}
206
CreateProxyResolver()207 virtual ProxyResolver* CreateProxyResolver() {
208 DCHECK(IsSupported());
209 #if defined(OS_WIN)
210 return new ProxyResolverWinHttp();
211 #elif defined(OS_MACOSX)
212 return new ProxyResolverMac();
213 #else
214 NOTREACHED();
215 return NULL;
216 #endif
217 }
218
IsSupported()219 static bool IsSupported() {
220 #if defined(OS_WIN) || defined(OS_MACOSX)
221 return true;
222 #else
223 return false;
224 #endif
225 }
226 };
227
228 // NetLog parameter to describe a proxy configuration change.
229 class ProxyConfigChangedNetLogParam : public NetLog::EventParameters {
230 public:
ProxyConfigChangedNetLogParam(const ProxyConfig & old_config,const ProxyConfig & new_config)231 ProxyConfigChangedNetLogParam(const ProxyConfig& old_config,
232 const ProxyConfig& new_config)
233 : old_config_(old_config),
234 new_config_(new_config) {
235 }
236
ToValue() const237 virtual Value* ToValue() const {
238 DictionaryValue* dict = new DictionaryValue();
239 // The "old_config" is optional -- the first notification will not have
240 // any "previous" configuration.
241 if (old_config_.is_valid())
242 dict->Set("old_config", old_config_.ToValue());
243 dict->Set("new_config", new_config_.ToValue());
244 return dict;
245 }
246
247 private:
248 const ProxyConfig old_config_;
249 const ProxyConfig new_config_;
250 DISALLOW_COPY_AND_ASSIGN(ProxyConfigChangedNetLogParam);
251 };
252
253 } // namespace
254
255 // ProxyService::PacRequest ---------------------------------------------------
256
257 class ProxyService::PacRequest
258 : public base::RefCounted<ProxyService::PacRequest> {
259 public:
PacRequest(ProxyService * service,const GURL & url,ProxyInfo * results,CompletionCallback * user_callback,const BoundNetLog & net_log)260 PacRequest(ProxyService* service,
261 const GURL& url,
262 ProxyInfo* results,
263 CompletionCallback* user_callback,
264 const BoundNetLog& net_log)
265 : service_(service),
266 user_callback_(user_callback),
267 ALLOW_THIS_IN_INITIALIZER_LIST(io_callback_(
268 this, &PacRequest::QueryComplete)),
269 results_(results),
270 url_(url),
271 resolve_job_(NULL),
272 config_id_(ProxyConfig::INVALID_ID),
273 net_log_(net_log) {
274 DCHECK(user_callback);
275 }
276
277 // Starts the resolve proxy request.
Start()278 int Start() {
279 DCHECK(!was_cancelled());
280 DCHECK(!is_started());
281
282 DCHECK(service_->config_.is_valid());
283
284 config_id_ = service_->config_.id();
285
286 return resolver()->GetProxyForURL(
287 url_, results_, &io_callback_, &resolve_job_, net_log_);
288 }
289
is_started() const290 bool is_started() const {
291 // Note that !! casts to bool. (VS gives a warning otherwise).
292 return !!resolve_job_;
293 }
294
StartAndCompleteCheckingForSynchronous()295 void StartAndCompleteCheckingForSynchronous() {
296 int rv = service_->TryToCompleteSynchronously(url_, results_);
297 if (rv == ERR_IO_PENDING)
298 rv = Start();
299 if (rv != ERR_IO_PENDING)
300 QueryComplete(rv);
301 }
302
CancelResolveJob()303 void CancelResolveJob() {
304 DCHECK(is_started());
305 // The request may already be running in the resolver.
306 resolver()->CancelRequest(resolve_job_);
307 resolve_job_ = NULL;
308 DCHECK(!is_started());
309 }
310
Cancel()311 void Cancel() {
312 net_log_.AddEvent(NetLog::TYPE_CANCELLED, NULL);
313
314 if (is_started())
315 CancelResolveJob();
316
317 // Mark as cancelled, to prevent accessing this again later.
318 service_ = NULL;
319 user_callback_ = NULL;
320 results_ = NULL;
321
322 net_log_.EndEvent(NetLog::TYPE_PROXY_SERVICE, NULL);
323 }
324
325 // Returns true if Cancel() has been called.
was_cancelled() const326 bool was_cancelled() const { return user_callback_ == NULL; }
327
328 // Helper to call after ProxyResolver completion (both synchronous and
329 // asynchronous). Fixes up the result that is to be returned to user.
QueryDidComplete(int result_code)330 int QueryDidComplete(int result_code) {
331 DCHECK(!was_cancelled());
332
333 // Make a note in the results which configuration was in use at the
334 // time of the resolve.
335 results_->config_id_ = config_id_;
336
337 // Reset the state associated with in-progress-resolve.
338 resolve_job_ = NULL;
339 config_id_ = ProxyConfig::INVALID_ID;
340
341 return service_->DidFinishResolvingProxy(results_, result_code, net_log_);
342 }
343
net_log()344 BoundNetLog* net_log() { return &net_log_; }
345
346 private:
347 friend class base::RefCounted<ProxyService::PacRequest>;
348
~PacRequest()349 ~PacRequest() {}
350
351 // Callback for when the ProxyResolver request has completed.
QueryComplete(int result_code)352 void QueryComplete(int result_code) {
353 result_code = QueryDidComplete(result_code);
354
355 // Remove this completed PacRequest from the service's pending list.
356 /// (which will probably cause deletion of |this|).
357 CompletionCallback* callback = user_callback_;
358 service_->RemovePendingRequest(this);
359
360 callback->Run(result_code);
361 }
362
resolver() const363 ProxyResolver* resolver() const { return service_->resolver_.get(); }
364
365 // Note that we don't hold a reference to the ProxyService. Outstanding
366 // requests are cancelled during ~ProxyService, so this is guaranteed
367 // to be valid throughout our lifetime.
368 ProxyService* service_;
369 CompletionCallback* user_callback_;
370 CompletionCallbackImpl<PacRequest> io_callback_;
371 ProxyInfo* results_;
372 GURL url_;
373 ProxyResolver::RequestHandle resolve_job_;
374 ProxyConfig::ID config_id_; // The config id when the resolve was started.
375 BoundNetLog net_log_;
376 };
377
378 // ProxyService ---------------------------------------------------------------
379
ProxyService(ProxyConfigService * config_service,ProxyResolver * resolver,NetLog * net_log)380 ProxyService::ProxyService(ProxyConfigService* config_service,
381 ProxyResolver* resolver,
382 NetLog* net_log)
383 : resolver_(resolver),
384 next_config_id_(1),
385 ALLOW_THIS_IN_INITIALIZER_LIST(init_proxy_resolver_callback_(
386 this, &ProxyService::OnInitProxyResolverComplete)),
387 current_state_(STATE_NONE) ,
388 net_log_(net_log),
389 stall_proxy_auto_config_delay_(
390 base::TimeDelta::FromMilliseconds(
391 kNumMillisToStallAfterNetworkChanges)) {
392 NetworkChangeNotifier::AddIPAddressObserver(this);
393 ResetConfigService(config_service);
394 }
395
396 #ifndef ANDROID
397 // static
CreateUsingV8ProxyResolver(ProxyConfigService * proxy_config_service,size_t num_pac_threads,ProxyScriptFetcher * proxy_script_fetcher,HostResolver * host_resolver,NetLog * net_log)398 ProxyService* ProxyService::CreateUsingV8ProxyResolver(
399 ProxyConfigService* proxy_config_service,
400 size_t num_pac_threads,
401 ProxyScriptFetcher* proxy_script_fetcher,
402 HostResolver* host_resolver,
403 NetLog* net_log) {
404 DCHECK(proxy_config_service);
405 DCHECK(proxy_script_fetcher);
406 DCHECK(host_resolver);
407
408 if (num_pac_threads == 0)
409 num_pac_threads = kDefaultNumPacThreads;
410
411 ProxyResolverFactory* sync_resolver_factory =
412 new ProxyResolverFactoryForV8(
413 host_resolver,
414 MessageLoop::current(),
415 net_log);
416
417 ProxyResolver* proxy_resolver =
418 new MultiThreadedProxyResolver(sync_resolver_factory, num_pac_threads);
419
420 ProxyService* proxy_service =
421 new ProxyService(proxy_config_service, proxy_resolver, net_log);
422
423 // Configure PAC script downloads to be issued using |proxy_script_fetcher|.
424 proxy_service->SetProxyScriptFetcher(proxy_script_fetcher);
425
426 return proxy_service;
427 }
428 #endif
429
430 // static
CreateUsingSystemProxyResolver(ProxyConfigService * proxy_config_service,size_t num_pac_threads,NetLog * net_log)431 ProxyService* ProxyService::CreateUsingSystemProxyResolver(
432 ProxyConfigService* proxy_config_service,
433 size_t num_pac_threads,
434 NetLog* net_log) {
435 DCHECK(proxy_config_service);
436
437 if (!ProxyResolverFactoryForSystem::IsSupported()) {
438 LOG(WARNING) << "PAC support disabled because there is no "
439 "system implementation";
440 return CreateWithoutProxyResolver(proxy_config_service, net_log);
441 }
442
443 if (num_pac_threads == 0)
444 num_pac_threads = kDefaultNumPacThreads;
445
446 ProxyResolver* proxy_resolver = new MultiThreadedProxyResolver(
447 new ProxyResolverFactoryForSystem(), num_pac_threads);
448
449 return new ProxyService(proxy_config_service, proxy_resolver, net_log);
450 }
451
452 // static
CreateWithoutProxyResolver(ProxyConfigService * proxy_config_service,NetLog * net_log)453 ProxyService* ProxyService::CreateWithoutProxyResolver(
454 ProxyConfigService* proxy_config_service,
455 NetLog* net_log) {
456 return new ProxyService(proxy_config_service,
457 new ProxyResolverNull(),
458 net_log);
459 }
460
461 // static
CreateFixed(const ProxyConfig & pc)462 ProxyService* ProxyService::CreateFixed(const ProxyConfig& pc) {
463 // TODO(eroman): This isn't quite right, won't work if |pc| specifies
464 // a PAC script.
465 return CreateUsingSystemProxyResolver(new ProxyConfigServiceFixed(pc),
466 0, NULL);
467 }
468
469 // static
CreateFixed(const std::string & proxy)470 ProxyService* ProxyService::CreateFixed(const std::string& proxy) {
471 net::ProxyConfig proxy_config;
472 proxy_config.proxy_rules().ParseFromString(proxy);
473 return ProxyService::CreateFixed(proxy_config);
474 }
475
476 // static
CreateDirect()477 ProxyService* ProxyService::CreateDirect() {
478 return CreateDirectWithNetLog(NULL);
479 }
480
CreateDirectWithNetLog(NetLog * net_log)481 ProxyService* ProxyService::CreateDirectWithNetLog(NetLog* net_log) {
482 // Use direct connections.
483 return new ProxyService(new ProxyConfigServiceDirect, new ProxyResolverNull,
484 net_log);
485 }
486
487 // static
CreateFixedFromPacResult(const std::string & pac_string)488 ProxyService* ProxyService::CreateFixedFromPacResult(
489 const std::string& pac_string) {
490
491 // We need the settings to contain an "automatic" setting, otherwise the
492 // ProxyResolver dependency we give it will never be used.
493 scoped_ptr<ProxyConfigService> proxy_config_service(
494 new ProxyConfigServiceFixed(ProxyConfig::CreateAutoDetect()));
495
496 scoped_ptr<ProxyResolver> proxy_resolver(
497 new ProxyResolverFromPacString(pac_string));
498
499 return new ProxyService(proxy_config_service.release(),
500 proxy_resolver.release(),
501 NULL);
502 }
503
ResolveProxy(const GURL & raw_url,ProxyInfo * result,CompletionCallback * callback,PacRequest ** pac_request,const BoundNetLog & net_log)504 int ProxyService::ResolveProxy(const GURL& raw_url,
505 ProxyInfo* result,
506 CompletionCallback* callback,
507 PacRequest** pac_request,
508 const BoundNetLog& net_log) {
509 DCHECK(CalledOnValidThread());
510 DCHECK(callback);
511
512 net_log.BeginEvent(NetLog::TYPE_PROXY_SERVICE, NULL);
513
514 config_service_->OnLazyPoll();
515 if (current_state_ == STATE_NONE)
516 ApplyProxyConfigIfAvailable();
517
518 // Strip away any reference fragments and the username/password, as they
519 // are not relevant to proxy resolution.
520 GURL url = SimplifyUrlForRequest(raw_url);
521
522 // Check if the request can be completed right away. (This is the case when
523 // using a direct connection for example).
524 int rv = TryToCompleteSynchronously(url, result);
525 if (rv != ERR_IO_PENDING)
526 return DidFinishResolvingProxy(result, rv, net_log);
527
528 scoped_refptr<PacRequest> req(
529 new PacRequest(this, url, result, callback, net_log));
530
531 if (current_state_ == STATE_READY) {
532 // Start the resolve request.
533 rv = req->Start();
534 if (rv != ERR_IO_PENDING)
535 return req->QueryDidComplete(rv);
536 } else {
537 req->net_log()->BeginEvent(NetLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC,
538 NULL);
539 }
540
541 DCHECK_EQ(ERR_IO_PENDING, rv);
542 DCHECK(!ContainsPendingRequest(req));
543 pending_requests_.push_back(req);
544
545 // Completion will be notifed through |callback|, unless the caller cancels
546 // the request using |pac_request|.
547 if (pac_request)
548 *pac_request = req.get();
549 return rv; // ERR_IO_PENDING
550 }
551
TryToCompleteSynchronously(const GURL & url,ProxyInfo * result)552 int ProxyService::TryToCompleteSynchronously(const GURL& url,
553 ProxyInfo* result) {
554 DCHECK_NE(STATE_NONE, current_state_);
555
556 if (current_state_ != STATE_READY)
557 return ERR_IO_PENDING; // Still initializing.
558
559 DCHECK_NE(config_.id(), ProxyConfig::INVALID_ID);
560
561 if (config_.HasAutomaticSettings())
562 return ERR_IO_PENDING; // Must submit the request to the proxy resolver.
563
564 // Use the manual proxy settings.
565 config_.proxy_rules().Apply(url, result);
566 result->config_id_ = config_.id();
567 return OK;
568 }
569
~ProxyService()570 ProxyService::~ProxyService() {
571 NetworkChangeNotifier::RemoveIPAddressObserver(this);
572 config_service_->RemoveObserver(this);
573
574 // Cancel any inprogress requests.
575 for (PendingRequests::iterator it = pending_requests_.begin();
576 it != pending_requests_.end();
577 ++it) {
578 (*it)->Cancel();
579 }
580 }
581
SuspendAllPendingRequests()582 void ProxyService::SuspendAllPendingRequests() {
583 for (PendingRequests::iterator it = pending_requests_.begin();
584 it != pending_requests_.end();
585 ++it) {
586 PacRequest* req = it->get();
587 if (req->is_started()) {
588 req->CancelResolveJob();
589
590 req->net_log()->BeginEvent(
591 NetLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC, NULL);
592 }
593 }
594 }
595
SetReady()596 void ProxyService::SetReady() {
597 DCHECK(!init_proxy_resolver_.get());
598 current_state_ = STATE_READY;
599
600 // Make a copy in case |this| is deleted during the synchronous completion
601 // of one of the requests. If |this| is deleted then all of the PacRequest
602 // instances will be Cancel()-ed.
603 PendingRequests pending_copy = pending_requests_;
604
605 for (PendingRequests::iterator it = pending_copy.begin();
606 it != pending_copy.end();
607 ++it) {
608 PacRequest* req = it->get();
609 if (!req->is_started() && !req->was_cancelled()) {
610 req->net_log()->EndEvent(NetLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC,
611 NULL);
612
613 // Note that we re-check for synchronous completion, in case we are
614 // no longer using a ProxyResolver (can happen if we fell-back to manual).
615 req->StartAndCompleteCheckingForSynchronous();
616 }
617 }
618 }
619
ApplyProxyConfigIfAvailable()620 void ProxyService::ApplyProxyConfigIfAvailable() {
621 DCHECK_EQ(STATE_NONE, current_state_);
622
623 config_service_->OnLazyPoll();
624
625 // If we have already fetched the configuration, start applying it.
626 if (fetched_config_.is_valid()) {
627 InitializeUsingLastFetchedConfig();
628 return;
629 }
630
631 // Otherwise we need to first fetch the configuration.
632 current_state_ = STATE_WAITING_FOR_PROXY_CONFIG;
633
634 // Retrieve the current proxy configuration from the ProxyConfigService.
635 // If a configuration is not available yet, we will get called back later
636 // by our ProxyConfigService::Observer once it changes.
637 ProxyConfig config;
638 ProxyConfigService::ConfigAvailability availability =
639 config_service_->GetLatestProxyConfig(&config);
640 if (availability != ProxyConfigService::CONFIG_PENDING)
641 OnProxyConfigChanged(config, availability);
642 }
643
OnInitProxyResolverComplete(int result)644 void ProxyService::OnInitProxyResolverComplete(int result) {
645 DCHECK_EQ(STATE_WAITING_FOR_INIT_PROXY_RESOLVER, current_state_);
646 DCHECK(init_proxy_resolver_.get());
647 DCHECK(fetched_config_.HasAutomaticSettings());
648 init_proxy_resolver_.reset();
649
650 if (result != OK) {
651 VLOG(1) << "Failed configuring with PAC script, falling-back to manual "
652 "proxy servers.";
653 config_ = fetched_config_;
654 config_.ClearAutomaticSettings();
655 }
656
657 config_.set_id(fetched_config_.id());
658
659 // Resume any requests which we had to defer until the PAC script was
660 // downloaded.
661 SetReady();
662 }
663
ReconsiderProxyAfterError(const GURL & url,ProxyInfo * result,CompletionCallback * callback,PacRequest ** pac_request,const BoundNetLog & net_log)664 int ProxyService::ReconsiderProxyAfterError(const GURL& url,
665 ProxyInfo* result,
666 CompletionCallback* callback,
667 PacRequest** pac_request,
668 const BoundNetLog& net_log) {
669 DCHECK(CalledOnValidThread());
670
671 // Check to see if we have a new config since ResolveProxy was called. We
672 // want to re-run ResolveProxy in two cases: 1) we have a new config, or 2) a
673 // direct connection failed and we never tried the current config.
674
675 bool re_resolve = result->config_id_ != config_.id();
676
677 if (re_resolve) {
678 // If we have a new config or the config was never tried, we delete the
679 // list of bad proxies and we try again.
680 proxy_retry_info_.clear();
681 return ResolveProxy(url, result, callback, pac_request, net_log);
682 }
683
684 // We don't have new proxy settings to try, try to fallback to the next proxy
685 // in the list.
686 bool did_fallback = result->Fallback(&proxy_retry_info_);
687
688 // Return synchronous failure if there is nothing left to fall-back to.
689 // TODO(eroman): This is a yucky API, clean it up.
690 return did_fallback ? OK : ERR_FAILED;
691 }
692
CancelPacRequest(PacRequest * req)693 void ProxyService::CancelPacRequest(PacRequest* req) {
694 DCHECK(CalledOnValidThread());
695 DCHECK(req);
696 req->Cancel();
697 RemovePendingRequest(req);
698 }
699
ContainsPendingRequest(PacRequest * req)700 bool ProxyService::ContainsPendingRequest(PacRequest* req) {
701 PendingRequests::iterator it = std::find(
702 pending_requests_.begin(), pending_requests_.end(), req);
703 return pending_requests_.end() != it;
704 }
705
RemovePendingRequest(PacRequest * req)706 void ProxyService::RemovePendingRequest(PacRequest* req) {
707 DCHECK(ContainsPendingRequest(req));
708 PendingRequests::iterator it = std::find(
709 pending_requests_.begin(), pending_requests_.end(), req);
710 pending_requests_.erase(it);
711 }
712
DidFinishResolvingProxy(ProxyInfo * result,int result_code,const BoundNetLog & net_log)713 int ProxyService::DidFinishResolvingProxy(ProxyInfo* result,
714 int result_code,
715 const BoundNetLog& net_log) {
716 // Log the result of the proxy resolution.
717 if (result_code == OK) {
718 // When logging all events is enabled, dump the proxy list.
719 if (net_log.IsLoggingAllEvents()) {
720 net_log.AddEvent(
721 NetLog::TYPE_PROXY_SERVICE_RESOLVED_PROXY_LIST,
722 make_scoped_refptr(new NetLogStringParameter(
723 "pac_string", result->ToPacString())));
724 }
725 result->DeprioritizeBadProxies(proxy_retry_info_);
726 } else {
727 net_log.AddEvent(
728 NetLog::TYPE_PROXY_SERVICE_RESOLVED_PROXY_LIST,
729 make_scoped_refptr(new NetLogIntegerParameter(
730 "net_error", result_code)));
731
732 // Fall-back to direct when the proxy resolver fails. This corresponds
733 // with a javascript runtime error in the PAC script.
734 //
735 // This implicit fall-back to direct matches Firefox 3.5 and
736 // Internet Explorer 8. For more information, see:
737 //
738 // http://www.chromium.org/developers/design-documents/proxy-settings-fallback
739 result->UseDirect();
740 result_code = OK;
741 }
742
743 net_log.EndEvent(NetLog::TYPE_PROXY_SERVICE, NULL);
744 return result_code;
745 }
746
SetProxyScriptFetcher(ProxyScriptFetcher * proxy_script_fetcher)747 void ProxyService::SetProxyScriptFetcher(
748 ProxyScriptFetcher* proxy_script_fetcher) {
749 DCHECK(CalledOnValidThread());
750 State previous_state = ResetProxyConfig(false);
751 proxy_script_fetcher_.reset(proxy_script_fetcher);
752 if (previous_state != STATE_NONE)
753 ApplyProxyConfigIfAvailable();
754 }
755
GetProxyScriptFetcher() const756 ProxyScriptFetcher* ProxyService::GetProxyScriptFetcher() const {
757 DCHECK(CalledOnValidThread());
758 return proxy_script_fetcher_.get();
759 }
760
ResetProxyConfig(bool reset_fetched_config)761 ProxyService::State ProxyService::ResetProxyConfig(bool reset_fetched_config) {
762 DCHECK(CalledOnValidThread());
763 State previous_state = current_state_;
764
765 proxy_retry_info_.clear();
766 init_proxy_resolver_.reset();
767 SuspendAllPendingRequests();
768 config_ = ProxyConfig();
769 if (reset_fetched_config)
770 fetched_config_ = ProxyConfig();
771 current_state_ = STATE_NONE;
772
773 return previous_state;
774 }
775
ResetConfigService(ProxyConfigService * new_proxy_config_service)776 void ProxyService::ResetConfigService(
777 ProxyConfigService* new_proxy_config_service) {
778 DCHECK(CalledOnValidThread());
779 State previous_state = ResetProxyConfig(true);
780
781 // Release the old configuration service.
782 if (config_service_.get())
783 config_service_->RemoveObserver(this);
784
785 // Set the new configuration service.
786 config_service_.reset(new_proxy_config_service);
787 config_service_->AddObserver(this);
788
789 if (previous_state != STATE_NONE)
790 ApplyProxyConfigIfAvailable();
791 }
792
PurgeMemory()793 void ProxyService::PurgeMemory() {
794 DCHECK(CalledOnValidThread());
795 if (resolver_.get())
796 resolver_->PurgeMemory();
797 }
798
ForceReloadProxyConfig()799 void ProxyService::ForceReloadProxyConfig() {
800 DCHECK(CalledOnValidThread());
801 ResetProxyConfig(false);
802 ApplyProxyConfigIfAvailable();
803 }
804
805 // static
CreateSystemProxyConfigService(MessageLoop * io_loop,MessageLoop * file_loop)806 ProxyConfigService* ProxyService::CreateSystemProxyConfigService(
807 MessageLoop* io_loop, MessageLoop* file_loop) {
808 #if defined(OS_WIN)
809 return new ProxyConfigServiceWin();
810 #elif defined(OS_MACOSX)
811 return new ProxyConfigServiceMac(io_loop);
812 #elif defined(OS_CHROMEOS)
813 NOTREACHED() << "ProxyConfigService for ChromeOS should be created in "
814 << "profile_io_data.cc::CreateProxyConfigService.";
815 return NULL;
816 #elif defined(ANDROID)
817 NOTREACHED() << "ProxyConfigService for Android should be created in "
818 << "WebCache.cpp: WebCache::WebCache";
819 return NULL;
820 #elif defined(OS_LINUX)
821 ProxyConfigServiceLinux* linux_config_service =
822 new ProxyConfigServiceLinux();
823
824 // Assume we got called from the UI loop, which runs the default
825 // glib main loop, so the current thread is where we should be
826 // running gconf calls from.
827 MessageLoop* glib_default_loop = MessageLoopForUI::current();
828
829 // The file loop should be a MessageLoopForIO on Linux.
830 DCHECK_EQ(MessageLoop::TYPE_IO, file_loop->type());
831
832 // Synchronously fetch the current proxy config (since we are
833 // running on glib_default_loop). Additionally register for
834 // notifications (delivered in either |glib_default_loop| or
835 // |file_loop|) to keep us updated when the proxy config changes.
836 linux_config_service->SetupAndFetchInitialConfig(glib_default_loop, io_loop,
837 static_cast<MessageLoopForIO*>(file_loop));
838
839 return linux_config_service;
840 #else
841 LOG(WARNING) << "Failed to choose a system proxy settings fetcher "
842 "for this platform.";
843 return new ProxyConfigServiceNull();
844 #endif
845 }
846
OnProxyConfigChanged(const ProxyConfig & config,ProxyConfigService::ConfigAvailability availability)847 void ProxyService::OnProxyConfigChanged(
848 const ProxyConfig& config,
849 ProxyConfigService::ConfigAvailability availability) {
850 // Retrieve the current proxy configuration from the ProxyConfigService.
851 // If a configuration is not available yet, we will get called back later
852 // by our ProxyConfigService::Observer once it changes.
853 ProxyConfig effective_config;
854 switch (availability) {
855 case ProxyConfigService::CONFIG_PENDING:
856 // ProxyConfigService implementors should never pass CONFIG_PENDING.
857 NOTREACHED() << "Proxy config change with CONFIG_PENDING availability!";
858 return;
859 case ProxyConfigService::CONFIG_VALID:
860 effective_config = config;
861 break;
862 case ProxyConfigService::CONFIG_UNSET:
863 effective_config = ProxyConfig::CreateDirect();
864 break;
865 }
866
867 // Emit the proxy settings change to the NetLog stream.
868 if (net_log_) {
869 scoped_refptr<NetLog::EventParameters> params(
870 new ProxyConfigChangedNetLogParam(fetched_config_, effective_config));
871 net_log_->AddEntry(net::NetLog::TYPE_PROXY_CONFIG_CHANGED,
872 base::TimeTicks::Now(),
873 NetLog::Source(),
874 NetLog::PHASE_NONE,
875 params);
876 }
877
878 // Set the new configuration as the most recently fetched one.
879 fetched_config_ = effective_config;
880 fetched_config_.set_id(1); // Needed for a later DCHECK of is_valid().
881
882 InitializeUsingLastFetchedConfig();
883 }
884
InitializeUsingLastFetchedConfig()885 void ProxyService::InitializeUsingLastFetchedConfig() {
886 ResetProxyConfig(false);
887
888 DCHECK(fetched_config_.is_valid());
889
890 // Increment the ID to reflect that the config has changed.
891 fetched_config_.set_id(next_config_id_++);
892
893 if (!fetched_config_.HasAutomaticSettings()) {
894 config_ = fetched_config_;
895 SetReady();
896 return;
897 }
898
899 // Start downloading + testing the PAC scripts for this new configuration.
900 current_state_ = STATE_WAITING_FOR_INIT_PROXY_RESOLVER;
901
902 init_proxy_resolver_.reset(
903 new InitProxyResolver(resolver_.get(), proxy_script_fetcher_.get(),
904 net_log_));
905
906 // If we changed networks recently, we should delay running proxy auto-config.
907 base::TimeDelta wait_delay =
908 stall_proxy_autoconfig_until_ - base::TimeTicks::Now();
909
910 int rv = init_proxy_resolver_->Init(
911 fetched_config_, wait_delay, &config_, &init_proxy_resolver_callback_);
912
913 if (rv != ERR_IO_PENDING)
914 OnInitProxyResolverComplete(rv);
915 }
916
OnIPAddressChanged()917 void ProxyService::OnIPAddressChanged() {
918 // See the comment block by |kNumMillisToStallAfterNetworkChanges| for info.
919 stall_proxy_autoconfig_until_ =
920 base::TimeTicks::Now() + stall_proxy_auto_config_delay_;
921
922 State previous_state = ResetProxyConfig(false);
923 if (previous_state != STATE_NONE)
924 ApplyProxyConfigIfAvailable();
925 }
926
SyncProxyServiceHelper(MessageLoop * io_message_loop,ProxyService * proxy_service)927 SyncProxyServiceHelper::SyncProxyServiceHelper(MessageLoop* io_message_loop,
928 ProxyService* proxy_service)
929 : io_message_loop_(io_message_loop),
930 proxy_service_(proxy_service),
931 event_(false, false),
932 ALLOW_THIS_IN_INITIALIZER_LIST(callback_(
933 this, &SyncProxyServiceHelper::OnCompletion)) {
934 DCHECK(io_message_loop_ != MessageLoop::current());
935 }
936
ResolveProxy(const GURL & url,ProxyInfo * proxy_info,const BoundNetLog & net_log)937 int SyncProxyServiceHelper::ResolveProxy(const GURL& url,
938 ProxyInfo* proxy_info,
939 const BoundNetLog& net_log) {
940 DCHECK(io_message_loop_ != MessageLoop::current());
941
942 io_message_loop_->PostTask(FROM_HERE, NewRunnableMethod(
943 this, &SyncProxyServiceHelper::StartAsyncResolve, url, net_log));
944
945 event_.Wait();
946
947 if (result_ == net::OK) {
948 *proxy_info = proxy_info_;
949 }
950 return result_;
951 }
952
ReconsiderProxyAfterError(const GURL & url,ProxyInfo * proxy_info,const BoundNetLog & net_log)953 int SyncProxyServiceHelper::ReconsiderProxyAfterError(
954 const GURL& url, ProxyInfo* proxy_info, const BoundNetLog& net_log) {
955 DCHECK(io_message_loop_ != MessageLoop::current());
956
957 io_message_loop_->PostTask(FROM_HERE, NewRunnableMethod(
958 this, &SyncProxyServiceHelper::StartAsyncReconsider, url, net_log));
959
960 event_.Wait();
961
962 if (result_ == net::OK) {
963 *proxy_info = proxy_info_;
964 }
965 return result_;
966 }
967
~SyncProxyServiceHelper()968 SyncProxyServiceHelper::~SyncProxyServiceHelper() {}
969
StartAsyncResolve(const GURL & url,const BoundNetLog & net_log)970 void SyncProxyServiceHelper::StartAsyncResolve(const GURL& url,
971 const BoundNetLog& net_log) {
972 result_ = proxy_service_->ResolveProxy(
973 url, &proxy_info_, &callback_, NULL, net_log);
974 if (result_ != net::ERR_IO_PENDING) {
975 OnCompletion(result_);
976 }
977 }
978
StartAsyncReconsider(const GURL & url,const BoundNetLog & net_log)979 void SyncProxyServiceHelper::StartAsyncReconsider(const GURL& url,
980 const BoundNetLog& net_log) {
981 result_ = proxy_service_->ReconsiderProxyAfterError(
982 url, &proxy_info_, &callback_, NULL, net_log);
983 if (result_ != net::ERR_IO_PENDING) {
984 OnCompletion(result_);
985 }
986 }
987
OnCompletion(int rv)988 void SyncProxyServiceHelper::OnCompletion(int rv) {
989 result_ = rv;
990 event_.Signal();
991 }
992
993 } // namespace net
994