Index: net/proxy/single_threaded_proxy_resolver.cc |
=================================================================== |
--- net/proxy/single_threaded_proxy_resolver.cc (revision 20322) |
+++ net/proxy/single_threaded_proxy_resolver.cc (working copy) |
@@ -1,136 +1,76 @@ |
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
+// Copyright (c) 2009 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 "net/proxy/single_threaded_proxy_resolver.h" |
-#include <algorithm> |
- |
-#include "base/compiler_specific.h" |
-#include "base/logging.h" |
-#include "base/string_util.h" |
-#include "googleurl/src/gurl.h" |
+#include "base/thread.h" |
#include "net/base/net_errors.h" |
-#include "net/proxy/proxy_config_service_fixed.h" |
-#include "net/proxy/proxy_script_fetcher.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_resolver_mac.h" |
-#elif defined(OS_LINUX) |
-#include "net/proxy/proxy_config_service_linux.h" |
-#endif |
-#include "net/proxy/proxy_resolver.h" |
-#include "net/proxy/proxy_resolver_v8.h" |
-#include "net/url_request/url_request_context.h" |
+#include "net/proxy/proxy_info.h" |
-using base::TimeDelta; |
-using base::TimeTicks; |
- |
namespace net { |
-// Config getter that fails every time. |
-class ProxyConfigServiceNull : public ProxyConfigService { |
+// Runs on the worker thread to call ProxyResolver::SetPacScriptByUrl or |
+// ProxyResolver::SetPacScriptByData. |
+// TODO(eroman): the lifetime of this task is ill-defined; |resolver_| must |
+// be valid when the task eventually is run. Make this task cancellable. |
+class SetPacScriptTask : public Task { |
public: |
- // ProxyConfigService implementation: |
- virtual int GetProxyConfig(ProxyConfig* config) { |
- return ERR_NOT_IMPLEMENTED; |
- } |
-}; |
+ SetPacScriptTask(ProxyResolver* resolver, |
+ const GURL& pac_url, |
+ const std::string& bytes) |
+ : resolver_(resolver), pac_url_(pac_url), bytes_(bytes) {} |
-// Proxy resolver that fails every time. |
-class ProxyResolverNull : public ProxyResolver { |
- public: |
- ProxyResolverNull() : ProxyResolver(true /*does_fetch*/) {} |
- |
- // ProxyResolver implementation: |
- virtual int GetProxyForURL(const GURL& /*query_url*/, |
- const GURL& /*pac_url*/, |
- ProxyInfo* /*results*/) { |
- return ERR_NOT_IMPLEMENTED; |
- } |
-}; |
- |
-// Strip away any reference fragments and the username/password, as they |
-// are not relevant to proxy resolution. |
-static GURL SanitizeURLForProxyResolver(const GURL& url) { |
- // TODO(eroman): The following duplicates logic from |
- // HttpUtil::SpecForRequest. Should probably live in net_util.h |
- GURL::Replacements replacements; |
- replacements.ClearUsername(); |
- replacements.ClearPassword(); |
- replacements.ClearRef(); |
- return url.ReplaceComponents(replacements); |
-} |
- |
-// Runs on the PAC thread to notify the proxy resolver of the fetched PAC |
-// script contents. This task shouldn't outlive ProxyService, since |
-// |resolver| is owned by ProxyService. |
-class NotifyFetchCompletionTask : public Task { |
- public: |
- NotifyFetchCompletionTask(ProxyResolver* resolver, const std::string& bytes) |
- : resolver_(resolver), bytes_(bytes) {} |
- |
virtual void Run() { |
- resolver_->SetPacScript(bytes_); |
+ if (resolver_->expects_pac_bytes()) |
+ resolver_->SetPacScriptByData(bytes_); |
+ else |
+ resolver_->SetPacScriptByUrl(pac_url_); |
} |
private: |
ProxyResolver* resolver_; |
+ GURL pac_url_; |
std::string bytes_; |
}; |
-// ProxyService::PacRequest --------------------------------------------------- |
+// SingleThreadedProxyResolver::Job ------------------------------------------- |
-// We rely on the fact that the origin thread (and its message loop) will not |
-// be destroyed until after the PAC thread is destroyed. |
- |
-class ProxyService::PacRequest |
- : public base::RefCountedThreadSafe<ProxyService::PacRequest> { |
+class SingleThreadedProxyResolver::Job |
+ : public base::RefCountedThreadSafe<SingleThreadedProxyResolver::Job> { |
public: |
- // |service| -- the ProxyService that owns this request. |
- // |url| -- the url of the query. |
- // |results| -- the structure to fill with proxy resolve results. |
- PacRequest(ProxyService* service, |
- const GURL& url, |
- ProxyInfo* results, |
- CompletionCallback* callback) |
- : service_(service), |
- callback_(callback), |
- results_(results), |
- url_(url), |
- is_started_(false), |
- origin_loop_(MessageLoop::current()) { |
+ // |coordinator| -- the SingleThreadedProxyResolver that owns this job. |
+ // |url| -- the URL of the query. |
+ // |results| -- the structure to fill with proxy resolve results. |
+ Job(SingleThreadedProxyResolver* coordinator, |
+ const GURL& url, |
+ ProxyInfo* results, |
+ CompletionCallback* callback) |
+ : coordinator_(coordinator), |
+ callback_(callback), |
+ results_(results), |
+ url_(url), |
+ is_started_(false), |
+ origin_loop_(MessageLoop::current()) { |
DCHECK(callback); |
} |
- // Start the resolve proxy request on the PAC thread. |
- void Query() { |
+ // Start the resolve proxy request on the worker thread. |
+ void Start() { |
is_started_ = true; |
AddRef(); // balanced in QueryComplete |
- GURL query_url = SanitizeURLForProxyResolver(url_); |
- const GURL& pac_url = service_->config_.pac_url; |
- results_->config_id_ = service_->config_.id(); |
- |
- service_->pac_thread()->message_loop()->PostTask(FROM_HERE, |
- NewRunnableMethod(this, &ProxyService::PacRequest::DoQuery, |
- service_->resolver(), query_url, pac_url)); |
+ coordinator_->thread()->message_loop()->PostTask( |
+ FROM_HERE, NewRunnableMethod(this, &Job::DoQuery, |
+ coordinator_->resolver_.get())); |
} |
- // Run the request's callback on the current message loop. |
- void PostCallback(int result_code) { |
- AddRef(); // balanced in DoCallback |
- MessageLoop::current()->PostTask(FROM_HERE, |
- NewRunnableMethod(this, &ProxyService::PacRequest::DoCallback, |
- result_code)); |
- } |
+ bool is_started() const { return is_started_; } |
void Cancel() { |
// Clear these to inform QueryComplete that it should not try to |
// access them. |
- service_ = NULL; |
+ coordinator_ = NULL; |
callback_ = NULL; |
results_ = NULL; |
} |
@@ -139,632 +79,153 @@ |
bool was_cancelled() const { return callback_ == NULL; } |
private: |
- friend class ProxyService; |
- |
- // Runs on the PAC thread. |
- void DoQuery(ProxyResolver* resolver, |
- const GURL& query_url, |
- const GURL& pac_url) { |
- int rv = resolver->GetProxyForURL(query_url, pac_url, &results_buf_); |
+ // Runs on the worker thread. |
+ void DoQuery(ProxyResolver* resolver) { |
+ int rv = resolver->GetProxyForURL(url_, &results_buf_, NULL, NULL); |
+ DCHECK_NE(rv, ERR_IO_PENDING); |
origin_loop_->PostTask(FROM_HERE, |
- NewRunnableMethod(this, &PacRequest::QueryComplete, rv)); |
+ NewRunnableMethod(this, &Job::QueryComplete, rv)); |
} |
// Runs the completion callback on the origin thread. |
void QueryComplete(int result_code) { |
- // The PacRequest may have been cancelled after it was started. |
+ // The Job may have been cancelled after it was started. |
if (!was_cancelled()) { |
- service_->DidCompletePacRequest(results_->config_id_, result_code); |
- |
- if (result_code == OK) { |
+ if (result_code >= OK) { // Note: unit-tests use values > 0. |
results_->Use(results_buf_); |
- results_->RemoveBadProxies(service_->proxy_retry_info_); |
} |
callback_->Run(result_code); |
// We check for cancellation once again, in case the callback deleted |
// the owning ProxyService (whose destructor will in turn cancel us). |
if (!was_cancelled()) |
- service_->RemoveFrontOfRequestQueue(this); |
+ coordinator_->RemoveFrontOfJobsQueueAndStartNext(this); |
} |
Release(); // balances the AddRef in Query. we may get deleted after |
// we return. |
} |
- // Runs the completion callback on the origin thread. |
- void DoCallback(int result_code) { |
- if (!was_cancelled()) { |
- callback_->Run(result_code); |
- } |
- Release(); // balances the AddRef in PostCallback. |
- } |
- |
// Must only be used on the "origin" thread. |
- ProxyService* service_; |
+ SingleThreadedProxyResolver* coordinator_; |
CompletionCallback* callback_; |
ProxyInfo* results_; |
GURL url_; |
bool is_started_; |
- // Usable from within DoQuery on the PAC thread. |
+ // Usable from within DoQuery on the worker thread. |
ProxyInfo results_buf_; |
MessageLoop* origin_loop_; |
}; |
-// ProxyService --------------------------------------------------------------- |
+// SingleThreadedProxyResolver ------------------------------------------------ |
-ProxyService::ProxyService(ProxyConfigService* config_service, |
- ProxyResolver* resolver) |
- : config_service_(config_service), |
- resolver_(resolver), |
- next_config_id_(1), |
- config_is_bad_(false), |
- ALLOW_THIS_IN_INITIALIZER_LIST(proxy_script_fetcher_callback_( |
- this, &ProxyService::OnScriptFetchCompletion)), |
- fetched_pac_config_id_(ProxyConfig::INVALID_ID), |
- fetched_pac_error_(OK), |
- in_progress_fetch_config_id_(ProxyConfig::INVALID_ID) { |
+SingleThreadedProxyResolver::SingleThreadedProxyResolver( |
+ ProxyResolver* resolver) |
+ : ProxyResolver(resolver->expects_pac_bytes()), |
+ resolver_(resolver) { |
} |
-// static |
-ProxyService* ProxyService::Create( |
- const ProxyConfig* pc, |
- bool use_v8_resolver, |
- URLRequestContext* url_request_context, |
- MessageLoop* io_loop) { |
- // Choose the system configuration service appropriate for each platform. |
- ProxyConfigService* proxy_config_service = pc ? |
- new ProxyConfigServiceFixed(*pc) : |
- CreateSystemProxyConfigService(io_loop); |
- |
- ProxyResolver* proxy_resolver; |
- |
- if (use_v8_resolver) { |
- // Send javascript errors and alerts to LOG(INFO). |
- HostResolver* host_resolver = url_request_context->host_resolver(); |
- ProxyResolverV8::JSBindings* js_bindings = |
- ProxyResolverV8::CreateDefaultBindings(host_resolver, io_loop); |
- |
- proxy_resolver = new ProxyResolverV8(js_bindings); |
- } else { |
- proxy_resolver = CreateNonV8ProxyResolver(); |
- } |
- |
- ProxyService* proxy_service = new ProxyService( |
- proxy_config_service, proxy_resolver); |
- |
- if (!proxy_resolver->does_fetch()) { |
- // Configure PAC script downloads to be issued using |url_request_context|. |
- DCHECK(url_request_context); |
- proxy_service->SetProxyScriptFetcher( |
- ProxyScriptFetcher::Create(url_request_context)); |
- } |
- |
- return proxy_service; |
-} |
- |
-// static |
-ProxyService* ProxyService::CreateFixed(const ProxyConfig& pc) { |
- return Create(&pc, false, NULL, NULL); |
-} |
- |
-// static |
-ProxyService* ProxyService::CreateNull() { |
- // Use a configuration fetcher and proxy resolver which always fail. |
- return new ProxyService(new ProxyConfigServiceNull, new ProxyResolverNull); |
-} |
- |
-int ProxyService::ResolveProxy(const GURL& url, ProxyInfo* result, |
- CompletionCallback* callback, |
- PacRequest** pac_request) { |
- DCHECK(callback); |
- |
- // Check if the request can be completed right away. This is the case when |
- // using a direct connection, or when the config is bad. |
- UpdateConfigIfOld(); |
- int rv = TryToCompleteSynchronously(url, result); |
- if (rv != ERR_IO_PENDING) |
- return rv; |
- |
- // Otherwise, push the request into the work queue. |
- scoped_refptr<PacRequest> req = new PacRequest(this, url, result, callback); |
- pending_requests_.push_back(req); |
- ProcessPendingRequests(req.get()); |
- |
- // 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) { |
- result->config_id_ = config_.id(); |
- |
- DCHECK(config_.id() != ProxyConfig::INVALID_ID); |
- |
- // Fallback to a "direct" (no proxy) connection if the current configuration |
- // is known to be bad. |
- if (config_is_bad_) { |
- // Reset this flag to false in case the ProxyInfo object is being |
- // re-used by the caller. |
- result->config_was_tried_ = false; |
- } else { |
- // Remember that we are trying to use the current proxy configuration. |
- result->config_was_tried_ = true; |
- |
- if (!config_.proxy_rules.empty()) { |
- ApplyProxyRules(url, config_.proxy_rules, result); |
- return OK; |
- } |
- |
- if (config_.pac_url.is_valid() || config_.auto_detect) { |
- // If we failed to download the PAC script, return the network error |
- // from the failed download. This is only going to happen for the first |
- // request after the failed download -- after that |config_is_bad_| will |
- // be set to true, so we short-cuircuit sooner. |
- if (fetched_pac_error_ != OK && !IsFetchingPacScript()) { |
- DidCompletePacRequest(fetched_pac_config_id_, fetched_pac_error_); |
- return fetched_pac_error_; |
- } |
- return ERR_IO_PENDING; |
- } |
- } |
- |
- // otherwise, we have no proxy config |
- result->UseDirect(); |
- return OK; |
-} |
- |
-void ProxyService::ApplyProxyRules(const GURL& url, |
- const ProxyConfig::ProxyRules& proxy_rules, |
- ProxyInfo* result) { |
- DCHECK(!proxy_rules.empty()); |
- |
- if (ShouldBypassProxyForURL(url)) { |
- result->UseDirect(); |
- return; |
- } |
- |
- switch (proxy_rules.type) { |
- case ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY: |
- result->UseProxyServer(proxy_rules.single_proxy); |
- break; |
- case ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME: { |
- const ProxyServer* entry = proxy_rules.MapSchemeToProxy(url.scheme()); |
- if (entry) { |
- result->UseProxyServer(*entry); |
- } else { |
- // We failed to find a matching proxy server for the current URL |
- // scheme. Default to direct. |
- result->UseDirect(); |
- } |
- break; |
- } |
- default: |
- result->UseDirect(); |
- NOTREACHED(); |
- break; |
- } |
-} |
- |
-void ProxyService::InitPacThread() { |
- if (!pac_thread_.get()) { |
- pac_thread_.reset(new base::Thread("pac-thread")); |
- pac_thread_->Start(); |
- } |
-} |
- |
-ProxyService::~ProxyService() { |
- // Cancel the inprogress request (if any), and free the rest. |
- for (PendingRequestsQueue::iterator it = pending_requests_.begin(); |
- it != pending_requests_.end(); |
+SingleThreadedProxyResolver::~SingleThreadedProxyResolver() { |
+ // Cancel the inprogress job (if any), and free the rest. |
+ for (PendingJobsQueue::iterator it = pending_jobs_.begin(); |
+ it != pending_jobs_.end(); |
++it) { |
(*it)->Cancel(); |
} |
-} |
-void ProxyService::ProcessPendingRequests(PacRequest* recent_req) { |
- if (pending_requests_.empty()) |
- return; |
- |
- // While the PAC script is being downloaded, requests are blocked. |
- if (IsFetchingPacScript()) |
- return; |
- |
- // Get the next request to process (FIFO). |
- PacRequest* req = pending_requests_.front().get(); |
- if (req->is_started_) |
- return; |
- |
- // The configuration may have changed since |req| was added to the |
- // queue. It could be this request now completes synchronously. |
- if (req != recent_req) { |
- UpdateConfigIfOld(); |
- int rv = TryToCompleteSynchronously(req->url_, req->results_); |
- if (rv != ERR_IO_PENDING) { |
- req->PostCallback(rv); |
- RemoveFrontOfRequestQueue(req); |
- return; |
- } |
- } |
- |
- // Check if a new PAC script needs to be downloaded. |
- DCHECK(config_.id() != ProxyConfig::INVALID_ID); |
- if (!resolver_->does_fetch() && config_.id() != fetched_pac_config_id_) { |
- // For auto-detect we use the well known WPAD url. |
- GURL pac_url = config_.auto_detect ? |
- GURL("http://wpad/wpad.dat") : config_.pac_url; |
- |
- in_progress_fetch_config_id_ = config_.id(); |
- |
- LOG(INFO) << "Starting fetch of PAC script " << pac_url |
- << " for config_id=" << in_progress_fetch_config_id_; |
- |
- proxy_script_fetcher_->Fetch( |
- pac_url, &in_progress_fetch_bytes_, &proxy_script_fetcher_callback_); |
- return; |
- } |
- |
- // The only choice left now is to actually run the ProxyResolver on |
- // the PAC thread. |
- InitPacThread(); |
- req->Query(); |
+ // Note that |thread_| is destroyed before |resolver_|. This is important |
+ // since |resolver_| could be running on |thread_|. |
} |
-void ProxyService::RemoveFrontOfRequestQueue(PacRequest* expected_req) { |
- DCHECK(pending_requests_.front().get() == expected_req); |
- pending_requests_.pop_front(); |
+int SingleThreadedProxyResolver::GetProxyForURL(const GURL& url, |
+ ProxyInfo* results, |
+ CompletionCallback* callback, |
+ RequestHandle* request) { |
+ DCHECK(callback); |
- // Start next work item. |
- ProcessPendingRequests(NULL); |
-} |
+ scoped_refptr<Job> job = new Job(this, url, results, callback); |
+ pending_jobs_.push_back(job); |
+ ProcessPendingJobs(); // Jobs can never finish synchronously. |
-void ProxyService::OnScriptFetchCompletion(int result) { |
- DCHECK(IsFetchingPacScript()); |
- DCHECK(!resolver_->does_fetch()); |
+ // Completion will be notified through |callback|, unless the caller cancels |
+ // the request using |request|. |
+ if (request) |
+ *request = reinterpret_cast<RequestHandle>(job.get()); |
- LOG(INFO) << "Completed PAC script fetch for config_id=" |
- << in_progress_fetch_config_id_ |
- << " with error " << ErrorToString(result) |
- << ". Fetched a total of " << in_progress_fetch_bytes_.size() |
- << " bytes"; |
- |
- // Notify the ProxyResolver of the new script data (will be empty string if |
- // result != OK). |
- InitPacThread(); |
- pac_thread()->message_loop()->PostTask(FROM_HERE, |
- new NotifyFetchCompletionTask( |
- resolver_.get(), in_progress_fetch_bytes_)); |
- |
- fetched_pac_config_id_ = in_progress_fetch_config_id_; |
- fetched_pac_error_ = result; |
- in_progress_fetch_config_id_ = ProxyConfig::INVALID_ID; |
- in_progress_fetch_bytes_.clear(); |
- |
- // Start a pending request if any. |
- ProcessPendingRequests(NULL); |
+ return ERR_IO_PENDING; |
} |
-int ProxyService::ReconsiderProxyAfterError(const GURL& url, |
- ProxyInfo* result, |
- CompletionCallback* callback, |
- PacRequest** pac_request) { |
- // 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) { |
- UpdateConfig(); |
- if (result->config_id_ != config_.id()) { |
- // A new configuration! |
- re_resolve = true; |
- } else if (!result->config_was_tried_) { |
- // We never tried the proxy configuration since we thought it was bad, |
- // but because we failed to establish a connection, let's try the proxy |
- // configuration again to see if it will work now. |
- config_is_bad_ = false; |
- re_resolve = true; |
- } |
- } |
- 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); |
- } |
- |
- // We don't have new proxy settings to try, fallback to the next proxy |
- // in the list. |
- bool was_direct = result->is_direct(); |
- if (!was_direct && result->Fallback(&proxy_retry_info_)) |
- return OK; |
- |
- if (!config_.auto_detect && !config_.proxy_rules.empty()) { |
- // If auto detect is on, then we should try a DIRECT connection |
- // as the attempt to reach the proxy failed. |
- return ERR_FAILED; |
- } |
- |
- // If we already tried a direct connection, then just give up. |
- if (was_direct) |
- return ERR_FAILED; |
- |
- // Try going direct. |
- result->UseDirect(); |
- return OK; |
-} |
- |
-// There are four states of the request we need to handle: |
+// There are three states of the request we need to handle: |
// (1) Not started (just sitting in the queue). |
-// (2) Executing PacRequest::DoQuery in the PAC thread. |
-// (3) Waiting for PacRequest::QueryComplete to be run on the origin thread. |
-// (4) Waiting for PacRequest::DoCallback to be run on the origin thread. |
-void ProxyService::CancelPacRequest(PacRequest* req) { |
+// (2) Executing Job::DoQuery in the worker thread. |
+// (3) Waiting for Job::QueryComplete to be run on the origin thread. |
+void SingleThreadedProxyResolver::CancelRequest(RequestHandle req) { |
DCHECK(req); |
- bool is_active_request = req->is_started_ && !pending_requests_.empty() && |
- pending_requests_.front().get() == req; |
+ Job* job = reinterpret_cast<Job*>(req); |
- req->Cancel(); |
+ bool is_active_job = job->is_started() && !pending_jobs_.empty() && |
+ pending_jobs_.front().get() == job; |
- if (is_active_request) { |
- RemoveFrontOfRequestQueue(req); |
+ job->Cancel(); |
+ |
+ if (is_active_job) { |
+ RemoveFrontOfJobsQueueAndStartNext(job); |
return; |
} |
- // Otherwise just delete the request from the queue. |
- PendingRequestsQueue::iterator it = std::find( |
- pending_requests_.begin(), pending_requests_.end(), req); |
- if (it != pending_requests_.end()) { |
- pending_requests_.erase(it); |
- } |
+ // Otherwise just delete the job from the queue. |
+ PendingJobsQueue::iterator it = std::find( |
+ pending_jobs_.begin(), pending_jobs_.end(), job); |
+ DCHECK(it != pending_jobs_.end()); |
+ pending_jobs_.erase(it); |
} |
-void ProxyService::SetProxyScriptFetcher( |
- ProxyScriptFetcher* proxy_script_fetcher) { |
- proxy_script_fetcher_.reset(proxy_script_fetcher); |
+void SingleThreadedProxyResolver::SetPacScriptByUrlInternal( |
+ const GURL& pac_url) { |
+ SetPacScriptHelper(pac_url, std::string()); |
} |
-void ProxyService::ResetConfigService( |
- ProxyConfigService* new_proxy_config_service) { |
- config_service_.reset(new_proxy_config_service); |
- UpdateConfig(); |
+void SingleThreadedProxyResolver::SetPacScriptByDataInternal( |
+ const std::string& bytes) { |
+ SetPacScriptHelper(GURL(), bytes); |
} |
-void ProxyService::DidCompletePacRequest(int config_id, int result_code) { |
- // If we get an error that indicates a bad PAC config, then we should |
- // remember that, and not try the PAC config again for a while. |
- |
- // Our config may have already changed. |
- if (result_code == OK || config_id != config_.id()) |
- return; |
- |
- // Remember that this configuration doesn't work. |
- config_is_bad_ = true; |
+void SingleThreadedProxyResolver::SetPacScriptHelper(const GURL& pac_url, |
+ const std::string& bytes) { |
+ EnsureThreadStarted(); |
+ thread()->message_loop()->PostTask( |
+ FROM_HERE, new SetPacScriptTask(resolver(), pac_url, bytes)); |
} |
-// static |
-ProxyConfigService* ProxyService::CreateSystemProxyConfigService( |
- MessageLoop* io_loop) { |
-#if defined(OS_WIN) |
- return new ProxyConfigServiceWin(); |
-#elif defined(OS_MACOSX) |
- return new ProxyConfigServiceMac(); |
-#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(); |
- |
- // Synchronously fetch the current proxy config (since we are |
- // running on glib_default_loop). Additionally register for gconf |
- // notifications (delivered in |glib_default_loop|) to keep us |
- // updated on when the proxy config has changed. |
- linux_config_service->SetupAndFetchInitialConfig(glib_default_loop, io_loop); |
- |
- return linux_config_service; |
-#else |
- LOG(WARNING) << "Failed to choose a system proxy settings fetcher " |
- "for this platform."; |
- return new ProxyConfigServiceNull(); |
-#endif |
+void SingleThreadedProxyResolver::EnsureThreadStarted() { |
+ if (!thread_.get()) { |
+ thread_.reset(new base::Thread("pac-thread")); |
+ thread_->Start(); |
+ } |
} |
-// static |
-ProxyResolver* ProxyService::CreateNonV8ProxyResolver() { |
-#if defined(OS_WIN) |
- return new ProxyResolverWinHttp(); |
-#elif defined(OS_MACOSX) |
- return new ProxyResolverMac(); |
-#else |
- LOG(WARNING) << "PAC support disabled because there is no fallback " |
- "non-V8 implementation"; |
- return new ProxyResolverNull(); |
-#endif |
-} |
- |
-void ProxyService::UpdateConfig() { |
- bool is_first_update = !config_has_been_initialized(); |
- |
- ProxyConfig latest; |
- if (config_service_->GetProxyConfig(&latest) != OK) { |
- if (is_first_update) { |
- // Default to direct-connection if the first fetch fails. |
- LOG(INFO) << "Failed initial proxy configuration fetch."; |
- SetConfig(ProxyConfig()); |
- } |
+void SingleThreadedProxyResolver::ProcessPendingJobs() { |
+ if (pending_jobs_.empty()) |
return; |
- } |
- config_last_update_time_ = TimeTicks::Now(); |
- if (!is_first_update && latest.Equals(config_)) |
+ // Get the next job to process (FIFO). |
+ Job* job = pending_jobs_.front().get(); |
+ if (job->is_started()) |
return; |
- SetConfig(latest); |
+ EnsureThreadStarted(); |
+ job->Start(); |
} |
-void ProxyService::SetConfig(const ProxyConfig& config) { |
- config_ = config; |
+void SingleThreadedProxyResolver::RemoveFrontOfJobsQueueAndStartNext( |
+ Job* expected_job) { |
+ DCHECK_EQ(expected_job, pending_jobs_.front().get()); |
+ pending_jobs_.pop_front(); |
- // Increment the ID to reflect that the config has changed. |
- config_.set_id(next_config_id_++); |
- |
- LOG(INFO) << "New proxy configuration was loaded:\n" << config_; |
- |
- // Reset state associated with latest config. |
- config_is_bad_ = false; |
- proxy_retry_info_.clear(); |
+ // Start next work item. |
+ ProcessPendingJobs(); |
} |
-void ProxyService::UpdateConfigIfOld() { |
- // The overhead of calling ProxyConfigService::GetProxyConfig is very low. |
- const TimeDelta kProxyConfigMaxAge = TimeDelta::FromSeconds(5); |
- |
- // Periodically check for a new config. |
- if (!config_has_been_initialized() || |
- (TimeTicks::Now() - config_last_update_time_) > kProxyConfigMaxAge) |
- UpdateConfig(); |
-} |
- |
-bool ProxyService::ShouldBypassProxyForURL(const GURL& url) { |
- std::string url_domain = url.scheme(); |
- if (!url_domain.empty()) |
- url_domain += "://"; |
- |
- url_domain += url.host(); |
- // This isn't superfluous; GURL case canonicalization doesn't hit the embedded |
- // percent-encoded characters. |
- StringToLowerASCII(&url_domain); |
- |
- // TODO(eroman): use GetHostAndPort(). |
- std::string url_domain_and_port = url_domain + ":" |
- + IntToString(url.EffectiveIntPort()); |
- |
- if (config_.proxy_bypass_local_names) { |
- if (url.host().find('.') == std::string::npos) |
- return true; |
- } |
- |
- for(std::vector<std::string>::const_iterator i = config_.proxy_bypass.begin(); |
- i != config_.proxy_bypass.end(); ++i) { |
- std::string bypass_url_domain = *i; |
- |
- // The proxy server bypass list can contain entities with http/https |
- // If no scheme is specified then it indicates that all schemes are |
- // allowed for the current entry. For matching this we just use |
- // the protocol scheme of the url passed in. |
- size_t scheme_colon = bypass_url_domain.find("://"); |
- if (scheme_colon == std::string::npos) { |
- std::string bypass_url_domain_with_scheme = url.scheme(); |
- scheme_colon = bypass_url_domain_with_scheme.length(); |
- bypass_url_domain_with_scheme += "://"; |
- bypass_url_domain_with_scheme += bypass_url_domain; |
- |
- bypass_url_domain = bypass_url_domain_with_scheme; |
- } |
- std::string* url_compare_reference = &url_domain; |
- size_t port_colon = bypass_url_domain.rfind(":"); |
- if (port_colon > scheme_colon) { |
- // If our match pattern includes a colon followed by a digit, |
- // and either it's preceded by ']' (IPv6 with port) |
- // or has no other colon (IPv4), |
- // then match against <domain>:<port>. |
- // TODO(sdoyon): straighten this out, in particular the IPv6 brackets, |
- // and do the parsing in ProxyConfig when we do the CIDR matching |
- // mentioned below. |
- std::string::const_iterator domain_begin = |
- bypass_url_domain.begin() + scheme_colon + 3; // after :// |
- std::string::const_iterator port_iter = |
- bypass_url_domain.begin() + port_colon; |
- std::string::const_iterator end = bypass_url_domain.end(); |
- if ((port_iter + 1) < end && IsAsciiDigit(*(port_iter + 1)) && |
- (*(port_iter - 1) == ']' || |
- std::find(domain_begin, port_iter, ':') == port_iter)) |
- url_compare_reference = &url_domain_and_port; |
- } |
- |
- StringToLowerASCII(&bypass_url_domain); |
- |
- if (MatchPattern(*url_compare_reference, bypass_url_domain)) |
- return true; |
- |
- // Some systems (the Mac, for example) allow CIDR-style specification of |
- // proxy bypass for IP-specified hosts (e.g. "10.0.0.0/8"; see |
- // http://www.tcd.ie/iss/internet/osx_proxy.php for a real-world example). |
- // That's kinda cool so we'll provide that for everyone. |
- // TODO(avi): implement here. See: http://crbug.com/9835. |
- // IP addresses ought to be canonicalized for comparison (whether |
- // with CIDR, port, or IP address alone). |
- } |
- |
- return false; |
-} |
- |
-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) { |
- DCHECK(io_message_loop_ != MessageLoop::current()); |
- |
- io_message_loop_->PostTask(FROM_HERE, NewRunnableMethod( |
- this, &SyncProxyServiceHelper::StartAsyncResolve, url)); |
- |
- event_.Wait(); |
- |
- if (result_ == net::OK) { |
- *proxy_info = proxy_info_; |
- } |
- return result_; |
-} |
- |
-int SyncProxyServiceHelper::ReconsiderProxyAfterError(const GURL& url, |
- ProxyInfo* proxy_info) { |
- DCHECK(io_message_loop_ != MessageLoop::current()); |
- |
- io_message_loop_->PostTask(FROM_HERE, NewRunnableMethod( |
- this, &SyncProxyServiceHelper::StartAsyncReconsider, url)); |
- |
- event_.Wait(); |
- |
- if (result_ == net::OK) { |
- *proxy_info = proxy_info_; |
- } |
- return result_; |
-} |
- |
-void SyncProxyServiceHelper::StartAsyncResolve(const GURL& url) { |
- result_ = proxy_service_->ResolveProxy(url, &proxy_info_, &callback_, NULL); |
- if (result_ != net::ERR_IO_PENDING) { |
- OnCompletion(result_); |
- } |
-} |
- |
-void SyncProxyServiceHelper::StartAsyncReconsider(const GURL& url) { |
- result_ = proxy_service_->ReconsiderProxyAfterError( |
- url, &proxy_info_, &callback_, NULL); |
- if (result_ != net::ERR_IO_PENDING) { |
- OnCompletion(result_); |
- } |
-} |
- |
-void SyncProxyServiceHelper::OnCompletion(int rv) { |
- result_ = rv; |
- event_.Signal(); |
-} |
- |
} // namespace net |