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

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

Issue 992733002: Remove //net (except for Android test stuff) and sdch (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Created 5 years, 9 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
« no previous file with comments | « net/proxy/proxy_script_decider.h ('k') | net/proxy/proxy_script_decider_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2012 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_script_decider.h"
6
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/compiler_specific.h"
10 #include "base/format_macros.h"
11 #include "base/logging.h"
12 #include "base/metrics/histogram.h"
13 #include "base/profiler/scoped_tracker.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/values.h"
17 #include "net/base/net_errors.h"
18 #include "net/proxy/dhcp_proxy_script_fetcher.h"
19 #include "net/proxy/dhcp_proxy_script_fetcher_factory.h"
20 #include "net/proxy/proxy_script_fetcher.h"
21 #include "net/url_request/url_request_context.h"
22
23 namespace net {
24
25 namespace {
26
27 bool LooksLikePacScript(const base::string16& script) {
28 // Note: this is only an approximation! It may not always work correctly,
29 // however it is very likely that legitimate scripts have this exact string,
30 // since they must minimally define a function of this name. Conversely, a
31 // file not containing the string is not likely to be a PAC script.
32 //
33 // An exact test would have to load the script in a javascript evaluator.
34 return script.find(base::ASCIIToUTF16("FindProxyForURL")) !=
35 base::string16::npos;
36 }
37
38 }
39
40 // This is the hard-coded location used by the DNS portion of web proxy
41 // auto-discovery.
42 //
43 // Note that we not use DNS devolution to find the WPAD host, since that could
44 // be dangerous should our top level domain registry become out of date.
45 //
46 // Instead we directly resolve "wpad", and let the operating system apply the
47 // DNS suffix search paths. This is the same approach taken by Firefox, and
48 // compatibility hasn't been an issue.
49 //
50 // For more details, also check out this comment:
51 // http://code.google.com/p/chromium/issues/detail?id=18575#c20
52 namespace {
53 const char kWpadUrl[] = "http://wpad/wpad.dat";
54 const int kQuickCheckDelayMs = 1000;
55 };
56
57 base::Value* ProxyScriptDecider::PacSource::NetLogCallback(
58 const GURL* effective_pac_url,
59 NetLog::LogLevel /* log_level */) const {
60 base::DictionaryValue* dict = new base::DictionaryValue();
61 std::string source;
62 switch (type) {
63 case PacSource::WPAD_DHCP:
64 source = "WPAD DHCP";
65 break;
66 case PacSource::WPAD_DNS:
67 source = "WPAD DNS: ";
68 source += effective_pac_url->possibly_invalid_spec();
69 break;
70 case PacSource::CUSTOM:
71 source = "Custom PAC URL: ";
72 source += effective_pac_url->possibly_invalid_spec();
73 break;
74 }
75 dict->SetString("source", source);
76 return dict;
77 }
78
79 ProxyScriptDecider::ProxyScriptDecider(
80 ProxyScriptFetcher* proxy_script_fetcher,
81 DhcpProxyScriptFetcher* dhcp_proxy_script_fetcher,
82 NetLog* net_log)
83 : proxy_script_fetcher_(proxy_script_fetcher),
84 dhcp_proxy_script_fetcher_(dhcp_proxy_script_fetcher),
85 current_pac_source_index_(0u),
86 pac_mandatory_(false),
87 next_state_(STATE_NONE),
88 net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_PROXY_SCRIPT_DECIDER)),
89 fetch_pac_bytes_(false),
90 quick_check_enabled_(true) {
91 if (proxy_script_fetcher &&
92 proxy_script_fetcher->GetRequestContext() &&
93 proxy_script_fetcher->GetRequestContext()->host_resolver()) {
94 host_resolver_.reset(new SingleRequestHostResolver(
95 proxy_script_fetcher->GetRequestContext()->host_resolver()));
96 }
97 }
98
99 ProxyScriptDecider::~ProxyScriptDecider() {
100 if (next_state_ != STATE_NONE)
101 Cancel();
102 }
103
104 int ProxyScriptDecider::Start(
105 const ProxyConfig& config, const base::TimeDelta wait_delay,
106 bool fetch_pac_bytes, const CompletionCallback& callback) {
107 DCHECK_EQ(STATE_NONE, next_state_);
108 DCHECK(!callback.is_null());
109 DCHECK(config.HasAutomaticSettings());
110
111 net_log_.BeginEvent(NetLog::TYPE_PROXY_SCRIPT_DECIDER);
112
113 fetch_pac_bytes_ = fetch_pac_bytes;
114
115 // Save the |wait_delay| as a non-negative value.
116 wait_delay_ = wait_delay;
117 if (wait_delay_ < base::TimeDelta())
118 wait_delay_ = base::TimeDelta();
119
120 pac_mandatory_ = config.pac_mandatory();
121 have_custom_pac_url_ = config.has_pac_url();
122
123 pac_sources_ = BuildPacSourcesFallbackList(config);
124 DCHECK(!pac_sources_.empty());
125
126 next_state_ = STATE_WAIT;
127
128 int rv = DoLoop(OK);
129 if (rv == ERR_IO_PENDING)
130 callback_ = callback;
131 else
132 DidComplete();
133
134 return rv;
135 }
136
137 const ProxyConfig& ProxyScriptDecider::effective_config() const {
138 DCHECK_EQ(STATE_NONE, next_state_);
139 return effective_config_;
140 }
141
142 // TODO(eroman): Return a const-pointer.
143 ProxyResolverScriptData* ProxyScriptDecider::script_data() const {
144 DCHECK_EQ(STATE_NONE, next_state_);
145 return script_data_.get();
146 }
147
148 // Initialize the fallback rules.
149 // (1) WPAD (DHCP).
150 // (2) WPAD (DNS).
151 // (3) Custom PAC URL.
152 ProxyScriptDecider::PacSourceList ProxyScriptDecider::
153 BuildPacSourcesFallbackList(
154 const ProxyConfig& config) const {
155 PacSourceList pac_sources;
156 if (config.auto_detect()) {
157 pac_sources.push_back(PacSource(PacSource::WPAD_DHCP, GURL(kWpadUrl)));
158 pac_sources.push_back(PacSource(PacSource::WPAD_DNS, GURL(kWpadUrl)));
159 }
160 if (config.has_pac_url())
161 pac_sources.push_back(PacSource(PacSource::CUSTOM, config.pac_url()));
162 return pac_sources;
163 }
164
165 void ProxyScriptDecider::OnIOCompletion(int result) {
166 // TODO(vadimt): Remove ScopedTracker below once crbug.com/436634 is fixed.
167 tracked_objects::ScopedTracker tracking_profile(
168 FROM_HERE_WITH_EXPLICIT_FUNCTION(
169 "436634 ProxyScriptDecider::OnIOCompletion"));
170
171 DCHECK_NE(STATE_NONE, next_state_);
172 int rv = DoLoop(result);
173 if (rv != ERR_IO_PENDING) {
174 DidComplete();
175 DoCallback(rv);
176 }
177 }
178
179 int ProxyScriptDecider::DoLoop(int result) {
180 DCHECK_NE(next_state_, STATE_NONE);
181 int rv = result;
182 do {
183 State state = next_state_;
184 next_state_ = STATE_NONE;
185 switch (state) {
186 case STATE_WAIT:
187 DCHECK_EQ(OK, rv);
188 rv = DoWait();
189 break;
190 case STATE_WAIT_COMPLETE:
191 rv = DoWaitComplete(rv);
192 break;
193 case STATE_QUICK_CHECK:
194 DCHECK_EQ(OK, rv);
195 rv = DoQuickCheck();
196 break;
197 case STATE_QUICK_CHECK_COMPLETE:
198 rv = DoQuickCheckComplete(rv);
199 break;
200 case STATE_FETCH_PAC_SCRIPT:
201 DCHECK_EQ(OK, rv);
202 rv = DoFetchPacScript();
203 break;
204 case STATE_FETCH_PAC_SCRIPT_COMPLETE:
205 rv = DoFetchPacScriptComplete(rv);
206 break;
207 case STATE_VERIFY_PAC_SCRIPT:
208 DCHECK_EQ(OK, rv);
209 rv = DoVerifyPacScript();
210 break;
211 case STATE_VERIFY_PAC_SCRIPT_COMPLETE:
212 rv = DoVerifyPacScriptComplete(rv);
213 break;
214 default:
215 NOTREACHED() << "bad state";
216 rv = ERR_UNEXPECTED;
217 break;
218 }
219 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
220 return rv;
221 }
222
223 void ProxyScriptDecider::DoCallback(int result) {
224 DCHECK_NE(ERR_IO_PENDING, result);
225 DCHECK(!callback_.is_null());
226 callback_.Run(result);
227 }
228
229 int ProxyScriptDecider::DoWait() {
230 next_state_ = STATE_WAIT_COMPLETE;
231
232 // If no waiting is required, continue on to the next state.
233 if (wait_delay_.ToInternalValue() == 0)
234 return OK;
235
236 // Otherwise wait the specified amount of time.
237 wait_timer_.Start(FROM_HERE, wait_delay_, this,
238 &ProxyScriptDecider::OnWaitTimerFired);
239 net_log_.BeginEvent(NetLog::TYPE_PROXY_SCRIPT_DECIDER_WAIT);
240 return ERR_IO_PENDING;
241 }
242
243 int ProxyScriptDecider::DoWaitComplete(int result) {
244 DCHECK_EQ(OK, result);
245 if (wait_delay_.ToInternalValue() != 0) {
246 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_PROXY_SCRIPT_DECIDER_WAIT,
247 result);
248 }
249 if (quick_check_enabled_ && current_pac_source().type == PacSource::WPAD_DNS)
250 next_state_ = STATE_QUICK_CHECK;
251 else
252 next_state_ = GetStartState();
253 return OK;
254 }
255
256 int ProxyScriptDecider::DoQuickCheck() {
257 DCHECK(quick_check_enabled_);
258 if (host_resolver_.get() == NULL) {
259 // If we have no resolver, skip QuickCheck altogether.
260 next_state_ = GetStartState();
261 return OK;
262 }
263
264 quick_check_start_time_ = base::Time::Now();
265 std::string host = current_pac_source().url.host();
266 HostResolver::RequestInfo reqinfo(HostPortPair(host, 80));
267 reqinfo.set_host_resolver_flags(HOST_RESOLVER_SYSTEM_ONLY);
268 CompletionCallback callback = base::Bind(
269 &ProxyScriptDecider::OnIOCompletion,
270 base::Unretained(this));
271
272 next_state_ = STATE_QUICK_CHECK_COMPLETE;
273 quick_check_timer_.Start(FROM_HERE,
274 base::TimeDelta::FromMilliseconds(
275 kQuickCheckDelayMs),
276 base::Bind(callback, ERR_NAME_NOT_RESOLVED));
277
278 // We use HIGHEST here because proxy decision blocks doing any other requests.
279 return host_resolver_->Resolve(reqinfo, HIGHEST, &wpad_addresses_,
280 callback, net_log_);
281 }
282
283 int ProxyScriptDecider::DoQuickCheckComplete(int result) {
284 DCHECK(quick_check_enabled_);
285 base::TimeDelta delta = base::Time::Now() - quick_check_start_time_;
286 if (result == OK)
287 UMA_HISTOGRAM_TIMES("Net.WpadQuickCheckSuccess", delta);
288 else
289 UMA_HISTOGRAM_TIMES("Net.WpadQuickCheckFailure", delta);
290 host_resolver_->Cancel();
291 quick_check_timer_.Stop();
292 if (result != OK)
293 return TryToFallbackPacSource(result);
294 next_state_ = GetStartState();
295 return result;
296 }
297
298 int ProxyScriptDecider::DoFetchPacScript() {
299 DCHECK(fetch_pac_bytes_);
300
301 next_state_ = STATE_FETCH_PAC_SCRIPT_COMPLETE;
302
303 const PacSource& pac_source = current_pac_source();
304
305 GURL effective_pac_url;
306 DetermineURL(pac_source, &effective_pac_url);
307
308 net_log_.BeginEvent(NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT,
309 base::Bind(&PacSource::NetLogCallback,
310 base::Unretained(&pac_source),
311 &effective_pac_url));
312
313 if (pac_source.type == PacSource::WPAD_DHCP) {
314 if (!dhcp_proxy_script_fetcher_) {
315 net_log_.AddEvent(NetLog::TYPE_PROXY_SCRIPT_DECIDER_HAS_NO_FETCHER);
316 return ERR_UNEXPECTED;
317 }
318
319 return dhcp_proxy_script_fetcher_->Fetch(
320 &pac_script_, base::Bind(&ProxyScriptDecider::OnIOCompletion,
321 base::Unretained(this)));
322 }
323
324 if (!proxy_script_fetcher_) {
325 net_log_.AddEvent(NetLog::TYPE_PROXY_SCRIPT_DECIDER_HAS_NO_FETCHER);
326 return ERR_UNEXPECTED;
327 }
328
329 return proxy_script_fetcher_->Fetch(
330 effective_pac_url, &pac_script_,
331 base::Bind(&ProxyScriptDecider::OnIOCompletion, base::Unretained(this)));
332 }
333
334 int ProxyScriptDecider::DoFetchPacScriptComplete(int result) {
335 DCHECK(fetch_pac_bytes_);
336
337 net_log_.EndEventWithNetErrorCode(
338 NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT, result);
339 if (result != OK)
340 return TryToFallbackPacSource(result);
341
342 next_state_ = STATE_VERIFY_PAC_SCRIPT;
343 return result;
344 }
345
346 int ProxyScriptDecider::DoVerifyPacScript() {
347 next_state_ = STATE_VERIFY_PAC_SCRIPT_COMPLETE;
348
349 // This is just a heuristic. Ideally we would try to parse the script.
350 if (fetch_pac_bytes_ && !LooksLikePacScript(pac_script_))
351 return ERR_PAC_SCRIPT_FAILED;
352
353 return OK;
354 }
355
356 int ProxyScriptDecider::DoVerifyPacScriptComplete(int result) {
357 if (result != OK)
358 return TryToFallbackPacSource(result);
359
360 const PacSource& pac_source = current_pac_source();
361
362 // Extract the current script data.
363 if (fetch_pac_bytes_) {
364 script_data_ = ProxyResolverScriptData::FromUTF16(pac_script_);
365 } else {
366 script_data_ = pac_source.type == PacSource::CUSTOM ?
367 ProxyResolverScriptData::FromURL(pac_source.url) :
368 ProxyResolverScriptData::ForAutoDetect();
369 }
370
371 // Let the caller know which automatic setting we ended up initializing the
372 // resolver for (there may have been multiple fallbacks to choose from.)
373 if (current_pac_source().type == PacSource::CUSTOM) {
374 effective_config_ =
375 ProxyConfig::CreateFromCustomPacURL(current_pac_source().url);
376 effective_config_.set_pac_mandatory(pac_mandatory_);
377 } else {
378 if (fetch_pac_bytes_) {
379 GURL auto_detected_url;
380
381 switch (current_pac_source().type) {
382 case PacSource::WPAD_DHCP:
383 auto_detected_url = dhcp_proxy_script_fetcher_->GetPacURL();
384 break;
385
386 case PacSource::WPAD_DNS:
387 auto_detected_url = GURL(kWpadUrl);
388 break;
389
390 default:
391 NOTREACHED();
392 }
393
394 effective_config_ =
395 ProxyConfig::CreateFromCustomPacURL(auto_detected_url);
396 } else {
397 // The resolver does its own resolution so we cannot know the
398 // URL. Just do the best we can and state that the configuration
399 // is to auto-detect proxy settings.
400 effective_config_ = ProxyConfig::CreateAutoDetect();
401 }
402 }
403
404 return OK;
405 }
406
407 int ProxyScriptDecider::TryToFallbackPacSource(int error) {
408 DCHECK_LT(error, 0);
409
410 if (current_pac_source_index_ + 1 >= pac_sources_.size()) {
411 // Nothing left to fall back to.
412 return error;
413 }
414
415 // Advance to next URL in our list.
416 ++current_pac_source_index_;
417
418 net_log_.AddEvent(
419 NetLog::TYPE_PROXY_SCRIPT_DECIDER_FALLING_BACK_TO_NEXT_PAC_SOURCE);
420 if (quick_check_enabled_ && current_pac_source().type == PacSource::WPAD_DNS)
421 next_state_ = STATE_QUICK_CHECK;
422 else
423 next_state_ = GetStartState();
424
425 return OK;
426 }
427
428 ProxyScriptDecider::State ProxyScriptDecider::GetStartState() const {
429 return fetch_pac_bytes_ ? STATE_FETCH_PAC_SCRIPT : STATE_VERIFY_PAC_SCRIPT;
430 }
431
432 void ProxyScriptDecider::DetermineURL(const PacSource& pac_source,
433 GURL* effective_pac_url) {
434 DCHECK(effective_pac_url);
435
436 switch (pac_source.type) {
437 case PacSource::WPAD_DHCP:
438 break;
439 case PacSource::WPAD_DNS:
440 *effective_pac_url = GURL(kWpadUrl);
441 break;
442 case PacSource::CUSTOM:
443 *effective_pac_url = pac_source.url;
444 break;
445 }
446 }
447
448 const ProxyScriptDecider::PacSource&
449 ProxyScriptDecider::current_pac_source() const {
450 DCHECK_LT(current_pac_source_index_, pac_sources_.size());
451 return pac_sources_[current_pac_source_index_];
452 }
453
454 void ProxyScriptDecider::OnWaitTimerFired() {
455 OnIOCompletion(OK);
456 }
457
458 void ProxyScriptDecider::DidComplete() {
459 net_log_.EndEvent(NetLog::TYPE_PROXY_SCRIPT_DECIDER);
460 }
461
462 void ProxyScriptDecider::Cancel() {
463 DCHECK_NE(STATE_NONE, next_state_);
464
465 net_log_.AddEvent(NetLog::TYPE_CANCELLED);
466
467 switch (next_state_) {
468 case STATE_WAIT_COMPLETE:
469 wait_timer_.Stop();
470 break;
471 case STATE_FETCH_PAC_SCRIPT_COMPLETE:
472 proxy_script_fetcher_->Cancel();
473 break;
474 default:
475 NOTREACHED();
476 break;
477 }
478
479 // This is safe to call in any state.
480 if (dhcp_proxy_script_fetcher_)
481 dhcp_proxy_script_fetcher_->Cancel();
482
483 DidComplete();
484 }
485
486 } // namespace net
OLDNEW
« no previous file with comments | « net/proxy/proxy_script_decider.h ('k') | net/proxy/proxy_script_decider_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698