Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(142)

Side by Side Diff: net/proxy/single_threaded_proxy_resolver.cc

Issue 149525: Remove the concept of threading from ProxyService, and move it into the Proxy... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Add missing header for mac Created 11 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
OLDNEW
« no previous file with comments | « net/proxy/single_threaded_proxy_resolver.h ('k') | net/proxy/single_threaded_proxy_resolver_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698