1// Copyright (c) 2011 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 <vector>
6
7#include "base/string_util.h"
8#include "base/utf_string_conversions.h"
9#include "net/base/net_errors.h"
10#include "net/base/net_log.h"
11#include "net/base/net_log_unittest.h"
12#include "net/base/test_completion_callback.h"
13#include "net/proxy/init_proxy_resolver.h"
14#include "net/proxy/proxy_config.h"
15#include "net/proxy/proxy_resolver.h"
16#include "net/proxy/proxy_script_fetcher.h"
17#include "testing/gtest/include/gtest/gtest.h"
18
19namespace net {
20namespace {
21
22enum Error {
23  kFailedDownloading = -100,
24  kFailedParsing = -200,
25};
26
27class Rules {
28 public:
29  struct Rule {
30    Rule(const GURL& url,
31         int fetch_error,
32         int set_pac_error)
33        : url(url),
34          fetch_error(fetch_error),
35          set_pac_error(set_pac_error) {
36    }
37
38    string16 text() const {
39      if (set_pac_error == OK)
40        return UTF8ToUTF16(url.spec() + "!valid-script");
41      if (fetch_error == OK)
42        return UTF8ToUTF16(url.spec() + "!invalid-script");
43      return string16();
44    }
45
46    GURL url;
47    int fetch_error;
48    int set_pac_error;
49  };
50
51  Rule AddSuccessRule(const char* url) {
52    Rule rule(GURL(url), OK /*fetch_error*/, OK /*set_pac_error*/);
53    rules_.push_back(rule);
54    return rule;
55  }
56
57  void AddFailDownloadRule(const char* url) {
58    rules_.push_back(Rule(GURL(url), kFailedDownloading /*fetch_error*/,
59        ERR_UNEXPECTED /*set_pac_error*/));
60  }
61
62  void AddFailParsingRule(const char* url) {
63    rules_.push_back(Rule(GURL(url), OK /*fetch_error*/,
64        kFailedParsing /*set_pac_error*/));
65  }
66
67  const Rule& GetRuleByUrl(const GURL& url) const {
68    for (RuleList::const_iterator it = rules_.begin(); it != rules_.end();
69         ++it) {
70      if (it->url == url)
71        return *it;
72    }
73    LOG(FATAL) << "Rule not found for " << url;
74    return rules_[0];
75  }
76
77  const Rule& GetRuleByText(const string16& text) const {
78    for (RuleList::const_iterator it = rules_.begin(); it != rules_.end();
79         ++it) {
80      if (it->text() == text)
81        return *it;
82    }
83    LOG(FATAL) << "Rule not found for " << text;
84    return rules_[0];
85  }
86
87 private:
88  typedef std::vector<Rule> RuleList;
89  RuleList rules_;
90};
91
92class RuleBasedProxyScriptFetcher : public ProxyScriptFetcher {
93 public:
94  explicit RuleBasedProxyScriptFetcher(const Rules* rules) : rules_(rules) {}
95
96  // ProxyScriptFetcher implementation.
97  virtual int Fetch(const GURL& url,
98                    string16* text,
99                    CompletionCallback* callback) {
100    const Rules::Rule& rule = rules_->GetRuleByUrl(url);
101    int rv = rule.fetch_error;
102    EXPECT_NE(ERR_UNEXPECTED, rv);
103    if (rv == OK)
104      *text = rule.text();
105    return rv;
106  }
107
108  virtual void Cancel() {}
109
110  virtual URLRequestContext* GetRequestContext() { return NULL; }
111
112 private:
113  const Rules* rules_;
114};
115
116class RuleBasedProxyResolver : public ProxyResolver {
117 public:
118  RuleBasedProxyResolver(const Rules* rules, bool expects_pac_bytes)
119      : ProxyResolver(expects_pac_bytes), rules_(rules) {}
120
121  // ProxyResolver implementation:
122  virtual int GetProxyForURL(const GURL& /*url*/,
123                             ProxyInfo* /*results*/,
124                             CompletionCallback* /*callback*/,
125                             RequestHandle* /*request_handle*/,
126                             const BoundNetLog& /*net_log*/) {
127    NOTREACHED();
128    return ERR_UNEXPECTED;
129  }
130
131  virtual void CancelRequest(RequestHandle request_handle) {
132    NOTREACHED();
133  }
134
135  virtual void CancelSetPacScript() {
136    NOTREACHED();
137  }
138
139  virtual int SetPacScript(
140      const scoped_refptr<ProxyResolverScriptData>& script_data,
141      CompletionCallback* callback) {
142
143   const GURL url =
144      script_data->type() == ProxyResolverScriptData::TYPE_SCRIPT_URL ?
145          script_data->url() : GURL();
146
147    const Rules::Rule& rule = expects_pac_bytes() ?
148        rules_->GetRuleByText(script_data->utf16()) :
149        rules_->GetRuleByUrl(url);
150
151    int rv = rule.set_pac_error;
152    EXPECT_NE(ERR_UNEXPECTED, rv);
153
154    if (expects_pac_bytes()) {
155      EXPECT_EQ(rule.text(), script_data->utf16());
156    } else {
157      EXPECT_EQ(rule.url, url);
158    }
159
160    if (rv == OK)
161      script_data_ = script_data;
162    return rv;
163  }
164
165  const ProxyResolverScriptData* script_data() const { return script_data_; }
166
167 private:
168  const Rules* rules_;
169  scoped_refptr<ProxyResolverScriptData> script_data_;
170};
171
172// Succeed using custom PAC script.
173TEST(InitProxyResolverTest, CustomPacSucceeds) {
174  Rules rules;
175  RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
176  RuleBasedProxyScriptFetcher fetcher(&rules);
177
178  ProxyConfig config;
179  config.set_pac_url(GURL("http://custom/proxy.pac"));
180
181  Rules::Rule rule = rules.AddSuccessRule("http://custom/proxy.pac");
182
183  TestCompletionCallback callback;
184  CapturingNetLog log(CapturingNetLog::kUnbounded);
185  InitProxyResolver init(&resolver, &fetcher, &log);
186  EXPECT_EQ(OK, init.Init(config, base::TimeDelta(), NULL, &callback));
187  EXPECT_EQ(rule.text(), resolver.script_data()->utf16());
188
189  // Check the NetLog was filled correctly.
190  CapturingNetLog::EntryList entries;
191  log.GetEntries(&entries);
192
193  EXPECT_EQ(6u, entries.size());
194  EXPECT_TRUE(LogContainsBeginEvent(
195      entries, 0, NetLog::TYPE_INIT_PROXY_RESOLVER));
196  EXPECT_TRUE(LogContainsBeginEvent(
197      entries, 1, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
198  EXPECT_TRUE(LogContainsEndEvent(
199      entries, 2, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
200  EXPECT_TRUE(LogContainsBeginEvent(
201      entries, 3, NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
202  EXPECT_TRUE(LogContainsEndEvent(
203      entries, 4, NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
204  EXPECT_TRUE(LogContainsEndEvent(
205      entries, 5, NetLog::TYPE_INIT_PROXY_RESOLVER));
206}
207
208// Fail downloading the custom PAC script.
209TEST(InitProxyResolverTest, CustomPacFails1) {
210  Rules rules;
211  RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
212  RuleBasedProxyScriptFetcher fetcher(&rules);
213
214  ProxyConfig config;
215  config.set_pac_url(GURL("http://custom/proxy.pac"));
216
217  rules.AddFailDownloadRule("http://custom/proxy.pac");
218
219  TestCompletionCallback callback;
220  CapturingNetLog log(CapturingNetLog::kUnbounded);
221  InitProxyResolver init(&resolver, &fetcher, &log);
222  EXPECT_EQ(kFailedDownloading,
223            init.Init(config, base::TimeDelta(), NULL, &callback));
224  EXPECT_EQ(NULL, resolver.script_data());
225
226  // Check the NetLog was filled correctly.
227  CapturingNetLog::EntryList entries;
228  log.GetEntries(&entries);
229
230  EXPECT_EQ(4u, entries.size());
231  EXPECT_TRUE(LogContainsBeginEvent(
232      entries, 0, NetLog::TYPE_INIT_PROXY_RESOLVER));
233  EXPECT_TRUE(LogContainsBeginEvent(
234      entries, 1, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
235  EXPECT_TRUE(LogContainsEndEvent(
236      entries, 2, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
237  EXPECT_TRUE(LogContainsEndEvent(
238      entries, 3, NetLog::TYPE_INIT_PROXY_RESOLVER));
239}
240
241// Fail parsing the custom PAC script.
242TEST(InitProxyResolverTest, CustomPacFails2) {
243  Rules rules;
244  RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
245  RuleBasedProxyScriptFetcher fetcher(&rules);
246
247  ProxyConfig config;
248  config.set_pac_url(GURL("http://custom/proxy.pac"));
249
250  rules.AddFailParsingRule("http://custom/proxy.pac");
251
252  TestCompletionCallback callback;
253  InitProxyResolver init(&resolver, &fetcher, NULL);
254  EXPECT_EQ(kFailedParsing,
255            init.Init(config, base::TimeDelta(), NULL, &callback));
256  EXPECT_EQ(NULL, resolver.script_data());
257}
258
259// Fail downloading the custom PAC script, because the fetcher was NULL.
260TEST(InitProxyResolverTest, HasNullProxyScriptFetcher) {
261  Rules rules;
262  RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
263
264  ProxyConfig config;
265  config.set_pac_url(GURL("http://custom/proxy.pac"));
266
267  TestCompletionCallback callback;
268  InitProxyResolver init(&resolver, NULL, NULL);
269  EXPECT_EQ(ERR_UNEXPECTED,
270            init.Init(config, base::TimeDelta(), NULL, &callback));
271  EXPECT_EQ(NULL, resolver.script_data());
272}
273
274// Succeeds in choosing autodetect (wpad).
275TEST(InitProxyResolverTest, AutodetectSuccess) {
276  Rules rules;
277  RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
278  RuleBasedProxyScriptFetcher fetcher(&rules);
279
280  ProxyConfig config;
281  config.set_auto_detect(true);
282
283  Rules::Rule rule = rules.AddSuccessRule("http://wpad/wpad.dat");
284
285  TestCompletionCallback callback;
286  InitProxyResolver init(&resolver, &fetcher, NULL);
287  EXPECT_EQ(OK, init.Init(config, base::TimeDelta(), NULL, &callback));
288  EXPECT_EQ(rule.text(), resolver.script_data()->utf16());
289}
290
291// Fails at WPAD (downloading), but succeeds in choosing the custom PAC.
292TEST(InitProxyResolverTest, AutodetectFailCustomSuccess1) {
293  Rules rules;
294  RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
295  RuleBasedProxyScriptFetcher fetcher(&rules);
296
297  ProxyConfig config;
298  config.set_auto_detect(true);
299  config.set_pac_url(GURL("http://custom/proxy.pac"));
300
301  rules.AddFailDownloadRule("http://wpad/wpad.dat");
302  Rules::Rule rule = rules.AddSuccessRule("http://custom/proxy.pac");
303
304  TestCompletionCallback callback;
305  InitProxyResolver init(&resolver, &fetcher, NULL);
306  EXPECT_EQ(OK, init.Init(config, base::TimeDelta(), NULL, &callback));
307  EXPECT_EQ(rule.text(), resolver.script_data()->utf16());
308}
309
310// Fails at WPAD (parsing), but succeeds in choosing the custom PAC.
311TEST(InitProxyResolverTest, AutodetectFailCustomSuccess2) {
312  Rules rules;
313  RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
314  RuleBasedProxyScriptFetcher fetcher(&rules);
315
316  ProxyConfig config;
317  config.set_auto_detect(true);
318  config.set_pac_url(GURL("http://custom/proxy.pac"));
319  config.proxy_rules().ParseFromString("unused-manual-proxy:99");
320
321  rules.AddFailParsingRule("http://wpad/wpad.dat");
322  Rules::Rule rule = rules.AddSuccessRule("http://custom/proxy.pac");
323
324  TestCompletionCallback callback;
325  CapturingNetLog log(CapturingNetLog::kUnbounded);
326
327  ProxyConfig effective_config;
328  InitProxyResolver init(&resolver, &fetcher, &log);
329  EXPECT_EQ(OK, init.Init(config, base::TimeDelta(),
330                          &effective_config, &callback));
331  EXPECT_EQ(rule.text(), resolver.script_data()->utf16());
332
333  // Verify that the effective configuration no longer contains auto detect or
334  // any of the manual settings.
335  EXPECT_TRUE(effective_config.Equals(
336      ProxyConfig::CreateFromCustomPacURL(GURL("http://custom/proxy.pac"))));
337
338  // Check the NetLog was filled correctly.
339  // (Note that the Fetch and Set states are repeated since both WPAD and custom
340  // PAC scripts are tried).
341  CapturingNetLog::EntryList entries;
342  log.GetEntries(&entries);
343
344  EXPECT_EQ(11u, entries.size());
345  EXPECT_TRUE(LogContainsBeginEvent(
346      entries, 0, NetLog::TYPE_INIT_PROXY_RESOLVER));
347  EXPECT_TRUE(LogContainsBeginEvent(
348      entries, 1, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
349  EXPECT_TRUE(LogContainsEndEvent(
350      entries, 2, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
351  EXPECT_TRUE(LogContainsBeginEvent(
352      entries, 3, NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
353  EXPECT_TRUE(LogContainsEndEvent(
354      entries, 4, NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
355  EXPECT_TRUE(LogContainsEvent(
356      entries, 5,
357      NetLog::TYPE_INIT_PROXY_RESOLVER_FALLING_BACK_TO_NEXT_PAC_URL,
358      NetLog::PHASE_NONE));
359  EXPECT_TRUE(LogContainsBeginEvent(
360      entries, 6, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
361  EXPECT_TRUE(LogContainsEndEvent(
362      entries, 7, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
363  EXPECT_TRUE(LogContainsBeginEvent(
364      entries, 8, NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
365  EXPECT_TRUE(LogContainsEndEvent(
366      entries, 9, NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
367  EXPECT_TRUE(LogContainsEndEvent(
368      entries, 10, NetLog::TYPE_INIT_PROXY_RESOLVER));
369}
370
371// Fails at WPAD (downloading), and fails at custom PAC (downloading).
372TEST(InitProxyResolverTest, AutodetectFailCustomFails1) {
373  Rules rules;
374  RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
375  RuleBasedProxyScriptFetcher fetcher(&rules);
376
377  ProxyConfig config;
378  config.set_auto_detect(true);
379  config.set_pac_url(GURL("http://custom/proxy.pac"));
380
381  rules.AddFailDownloadRule("http://wpad/wpad.dat");
382  rules.AddFailDownloadRule("http://custom/proxy.pac");
383
384  TestCompletionCallback callback;
385  InitProxyResolver init(&resolver, &fetcher, NULL);
386  EXPECT_EQ(kFailedDownloading,
387            init.Init(config, base::TimeDelta(), NULL, &callback));
388  EXPECT_EQ(NULL, resolver.script_data());
389}
390
391// Fails at WPAD (downloading), and fails at custom PAC (parsing).
392TEST(InitProxyResolverTest, AutodetectFailCustomFails2) {
393  Rules rules;
394  RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
395  RuleBasedProxyScriptFetcher fetcher(&rules);
396
397  ProxyConfig config;
398  config.set_auto_detect(true);
399  config.set_pac_url(GURL("http://custom/proxy.pac"));
400
401  rules.AddFailDownloadRule("http://wpad/wpad.dat");
402  rules.AddFailParsingRule("http://custom/proxy.pac");
403
404  TestCompletionCallback callback;
405  InitProxyResolver init(&resolver, &fetcher, NULL);
406  EXPECT_EQ(kFailedParsing,
407            init.Init(config, base::TimeDelta(), NULL, &callback));
408  EXPECT_EQ(NULL, resolver.script_data());
409}
410
411// Fails at WPAD (parsing), but succeeds in choosing the custom PAC.
412// This is the same as AutodetectFailCustomSuccess2, but using a ProxyResolver
413// that doesn't |expects_pac_bytes|.
414TEST(InitProxyResolverTest, AutodetectFailCustomSuccess2_NoFetch) {
415  Rules rules;
416  RuleBasedProxyResolver resolver(&rules, false /*expects_pac_bytes*/);
417  RuleBasedProxyScriptFetcher fetcher(&rules);
418
419  ProxyConfig config;
420  config.set_auto_detect(true);
421  config.set_pac_url(GURL("http://custom/proxy.pac"));
422
423  rules.AddFailParsingRule("");  // Autodetect.
424  Rules::Rule rule = rules.AddSuccessRule("http://custom/proxy.pac");
425
426  TestCompletionCallback callback;
427  InitProxyResolver init(&resolver, &fetcher, NULL);
428  EXPECT_EQ(OK, init.Init(config, base::TimeDelta(), NULL, &callback));
429  EXPECT_EQ(rule.url, resolver.script_data()->url());
430}
431
432// This is a copy-paste of CustomPacFails1, with the exception that we give it
433// a 1 millisecond delay. This means it will now complete asynchronously.
434// Moreover, we test the NetLog to make sure it logged the pause.
435TEST(InitProxyResolverTest, CustomPacFails1_WithPositiveDelay) {
436  Rules rules;
437  RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
438  RuleBasedProxyScriptFetcher fetcher(&rules);
439
440  ProxyConfig config;
441  config.set_pac_url(GURL("http://custom/proxy.pac"));
442
443  rules.AddFailDownloadRule("http://custom/proxy.pac");
444
445  TestCompletionCallback callback;
446  CapturingNetLog log(CapturingNetLog::kUnbounded);
447  InitProxyResolver init(&resolver, &fetcher, &log);
448  EXPECT_EQ(ERR_IO_PENDING,
449            init.Init(config, base::TimeDelta::FromMilliseconds(1),
450                      NULL, &callback));
451
452  EXPECT_EQ(kFailedDownloading, callback.WaitForResult());
453  EXPECT_EQ(NULL, resolver.script_data());
454
455  // Check the NetLog was filled correctly.
456  CapturingNetLog::EntryList entries;
457  log.GetEntries(&entries);
458
459  EXPECT_EQ(6u, entries.size());
460  EXPECT_TRUE(LogContainsBeginEvent(
461      entries, 0, NetLog::TYPE_INIT_PROXY_RESOLVER));
462  EXPECT_TRUE(LogContainsBeginEvent(
463      entries, 1, NetLog::TYPE_INIT_PROXY_RESOLVER_WAIT));
464  EXPECT_TRUE(LogContainsEndEvent(
465      entries, 2, NetLog::TYPE_INIT_PROXY_RESOLVER_WAIT));
466  EXPECT_TRUE(LogContainsBeginEvent(
467      entries, 3, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
468  EXPECT_TRUE(LogContainsEndEvent(
469      entries, 4, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
470  EXPECT_TRUE(LogContainsEndEvent(
471      entries, 5, NetLog::TYPE_INIT_PROXY_RESOLVER));
472}
473
474// This is a copy-paste of CustomPacFails1, with the exception that we give it
475// a -5 second delay instead of a 0 ms delay. This change should have no effect
476// so the rest of the test is unchanged.
477TEST(InitProxyResolverTest, CustomPacFails1_WithNegativeDelay) {
478  Rules rules;
479  RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
480  RuleBasedProxyScriptFetcher fetcher(&rules);
481
482  ProxyConfig config;
483  config.set_pac_url(GURL("http://custom/proxy.pac"));
484
485  rules.AddFailDownloadRule("http://custom/proxy.pac");
486
487  TestCompletionCallback callback;
488  CapturingNetLog log(CapturingNetLog::kUnbounded);
489  InitProxyResolver init(&resolver, &fetcher, &log);
490  EXPECT_EQ(kFailedDownloading,
491            init.Init(config, base::TimeDelta::FromSeconds(-5),
492                      NULL, &callback));
493  EXPECT_EQ(NULL, resolver.script_data());
494
495  // Check the NetLog was filled correctly.
496  CapturingNetLog::EntryList entries;
497  log.GetEntries(&entries);
498
499  EXPECT_EQ(4u, entries.size());
500  EXPECT_TRUE(LogContainsBeginEvent(
501      entries, 0, NetLog::TYPE_INIT_PROXY_RESOLVER));
502  EXPECT_TRUE(LogContainsBeginEvent(
503      entries, 1, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
504  EXPECT_TRUE(LogContainsEndEvent(
505      entries, 2, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
506  EXPECT_TRUE(LogContainsEndEvent(
507      entries, 3, NetLog::TYPE_INIT_PROXY_RESOLVER));
508}
509
510}  // namespace
511}  // namespace net
512