Enabling tests which should be fixed by r173829.
[chromium-blink-merge.git] / net / proxy / proxy_script_decider.cc
blob21e3ae04bc27ff4595301335d243632ef252454a
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.
5 #include "net/proxy/proxy_script_decider.h"
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/string_util.h"
13 #include "base/utf_string_conversions.h"
14 #include "base/values.h"
15 #include "net/base/net_errors.h"
16 #include "net/proxy/dhcp_proxy_script_fetcher.h"
17 #include "net/proxy/dhcp_proxy_script_fetcher_factory.h"
18 #include "net/proxy/proxy_script_fetcher.h"
20 namespace net {
22 namespace {
24 bool LooksLikePacScript(const string16& script) {
25 // Note: this is only an approximation! It may not always work correctly,
26 // however it is very likely that legitimate scripts have this exact string,
27 // since they must minimally define a function of this name. Conversely, a
28 // file not containing the string is not likely to be a PAC script.
30 // An exact test would have to load the script in a javascript evaluator.
31 return script.find(ASCIIToUTF16("FindProxyForURL")) != string16::npos;
36 // This is the hard-coded location used by the DNS portion of web proxy
37 // auto-discovery.
39 // Note that we not use DNS devolution to find the WPAD host, since that could
40 // be dangerous should our top level domain registry become out of date.
42 // Instead we directly resolve "wpad", and let the operating system apply the
43 // DNS suffix search paths. This is the same approach taken by Firefox, and
44 // compatibility hasn't been an issue.
46 // For more details, also check out this comment:
47 // http://code.google.com/p/chromium/issues/detail?id=18575#c20
48 static const char kWpadUrl[] = "http://wpad/wpad.dat";
50 Value* ProxyScriptDecider::PacSource::NetLogCallback(
51 const GURL* effective_pac_url,
52 NetLog::LogLevel /* log_level */) const {
53 DictionaryValue* dict = new DictionaryValue();
54 std::string source;
55 switch (type) {
56 case PacSource::WPAD_DHCP:
57 source = "WPAD DHCP";
58 break;
59 case PacSource::WPAD_DNS:
60 source = "WPAD DNS: ";
61 source += effective_pac_url->possibly_invalid_spec();
62 break;
63 case PacSource::CUSTOM:
64 source = "Custom PAC URL: ";
65 source += effective_pac_url->possibly_invalid_spec();
66 break;
68 dict->SetString("source", source);
69 return dict;
72 ProxyScriptDecider::ProxyScriptDecider(
73 ProxyScriptFetcher* proxy_script_fetcher,
74 DhcpProxyScriptFetcher* dhcp_proxy_script_fetcher,
75 NetLog* net_log)
76 : resolver_(NULL),
77 proxy_script_fetcher_(proxy_script_fetcher),
78 dhcp_proxy_script_fetcher_(dhcp_proxy_script_fetcher),
79 current_pac_source_index_(0u),
80 pac_mandatory_(false),
81 next_state_(STATE_NONE),
82 net_log_(BoundNetLog::Make(
83 net_log, NetLog::SOURCE_PROXY_SCRIPT_DECIDER)),
84 fetch_pac_bytes_(false) {
87 ProxyScriptDecider::~ProxyScriptDecider() {
88 if (next_state_ != STATE_NONE)
89 Cancel();
92 int ProxyScriptDecider::Start(
93 const ProxyConfig& config, const base::TimeDelta wait_delay,
94 bool fetch_pac_bytes, const CompletionCallback& callback) {
95 DCHECK_EQ(STATE_NONE, next_state_);
96 DCHECK(!callback.is_null());
97 DCHECK(config.HasAutomaticSettings());
99 net_log_.BeginEvent(NetLog::TYPE_PROXY_SCRIPT_DECIDER);
101 fetch_pac_bytes_ = fetch_pac_bytes;
103 // Save the |wait_delay| as a non-negative value.
104 wait_delay_ = wait_delay;
105 if (wait_delay_ < base::TimeDelta())
106 wait_delay_ = base::TimeDelta();
108 pac_mandatory_ = config.pac_mandatory();
110 pac_sources_ = BuildPacSourcesFallbackList(config);
111 DCHECK(!pac_sources_.empty());
113 next_state_ = STATE_WAIT;
115 int rv = DoLoop(OK);
116 if (rv == ERR_IO_PENDING)
117 callback_ = callback;
118 else
119 DidComplete();
121 return rv;
124 const ProxyConfig& ProxyScriptDecider::effective_config() const {
125 DCHECK_EQ(STATE_NONE, next_state_);
126 return effective_config_;
129 // TODO(eroman): Return a const-pointer.
130 ProxyResolverScriptData* ProxyScriptDecider::script_data() const {
131 DCHECK_EQ(STATE_NONE, next_state_);
132 return script_data_.get();
135 // Initialize the fallback rules.
136 // (1) WPAD (DHCP).
137 // (2) WPAD (DNS).
138 // (3) Custom PAC URL.
139 ProxyScriptDecider::PacSourceList ProxyScriptDecider::
140 BuildPacSourcesFallbackList(
141 const ProxyConfig& config) const {
142 PacSourceList pac_sources;
143 if (config.auto_detect()) {
144 pac_sources.push_back(PacSource(PacSource::WPAD_DHCP, GURL()));
145 pac_sources.push_back(PacSource(PacSource::WPAD_DNS, GURL()));
147 if (config.has_pac_url())
148 pac_sources.push_back(PacSource(PacSource::CUSTOM, config.pac_url()));
149 return pac_sources;
152 void ProxyScriptDecider::OnIOCompletion(int result) {
153 DCHECK_NE(STATE_NONE, next_state_);
154 int rv = DoLoop(result);
155 if (rv != ERR_IO_PENDING) {
156 DidComplete();
157 DoCallback(rv);
161 int ProxyScriptDecider::DoLoop(int result) {
162 DCHECK_NE(next_state_, STATE_NONE);
163 int rv = result;
164 do {
165 State state = next_state_;
166 next_state_ = STATE_NONE;
167 switch (state) {
168 case STATE_WAIT:
169 DCHECK_EQ(OK, rv);
170 rv = DoWait();
171 break;
172 case STATE_WAIT_COMPLETE:
173 rv = DoWaitComplete(rv);
174 break;
175 case STATE_FETCH_PAC_SCRIPT:
176 DCHECK_EQ(OK, rv);
177 rv = DoFetchPacScript();
178 break;
179 case STATE_FETCH_PAC_SCRIPT_COMPLETE:
180 rv = DoFetchPacScriptComplete(rv);
181 break;
182 case STATE_VERIFY_PAC_SCRIPT:
183 DCHECK_EQ(OK, rv);
184 rv = DoVerifyPacScript();
185 break;
186 case STATE_VERIFY_PAC_SCRIPT_COMPLETE:
187 rv = DoVerifyPacScriptComplete(rv);
188 break;
189 default:
190 NOTREACHED() << "bad state";
191 rv = ERR_UNEXPECTED;
192 break;
194 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
195 return rv;
198 void ProxyScriptDecider::DoCallback(int result) {
199 DCHECK_NE(ERR_IO_PENDING, result);
200 DCHECK(!callback_.is_null());
201 callback_.Run(result);
204 int ProxyScriptDecider::DoWait() {
205 next_state_ = STATE_WAIT_COMPLETE;
207 // If no waiting is required, continue on to the next state.
208 if (wait_delay_.ToInternalValue() == 0)
209 return OK;
211 // Otherwise wait the specified amount of time.
212 wait_timer_.Start(FROM_HERE, wait_delay_, this,
213 &ProxyScriptDecider::OnWaitTimerFired);
214 net_log_.BeginEvent(NetLog::TYPE_PROXY_SCRIPT_DECIDER_WAIT);
215 return ERR_IO_PENDING;
218 int ProxyScriptDecider::DoWaitComplete(int result) {
219 DCHECK_EQ(OK, result);
220 if (wait_delay_.ToInternalValue() != 0) {
221 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_PROXY_SCRIPT_DECIDER_WAIT,
222 result);
224 next_state_ = GetStartState();
225 return OK;
228 int ProxyScriptDecider::DoFetchPacScript() {
229 DCHECK(fetch_pac_bytes_);
231 next_state_ = STATE_FETCH_PAC_SCRIPT_COMPLETE;
233 const PacSource& pac_source = current_pac_source();
235 GURL effective_pac_url;
236 DetermineURL(pac_source, &effective_pac_url);
238 net_log_.BeginEvent(NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT,
239 base::Bind(&PacSource::NetLogCallback,
240 base::Unretained(&pac_source),
241 &effective_pac_url));
243 if (pac_source.type == PacSource::WPAD_DHCP) {
244 if (!dhcp_proxy_script_fetcher_) {
245 net_log_.AddEvent(NetLog::TYPE_PROXY_SCRIPT_DECIDER_HAS_NO_FETCHER);
246 return ERR_UNEXPECTED;
249 return dhcp_proxy_script_fetcher_->Fetch(
250 &pac_script_, base::Bind(&ProxyScriptDecider::OnIOCompletion,
251 base::Unretained(this)));
254 if (!proxy_script_fetcher_) {
255 net_log_.AddEvent(NetLog::TYPE_PROXY_SCRIPT_DECIDER_HAS_NO_FETCHER);
256 return ERR_UNEXPECTED;
259 return proxy_script_fetcher_->Fetch(
260 effective_pac_url, &pac_script_,
261 base::Bind(&ProxyScriptDecider::OnIOCompletion, base::Unretained(this)));
264 int ProxyScriptDecider::DoFetchPacScriptComplete(int result) {
265 DCHECK(fetch_pac_bytes_);
267 net_log_.EndEventWithNetErrorCode(
268 NetLog::TYPE_PROXY_SCRIPT_DECIDER_FETCH_PAC_SCRIPT, result);
269 if (result != OK)
270 return TryToFallbackPacSource(result);
272 next_state_ = STATE_VERIFY_PAC_SCRIPT;
273 return result;
276 int ProxyScriptDecider::DoVerifyPacScript() {
277 next_state_ = STATE_VERIFY_PAC_SCRIPT_COMPLETE;
279 // This is just a heuristic. Ideally we would try to parse the script.
280 if (fetch_pac_bytes_ && !LooksLikePacScript(pac_script_))
281 return ERR_PAC_SCRIPT_FAILED;
283 return OK;
286 int ProxyScriptDecider::DoVerifyPacScriptComplete(int result) {
287 if (result != OK)
288 return TryToFallbackPacSource(result);
290 const PacSource& pac_source = current_pac_source();
292 // Extract the current script data.
293 if (fetch_pac_bytes_) {
294 script_data_ = ProxyResolverScriptData::FromUTF16(pac_script_);
295 } else {
296 script_data_ = pac_source.type == PacSource::CUSTOM ?
297 ProxyResolverScriptData::FromURL(pac_source.url) :
298 ProxyResolverScriptData::ForAutoDetect();
301 // Let the caller know which automatic setting we ended up initializing the
302 // resolver for (there may have been multiple fallbacks to choose from.)
303 if (current_pac_source().type == PacSource::CUSTOM) {
304 effective_config_ =
305 ProxyConfig::CreateFromCustomPacURL(current_pac_source().url);
306 effective_config_.set_pac_mandatory(pac_mandatory_);
307 } else {
308 if (fetch_pac_bytes_) {
309 GURL auto_detected_url;
311 switch (current_pac_source().type) {
312 case PacSource::WPAD_DHCP:
313 auto_detected_url = dhcp_proxy_script_fetcher_->GetPacURL();
314 break;
316 case PacSource::WPAD_DNS:
317 auto_detected_url = GURL(kWpadUrl);
318 break;
320 default:
321 NOTREACHED();
324 effective_config_ =
325 ProxyConfig::CreateFromCustomPacURL(auto_detected_url);
326 } else {
327 // The resolver does its own resolution so we cannot know the
328 // URL. Just do the best we can and state that the configuration
329 // is to auto-detect proxy settings.
330 effective_config_ = ProxyConfig::CreateAutoDetect();
334 return OK;
337 int ProxyScriptDecider::TryToFallbackPacSource(int error) {
338 DCHECK_LT(error, 0);
340 if (current_pac_source_index_ + 1 >= pac_sources_.size()) {
341 // Nothing left to fall back to.
342 return error;
345 // Advance to next URL in our list.
346 ++current_pac_source_index_;
348 net_log_.AddEvent(
349 NetLog::TYPE_PROXY_SCRIPT_DECIDER_FALLING_BACK_TO_NEXT_PAC_SOURCE);
351 next_state_ = GetStartState();
353 return OK;
356 ProxyScriptDecider::State ProxyScriptDecider::GetStartState() const {
357 return fetch_pac_bytes_ ? STATE_FETCH_PAC_SCRIPT : STATE_VERIFY_PAC_SCRIPT;
360 void ProxyScriptDecider::DetermineURL(const PacSource& pac_source,
361 GURL* effective_pac_url) {
362 DCHECK(effective_pac_url);
364 switch (pac_source.type) {
365 case PacSource::WPAD_DHCP:
366 break;
367 case PacSource::WPAD_DNS:
368 *effective_pac_url = GURL(kWpadUrl);
369 break;
370 case PacSource::CUSTOM:
371 *effective_pac_url = pac_source.url;
372 break;
376 const ProxyScriptDecider::PacSource&
377 ProxyScriptDecider::current_pac_source() const {
378 DCHECK_LT(current_pac_source_index_, pac_sources_.size());
379 return pac_sources_[current_pac_source_index_];
382 void ProxyScriptDecider::OnWaitTimerFired() {
383 OnIOCompletion(OK);
386 void ProxyScriptDecider::DidComplete() {
387 net_log_.EndEvent(NetLog::TYPE_PROXY_SCRIPT_DECIDER);
390 void ProxyScriptDecider::Cancel() {
391 DCHECK_NE(STATE_NONE, next_state_);
393 net_log_.AddEvent(NetLog::TYPE_CANCELLED);
395 switch (next_state_) {
396 case STATE_WAIT_COMPLETE:
397 wait_timer_.Stop();
398 break;
399 case STATE_FETCH_PAC_SCRIPT_COMPLETE:
400 proxy_script_fetcher_->Cancel();
401 break;
402 default:
403 NOTREACHED();
404 break;
407 // This is safe to call in any state.
408 if (dhcp_proxy_script_fetcher_)
409 dhcp_proxy_script_fetcher_->Cancel();
411 DidComplete();
414 } // namespace net