// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/proxy/proxy_service.h" #include #include "base/compiler_specific.h" #include "base/logging.h" #include "base/message_loop.h" #include "base/string_util.h" #include "base/values.h" #include "googleurl/src/gurl.h" #include "net/base/net_errors.h" #include "net/base/net_log.h" #include "net/base/net_util.h" #include "net/proxy/init_proxy_resolver.h" #include "net/proxy/multi_threaded_proxy_resolver.h" #include "net/proxy/proxy_config_service_fixed.h" #include "net/proxy/proxy_resolver.h" #include "net/proxy/proxy_resolver_js_bindings.h" #ifndef ANDROID #include "net/proxy/proxy_resolver_v8.h" #endif #include "net/proxy/proxy_script_fetcher.h" #include "net/proxy/sync_host_resolver_bridge.h" #include "net/url_request/url_request_context.h" #if defined(OS_WIN) #include "net/proxy/proxy_config_service_win.h" #include "net/proxy/proxy_resolver_winhttp.h" #elif defined(OS_MACOSX) #include "net/proxy/proxy_config_service_mac.h" #include "net/proxy/proxy_resolver_mac.h" #elif defined(OS_LINUX) && !defined(OS_CHROMEOS) #include "net/proxy/proxy_config_service_linux.h" #endif using base::TimeDelta; using base::TimeTicks; namespace net { namespace { const size_t kMaxNumNetLogEntries = 100; const size_t kDefaultNumPacThreads = 4; // When the IP address changes we don't immediately re-run proxy auto-config. // Instead, we wait for |kNumMillisToStallAfterNetworkChanges| before // attempting to re-valuate proxy auto-config. // // During this time window, any resolve requests sent to the ProxyService will // be queued. Once we have waited the required amount of them, the proxy // auto-config step will be run, and the queued requests resumed. // // The reason we play this game is that our signal for detecting network // changes (NetworkChangeNotifier) may fire *before* the system's networking // dependencies are fully configured. This is a problem since it means if // we were to run proxy auto-config right away, it could fail due to spurious // DNS failures. (see http://crbug.com/50779 for more details.) // // By adding the wait window, we give things a chance to get properly set up. // Now by the time we run the proxy-autoconfig there is a lower chance of // getting transient DNS / connect failures. // // Admitedly this is a hack. Ideally we would have NetworkChangeNotifier // deliver a reliable signal indicating that the network has changed AND is // ready for action... But until then, we can reduce the likelihood of users // getting wedged because of proxy detection failures on network switch. // // The obvious downside to this strategy is it introduces an additional // latency when switching networks. This delay shouldn't be too disruptive // assuming network switches are infrequent and user initiated. However if // NetworkChangeNotifier delivers network changes more frequently this could // cause jankiness. (NetworkChangeNotifier broadcasts a change event when ANY // interface goes up/down. So in theory if the non-primary interface were // hopping on and off wireless networks our constant delayed reconfiguration // could add noticeable jank.) // // The specific hard-coded wait time below is arbitrary. // Basically I ran some experiments switching between wireless networks on // a Linux Ubuntu (Lucid) laptop, and experimentally found this timeout fixes // things. It is entirely possible that the value is insuficient for other // setups. const int64 kNumMillisToStallAfterNetworkChanges = 2000; // Config getter that always returns direct settings. class ProxyConfigServiceDirect : public ProxyConfigService { public: // ProxyConfigService implementation: virtual void AddObserver(Observer* observer) {} virtual void RemoveObserver(Observer* observer) {} virtual ConfigAvailability GetLatestProxyConfig(ProxyConfig* config) { *config = ProxyConfig::CreateDirect(); return CONFIG_VALID; } }; // Proxy resolver that fails every time. class ProxyResolverNull : public ProxyResolver { public: ProxyResolverNull() : ProxyResolver(false /*expects_pac_bytes*/) {} // ProxyResolver implementation: virtual int GetProxyForURL(const GURL& url, ProxyInfo* results, CompletionCallback* callback, RequestHandle* request, const BoundNetLog& net_log) { return ERR_NOT_IMPLEMENTED; } virtual void CancelRequest(RequestHandle request) { NOTREACHED(); } virtual void CancelSetPacScript() { NOTREACHED(); } virtual int SetPacScript( const scoped_refptr& /*script_data*/, CompletionCallback* /*callback*/) { return ERR_NOT_IMPLEMENTED; } }; // ProxyResolver that simulates a PAC script which returns // |pac_string| for every single URL. class ProxyResolverFromPacString : public ProxyResolver { public: ProxyResolverFromPacString(const std::string& pac_string) : ProxyResolver(false /*expects_pac_bytes*/), pac_string_(pac_string) {} virtual int GetProxyForURL(const GURL& url, ProxyInfo* results, CompletionCallback* callback, RequestHandle* request, const BoundNetLog& net_log) { results->UsePacString(pac_string_); return OK; } virtual void CancelRequest(RequestHandle request) { NOTREACHED(); } virtual void CancelSetPacScript() { NOTREACHED(); } virtual int SetPacScript( const scoped_refptr& pac_script, CompletionCallback* callback) { return OK; } private: const std::string pac_string_; }; #ifndef ANDROID // This factory creates V8ProxyResolvers with appropriate javascript bindings. class ProxyResolverFactoryForV8 : public ProxyResolverFactory { public: // |async_host_resolver|, |io_loop| and |net_log| must remain // valid for the duration of our lifetime. // |async_host_resolver| will only be operated on |io_loop|. ProxyResolverFactoryForV8(HostResolver* async_host_resolver, MessageLoop* io_loop, NetLog* net_log) : ProxyResolverFactory(true /*expects_pac_bytes*/), async_host_resolver_(async_host_resolver), io_loop_(io_loop), net_log_(net_log) { } virtual ProxyResolver* CreateProxyResolver() { // Create a synchronous host resolver wrapper that operates // |async_host_resolver_| on |io_loop_|. SyncHostResolverBridge* sync_host_resolver = new SyncHostResolverBridge(async_host_resolver_, io_loop_); ProxyResolverJSBindings* js_bindings = ProxyResolverJSBindings::CreateDefault(sync_host_resolver, net_log_); // ProxyResolverV8 takes ownership of |js_bindings|. return new ProxyResolverV8(js_bindings); } private: HostResolver* const async_host_resolver_; MessageLoop* io_loop_; NetLog* net_log_; }; #endif // Creates ProxyResolvers using a platform-specific implementation. class ProxyResolverFactoryForSystem : public ProxyResolverFactory { public: ProxyResolverFactoryForSystem() : ProxyResolverFactory(false /*expects_pac_bytes*/) {} virtual ProxyResolver* CreateProxyResolver() { DCHECK(IsSupported()); #if defined(OS_WIN) return new ProxyResolverWinHttp(); #elif defined(OS_MACOSX) return new ProxyResolverMac(); #else NOTREACHED(); return NULL; #endif } static bool IsSupported() { #if defined(OS_WIN) || defined(OS_MACOSX) return true; #else return false; #endif } }; // NetLog parameter to describe a proxy configuration change. class ProxyConfigChangedNetLogParam : public NetLog::EventParameters { public: ProxyConfigChangedNetLogParam(const ProxyConfig& old_config, const ProxyConfig& new_config) : old_config_(old_config), new_config_(new_config) { } virtual Value* ToValue() const { DictionaryValue* dict = new DictionaryValue(); // The "old_config" is optional -- the first notification will not have // any "previous" configuration. if (old_config_.is_valid()) dict->Set("old_config", old_config_.ToValue()); dict->Set("new_config", new_config_.ToValue()); return dict; } private: const ProxyConfig old_config_; const ProxyConfig new_config_; DISALLOW_COPY_AND_ASSIGN(ProxyConfigChangedNetLogParam); }; } // namespace // ProxyService::PacRequest --------------------------------------------------- class ProxyService::PacRequest : public base::RefCounted { public: PacRequest(ProxyService* service, const GURL& url, ProxyInfo* results, CompletionCallback* user_callback, const BoundNetLog& net_log) : service_(service), user_callback_(user_callback), ALLOW_THIS_IN_INITIALIZER_LIST(io_callback_( this, &PacRequest::QueryComplete)), results_(results), url_(url), resolve_job_(NULL), config_id_(ProxyConfig::INVALID_ID), net_log_(net_log) { DCHECK(user_callback); } // Starts the resolve proxy request. int Start() { DCHECK(!was_cancelled()); DCHECK(!is_started()); DCHECK(service_->config_.is_valid()); config_id_ = service_->config_.id(); return resolver()->GetProxyForURL( url_, results_, &io_callback_, &resolve_job_, net_log_); } bool is_started() const { // Note that !! casts to bool. (VS gives a warning otherwise). return !!resolve_job_; } void StartAndCompleteCheckingForSynchronous() { int rv = service_->TryToCompleteSynchronously(url_, results_); if (rv == ERR_IO_PENDING) rv = Start(); if (rv != ERR_IO_PENDING) QueryComplete(rv); } void CancelResolveJob() { DCHECK(is_started()); // The request may already be running in the resolver. resolver()->CancelRequest(resolve_job_); resolve_job_ = NULL; DCHECK(!is_started()); } void Cancel() { net_log_.AddEvent(NetLog::TYPE_CANCELLED, NULL); if (is_started()) CancelResolveJob(); // Mark as cancelled, to prevent accessing this again later. service_ = NULL; user_callback_ = NULL; results_ = NULL; net_log_.EndEvent(NetLog::TYPE_PROXY_SERVICE, NULL); } // Returns true if Cancel() has been called. bool was_cancelled() const { return user_callback_ == NULL; } // Helper to call after ProxyResolver completion (both synchronous and // asynchronous). Fixes up the result that is to be returned to user. int QueryDidComplete(int result_code) { DCHECK(!was_cancelled()); // Make a note in the results which configuration was in use at the // time of the resolve. results_->config_id_ = config_id_; // Reset the state associated with in-progress-resolve. resolve_job_ = NULL; config_id_ = ProxyConfig::INVALID_ID; return service_->DidFinishResolvingProxy(results_, result_code, net_log_); } BoundNetLog* net_log() { return &net_log_; } private: friend class base::RefCounted; ~PacRequest() {} // Callback for when the ProxyResolver request has completed. void QueryComplete(int result_code) { result_code = QueryDidComplete(result_code); // Remove this completed PacRequest from the service's pending list. /// (which will probably cause deletion of |this|). CompletionCallback* callback = user_callback_; service_->RemovePendingRequest(this); callback->Run(result_code); } ProxyResolver* resolver() const { return service_->resolver_.get(); } // Note that we don't hold a reference to the ProxyService. Outstanding // requests are cancelled during ~ProxyService, so this is guaranteed // to be valid throughout our lifetime. ProxyService* service_; CompletionCallback* user_callback_; CompletionCallbackImpl io_callback_; ProxyInfo* results_; GURL url_; ProxyResolver::RequestHandle resolve_job_; ProxyConfig::ID config_id_; // The config id when the resolve was started. BoundNetLog net_log_; }; // ProxyService --------------------------------------------------------------- ProxyService::ProxyService(ProxyConfigService* config_service, ProxyResolver* resolver, NetLog* net_log) : resolver_(resolver), next_config_id_(1), ALLOW_THIS_IN_INITIALIZER_LIST(init_proxy_resolver_callback_( this, &ProxyService::OnInitProxyResolverComplete)), current_state_(STATE_NONE) , net_log_(net_log), stall_proxy_auto_config_delay_( base::TimeDelta::FromMilliseconds( kNumMillisToStallAfterNetworkChanges)) { NetworkChangeNotifier::AddIPAddressObserver(this); ResetConfigService(config_service); } #ifndef ANDROID // static ProxyService* ProxyService::CreateUsingV8ProxyResolver( ProxyConfigService* proxy_config_service, size_t num_pac_threads, ProxyScriptFetcher* proxy_script_fetcher, HostResolver* host_resolver, NetLog* net_log) { DCHECK(proxy_config_service); DCHECK(proxy_script_fetcher); DCHECK(host_resolver); if (num_pac_threads == 0) num_pac_threads = kDefaultNumPacThreads; ProxyResolverFactory* sync_resolver_factory = new ProxyResolverFactoryForV8( host_resolver, MessageLoop::current(), net_log); ProxyResolver* proxy_resolver = new MultiThreadedProxyResolver(sync_resolver_factory, num_pac_threads); ProxyService* proxy_service = new ProxyService(proxy_config_service, proxy_resolver, net_log); // Configure PAC script downloads to be issued using |proxy_script_fetcher|. proxy_service->SetProxyScriptFetcher(proxy_script_fetcher); return proxy_service; } #endif // static ProxyService* ProxyService::CreateUsingSystemProxyResolver( ProxyConfigService* proxy_config_service, size_t num_pac_threads, NetLog* net_log) { DCHECK(proxy_config_service); if (!ProxyResolverFactoryForSystem::IsSupported()) { LOG(WARNING) << "PAC support disabled because there is no " "system implementation"; return CreateWithoutProxyResolver(proxy_config_service, net_log); } if (num_pac_threads == 0) num_pac_threads = kDefaultNumPacThreads; ProxyResolver* proxy_resolver = new MultiThreadedProxyResolver( new ProxyResolverFactoryForSystem(), num_pac_threads); return new ProxyService(proxy_config_service, proxy_resolver, net_log); } // static ProxyService* ProxyService::CreateWithoutProxyResolver( ProxyConfigService* proxy_config_service, NetLog* net_log) { return new ProxyService(proxy_config_service, new ProxyResolverNull(), net_log); } // static ProxyService* ProxyService::CreateFixed(const ProxyConfig& pc) { // TODO(eroman): This isn't quite right, won't work if |pc| specifies // a PAC script. return CreateUsingSystemProxyResolver(new ProxyConfigServiceFixed(pc), 0, NULL); } // static ProxyService* ProxyService::CreateFixed(const std::string& proxy) { net::ProxyConfig proxy_config; proxy_config.proxy_rules().ParseFromString(proxy); return ProxyService::CreateFixed(proxy_config); } // static ProxyService* ProxyService::CreateDirect() { return CreateDirectWithNetLog(NULL); } ProxyService* ProxyService::CreateDirectWithNetLog(NetLog* net_log) { // Use direct connections. return new ProxyService(new ProxyConfigServiceDirect, new ProxyResolverNull, net_log); } // static ProxyService* ProxyService::CreateFixedFromPacResult( const std::string& pac_string) { // We need the settings to contain an "automatic" setting, otherwise the // ProxyResolver dependency we give it will never be used. scoped_ptr proxy_config_service( new ProxyConfigServiceFixed(ProxyConfig::CreateAutoDetect())); scoped_ptr proxy_resolver( new ProxyResolverFromPacString(pac_string)); return new ProxyService(proxy_config_service.release(), proxy_resolver.release(), NULL); } int ProxyService::ResolveProxy(const GURL& raw_url, ProxyInfo* result, CompletionCallback* callback, PacRequest** pac_request, const BoundNetLog& net_log) { DCHECK(CalledOnValidThread()); DCHECK(callback); net_log.BeginEvent(NetLog::TYPE_PROXY_SERVICE, NULL); config_service_->OnLazyPoll(); if (current_state_ == STATE_NONE) ApplyProxyConfigIfAvailable(); // Strip away any reference fragments and the username/password, as they // are not relevant to proxy resolution. GURL url = SimplifyUrlForRequest(raw_url); // Check if the request can be completed right away. (This is the case when // using a direct connection for example). int rv = TryToCompleteSynchronously(url, result); if (rv != ERR_IO_PENDING) return DidFinishResolvingProxy(result, rv, net_log); scoped_refptr req( new PacRequest(this, url, result, callback, net_log)); if (current_state_ == STATE_READY) { // Start the resolve request. rv = req->Start(); if (rv != ERR_IO_PENDING) return req->QueryDidComplete(rv); } else { req->net_log()->BeginEvent(NetLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC, NULL); } DCHECK_EQ(ERR_IO_PENDING, rv); DCHECK(!ContainsPendingRequest(req)); pending_requests_.push_back(req); // Completion will be notifed through |callback|, unless the caller cancels // the request using |pac_request|. if (pac_request) *pac_request = req.get(); return rv; // ERR_IO_PENDING } int ProxyService::TryToCompleteSynchronously(const GURL& url, ProxyInfo* result) { DCHECK_NE(STATE_NONE, current_state_); if (current_state_ != STATE_READY) return ERR_IO_PENDING; // Still initializing. DCHECK_NE(config_.id(), ProxyConfig::INVALID_ID); if (config_.HasAutomaticSettings()) return ERR_IO_PENDING; // Must submit the request to the proxy resolver. // Use the manual proxy settings. config_.proxy_rules().Apply(url, result); result->config_id_ = config_.id(); return OK; } ProxyService::~ProxyService() { NetworkChangeNotifier::RemoveIPAddressObserver(this); config_service_->RemoveObserver(this); // Cancel any inprogress requests. for (PendingRequests::iterator it = pending_requests_.begin(); it != pending_requests_.end(); ++it) { (*it)->Cancel(); } } void ProxyService::SuspendAllPendingRequests() { for (PendingRequests::iterator it = pending_requests_.begin(); it != pending_requests_.end(); ++it) { PacRequest* req = it->get(); if (req->is_started()) { req->CancelResolveJob(); req->net_log()->BeginEvent( NetLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC, NULL); } } } void ProxyService::SetReady() { DCHECK(!init_proxy_resolver_.get()); current_state_ = STATE_READY; // Make a copy in case |this| is deleted during the synchronous completion // of one of the requests. If |this| is deleted then all of the PacRequest // instances will be Cancel()-ed. PendingRequests pending_copy = pending_requests_; for (PendingRequests::iterator it = pending_copy.begin(); it != pending_copy.end(); ++it) { PacRequest* req = it->get(); if (!req->is_started() && !req->was_cancelled()) { req->net_log()->EndEvent(NetLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC, NULL); // Note that we re-check for synchronous completion, in case we are // no longer using a ProxyResolver (can happen if we fell-back to manual). req->StartAndCompleteCheckingForSynchronous(); } } } void ProxyService::ApplyProxyConfigIfAvailable() { DCHECK_EQ(STATE_NONE, current_state_); config_service_->OnLazyPoll(); // If we have already fetched the configuration, start applying it. if (fetched_config_.is_valid()) { InitializeUsingLastFetchedConfig(); return; } // Otherwise we need to first fetch the configuration. current_state_ = STATE_WAITING_FOR_PROXY_CONFIG; // Retrieve the current proxy configuration from the ProxyConfigService. // If a configuration is not available yet, we will get called back later // by our ProxyConfigService::Observer once it changes. ProxyConfig config; ProxyConfigService::ConfigAvailability availability = config_service_->GetLatestProxyConfig(&config); if (availability != ProxyConfigService::CONFIG_PENDING) OnProxyConfigChanged(config, availability); } void ProxyService::OnInitProxyResolverComplete(int result) { DCHECK_EQ(STATE_WAITING_FOR_INIT_PROXY_RESOLVER, current_state_); DCHECK(init_proxy_resolver_.get()); DCHECK(fetched_config_.HasAutomaticSettings()); init_proxy_resolver_.reset(); if (result != OK) { VLOG(1) << "Failed configuring with PAC script, falling-back to manual " "proxy servers."; config_ = fetched_config_; config_.ClearAutomaticSettings(); } config_.set_id(fetched_config_.id()); // Resume any requests which we had to defer until the PAC script was // downloaded. SetReady(); } int ProxyService::ReconsiderProxyAfterError(const GURL& url, ProxyInfo* result, CompletionCallback* callback, PacRequest** pac_request, const BoundNetLog& net_log) { DCHECK(CalledOnValidThread()); // Check to see if we have a new config since ResolveProxy was called. We // want to re-run ResolveProxy in two cases: 1) we have a new config, or 2) a // direct connection failed and we never tried the current config. bool re_resolve = result->config_id_ != config_.id(); if (re_resolve) { // If we have a new config or the config was never tried, we delete the // list of bad proxies and we try again. proxy_retry_info_.clear(); return ResolveProxy(url, result, callback, pac_request, net_log); } // We don't have new proxy settings to try, try to fallback to the next proxy // in the list. bool did_fallback = result->Fallback(&proxy_retry_info_); // Return synchronous failure if there is nothing left to fall-back to. // TODO(eroman): This is a yucky API, clean it up. return did_fallback ? OK : ERR_FAILED; } void ProxyService::CancelPacRequest(PacRequest* req) { DCHECK(CalledOnValidThread()); DCHECK(req); req->Cancel(); RemovePendingRequest(req); } bool ProxyService::ContainsPendingRequest(PacRequest* req) { PendingRequests::iterator it = std::find( pending_requests_.begin(), pending_requests_.end(), req); return pending_requests_.end() != it; } void ProxyService::RemovePendingRequest(PacRequest* req) { DCHECK(ContainsPendingRequest(req)); PendingRequests::iterator it = std::find( pending_requests_.begin(), pending_requests_.end(), req); pending_requests_.erase(it); } int ProxyService::DidFinishResolvingProxy(ProxyInfo* result, int result_code, const BoundNetLog& net_log) { // Log the result of the proxy resolution. if (result_code == OK) { // When logging all events is enabled, dump the proxy list. if (net_log.IsLoggingAllEvents()) { net_log.AddEvent( NetLog::TYPE_PROXY_SERVICE_RESOLVED_PROXY_LIST, make_scoped_refptr(new NetLogStringParameter( "pac_string", result->ToPacString()))); } result->DeprioritizeBadProxies(proxy_retry_info_); } else { net_log.AddEvent( NetLog::TYPE_PROXY_SERVICE_RESOLVED_PROXY_LIST, make_scoped_refptr(new NetLogIntegerParameter( "net_error", result_code))); // Fall-back to direct when the proxy resolver fails. This corresponds // with a javascript runtime error in the PAC script. // // This implicit fall-back to direct matches Firefox 3.5 and // Internet Explorer 8. For more information, see: // // http://www.chromium.org/developers/design-documents/proxy-settings-fallback result->UseDirect(); result_code = OK; } net_log.EndEvent(NetLog::TYPE_PROXY_SERVICE, NULL); return result_code; } void ProxyService::SetProxyScriptFetcher( ProxyScriptFetcher* proxy_script_fetcher) { DCHECK(CalledOnValidThread()); State previous_state = ResetProxyConfig(false); proxy_script_fetcher_.reset(proxy_script_fetcher); if (previous_state != STATE_NONE) ApplyProxyConfigIfAvailable(); } ProxyScriptFetcher* ProxyService::GetProxyScriptFetcher() const { DCHECK(CalledOnValidThread()); return proxy_script_fetcher_.get(); } ProxyService::State ProxyService::ResetProxyConfig(bool reset_fetched_config) { DCHECK(CalledOnValidThread()); State previous_state = current_state_; proxy_retry_info_.clear(); init_proxy_resolver_.reset(); SuspendAllPendingRequests(); config_ = ProxyConfig(); if (reset_fetched_config) fetched_config_ = ProxyConfig(); current_state_ = STATE_NONE; return previous_state; } void ProxyService::ResetConfigService( ProxyConfigService* new_proxy_config_service) { DCHECK(CalledOnValidThread()); State previous_state = ResetProxyConfig(true); // Release the old configuration service. if (config_service_.get()) config_service_->RemoveObserver(this); // Set the new configuration service. config_service_.reset(new_proxy_config_service); config_service_->AddObserver(this); if (previous_state != STATE_NONE) ApplyProxyConfigIfAvailable(); } void ProxyService::PurgeMemory() { DCHECK(CalledOnValidThread()); if (resolver_.get()) resolver_->PurgeMemory(); } void ProxyService::ForceReloadProxyConfig() { DCHECK(CalledOnValidThread()); ResetProxyConfig(false); ApplyProxyConfigIfAvailable(); } // static ProxyConfigService* ProxyService::CreateSystemProxyConfigService( MessageLoop* io_loop, MessageLoop* file_loop) { #if defined(OS_WIN) return new ProxyConfigServiceWin(); #elif defined(OS_MACOSX) return new ProxyConfigServiceMac(io_loop); #elif defined(OS_CHROMEOS) NOTREACHED() << "ProxyConfigService for ChromeOS should be created in " << "profile_io_data.cc::CreateProxyConfigService."; return NULL; #elif defined(ANDROID) NOTREACHED() << "ProxyConfigService for Android should be created in " << "WebCache.cpp: WebCache::WebCache"; return NULL; #elif defined(OS_LINUX) ProxyConfigServiceLinux* linux_config_service = new ProxyConfigServiceLinux(); // Assume we got called from the UI loop, which runs the default // glib main loop, so the current thread is where we should be // running gconf calls from. MessageLoop* glib_default_loop = MessageLoopForUI::current(); // The file loop should be a MessageLoopForIO on Linux. DCHECK_EQ(MessageLoop::TYPE_IO, file_loop->type()); // Synchronously fetch the current proxy config (since we are // running on glib_default_loop). Additionally register for // notifications (delivered in either |glib_default_loop| or // |file_loop|) to keep us updated when the proxy config changes. linux_config_service->SetupAndFetchInitialConfig(glib_default_loop, io_loop, static_cast(file_loop)); return linux_config_service; #else LOG(WARNING) << "Failed to choose a system proxy settings fetcher " "for this platform."; return new ProxyConfigServiceNull(); #endif } void ProxyService::OnProxyConfigChanged( const ProxyConfig& config, ProxyConfigService::ConfigAvailability availability) { // Retrieve the current proxy configuration from the ProxyConfigService. // If a configuration is not available yet, we will get called back later // by our ProxyConfigService::Observer once it changes. ProxyConfig effective_config; switch (availability) { case ProxyConfigService::CONFIG_PENDING: // ProxyConfigService implementors should never pass CONFIG_PENDING. NOTREACHED() << "Proxy config change with CONFIG_PENDING availability!"; return; case ProxyConfigService::CONFIG_VALID: effective_config = config; break; case ProxyConfigService::CONFIG_UNSET: effective_config = ProxyConfig::CreateDirect(); break; } // Emit the proxy settings change to the NetLog stream. if (net_log_) { scoped_refptr params( new ProxyConfigChangedNetLogParam(fetched_config_, effective_config)); net_log_->AddEntry(net::NetLog::TYPE_PROXY_CONFIG_CHANGED, base::TimeTicks::Now(), NetLog::Source(), NetLog::PHASE_NONE, params); } // Set the new configuration as the most recently fetched one. fetched_config_ = effective_config; fetched_config_.set_id(1); // Needed for a later DCHECK of is_valid(). InitializeUsingLastFetchedConfig(); } void ProxyService::InitializeUsingLastFetchedConfig() { ResetProxyConfig(false); DCHECK(fetched_config_.is_valid()); // Increment the ID to reflect that the config has changed. fetched_config_.set_id(next_config_id_++); if (!fetched_config_.HasAutomaticSettings()) { config_ = fetched_config_; SetReady(); return; } // Start downloading + testing the PAC scripts for this new configuration. current_state_ = STATE_WAITING_FOR_INIT_PROXY_RESOLVER; init_proxy_resolver_.reset( new InitProxyResolver(resolver_.get(), proxy_script_fetcher_.get(), net_log_)); // If we changed networks recently, we should delay running proxy auto-config. base::TimeDelta wait_delay = stall_proxy_autoconfig_until_ - base::TimeTicks::Now(); int rv = init_proxy_resolver_->Init( fetched_config_, wait_delay, &config_, &init_proxy_resolver_callback_); if (rv != ERR_IO_PENDING) OnInitProxyResolverComplete(rv); } void ProxyService::OnIPAddressChanged() { // See the comment block by |kNumMillisToStallAfterNetworkChanges| for info. stall_proxy_autoconfig_until_ = base::TimeTicks::Now() + stall_proxy_auto_config_delay_; State previous_state = ResetProxyConfig(false); if (previous_state != STATE_NONE) ApplyProxyConfigIfAvailable(); } SyncProxyServiceHelper::SyncProxyServiceHelper(MessageLoop* io_message_loop, ProxyService* proxy_service) : io_message_loop_(io_message_loop), proxy_service_(proxy_service), event_(false, false), ALLOW_THIS_IN_INITIALIZER_LIST(callback_( this, &SyncProxyServiceHelper::OnCompletion)) { DCHECK(io_message_loop_ != MessageLoop::current()); } int SyncProxyServiceHelper::ResolveProxy(const GURL& url, ProxyInfo* proxy_info, const BoundNetLog& net_log) { DCHECK(io_message_loop_ != MessageLoop::current()); io_message_loop_->PostTask(FROM_HERE, NewRunnableMethod( this, &SyncProxyServiceHelper::StartAsyncResolve, url, net_log)); event_.Wait(); if (result_ == net::OK) { *proxy_info = proxy_info_; } return result_; } int SyncProxyServiceHelper::ReconsiderProxyAfterError( const GURL& url, ProxyInfo* proxy_info, const BoundNetLog& net_log) { DCHECK(io_message_loop_ != MessageLoop::current()); io_message_loop_->PostTask(FROM_HERE, NewRunnableMethod( this, &SyncProxyServiceHelper::StartAsyncReconsider, url, net_log)); event_.Wait(); if (result_ == net::OK) { *proxy_info = proxy_info_; } return result_; } SyncProxyServiceHelper::~SyncProxyServiceHelper() {} void SyncProxyServiceHelper::StartAsyncResolve(const GURL& url, const BoundNetLog& net_log) { result_ = proxy_service_->ResolveProxy( url, &proxy_info_, &callback_, NULL, net_log); if (result_ != net::ERR_IO_PENDING) { OnCompletion(result_); } } void SyncProxyServiceHelper::StartAsyncReconsider(const GURL& url, const BoundNetLog& net_log) { result_ = proxy_service_->ReconsiderProxyAfterError( url, &proxy_info_, &callback_, NULL, net_log); if (result_ != net::ERR_IO_PENDING) { OnCompletion(result_); } } void SyncProxyServiceHelper::OnCompletion(int rv) { result_ = rv; event_.Signal(); } } // namespace net