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 |