| OLD | NEW |
| 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "net/proxy/proxy_service.h" | 5 #include "net/proxy/single_threaded_proxy_resolver.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include "base/thread.h" |
| 8 | |
| 9 #include "base/compiler_specific.h" | |
| 10 #include "base/logging.h" | |
| 11 #include "base/string_util.h" | |
| 12 #include "googleurl/src/gurl.h" | |
| 13 #include "net/base/net_errors.h" | 8 #include "net/base/net_errors.h" |
| 14 #include "net/proxy/proxy_config_service_fixed.h" | 9 #include "net/proxy/proxy_info.h" |
| 15 #include "net/proxy/proxy_script_fetcher.h" | |
| 16 #if defined(OS_WIN) | |
| 17 #include "net/proxy/proxy_config_service_win.h" | |
| 18 #include "net/proxy/proxy_resolver_winhttp.h" | |
| 19 #elif defined(OS_MACOSX) | |
| 20 #include "net/proxy/proxy_resolver_mac.h" | |
| 21 #elif defined(OS_LINUX) | |
| 22 #include "net/proxy/proxy_config_service_linux.h" | |
| 23 #endif | |
| 24 #include "net/proxy/proxy_resolver.h" | |
| 25 #include "net/proxy/proxy_resolver_v8.h" | |
| 26 #include "net/url_request/url_request_context.h" | |
| 27 | |
| 28 using base::TimeDelta; | |
| 29 using base::TimeTicks; | |
| 30 | 10 |
| 31 namespace net { | 11 namespace net { |
| 32 | 12 |
| 33 // Config getter that fails every time. | 13 // Runs on the worker thread to call ProxyResolver::SetPacScriptByUrl or |
| 34 class ProxyConfigServiceNull : public ProxyConfigService { | 14 // ProxyResolver::SetPacScriptByData. |
| 15 // TODO(eroman): the lifetime of this task is ill-defined; |resolver_| must |
| 16 // be valid when the task eventually is run. Make this task cancellable. |
| 17 class SetPacScriptTask : public Task { |
| 35 public: | 18 public: |
| 36 // ProxyConfigService implementation: | 19 SetPacScriptTask(ProxyResolver* resolver, |
| 37 virtual int GetProxyConfig(ProxyConfig* config) { | 20 const GURL& pac_url, |
| 38 return ERR_NOT_IMPLEMENTED; | 21 const std::string& bytes) |
| 39 } | 22 : resolver_(resolver), pac_url_(pac_url), bytes_(bytes) {} |
| 40 }; | |
| 41 | |
| 42 // Proxy resolver that fails every time. | |
| 43 class ProxyResolverNull : public ProxyResolver { | |
| 44 public: | |
| 45 ProxyResolverNull() : ProxyResolver(true /*does_fetch*/) {} | |
| 46 | |
| 47 // ProxyResolver implementation: | |
| 48 virtual int GetProxyForURL(const GURL& /*query_url*/, | |
| 49 const GURL& /*pac_url*/, | |
| 50 ProxyInfo* /*results*/) { | |
| 51 return ERR_NOT_IMPLEMENTED; | |
| 52 } | |
| 53 }; | |
| 54 | |
| 55 // Strip away any reference fragments and the username/password, as they | |
| 56 // are not relevant to proxy resolution. | |
| 57 static GURL SanitizeURLForProxyResolver(const GURL& url) { | |
| 58 // TODO(eroman): The following duplicates logic from | |
| 59 // HttpUtil::SpecForRequest. Should probably live in net_util.h | |
| 60 GURL::Replacements replacements; | |
| 61 replacements.ClearUsername(); | |
| 62 replacements.ClearPassword(); | |
| 63 replacements.ClearRef(); | |
| 64 return url.ReplaceComponents(replacements); | |
| 65 } | |
| 66 | |
| 67 // Runs on the PAC thread to notify the proxy resolver of the fetched PAC | |
| 68 // script contents. This task shouldn't outlive ProxyService, since | |
| 69 // |resolver| is owned by ProxyService. | |
| 70 class NotifyFetchCompletionTask : public Task { | |
| 71 public: | |
| 72 NotifyFetchCompletionTask(ProxyResolver* resolver, const std::string& bytes) | |
| 73 : resolver_(resolver), bytes_(bytes) {} | |
| 74 | 23 |
| 75 virtual void Run() { | 24 virtual void Run() { |
| 76 resolver_->SetPacScript(bytes_); | 25 if (resolver_->expects_pac_bytes()) |
| 26 resolver_->SetPacScriptByData(bytes_); |
| 27 else |
| 28 resolver_->SetPacScriptByUrl(pac_url_); |
| 77 } | 29 } |
| 78 | 30 |
| 79 private: | 31 private: |
| 80 ProxyResolver* resolver_; | 32 ProxyResolver* resolver_; |
| 33 GURL pac_url_; |
| 81 std::string bytes_; | 34 std::string bytes_; |
| 82 }; | 35 }; |
| 83 | 36 |
| 84 // ProxyService::PacRequest --------------------------------------------------- | 37 // SingleThreadedProxyResolver::Job ------------------------------------------- |
| 85 | 38 |
| 86 // We rely on the fact that the origin thread (and its message loop) will not | 39 class SingleThreadedProxyResolver::Job |
| 87 // be destroyed until after the PAC thread is destroyed. | 40 : public base::RefCountedThreadSafe<SingleThreadedProxyResolver::Job> { |
| 88 | |
| 89 class ProxyService::PacRequest | |
| 90 : public base::RefCountedThreadSafe<ProxyService::PacRequest> { | |
| 91 public: | 41 public: |
| 92 // |service| -- the ProxyService that owns this request. | 42 // |coordinator| -- the SingleThreadedProxyResolver that owns this job. |
| 93 // |url| -- the url of the query. | 43 // |url| -- the URL of the query. |
| 94 // |results| -- the structure to fill with proxy resolve results. | 44 // |results| -- the structure to fill with proxy resolve results. |
| 95 PacRequest(ProxyService* service, | 45 Job(SingleThreadedProxyResolver* coordinator, |
| 96 const GURL& url, | 46 const GURL& url, |
| 97 ProxyInfo* results, | 47 ProxyInfo* results, |
| 98 CompletionCallback* callback) | 48 CompletionCallback* callback) |
| 99 : service_(service), | 49 : coordinator_(coordinator), |
| 100 callback_(callback), | 50 callback_(callback), |
| 101 results_(results), | 51 results_(results), |
| 102 url_(url), | 52 url_(url), |
| 103 is_started_(false), | 53 is_started_(false), |
| 104 origin_loop_(MessageLoop::current()) { | 54 origin_loop_(MessageLoop::current()) { |
| 105 DCHECK(callback); | 55 DCHECK(callback); |
| 106 } | 56 } |
| 107 | 57 |
| 108 // Start the resolve proxy request on the PAC thread. | 58 // Start the resolve proxy request on the worker thread. |
| 109 void Query() { | 59 void Start() { |
| 110 is_started_ = true; | 60 is_started_ = true; |
| 111 AddRef(); // balanced in QueryComplete | 61 AddRef(); // balanced in QueryComplete |
| 112 | 62 |
| 113 GURL query_url = SanitizeURLForProxyResolver(url_); | 63 coordinator_->thread()->message_loop()->PostTask( |
| 114 const GURL& pac_url = service_->config_.pac_url; | 64 FROM_HERE, NewRunnableMethod(this, &Job::DoQuery, |
| 115 results_->config_id_ = service_->config_.id(); | 65 coordinator_->resolver_.get())); |
| 116 | |
| 117 service_->pac_thread()->message_loop()->PostTask(FROM_HERE, | |
| 118 NewRunnableMethod(this, &ProxyService::PacRequest::DoQuery, | |
| 119 service_->resolver(), query_url, pac_url)); | |
| 120 } | 66 } |
| 121 | 67 |
| 122 // Run the request's callback on the current message loop. | 68 bool is_started() const { return is_started_; } |
| 123 void PostCallback(int result_code) { | |
| 124 AddRef(); // balanced in DoCallback | |
| 125 MessageLoop::current()->PostTask(FROM_HERE, | |
| 126 NewRunnableMethod(this, &ProxyService::PacRequest::DoCallback, | |
| 127 result_code)); | |
| 128 } | |
| 129 | 69 |
| 130 void Cancel() { | 70 void Cancel() { |
| 131 // Clear these to inform QueryComplete that it should not try to | 71 // Clear these to inform QueryComplete that it should not try to |
| 132 // access them. | 72 // access them. |
| 133 service_ = NULL; | 73 coordinator_ = NULL; |
| 134 callback_ = NULL; | 74 callback_ = NULL; |
| 135 results_ = NULL; | 75 results_ = NULL; |
| 136 } | 76 } |
| 137 | 77 |
| 138 // Returns true if Cancel() has been called. | 78 // Returns true if Cancel() has been called. |
| 139 bool was_cancelled() const { return callback_ == NULL; } | 79 bool was_cancelled() const { return callback_ == NULL; } |
| 140 | 80 |
| 141 private: | 81 private: |
| 142 friend class ProxyService; | 82 // Runs on the worker thread. |
| 143 | 83 void DoQuery(ProxyResolver* resolver) { |
| 144 // Runs on the PAC thread. | 84 int rv = resolver->GetProxyForURL(url_, &results_buf_, NULL, NULL); |
| 145 void DoQuery(ProxyResolver* resolver, | 85 DCHECK_NE(rv, ERR_IO_PENDING); |
| 146 const GURL& query_url, | |
| 147 const GURL& pac_url) { | |
| 148 int rv = resolver->GetProxyForURL(query_url, pac_url, &results_buf_); | |
| 149 origin_loop_->PostTask(FROM_HERE, | 86 origin_loop_->PostTask(FROM_HERE, |
| 150 NewRunnableMethod(this, &PacRequest::QueryComplete, rv)); | 87 NewRunnableMethod(this, &Job::QueryComplete, rv)); |
| 151 } | 88 } |
| 152 | 89 |
| 153 // Runs the completion callback on the origin thread. | 90 // Runs the completion callback on the origin thread. |
| 154 void QueryComplete(int result_code) { | 91 void QueryComplete(int result_code) { |
| 155 // The PacRequest may have been cancelled after it was started. | 92 // The Job may have been cancelled after it was started. |
| 156 if (!was_cancelled()) { | 93 if (!was_cancelled()) { |
| 157 service_->DidCompletePacRequest(results_->config_id_, result_code); | 94 if (result_code >= OK) { // Note: unit-tests use values > 0. |
| 158 | |
| 159 if (result_code == OK) { | |
| 160 results_->Use(results_buf_); | 95 results_->Use(results_buf_); |
| 161 results_->RemoveBadProxies(service_->proxy_retry_info_); | |
| 162 } | 96 } |
| 163 callback_->Run(result_code); | 97 callback_->Run(result_code); |
| 164 | 98 |
| 165 // We check for cancellation once again, in case the callback deleted | 99 // We check for cancellation once again, in case the callback deleted |
| 166 // the owning ProxyService (whose destructor will in turn cancel us). | 100 // the owning ProxyService (whose destructor will in turn cancel us). |
| 167 if (!was_cancelled()) | 101 if (!was_cancelled()) |
| 168 service_->RemoveFrontOfRequestQueue(this); | 102 coordinator_->RemoveFrontOfJobsQueueAndStartNext(this); |
| 169 } | 103 } |
| 170 | 104 |
| 171 Release(); // balances the AddRef in Query. we may get deleted after | 105 Release(); // balances the AddRef in Query. we may get deleted after |
| 172 // we return. | 106 // we return. |
| 173 } | 107 } |
| 174 | 108 |
| 175 // Runs the completion callback on the origin thread. | |
| 176 void DoCallback(int result_code) { | |
| 177 if (!was_cancelled()) { | |
| 178 callback_->Run(result_code); | |
| 179 } | |
| 180 Release(); // balances the AddRef in PostCallback. | |
| 181 } | |
| 182 | |
| 183 // Must only be used on the "origin" thread. | 109 // Must only be used on the "origin" thread. |
| 184 ProxyService* service_; | 110 SingleThreadedProxyResolver* coordinator_; |
| 185 CompletionCallback* callback_; | 111 CompletionCallback* callback_; |
| 186 ProxyInfo* results_; | 112 ProxyInfo* results_; |
| 187 GURL url_; | 113 GURL url_; |
| 188 bool is_started_; | 114 bool is_started_; |
| 189 | 115 |
| 190 // Usable from within DoQuery on the PAC thread. | 116 // Usable from within DoQuery on the worker thread. |
| 191 ProxyInfo results_buf_; | 117 ProxyInfo results_buf_; |
| 192 MessageLoop* origin_loop_; | 118 MessageLoop* origin_loop_; |
| 193 }; | 119 }; |
| 194 | 120 |
| 195 // ProxyService --------------------------------------------------------------- | 121 // SingleThreadedProxyResolver ------------------------------------------------ |
| 196 | 122 |
| 197 ProxyService::ProxyService(ProxyConfigService* config_service, | 123 SingleThreadedProxyResolver::SingleThreadedProxyResolver( |
| 198 ProxyResolver* resolver) | 124 ProxyResolver* resolver) |
| 199 : config_service_(config_service), | 125 : ProxyResolver(resolver->expects_pac_bytes()), |
| 200 resolver_(resolver), | 126 resolver_(resolver) { |
| 201 next_config_id_(1), | |
| 202 config_is_bad_(false), | |
| 203 ALLOW_THIS_IN_INITIALIZER_LIST(proxy_script_fetcher_callback_( | |
| 204 this, &ProxyService::OnScriptFetchCompletion)), | |
| 205 fetched_pac_config_id_(ProxyConfig::INVALID_ID), | |
| 206 fetched_pac_error_(OK), | |
| 207 in_progress_fetch_config_id_(ProxyConfig::INVALID_ID) { | |
| 208 } | 127 } |
| 209 | 128 |
| 210 // static | 129 SingleThreadedProxyResolver::~SingleThreadedProxyResolver() { |
| 211 ProxyService* ProxyService::Create( | 130 // Cancel the inprogress job (if any), and free the rest. |
| 212 const ProxyConfig* pc, | 131 for (PendingJobsQueue::iterator it = pending_jobs_.begin(); |
| 213 bool use_v8_resolver, | 132 it != pending_jobs_.end(); |
| 214 URLRequestContext* url_request_context, | 133 ++it) { |
| 215 MessageLoop* io_loop) { | 134 (*it)->Cancel(); |
| 216 // Choose the system configuration service appropriate for each platform. | |
| 217 ProxyConfigService* proxy_config_service = pc ? | |
| 218 new ProxyConfigServiceFixed(*pc) : | |
| 219 CreateSystemProxyConfigService(io_loop); | |
| 220 | |
| 221 ProxyResolver* proxy_resolver; | |
| 222 | |
| 223 if (use_v8_resolver) { | |
| 224 // Send javascript errors and alerts to LOG(INFO). | |
| 225 HostResolver* host_resolver = url_request_context->host_resolver(); | |
| 226 ProxyResolverV8::JSBindings* js_bindings = | |
| 227 ProxyResolverV8::CreateDefaultBindings(host_resolver, io_loop); | |
| 228 | |
| 229 proxy_resolver = new ProxyResolverV8(js_bindings); | |
| 230 } else { | |
| 231 proxy_resolver = CreateNonV8ProxyResolver(); | |
| 232 } | 135 } |
| 233 | 136 |
| 234 ProxyService* proxy_service = new ProxyService( | 137 // Note that |thread_| is destroyed before |resolver_|. This is important |
| 235 proxy_config_service, proxy_resolver); | 138 // since |resolver_| could be running on |thread_|. |
| 236 | |
| 237 if (!proxy_resolver->does_fetch()) { | |
| 238 // Configure PAC script downloads to be issued using |url_request_context|. | |
| 239 DCHECK(url_request_context); | |
| 240 proxy_service->SetProxyScriptFetcher( | |
| 241 ProxyScriptFetcher::Create(url_request_context)); | |
| 242 } | |
| 243 | |
| 244 return proxy_service; | |
| 245 } | 139 } |
| 246 | 140 |
| 247 // static | 141 int SingleThreadedProxyResolver::GetProxyForURL(const GURL& url, |
| 248 ProxyService* ProxyService::CreateFixed(const ProxyConfig& pc) { | 142 ProxyInfo* results, |
| 249 return Create(&pc, false, NULL, NULL); | 143 CompletionCallback* callback, |
| 144 RequestHandle* request) { |
| 145 DCHECK(callback); |
| 146 |
| 147 scoped_refptr<Job> job = new Job(this, url, results, callback); |
| 148 pending_jobs_.push_back(job); |
| 149 ProcessPendingJobs(); // Jobs can never finish synchronously. |
| 150 |
| 151 // Completion will be notified through |callback|, unless the caller cancels |
| 152 // the request using |request|. |
| 153 if (request) |
| 154 *request = reinterpret_cast<RequestHandle>(job.get()); |
| 155 |
| 156 return ERR_IO_PENDING; |
| 250 } | 157 } |
| 251 | 158 |
| 252 // static | 159 // There are three states of the request we need to handle: |
| 253 ProxyService* ProxyService::CreateNull() { | 160 // (1) Not started (just sitting in the queue). |
| 254 // Use a configuration fetcher and proxy resolver which always fail. | 161 // (2) Executing Job::DoQuery in the worker thread. |
| 255 return new ProxyService(new ProxyConfigServiceNull, new ProxyResolverNull); | 162 // (3) Waiting for Job::QueryComplete to be run on the origin thread. |
| 256 } | 163 void SingleThreadedProxyResolver::CancelRequest(RequestHandle req) { |
| 164 DCHECK(req); |
| 257 | 165 |
| 258 int ProxyService::ResolveProxy(const GURL& url, ProxyInfo* result, | 166 Job* job = reinterpret_cast<Job*>(req); |
| 259 CompletionCallback* callback, | |
| 260 PacRequest** pac_request) { | |
| 261 DCHECK(callback); | |
| 262 | 167 |
| 263 // Check if the request can be completed right away. This is the case when | 168 bool is_active_job = job->is_started() && !pending_jobs_.empty() && |
| 264 // using a direct connection, or when the config is bad. | 169 pending_jobs_.front().get() == job; |
| 265 UpdateConfigIfOld(); | |
| 266 int rv = TryToCompleteSynchronously(url, result); | |
| 267 if (rv != ERR_IO_PENDING) | |
| 268 return rv; | |
| 269 | 170 |
| 270 // Otherwise, push the request into the work queue. | 171 job->Cancel(); |
| 271 scoped_refptr<PacRequest> req = new PacRequest(this, url, result, callback); | |
| 272 pending_requests_.push_back(req); | |
| 273 ProcessPendingRequests(req.get()); | |
| 274 | 172 |
| 275 // Completion will be notifed through |callback|, unless the caller cancels | 173 if (is_active_job) { |
| 276 // the request using |pac_request|. | 174 RemoveFrontOfJobsQueueAndStartNext(job); |
| 277 if (pac_request) | |
| 278 *pac_request = req.get(); | |
| 279 return rv; // ERR_IO_PENDING | |
| 280 } | |
| 281 | |
| 282 int ProxyService::TryToCompleteSynchronously(const GURL& url, | |
| 283 ProxyInfo* result) { | |
| 284 result->config_id_ = config_.id(); | |
| 285 | |
| 286 DCHECK(config_.id() != ProxyConfig::INVALID_ID); | |
| 287 | |
| 288 // Fallback to a "direct" (no proxy) connection if the current configuration | |
| 289 // is known to be bad. | |
| 290 if (config_is_bad_) { | |
| 291 // Reset this flag to false in case the ProxyInfo object is being | |
| 292 // re-used by the caller. | |
| 293 result->config_was_tried_ = false; | |
| 294 } else { | |
| 295 // Remember that we are trying to use the current proxy configuration. | |
| 296 result->config_was_tried_ = true; | |
| 297 | |
| 298 if (!config_.proxy_rules.empty()) { | |
| 299 ApplyProxyRules(url, config_.proxy_rules, result); | |
| 300 return OK; | |
| 301 } | |
| 302 | |
| 303 if (config_.pac_url.is_valid() || config_.auto_detect) { | |
| 304 // If we failed to download the PAC script, return the network error | |
| 305 // from the failed download. This is only going to happen for the first | |
| 306 // request after the failed download -- after that |config_is_bad_| will | |
| 307 // be set to true, so we short-cuircuit sooner. | |
| 308 if (fetched_pac_error_ != OK && !IsFetchingPacScript()) { | |
| 309 DidCompletePacRequest(fetched_pac_config_id_, fetched_pac_error_); | |
| 310 return fetched_pac_error_; | |
| 311 } | |
| 312 return ERR_IO_PENDING; | |
| 313 } | |
| 314 } | |
| 315 | |
| 316 // otherwise, we have no proxy config | |
| 317 result->UseDirect(); | |
| 318 return OK; | |
| 319 } | |
| 320 | |
| 321 void ProxyService::ApplyProxyRules(const GURL& url, | |
| 322 const ProxyConfig::ProxyRules& proxy_rules, | |
| 323 ProxyInfo* result) { | |
| 324 DCHECK(!proxy_rules.empty()); | |
| 325 | |
| 326 if (ShouldBypassProxyForURL(url)) { | |
| 327 result->UseDirect(); | |
| 328 return; | 175 return; |
| 329 } | 176 } |
| 330 | 177 |
| 331 switch (proxy_rules.type) { | 178 // Otherwise just delete the job from the queue. |
| 332 case ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY: | 179 PendingJobsQueue::iterator it = std::find( |
| 333 result->UseProxyServer(proxy_rules.single_proxy); | 180 pending_jobs_.begin(), pending_jobs_.end(), job); |
| 334 break; | 181 DCHECK(it != pending_jobs_.end()); |
| 335 case ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME: { | 182 pending_jobs_.erase(it); |
| 336 const ProxyServer* entry = proxy_rules.MapSchemeToProxy(url.scheme()); | 183 } |
| 337 if (entry) { | 184 |
| 338 result->UseProxyServer(*entry); | 185 void SingleThreadedProxyResolver::SetPacScriptByUrlInternal( |
| 339 } else { | 186 const GURL& pac_url) { |
| 340 // We failed to find a matching proxy server for the current URL | 187 SetPacScriptHelper(pac_url, std::string()); |
| 341 // scheme. Default to direct. | 188 } |
| 342 result->UseDirect(); | 189 |
| 343 } | 190 void SingleThreadedProxyResolver::SetPacScriptByDataInternal( |
| 344 break; | 191 const std::string& bytes) { |
| 345 } | 192 SetPacScriptHelper(GURL(), bytes); |
| 346 default: | 193 } |
| 347 result->UseDirect(); | 194 |
| 348 NOTREACHED(); | 195 void SingleThreadedProxyResolver::SetPacScriptHelper(const GURL& pac_url, |
| 349 break; | 196 const std::string& bytes) { |
| 197 EnsureThreadStarted(); |
| 198 thread()->message_loop()->PostTask( |
| 199 FROM_HERE, new SetPacScriptTask(resolver(), pac_url, bytes)); |
| 200 } |
| 201 |
| 202 void SingleThreadedProxyResolver::EnsureThreadStarted() { |
| 203 if (!thread_.get()) { |
| 204 thread_.reset(new base::Thread("pac-thread")); |
| 205 thread_->Start(); |
| 350 } | 206 } |
| 351 } | 207 } |
| 352 | 208 |
| 353 void ProxyService::InitPacThread() { | 209 void SingleThreadedProxyResolver::ProcessPendingJobs() { |
| 354 if (!pac_thread_.get()) { | 210 if (pending_jobs_.empty()) |
| 355 pac_thread_.reset(new base::Thread("pac-thread")); | 211 return; |
| 356 pac_thread_->Start(); | 212 |
| 357 } | 213 // Get the next job to process (FIFO). |
| 214 Job* job = pending_jobs_.front().get(); |
| 215 if (job->is_started()) |
| 216 return; |
| 217 |
| 218 EnsureThreadStarted(); |
| 219 job->Start(); |
| 358 } | 220 } |
| 359 | 221 |
| 360 ProxyService::~ProxyService() { | 222 void SingleThreadedProxyResolver::RemoveFrontOfJobsQueueAndStartNext( |
| 361 // Cancel the inprogress request (if any), and free the rest. | 223 Job* expected_job) { |
| 362 for (PendingRequestsQueue::iterator it = pending_requests_.begin(); | 224 DCHECK_EQ(expected_job, pending_jobs_.front().get()); |
| 363 it != pending_requests_.end(); | 225 pending_jobs_.pop_front(); |
| 364 ++it) { | |
| 365 (*it)->Cancel(); | |
| 366 } | |
| 367 } | |
| 368 | |
| 369 void ProxyService::ProcessPendingRequests(PacRequest* recent_req) { | |
| 370 if (pending_requests_.empty()) | |
| 371 return; | |
| 372 | |
| 373 // While the PAC script is being downloaded, requests are blocked. | |
| 374 if (IsFetchingPacScript()) | |
| 375 return; | |
| 376 | |
| 377 // Get the next request to process (FIFO). | |
| 378 PacRequest* req = pending_requests_.front().get(); | |
| 379 if (req->is_started_) | |
| 380 return; | |
| 381 | |
| 382 // The configuration may have changed since |req| was added to the | |
| 383 // queue. It could be this request now completes synchronously. | |
| 384 if (req != recent_req) { | |
| 385 UpdateConfigIfOld(); | |
| 386 int rv = TryToCompleteSynchronously(req->url_, req->results_); | |
| 387 if (rv != ERR_IO_PENDING) { | |
| 388 req->PostCallback(rv); | |
| 389 RemoveFrontOfRequestQueue(req); | |
| 390 return; | |
| 391 } | |
| 392 } | |
| 393 | |
| 394 // Check if a new PAC script needs to be downloaded. | |
| 395 DCHECK(config_.id() != ProxyConfig::INVALID_ID); | |
| 396 if (!resolver_->does_fetch() && config_.id() != fetched_pac_config_id_) { | |
| 397 // For auto-detect we use the well known WPAD url. | |
| 398 GURL pac_url = config_.auto_detect ? | |
| 399 GURL("http://wpad/wpad.dat") : config_.pac_url; | |
| 400 | |
| 401 in_progress_fetch_config_id_ = config_.id(); | |
| 402 | |
| 403 LOG(INFO) << "Starting fetch of PAC script " << pac_url | |
| 404 << " for config_id=" << in_progress_fetch_config_id_; | |
| 405 | |
| 406 proxy_script_fetcher_->Fetch( | |
| 407 pac_url, &in_progress_fetch_bytes_, &proxy_script_fetcher_callback_); | |
| 408 return; | |
| 409 } | |
| 410 | |
| 411 // The only choice left now is to actually run the ProxyResolver on | |
| 412 // the PAC thread. | |
| 413 InitPacThread(); | |
| 414 req->Query(); | |
| 415 } | |
| 416 | |
| 417 void ProxyService::RemoveFrontOfRequestQueue(PacRequest* expected_req) { | |
| 418 DCHECK(pending_requests_.front().get() == expected_req); | |
| 419 pending_requests_.pop_front(); | |
| 420 | 226 |
| 421 // Start next work item. | 227 // Start next work item. |
| 422 ProcessPendingRequests(NULL); | 228 ProcessPendingJobs(); |
| 423 } | |
| 424 | |
| 425 void ProxyService::OnScriptFetchCompletion(int result) { | |
| 426 DCHECK(IsFetchingPacScript()); | |
| 427 DCHECK(!resolver_->does_fetch()); | |
| 428 | |
| 429 LOG(INFO) << "Completed PAC script fetch for config_id=" | |
| 430 << in_progress_fetch_config_id_ | |
| 431 << " with error " << ErrorToString(result) | |
| 432 << ". Fetched a total of " << in_progress_fetch_bytes_.size() | |
| 433 << " bytes"; | |
| 434 | |
| 435 // Notify the ProxyResolver of the new script data (will be empty string if | |
| 436 // result != OK). | |
| 437 InitPacThread(); | |
| 438 pac_thread()->message_loop()->PostTask(FROM_HERE, | |
| 439 new NotifyFetchCompletionTask( | |
| 440 resolver_.get(), in_progress_fetch_bytes_)); | |
| 441 | |
| 442 fetched_pac_config_id_ = in_progress_fetch_config_id_; | |
| 443 fetched_pac_error_ = result; | |
| 444 in_progress_fetch_config_id_ = ProxyConfig::INVALID_ID; | |
| 445 in_progress_fetch_bytes_.clear(); | |
| 446 | |
| 447 // Start a pending request if any. | |
| 448 ProcessPendingRequests(NULL); | |
| 449 } | |
| 450 | |
| 451 int ProxyService::ReconsiderProxyAfterError(const GURL& url, | |
| 452 ProxyInfo* result, | |
| 453 CompletionCallback* callback, | |
| 454 PacRequest** pac_request) { | |
| 455 // Check to see if we have a new config since ResolveProxy was called. We | |
| 456 // want to re-run ResolveProxy in two cases: 1) we have a new config, or 2) a | |
| 457 // direct connection failed and we never tried the current config. | |
| 458 | |
| 459 bool re_resolve = result->config_id_ != config_.id(); | |
| 460 if (!re_resolve) { | |
| 461 UpdateConfig(); | |
| 462 if (result->config_id_ != config_.id()) { | |
| 463 // A new configuration! | |
| 464 re_resolve = true; | |
| 465 } else if (!result->config_was_tried_) { | |
| 466 // We never tried the proxy configuration since we thought it was bad, | |
| 467 // but because we failed to establish a connection, let's try the proxy | |
| 468 // configuration again to see if it will work now. | |
| 469 config_is_bad_ = false; | |
| 470 re_resolve = true; | |
| 471 } | |
| 472 } | |
| 473 if (re_resolve) { | |
| 474 // If we have a new config or the config was never tried, we delete the | |
| 475 // list of bad proxies and we try again. | |
| 476 proxy_retry_info_.clear(); | |
| 477 return ResolveProxy(url, result, callback, pac_request); | |
| 478 } | |
| 479 | |
| 480 // We don't have new proxy settings to try, fallback to the next proxy | |
| 481 // in the list. | |
| 482 bool was_direct = result->is_direct(); | |
| 483 if (!was_direct && result->Fallback(&proxy_retry_info_)) | |
| 484 return OK; | |
| 485 | |
| 486 if (!config_.auto_detect && !config_.proxy_rules.empty()) { | |
| 487 // If auto detect is on, then we should try a DIRECT connection | |
| 488 // as the attempt to reach the proxy failed. | |
| 489 return ERR_FAILED; | |
| 490 } | |
| 491 | |
| 492 // If we already tried a direct connection, then just give up. | |
| 493 if (was_direct) | |
| 494 return ERR_FAILED; | |
| 495 | |
| 496 // Try going direct. | |
| 497 result->UseDirect(); | |
| 498 return OK; | |
| 499 } | |
| 500 | |
| 501 // There are four states of the request we need to handle: | |
| 502 // (1) Not started (just sitting in the queue). | |
| 503 // (2) Executing PacRequest::DoQuery in the PAC thread. | |
| 504 // (3) Waiting for PacRequest::QueryComplete to be run on the origin thread. | |
| 505 // (4) Waiting for PacRequest::DoCallback to be run on the origin thread. | |
| 506 void ProxyService::CancelPacRequest(PacRequest* req) { | |
| 507 DCHECK(req); | |
| 508 | |
| 509 bool is_active_request = req->is_started_ && !pending_requests_.empty() && | |
| 510 pending_requests_.front().get() == req; | |
| 511 | |
| 512 req->Cancel(); | |
| 513 | |
| 514 if (is_active_request) { | |
| 515 RemoveFrontOfRequestQueue(req); | |
| 516 return; | |
| 517 } | |
| 518 | |
| 519 // Otherwise just delete the request from the queue. | |
| 520 PendingRequestsQueue::iterator it = std::find( | |
| 521 pending_requests_.begin(), pending_requests_.end(), req); | |
| 522 if (it != pending_requests_.end()) { | |
| 523 pending_requests_.erase(it); | |
| 524 } | |
| 525 } | |
| 526 | |
| 527 void ProxyService::SetProxyScriptFetcher( | |
| 528 ProxyScriptFetcher* proxy_script_fetcher) { | |
| 529 proxy_script_fetcher_.reset(proxy_script_fetcher); | |
| 530 } | |
| 531 | |
| 532 void ProxyService::ResetConfigService( | |
| 533 ProxyConfigService* new_proxy_config_service) { | |
| 534 config_service_.reset(new_proxy_config_service); | |
| 535 UpdateConfig(); | |
| 536 } | |
| 537 | |
| 538 void ProxyService::DidCompletePacRequest(int config_id, int result_code) { | |
| 539 // If we get an error that indicates a bad PAC config, then we should | |
| 540 // remember that, and not try the PAC config again for a while. | |
| 541 | |
| 542 // Our config may have already changed. | |
| 543 if (result_code == OK || config_id != config_.id()) | |
| 544 return; | |
| 545 | |
| 546 // Remember that this configuration doesn't work. | |
| 547 config_is_bad_ = true; | |
| 548 } | |
| 549 | |
| 550 // static | |
| 551 ProxyConfigService* ProxyService::CreateSystemProxyConfigService( | |
| 552 MessageLoop* io_loop) { | |
| 553 #if defined(OS_WIN) | |
| 554 return new ProxyConfigServiceWin(); | |
| 555 #elif defined(OS_MACOSX) | |
| 556 return new ProxyConfigServiceMac(); | |
| 557 #elif defined(OS_LINUX) | |
| 558 ProxyConfigServiceLinux* linux_config_service | |
| 559 = new ProxyConfigServiceLinux(); | |
| 560 | |
| 561 // Assume we got called from the UI loop, which runs the default | |
| 562 // glib main loop, so the current thread is where we should be | |
| 563 // running gconf calls from. | |
| 564 MessageLoop* glib_default_loop = MessageLoopForUI::current(); | |
| 565 | |
| 566 // Synchronously fetch the current proxy config (since we are | |
| 567 // running on glib_default_loop). Additionally register for gconf | |
| 568 // notifications (delivered in |glib_default_loop|) to keep us | |
| 569 // updated on when the proxy config has changed. | |
| 570 linux_config_service->SetupAndFetchInitialConfig(glib_default_loop, io_loop); | |
| 571 | |
| 572 return linux_config_service; | |
| 573 #else | |
| 574 LOG(WARNING) << "Failed to choose a system proxy settings fetcher " | |
| 575 "for this platform."; | |
| 576 return new ProxyConfigServiceNull(); | |
| 577 #endif | |
| 578 } | |
| 579 | |
| 580 // static | |
| 581 ProxyResolver* ProxyService::CreateNonV8ProxyResolver() { | |
| 582 #if defined(OS_WIN) | |
| 583 return new ProxyResolverWinHttp(); | |
| 584 #elif defined(OS_MACOSX) | |
| 585 return new ProxyResolverMac(); | |
| 586 #else | |
| 587 LOG(WARNING) << "PAC support disabled because there is no fallback " | |
| 588 "non-V8 implementation"; | |
| 589 return new ProxyResolverNull(); | |
| 590 #endif | |
| 591 } | |
| 592 | |
| 593 void ProxyService::UpdateConfig() { | |
| 594 bool is_first_update = !config_has_been_initialized(); | |
| 595 | |
| 596 ProxyConfig latest; | |
| 597 if (config_service_->GetProxyConfig(&latest) != OK) { | |
| 598 if (is_first_update) { | |
| 599 // Default to direct-connection if the first fetch fails. | |
| 600 LOG(INFO) << "Failed initial proxy configuration fetch."; | |
| 601 SetConfig(ProxyConfig()); | |
| 602 } | |
| 603 return; | |
| 604 } | |
| 605 config_last_update_time_ = TimeTicks::Now(); | |
| 606 | |
| 607 if (!is_first_update && latest.Equals(config_)) | |
| 608 return; | |
| 609 | |
| 610 SetConfig(latest); | |
| 611 } | |
| 612 | |
| 613 void ProxyService::SetConfig(const ProxyConfig& config) { | |
| 614 config_ = config; | |
| 615 | |
| 616 // Increment the ID to reflect that the config has changed. | |
| 617 config_.set_id(next_config_id_++); | |
| 618 | |
| 619 LOG(INFO) << "New proxy configuration was loaded:\n" << config_; | |
| 620 | |
| 621 // Reset state associated with latest config. | |
| 622 config_is_bad_ = false; | |
| 623 proxy_retry_info_.clear(); | |
| 624 } | |
| 625 | |
| 626 void ProxyService::UpdateConfigIfOld() { | |
| 627 // The overhead of calling ProxyConfigService::GetProxyConfig is very low. | |
| 628 const TimeDelta kProxyConfigMaxAge = TimeDelta::FromSeconds(5); | |
| 629 | |
| 630 // Periodically check for a new config. | |
| 631 if (!config_has_been_initialized() || | |
| 632 (TimeTicks::Now() - config_last_update_time_) > kProxyConfigMaxAge) | |
| 633 UpdateConfig(); | |
| 634 } | |
| 635 | |
| 636 bool ProxyService::ShouldBypassProxyForURL(const GURL& url) { | |
| 637 std::string url_domain = url.scheme(); | |
| 638 if (!url_domain.empty()) | |
| 639 url_domain += "://"; | |
| 640 | |
| 641 url_domain += url.host(); | |
| 642 // This isn't superfluous; GURL case canonicalization doesn't hit the embedded | |
| 643 // percent-encoded characters. | |
| 644 StringToLowerASCII(&url_domain); | |
| 645 | |
| 646 // TODO(eroman): use GetHostAndPort(). | |
| 647 std::string url_domain_and_port = url_domain + ":" | |
| 648 + IntToString(url.EffectiveIntPort()); | |
| 649 | |
| 650 if (config_.proxy_bypass_local_names) { | |
| 651 if (url.host().find('.') == std::string::npos) | |
| 652 return true; | |
| 653 } | |
| 654 | |
| 655 for(std::vector<std::string>::const_iterator i = config_.proxy_bypass.begin(); | |
| 656 i != config_.proxy_bypass.end(); ++i) { | |
| 657 std::string bypass_url_domain = *i; | |
| 658 | |
| 659 // The proxy server bypass list can contain entities with http/https | |
| 660 // If no scheme is specified then it indicates that all schemes are | |
| 661 // allowed for the current entry. For matching this we just use | |
| 662 // the protocol scheme of the url passed in. | |
| 663 size_t scheme_colon = bypass_url_domain.find("://"); | |
| 664 if (scheme_colon == std::string::npos) { | |
| 665 std::string bypass_url_domain_with_scheme = url.scheme(); | |
| 666 scheme_colon = bypass_url_domain_with_scheme.length(); | |
| 667 bypass_url_domain_with_scheme += "://"; | |
| 668 bypass_url_domain_with_scheme += bypass_url_domain; | |
| 669 | |
| 670 bypass_url_domain = bypass_url_domain_with_scheme; | |
| 671 } | |
| 672 std::string* url_compare_reference = &url_domain; | |
| 673 size_t port_colon = bypass_url_domain.rfind(":"); | |
| 674 if (port_colon > scheme_colon) { | |
| 675 // If our match pattern includes a colon followed by a digit, | |
| 676 // and either it's preceded by ']' (IPv6 with port) | |
| 677 // or has no other colon (IPv4), | |
| 678 // then match against <domain>:<port>. | |
| 679 // TODO(sdoyon): straighten this out, in particular the IPv6 brackets, | |
| 680 // and do the parsing in ProxyConfig when we do the CIDR matching | |
| 681 // mentioned below. | |
| 682 std::string::const_iterator domain_begin = | |
| 683 bypass_url_domain.begin() + scheme_colon + 3; // after :// | |
| 684 std::string::const_iterator port_iter = | |
| 685 bypass_url_domain.begin() + port_colon; | |
| 686 std::string::const_iterator end = bypass_url_domain.end(); | |
| 687 if ((port_iter + 1) < end && IsAsciiDigit(*(port_iter + 1)) && | |
| 688 (*(port_iter - 1) == ']' || | |
| 689 std::find(domain_begin, port_iter, ':') == port_iter)) | |
| 690 url_compare_reference = &url_domain_and_port; | |
| 691 } | |
| 692 | |
| 693 StringToLowerASCII(&bypass_url_domain); | |
| 694 | |
| 695 if (MatchPattern(*url_compare_reference, bypass_url_domain)) | |
| 696 return true; | |
| 697 | |
| 698 // Some systems (the Mac, for example) allow CIDR-style specification of | |
| 699 // proxy bypass for IP-specified hosts (e.g. "10.0.0.0/8"; see | |
| 700 // http://www.tcd.ie/iss/internet/osx_proxy.php for a real-world example). | |
| 701 // That's kinda cool so we'll provide that for everyone. | |
| 702 // TODO(avi): implement here. See: http://crbug.com/9835. | |
| 703 // IP addresses ought to be canonicalized for comparison (whether | |
| 704 // with CIDR, port, or IP address alone). | |
| 705 } | |
| 706 | |
| 707 return false; | |
| 708 } | |
| 709 | |
| 710 SyncProxyServiceHelper::SyncProxyServiceHelper(MessageLoop* io_message_loop, | |
| 711 ProxyService* proxy_service) | |
| 712 : io_message_loop_(io_message_loop), | |
| 713 proxy_service_(proxy_service), | |
| 714 event_(false, false), | |
| 715 ALLOW_THIS_IN_INITIALIZER_LIST(callback_( | |
| 716 this, &SyncProxyServiceHelper::OnCompletion)) { | |
| 717 DCHECK(io_message_loop_ != MessageLoop::current()); | |
| 718 } | |
| 719 | |
| 720 int SyncProxyServiceHelper::ResolveProxy(const GURL& url, | |
| 721 ProxyInfo* proxy_info) { | |
| 722 DCHECK(io_message_loop_ != MessageLoop::current()); | |
| 723 | |
| 724 io_message_loop_->PostTask(FROM_HERE, NewRunnableMethod( | |
| 725 this, &SyncProxyServiceHelper::StartAsyncResolve, url)); | |
| 726 | |
| 727 event_.Wait(); | |
| 728 | |
| 729 if (result_ == net::OK) { | |
| 730 *proxy_info = proxy_info_; | |
| 731 } | |
| 732 return result_; | |
| 733 } | |
| 734 | |
| 735 int SyncProxyServiceHelper::ReconsiderProxyAfterError(const GURL& url, | |
| 736 ProxyInfo* proxy_info) { | |
| 737 DCHECK(io_message_loop_ != MessageLoop::current()); | |
| 738 | |
| 739 io_message_loop_->PostTask(FROM_HERE, NewRunnableMethod( | |
| 740 this, &SyncProxyServiceHelper::StartAsyncReconsider, url)); | |
| 741 | |
| 742 event_.Wait(); | |
| 743 | |
| 744 if (result_ == net::OK) { | |
| 745 *proxy_info = proxy_info_; | |
| 746 } | |
| 747 return result_; | |
| 748 } | |
| 749 | |
| 750 void SyncProxyServiceHelper::StartAsyncResolve(const GURL& url) { | |
| 751 result_ = proxy_service_->ResolveProxy(url, &proxy_info_, &callback_, NULL); | |
| 752 if (result_ != net::ERR_IO_PENDING) { | |
| 753 OnCompletion(result_); | |
| 754 } | |
| 755 } | |
| 756 | |
| 757 void SyncProxyServiceHelper::StartAsyncReconsider(const GURL& url) { | |
| 758 result_ = proxy_service_->ReconsiderProxyAfterError( | |
| 759 url, &proxy_info_, &callback_, NULL); | |
| 760 if (result_ != net::ERR_IO_PENDING) { | |
| 761 OnCompletion(result_); | |
| 762 } | |
| 763 } | |
| 764 | |
| 765 void SyncProxyServiceHelper::OnCompletion(int rv) { | |
| 766 result_ = rv; | |
| 767 event_.Signal(); | |
| 768 } | 229 } |
| 769 | 230 |
| 770 } // namespace net | 231 } // namespace net |
| OLD | NEW |