1// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "net/proxy/proxy_service.h" 6 7#include <algorithm> 8 9#include "base/compiler_specific.h" 10#include "base/logging.h" 11#include "base/message_loop.h" 12#include "base/string_util.h" 13#include "googleurl/src/gurl.h" 14#include "net/base/load_log.h" 15#include "net/base/net_errors.h" 16#include "net/base/net_util.h" 17#include "net/proxy/init_proxy_resolver.h" 18#include "net/proxy/proxy_config_service_fixed.h" 19#include "net/proxy/proxy_script_fetcher.h" 20#if defined(OS_WIN) 21#include "net/proxy/proxy_config_service_win.h" 22#include "net/proxy/proxy_resolver_winhttp.h" 23#elif defined(OS_MACOSX) 24#include "net/proxy/proxy_config_service_mac.h" 25#include "net/proxy/proxy_resolver_mac.h" 26#elif defined(OS_LINUX) 27#include "net/proxy/proxy_config_service_linux.h" 28#endif 29#include "net/proxy/proxy_resolver.h" 30#include "net/proxy/proxy_resolver_js_bindings.h" 31#include "net/proxy/proxy_resolver_v8.h" 32#include "net/proxy/single_threaded_proxy_resolver.h" 33#include "net/url_request/url_request_context.h" 34 35using base::TimeDelta; 36using base::TimeTicks; 37 38namespace net { 39 40static const size_t kMaxNumLoadLogEntries = 100; 41 42// Config getter that fails every time. 43class ProxyConfigServiceNull : public ProxyConfigService { 44 public: 45 // ProxyConfigService implementation: 46 virtual int GetProxyConfig(ProxyConfig* config) { 47 return ERR_NOT_IMPLEMENTED; 48 } 49}; 50 51// Proxy resolver that fails every time. 52class ProxyResolverNull : public ProxyResolver { 53 public: 54 ProxyResolverNull() : ProxyResolver(false /*expects_pac_bytes*/) {} 55 56 // ProxyResolver implementation: 57 virtual int GetProxyForURL(const GURL& url, 58 ProxyInfo* results, 59 CompletionCallback* callback, 60 RequestHandle* request, 61 LoadLog* load_log) { 62 return ERR_NOT_IMPLEMENTED; 63 } 64 65 virtual void CancelRequest(RequestHandle request) { 66 NOTREACHED(); 67 } 68 69 private: 70 virtual int SetPacScript(const GURL& /*pac_url*/, 71 const std::string& /*pac_bytes*/, 72 CompletionCallback* /*callback*/) { 73 return ERR_NOT_IMPLEMENTED; 74 } 75}; 76 77// ProxyService::PacRequest --------------------------------------------------- 78 79class ProxyService::PacRequest 80 : public base::RefCounted<ProxyService::PacRequest> { 81 public: 82 PacRequest(ProxyService* service, 83 const GURL& url, 84 ProxyInfo* results, 85 CompletionCallback* user_callback, 86 LoadLog* load_log) 87 : service_(service), 88 user_callback_(user_callback), 89 ALLOW_THIS_IN_INITIALIZER_LIST(io_callback_( 90 this, &PacRequest::QueryComplete)), 91 results_(results), 92 url_(url), 93 resolve_job_(NULL), 94 config_id_(ProxyConfig::INVALID_ID), 95 load_log_(load_log) { 96 DCHECK(user_callback); 97 } 98 99 // Starts the resolve proxy request. 100 int Start() { 101 DCHECK(!was_cancelled()); 102 DCHECK(!is_started()); 103 104 config_id_ = service_->config_.id(); 105 106 return resolver()->GetProxyForURL( 107 url_, results_, &io_callback_, &resolve_job_, load_log_); 108 } 109 110 bool is_started() const { 111 // Note that !! casts to bool. (VS gives a warning otherwise). 112 return !!resolve_job_; 113 } 114 115 void StartAndCompleteCheckingForSynchronous() { 116 int rv = service_->TryToCompleteSynchronously(url_, results_); 117 if (rv == ERR_IO_PENDING) 118 rv = Start(); 119 if (rv != ERR_IO_PENDING) 120 QueryComplete(rv); 121 } 122 123 void CancelResolveJob() { 124 DCHECK(is_started()); 125 // The request may already be running in the resolver. 126 resolver()->CancelRequest(resolve_job_); 127 resolve_job_ = NULL; 128 DCHECK(!is_started()); 129 } 130 131 void Cancel() { 132 LoadLog::AddEvent(load_log_, LoadLog::TYPE_CANCELLED); 133 134 if (is_started()) 135 CancelResolveJob(); 136 137 // Mark as cancelled, to prevent accessing this again later. 138 service_ = NULL; 139 user_callback_ = NULL; 140 results_ = NULL; 141 142 LoadLog::EndEvent(load_log_, LoadLog::TYPE_PROXY_SERVICE); 143 } 144 145 // Returns true if Cancel() has been called. 146 bool was_cancelled() const { return user_callback_ == NULL; } 147 148 // Helper to call after ProxyResolver completion (both synchronous and 149 // asynchronous). Fixes up the result that is to be returned to user. 150 int QueryDidComplete(int result_code) { 151 DCHECK(!was_cancelled()); 152 153 // Make a note in the results which configuration was in use at the 154 // time of the resolve. 155 results_->config_id_ = config_id_; 156 157 // Reset the state associated with in-progress-resolve. 158 resolve_job_ = NULL; 159 config_id_ = ProxyConfig::INVALID_ID; 160 161 return service_->DidFinishResolvingProxy(results_, result_code, load_log_); 162 } 163 164 LoadLog* load_log() const { return load_log_; } 165 166 private: 167 friend class base::RefCounted<ProxyService::PacRequest>; 168 169 ~PacRequest() {} 170 171 // Callback for when the ProxyResolver request has completed. 172 void QueryComplete(int result_code) { 173 result_code = QueryDidComplete(result_code); 174 175 // Remove this completed PacRequest from the service's pending list. 176 /// (which will probably cause deletion of |this|). 177 CompletionCallback* callback = user_callback_; 178 service_->RemovePendingRequest(this); 179 180 callback->Run(result_code); 181 } 182 183 ProxyResolver* resolver() const { return service_->resolver_.get(); } 184 185 // Note that we don't hold a reference to the ProxyService. Outstanding 186 // requests are cancelled during ~ProxyService, so this is guaranteed 187 // to be valid throughout our lifetime. 188 ProxyService* service_; 189 CompletionCallback* user_callback_; 190 CompletionCallbackImpl<PacRequest> io_callback_; 191 ProxyInfo* results_; 192 GURL url_; 193 ProxyResolver::RequestHandle resolve_job_; 194 ProxyConfig::ID config_id_; // The config id when the resolve was started. 195 scoped_refptr<LoadLog> load_log_; 196}; 197 198// ProxyService --------------------------------------------------------------- 199 200ProxyService::ProxyService(ProxyConfigService* config_service, 201 ProxyResolver* resolver, 202 NetworkChangeNotifier* network_change_notifier) 203 : config_service_(config_service), 204 resolver_(resolver), 205 next_config_id_(1), 206 should_use_proxy_resolver_(false), 207 ALLOW_THIS_IN_INITIALIZER_LIST(init_proxy_resolver_callback_( 208 this, &ProxyService::OnInitProxyResolverComplete)), 209 network_change_notifier_(network_change_notifier) { 210 // Register to receive network change notifications. 211 if (network_change_notifier_) 212 network_change_notifier_->AddObserver(this); 213} 214 215// static 216ProxyService* ProxyService::Create( 217 ProxyConfigService* proxy_config_service, 218 bool use_v8_resolver, 219 URLRequestContext* url_request_context, 220 NetworkChangeNotifier* network_change_notifier, 221 MessageLoop* io_loop) { 222 ProxyResolver* proxy_resolver; 223 224 if (use_v8_resolver) { 225 // Send javascript errors and alerts to LOG(INFO). 226 HostResolver* host_resolver = url_request_context->host_resolver(); 227 ProxyResolverJSBindings* js_bindings = 228 ProxyResolverJSBindings::CreateDefault(host_resolver, io_loop); 229 230 proxy_resolver = new ProxyResolverV8(js_bindings); 231 } else { 232 proxy_resolver = CreateNonV8ProxyResolver(); 233 } 234 235 // Wrap the (synchronous) ProxyResolver implementation in a single-threaded 236 // runner. This will dispatch requests to a threadpool of size 1. 237 proxy_resolver = new SingleThreadedProxyResolver(proxy_resolver); 238 239 ProxyService* proxy_service = new ProxyService( 240 proxy_config_service, proxy_resolver, network_change_notifier); 241 242 if (proxy_resolver->expects_pac_bytes()) { 243 // Configure PAC script downloads to be issued using |url_request_context|. 244 DCHECK(url_request_context); 245 proxy_service->SetProxyScriptFetcher( 246 ProxyScriptFetcher::Create(url_request_context)); 247 } 248 249 return proxy_service; 250} 251 252// static 253ProxyService* ProxyService::CreateFixed(const ProxyConfig& pc) { 254 return Create(new ProxyConfigServiceFixed(pc), false, NULL, NULL, NULL); 255} 256 257// static 258ProxyService* ProxyService::CreateNull() { 259 // Use a configuration fetcher and proxy resolver which always fail. 260 return new ProxyService(new ProxyConfigServiceNull, 261 new ProxyResolverNull, 262 NULL); 263} 264 265int ProxyService::ResolveProxy(const GURL& raw_url, 266 ProxyInfo* result, 267 CompletionCallback* callback, 268 PacRequest** pac_request, 269 LoadLog* load_log) { 270 DCHECK(callback); 271 272 LoadLog::BeginEvent(load_log, LoadLog::TYPE_PROXY_SERVICE); 273 274 // Strip away any reference fragments and the username/password, as they 275 // are not relevant to proxy resolution. 276 GURL url = SimplifyUrlForRequest(raw_url); 277 278 // Check if the request can be completed right away. This is the case when 279 // using a direct connection, or when the config is bad. 280 UpdateConfigIfOld(load_log); 281 int rv = TryToCompleteSynchronously(url, result); 282 if (rv != ERR_IO_PENDING) 283 return DidFinishResolvingProxy(result, rv, load_log); 284 285 scoped_refptr<PacRequest> req = 286 new PacRequest(this, url, result, callback, load_log); 287 288 bool resolver_is_ready = !IsInitializingProxyResolver(); 289 290 if (resolver_is_ready) { 291 // Start the resolve request. 292 rv = req->Start(); 293 if (rv != ERR_IO_PENDING) 294 return req->QueryDidComplete(rv); 295 } else { 296 LoadLog::BeginEvent(req->load_log(), 297 LoadLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC); 298 } 299 300 DCHECK_EQ(ERR_IO_PENDING, rv); 301 DCHECK(!ContainsPendingRequest(req)); 302 pending_requests_.push_back(req); 303 304 // Completion will be notifed through |callback|, unless the caller cancels 305 // the request using |pac_request|. 306 if (pac_request) 307 *pac_request = req.get(); 308 return rv; // ERR_IO_PENDING 309} 310 311int ProxyService::TryToCompleteSynchronously(const GURL& url, 312 ProxyInfo* result) { 313 result->config_id_ = config_.id(); 314 315 DCHECK(config_.id() != ProxyConfig::INVALID_ID); 316 317 if (should_use_proxy_resolver_ || IsInitializingProxyResolver()) { 318 // May need to go through ProxyResolver for this. 319 return ERR_IO_PENDING; 320 } 321 322 if (!config_.proxy_rules.empty()) { 323 ApplyProxyRules(url, config_.proxy_rules, result); 324 return OK; 325 } 326 327 // otherwise, we have no proxy config 328 result->UseDirect(); 329 return OK; 330} 331 332void ProxyService::ApplyProxyRules(const GURL& url, 333 const ProxyConfig::ProxyRules& proxy_rules, 334 ProxyInfo* result) { 335 DCHECK(!proxy_rules.empty()); 336 337 if (ShouldBypassProxyForURL(url)) { 338 result->UseDirect(); 339 return; 340 } 341 342 switch (proxy_rules.type) { 343 case ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY: 344 result->UseProxyServer(proxy_rules.single_proxy); 345 break; 346 case ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME: { 347 const ProxyServer* entry = proxy_rules.MapUrlSchemeToProxy(url.scheme()); 348 if (entry) { 349 result->UseProxyServer(*entry); 350 } else { 351 // We failed to find a matching proxy server for the current URL 352 // scheme. Default to direct. 353 result->UseDirect(); 354 } 355 break; 356 } 357 default: 358 result->UseDirect(); 359 NOTREACHED(); 360 break; 361 } 362} 363 364ProxyService::~ProxyService() { 365 // Unregister to receive network change notifications. 366 if (network_change_notifier_) 367 network_change_notifier_->RemoveObserver(this); 368 369 // Cancel any inprogress requests. 370 for (PendingRequests::iterator it = pending_requests_.begin(); 371 it != pending_requests_.end(); 372 ++it) { 373 (*it)->Cancel(); 374 } 375} 376 377void ProxyService::SuspendAllPendingRequests() { 378 for (PendingRequests::iterator it = pending_requests_.begin(); 379 it != pending_requests_.end(); 380 ++it) { 381 PacRequest* req = it->get(); 382 if (req->is_started()) { 383 req->CancelResolveJob(); 384 385 LoadLog::BeginEvent(req->load_log(), 386 LoadLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC); 387 } 388 } 389} 390 391void ProxyService::ResumeAllPendingRequests() { 392 DCHECK(!IsInitializingProxyResolver()); 393 394 // Make a copy in case |this| is deleted during the synchronous completion 395 // of one of the requests. If |this| is deleted then all of the PacRequest 396 // instances will be Cancel()-ed. 397 PendingRequests pending_copy = pending_requests_; 398 399 for (PendingRequests::iterator it = pending_copy.begin(); 400 it != pending_copy.end(); 401 ++it) { 402 PacRequest* req = it->get(); 403 if (!req->is_started() && !req->was_cancelled()) { 404 LoadLog::EndEvent(req->load_log(), 405 LoadLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC); 406 407 // Note that we re-check for synchronous completion, in case we are 408 // no longer using a ProxyResolver (can happen if we fell-back to manual). 409 req->StartAndCompleteCheckingForSynchronous(); 410 } 411 } 412} 413 414void ProxyService::OnInitProxyResolverComplete(int result) { 415 DCHECK(init_proxy_resolver_.get()); 416 DCHECK(config_.MayRequirePACResolver()); 417 DCHECK(!should_use_proxy_resolver_); 418 init_proxy_resolver_.reset(); 419 420 should_use_proxy_resolver_ = result == OK; 421 422 if (result != OK) { 423 LOG(INFO) << "Failed configuring with PAC script, falling-back to manual " 424 "proxy servers."; 425 } 426 427 // Resume any requests which we had to defer until the PAC script was 428 // downloaded. 429 ResumeAllPendingRequests(); 430} 431 432int ProxyService::ReconsiderProxyAfterError(const GURL& url, 433 ProxyInfo* result, 434 CompletionCallback* callback, 435 PacRequest** pac_request, 436 LoadLog* load_log) { 437 // Check to see if we have a new config since ResolveProxy was called. We 438 // want to re-run ResolveProxy in two cases: 1) we have a new config, or 2) a 439 // direct connection failed and we never tried the current config. 440 441 bool re_resolve = result->config_id_ != config_.id(); 442 if (!re_resolve) { 443 UpdateConfig(load_log); 444 if (result->config_id_ != config_.id()) { 445 // A new configuration! 446 re_resolve = true; 447 } 448 } 449 if (re_resolve) { 450 // If we have a new config or the config was never tried, we delete the 451 // list of bad proxies and we try again. 452 proxy_retry_info_.clear(); 453 return ResolveProxy(url, result, callback, pac_request, load_log); 454 } 455 456 // We don't have new proxy settings to try, try to fallback to the next proxy 457 // in the list. 458 bool did_fallback = result->Fallback(&proxy_retry_info_); 459 460 // Return synchronous failure if there is nothing left to fall-back to. 461 // TODO(eroman): This is a yucky API, clean it up. 462 return did_fallback ? OK : ERR_FAILED; 463} 464 465void ProxyService::CancelPacRequest(PacRequest* req) { 466 DCHECK(req); 467 req->Cancel(); 468 RemovePendingRequest(req); 469} 470 471bool ProxyService::ContainsPendingRequest(PacRequest* req) { 472 PendingRequests::iterator it = std::find( 473 pending_requests_.begin(), pending_requests_.end(), req); 474 return pending_requests_.end() != it; 475} 476 477void ProxyService::RemovePendingRequest(PacRequest* req) { 478 DCHECK(ContainsPendingRequest(req)); 479 PendingRequests::iterator it = std::find( 480 pending_requests_.begin(), pending_requests_.end(), req); 481 pending_requests_.erase(it); 482} 483 484int ProxyService::DidFinishResolvingProxy(ProxyInfo* result, 485 int result_code, 486 LoadLog* load_log) { 487 // Log the result of the proxy resolution. 488 if (result_code == OK) { 489 // When full logging is enabled, dump the proxy list. 490 if (LoadLog::IsUnbounded(load_log)) { 491 LoadLog::AddString( 492 load_log, 493 std::string("Resolved proxy list: ") + result->ToPacString()); 494 } 495 } else { 496 LoadLog::AddErrorCode(load_log, result_code); 497 } 498 499 // Clean up the results list. 500 if (result_code == OK) 501 result->DeprioritizeBadProxies(proxy_retry_info_); 502 503 LoadLog::EndEvent(load_log, LoadLog::TYPE_PROXY_SERVICE); 504 return result_code; 505} 506 507void ProxyService::SetProxyScriptFetcher( 508 ProxyScriptFetcher* proxy_script_fetcher) { 509 if (init_proxy_resolver_.get()) { 510 // We need to be careful to first cancel |init_proxy_resolver_|, since it 511 // holds a pointer to the old proxy script fetcher we are about to delete. 512 513 DCHECK(IsInitializingProxyResolver()); 514 init_proxy_resolver_.reset(); 515 proxy_script_fetcher_.reset(proxy_script_fetcher); 516 517 // Restart the initialization, using the new proxy script fetcher. 518 StartInitProxyResolver(); 519 } else { 520 proxy_script_fetcher_.reset(proxy_script_fetcher); 521 } 522} 523 524ProxyScriptFetcher* ProxyService::GetProxyScriptFetcher() const { 525 return proxy_script_fetcher_.get(); 526} 527 528void ProxyService::ResetConfigService( 529 ProxyConfigService* new_proxy_config_service) { 530 config_service_.reset(new_proxy_config_service); 531 UpdateConfig(NULL); 532} 533 534void ProxyService::PurgeMemory() { 535 if (resolver_.get()) 536 resolver_->PurgeMemory(); 537} 538 539void ProxyService::ForceReloadProxyConfig() { 540 // Mark the current configuration as being un-initialized, then force it to 541 // start updating (normally this would happen lazily during the next 542 // call to ResolveProxy()). 543 config_.set_id(ProxyConfig::INVALID_ID); 544 UpdateConfig(NULL); 545} 546 547// static 548ProxyConfigService* ProxyService::CreateSystemProxyConfigService( 549 MessageLoop* io_loop, MessageLoop* file_loop) { 550#if defined(OS_WIN) 551 return new ProxyConfigServiceWin(); 552#elif defined(OS_MACOSX) 553 return new ProxyConfigServiceMac(); 554#elif defined(OS_LINUX) 555 ProxyConfigServiceLinux* linux_config_service 556 = new ProxyConfigServiceLinux(); 557 558 // Assume we got called from the UI loop, which runs the default 559 // glib main loop, so the current thread is where we should be 560 // running gconf calls from. 561 MessageLoop* glib_default_loop = MessageLoopForUI::current(); 562 563 // The file loop should be a MessageLoopForIO on Linux. 564 DCHECK_EQ(MessageLoop::TYPE_IO, file_loop->type()); 565 566 // Synchronously fetch the current proxy config (since we are 567 // running on glib_default_loop). Additionally register for 568 // notifications (delivered in either |glib_default_loop| or 569 // |file_loop|) to keep us updated when the proxy config changes. 570 linux_config_service->SetupAndFetchInitialConfig(glib_default_loop, io_loop, 571 static_cast<MessageLoopForIO*>(file_loop)); 572 573 return linux_config_service; 574#else 575 LOG(WARNING) << "Failed to choose a system proxy settings fetcher " 576 "for this platform."; 577 return new ProxyConfigServiceNull(); 578#endif 579} 580 581// static 582ProxyResolver* ProxyService::CreateNonV8ProxyResolver() { 583#if defined(OS_WIN) 584 return new ProxyResolverWinHttp(); 585#elif defined(OS_MACOSX) 586 return new ProxyResolverMac(); 587#else 588 LOG(WARNING) << "PAC support disabled because there is no fallback " 589 "non-V8 implementation"; 590 return new ProxyResolverNull(); 591#endif 592} 593 594void ProxyService::UpdateConfig(LoadLog* load_log) { 595 bool is_first_update = !config_has_been_initialized(); 596 597 ProxyConfig latest; 598 599 // Fetch the proxy settings. 600 TimeTicks start_time = TimeTicks::Now(); 601 LoadLog::BeginEvent(load_log, 602 LoadLog::TYPE_PROXY_SERVICE_POLL_CONFIG_SERVICE_FOR_CHANGES); 603 int rv = config_service_->GetProxyConfig(&latest); 604 LoadLog::EndEvent(load_log, 605 LoadLog::TYPE_PROXY_SERVICE_POLL_CONFIG_SERVICE_FOR_CHANGES); 606 TimeTicks end_time = TimeTicks::Now(); 607 608 // Record how long the call to config_service_->GetConfig() above took. 609 // On some setups of Windows, we have reports that querying the system 610 // proxy settings can take multiple seconds (http://crbug.com/12189). 611 UMA_HISTOGRAM_CUSTOM_TIMES("Net.ProxyPollConfigurationTime", 612 end_time - start_time, 613 TimeDelta::FromMilliseconds(1), 614 TimeDelta::FromSeconds(30), 615 50); 616 617 if (rv != OK) { 618 if (is_first_update) { 619 // Default to direct-connection if the first fetch fails. 620 LOG(INFO) << "Failed initial proxy configuration fetch."; 621 SetConfig(ProxyConfig()); 622 } 623 return; 624 } 625 config_last_update_time_ = TimeTicks::Now(); 626 627 if (!is_first_update && latest.Equals(config_)) 628 return; 629 630 SetConfig(latest); 631} 632 633void ProxyService::SetConfig(const ProxyConfig& config) { 634 config_ = config; 635 636 // Increment the ID to reflect that the config has changed. 637 config_.set_id(next_config_id_++); 638 639 // Reset state associated with latest config. 640 proxy_retry_info_.clear(); 641 642 // Cancel any PAC fetching / ProxyResolver::SetPacScript() which was 643 // in progress for the previous configuration. 644 init_proxy_resolver_.reset(); 645 should_use_proxy_resolver_ = false; 646 647 // Start downloading + testing the PAC scripts for this new configuration. 648 if (config_.MayRequirePACResolver()) { 649 // Since InitProxyResolver will be playing around with the proxy resolver 650 // as it tests the parsing of various PAC scripts, make sure there is 651 // nothing in-flight in |resolver_|. These paused requests are resumed by 652 // OnInitProxyResolverComplete(). 653 SuspendAllPendingRequests(); 654 655 // Calls OnInitProxyResolverComplete() on completion. 656 StartInitProxyResolver(); 657 } 658} 659 660void ProxyService::StartInitProxyResolver() { 661 DCHECK(!init_proxy_resolver_.get()); 662 663 init_proxy_resolver_.reset( 664 new InitProxyResolver(resolver_.get(), proxy_script_fetcher_.get())); 665 666 init_proxy_resolver_log_ = new LoadLog(kMaxNumLoadLogEntries); 667 668 int rv = init_proxy_resolver_->Init( 669 config_, &init_proxy_resolver_callback_, init_proxy_resolver_log_); 670 671 if (rv != ERR_IO_PENDING) 672 OnInitProxyResolverComplete(rv); 673} 674 675void ProxyService::UpdateConfigIfOld(LoadLog* load_log) { 676 // The overhead of calling ProxyConfigService::GetProxyConfig is very low. 677 const TimeDelta kProxyConfigMaxAge = TimeDelta::FromSeconds(5); 678 679 // Periodically check for a new config. 680 if (!config_has_been_initialized() || 681 (TimeTicks::Now() - config_last_update_time_) > kProxyConfigMaxAge) 682 UpdateConfig(load_log); 683} 684 685bool ProxyService::ShouldBypassProxyForURL(const GURL& url) { 686 std::string url_domain = url.scheme(); 687 if (!url_domain.empty()) 688 url_domain += "://"; 689 690 url_domain += url.host(); 691 // This isn't superfluous; GURL case canonicalization doesn't hit the embedded 692 // percent-encoded characters. 693 StringToLowerASCII(&url_domain); 694 695 // TODO(eroman): use GetHostAndPort(). 696 std::string url_domain_and_port = url_domain + ":" 697 + IntToString(url.EffectiveIntPort()); 698 699 if (config_.proxy_bypass_local_names && IsLocalName(url)) 700 return true; 701 702 for(std::vector<std::string>::const_iterator i = config_.proxy_bypass.begin(); 703 i != config_.proxy_bypass.end(); ++i) { 704 std::string bypass_url_domain = *i; 705 706 // The proxy server bypass list can contain entities with http/https 707 // If no scheme is specified then it indicates that all schemes are 708 // allowed for the current entry. For matching this we just use 709 // the protocol scheme of the url passed in. 710 size_t scheme_colon = bypass_url_domain.find("://"); 711 if (scheme_colon == std::string::npos) { 712 std::string bypass_url_domain_with_scheme = url.scheme(); 713 scheme_colon = bypass_url_domain_with_scheme.length(); 714 bypass_url_domain_with_scheme += "://"; 715 bypass_url_domain_with_scheme += bypass_url_domain; 716 717 bypass_url_domain = bypass_url_domain_with_scheme; 718 } 719 std::string* url_compare_reference = &url_domain; 720 size_t port_colon = bypass_url_domain.rfind(":"); 721 if (port_colon > scheme_colon) { 722 // If our match pattern includes a colon followed by a digit, 723 // and either it's preceded by ']' (IPv6 with port) 724 // or has no other colon (IPv4), 725 // then match against <domain>:<port>. 726 // TODO(sdoyon): straighten this out, in particular the IPv6 brackets, 727 // and do the parsing in ProxyConfig when we do the CIDR matching 728 // mentioned below. 729 std::string::const_iterator domain_begin = 730 bypass_url_domain.begin() + scheme_colon + 3; // after :// 731 std::string::const_iterator port_iter = 732 bypass_url_domain.begin() + port_colon; 733 std::string::const_iterator end = bypass_url_domain.end(); 734 if ((port_iter + 1) < end && IsAsciiDigit(*(port_iter + 1)) && 735 (*(port_iter - 1) == ']' || 736 std::find(domain_begin, port_iter, ':') == port_iter)) 737 url_compare_reference = &url_domain_and_port; 738 } 739 740 StringToLowerASCII(&bypass_url_domain); 741 742 if (MatchPatternASCII(*url_compare_reference, bypass_url_domain)) 743 return true; 744 745 // Some systems (the Mac, for example) allow CIDR-style specification of 746 // proxy bypass for IP-specified hosts (e.g. "10.0.0.0/8"; see 747 // http://www.tcd.ie/iss/internet/osx_proxy.php for a real-world example). 748 // That's kinda cool so we'll provide that for everyone. 749 // TODO(avi): implement here. See: http://crbug.com/9835. 750 // IP addresses ought to be canonicalized for comparison (whether 751 // with CIDR, port, or IP address alone). 752 } 753 754 return false; 755} 756 757// This matches IE's interpretation of the 758// "Bypass proxy server for local addresses" settings checkbox. Fully 759// qualified domain names or IP addresses are considered non-local, 760// regardless of what they map to. 761// 762// static 763bool ProxyService::IsLocalName(const GURL& url) { 764 const std::string& host = url.host(); 765 if (host == "127.0.0.1" || host == "[::1]") 766 return true; 767 return host.find('.') == std::string::npos; 768} 769 770void ProxyService::OnIPAddressChanged() { 771 DCHECK(network_change_notifier_); 772 773 // Mark the current configuration as being un-initialized. 774 // 775 // This will force us to re-fetch the configuration (and re-run all of 776 // the initialization steps) on the next ResolveProxy() request, as part 777 // of UpdateConfigIfOld(). 778 config_.set_id(ProxyConfig::INVALID_ID); 779} 780 781SyncProxyServiceHelper::SyncProxyServiceHelper(MessageLoop* io_message_loop, 782 ProxyService* proxy_service) 783 : io_message_loop_(io_message_loop), 784 proxy_service_(proxy_service), 785 event_(false, false), 786 ALLOW_THIS_IN_INITIALIZER_LIST(callback_( 787 this, &SyncProxyServiceHelper::OnCompletion)) { 788 DCHECK(io_message_loop_ != MessageLoop::current()); 789} 790 791int SyncProxyServiceHelper::ResolveProxy(const GURL& url, 792 ProxyInfo* proxy_info, 793 LoadLog* load_log) { 794 DCHECK(io_message_loop_ != MessageLoop::current()); 795 796 io_message_loop_->PostTask(FROM_HERE, NewRunnableMethod( 797 this, &SyncProxyServiceHelper::StartAsyncResolve, url, load_log)); 798 799 event_.Wait(); 800 801 if (result_ == net::OK) { 802 *proxy_info = proxy_info_; 803 } 804 return result_; 805} 806 807int SyncProxyServiceHelper::ReconsiderProxyAfterError( 808 const GURL& url, ProxyInfo* proxy_info, LoadLog* load_log) { 809 DCHECK(io_message_loop_ != MessageLoop::current()); 810 811 io_message_loop_->PostTask(FROM_HERE, NewRunnableMethod( 812 this, &SyncProxyServiceHelper::StartAsyncReconsider, url, load_log)); 813 814 event_.Wait(); 815 816 if (result_ == net::OK) { 817 *proxy_info = proxy_info_; 818 } 819 return result_; 820} 821 822void SyncProxyServiceHelper::StartAsyncResolve(const GURL& url, 823 LoadLog* load_log) { 824 result_ = proxy_service_->ResolveProxy( 825 url, &proxy_info_, &callback_, NULL, load_log); 826 if (result_ != net::ERR_IO_PENDING) { 827 OnCompletion(result_); 828 } 829} 830 831void SyncProxyServiceHelper::StartAsyncReconsider(const GURL& url, 832 LoadLog* load_log) { 833 result_ = proxy_service_->ReconsiderProxyAfterError( 834 url, &proxy_info_, &callback_, NULL, load_log); 835 if (result_ != net::ERR_IO_PENDING) { 836 OnCompletion(result_); 837 } 838} 839 840void SyncProxyServiceHelper::OnCompletion(int rv) { 841 result_ = rv; 842 event_.Signal(); 843} 844 845} // namespace net 846