init_proxy_resolver_unittest.cc revision 3345a6884c488ff3a535c2c9acdd33d74b37e311
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
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 private:
111  const Rules* rules_;
112};
113
114class RuleBasedProxyResolver : public ProxyResolver {
115 public:
116  RuleBasedProxyResolver(const Rules* rules, bool expects_pac_bytes)
117      : ProxyResolver(expects_pac_bytes), rules_(rules) {}
118
119  // ProxyResolver implementation:
120  virtual int GetProxyForURL(const GURL& /*url*/,
121                             ProxyInfo* /*results*/,
122                             CompletionCallback* /*callback*/,
123                             RequestHandle* /*request_handle*/,
124                             const BoundNetLog& /*net_log*/) {
125    NOTREACHED();
126    return ERR_UNEXPECTED;
127  }
128
129  virtual void CancelRequest(RequestHandle request_handle) {
130    NOTREACHED();
131  }
132
133  virtual int SetPacScript(
134      const scoped_refptr<ProxyResolverScriptData>& script_data,
135      CompletionCallback* callback) {
136
137   const GURL url =
138      script_data->type() == ProxyResolverScriptData::TYPE_SCRIPT_URL ?
139          script_data->url() : GURL();
140
141    const Rules::Rule& rule = expects_pac_bytes() ?
142        rules_->GetRuleByText(script_data->utf16()) :
143        rules_->GetRuleByUrl(url);
144
145    int rv = rule.set_pac_error;
146    EXPECT_NE(ERR_UNEXPECTED, rv);
147
148    if (expects_pac_bytes()) {
149      EXPECT_EQ(rule.text(), script_data->utf16());
150    } else {
151      EXPECT_EQ(rule.url, url);
152    }
153
154    if (rv == OK)
155      script_data_ = script_data;
156    return rv;
157  }
158
159  const ProxyResolverScriptData* script_data() const { return script_data_; }
160
161 private:
162  const Rules* rules_;
163  scoped_refptr<ProxyResolverScriptData> script_data_;
164};
165
166// Succeed using custom PAC script.
167TEST(InitProxyResolverTest, CustomPacSucceeds) {
168  Rules rules;
169  RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
170  RuleBasedProxyScriptFetcher fetcher(&rules);
171
172  ProxyConfig config;
173  config.set_pac_url(GURL("http://custom/proxy.pac"));
174
175  Rules::Rule rule = rules.AddSuccessRule("http://custom/proxy.pac");
176
177  TestCompletionCallback callback;
178  CapturingNetLog log(CapturingNetLog::kUnbounded);
179  InitProxyResolver init(&resolver, &fetcher, &log);
180  EXPECT_EQ(OK, init.Init(config, base::TimeDelta(), NULL, &callback));
181  EXPECT_EQ(rule.text(), resolver.script_data()->utf16());
182
183  // Check the NetLog was filled correctly.
184  EXPECT_EQ(6u, log.entries().size());
185  EXPECT_TRUE(LogContainsBeginEvent(
186      log.entries(), 0, NetLog::TYPE_INIT_PROXY_RESOLVER));
187  EXPECT_TRUE(LogContainsBeginEvent(
188      log.entries(), 1, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
189  EXPECT_TRUE(LogContainsEndEvent(
190      log.entries(), 2, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
191  EXPECT_TRUE(LogContainsBeginEvent(
192      log.entries(), 3, NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
193  EXPECT_TRUE(LogContainsEndEvent(
194      log.entries(), 4, NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
195  EXPECT_TRUE(LogContainsEndEvent(
196      log.entries(), 5, NetLog::TYPE_INIT_PROXY_RESOLVER));
197}
198
199// Fail downloading the custom PAC script.
200TEST(InitProxyResolverTest, CustomPacFails1) {
201  Rules rules;
202  RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
203  RuleBasedProxyScriptFetcher fetcher(&rules);
204
205  ProxyConfig config;
206  config.set_pac_url(GURL("http://custom/proxy.pac"));
207
208  rules.AddFailDownloadRule("http://custom/proxy.pac");
209
210  TestCompletionCallback callback;
211  CapturingNetLog log(CapturingNetLog::kUnbounded);
212  InitProxyResolver init(&resolver, &fetcher, &log);
213  EXPECT_EQ(kFailedDownloading,
214            init.Init(config, base::TimeDelta(), NULL, &callback));
215  EXPECT_EQ(NULL, resolver.script_data());
216
217  // Check the NetLog was filled correctly.
218  EXPECT_EQ(4u, log.entries().size());
219  EXPECT_TRUE(LogContainsBeginEvent(
220      log.entries(), 0, NetLog::TYPE_INIT_PROXY_RESOLVER));
221  EXPECT_TRUE(LogContainsBeginEvent(
222      log.entries(), 1, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
223  EXPECT_TRUE(LogContainsEndEvent(
224      log.entries(), 2, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
225  EXPECT_TRUE(LogContainsEndEvent(
226      log.entries(), 3, NetLog::TYPE_INIT_PROXY_RESOLVER));
227}
228
229// Fail parsing the custom PAC script.
230TEST(InitProxyResolverTest, CustomPacFails2) {
231  Rules rules;
232  RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
233  RuleBasedProxyScriptFetcher fetcher(&rules);
234
235  ProxyConfig config;
236  config.set_pac_url(GURL("http://custom/proxy.pac"));
237
238  rules.AddFailParsingRule("http://custom/proxy.pac");
239
240  TestCompletionCallback callback;
241  InitProxyResolver init(&resolver, &fetcher, NULL);
242  EXPECT_EQ(kFailedParsing,
243            init.Init(config, base::TimeDelta(), NULL, &callback));
244  EXPECT_EQ(NULL, resolver.script_data());
245}
246
247// Fail downloading the custom PAC script, because the fetcher was NULL.
248TEST(InitProxyResolverTest, HasNullProxyScriptFetcher) {
249  Rules rules;
250  RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
251
252  ProxyConfig config;
253  config.set_pac_url(GURL("http://custom/proxy.pac"));
254
255  TestCompletionCallback callback;
256  InitProxyResolver init(&resolver, NULL, NULL);
257  EXPECT_EQ(ERR_UNEXPECTED,
258            init.Init(config, base::TimeDelta(), NULL, &callback));
259  EXPECT_EQ(NULL, resolver.script_data());
260}
261
262// Succeeds in choosing autodetect (wpad).
263TEST(InitProxyResolverTest, AutodetectSuccess) {
264  Rules rules;
265  RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
266  RuleBasedProxyScriptFetcher fetcher(&rules);
267
268  ProxyConfig config;
269  config.set_auto_detect(true);
270
271  Rules::Rule rule = rules.AddSuccessRule("http://wpad/wpad.dat");
272
273  TestCompletionCallback callback;
274  InitProxyResolver init(&resolver, &fetcher, NULL);
275  EXPECT_EQ(OK, init.Init(config, base::TimeDelta(), NULL, &callback));
276  EXPECT_EQ(rule.text(), resolver.script_data()->utf16());
277}
278
279// Fails at WPAD (downloading), but succeeds in choosing the custom PAC.
280TEST(InitProxyResolverTest, AutodetectFailCustomSuccess1) {
281  Rules rules;
282  RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
283  RuleBasedProxyScriptFetcher fetcher(&rules);
284
285  ProxyConfig config;
286  config.set_auto_detect(true);
287  config.set_pac_url(GURL("http://custom/proxy.pac"));
288
289  rules.AddFailDownloadRule("http://wpad/wpad.dat");
290  Rules::Rule rule = rules.AddSuccessRule("http://custom/proxy.pac");
291
292  TestCompletionCallback callback;
293  InitProxyResolver init(&resolver, &fetcher, NULL);
294  EXPECT_EQ(OK, init.Init(config, base::TimeDelta(), NULL, &callback));
295  EXPECT_EQ(rule.text(), resolver.script_data()->utf16());
296}
297
298// Fails at WPAD (parsing), but succeeds in choosing the custom PAC.
299TEST(InitProxyResolverTest, AutodetectFailCustomSuccess2) {
300  Rules rules;
301  RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
302  RuleBasedProxyScriptFetcher fetcher(&rules);
303
304  ProxyConfig config;
305  config.set_auto_detect(true);
306  config.set_pac_url(GURL("http://custom/proxy.pac"));
307  config.proxy_rules().ParseFromString("unused-manual-proxy:99");
308
309  rules.AddFailParsingRule("http://wpad/wpad.dat");
310  Rules::Rule rule = rules.AddSuccessRule("http://custom/proxy.pac");
311
312  TestCompletionCallback callback;
313  CapturingNetLog log(CapturingNetLog::kUnbounded);
314
315  ProxyConfig effective_config;
316  InitProxyResolver init(&resolver, &fetcher, &log);
317  EXPECT_EQ(OK, init.Init(config, base::TimeDelta(),
318                          &effective_config, &callback));
319  EXPECT_EQ(rule.text(), resolver.script_data()->utf16());
320
321  // Verify that the effective configuration no longer contains auto detect or
322  // any of the manual settings.
323  EXPECT_TRUE(effective_config.Equals(
324      ProxyConfig::CreateFromCustomPacURL(GURL("http://custom/proxy.pac"))));
325
326  // Check the NetLog was filled correctly.
327  // (Note that the Fetch and Set states are repeated since both WPAD and custom
328  // PAC scripts are tried).
329  EXPECT_EQ(11u, log.entries().size());
330  EXPECT_TRUE(LogContainsBeginEvent(
331      log.entries(), 0, NetLog::TYPE_INIT_PROXY_RESOLVER));
332  EXPECT_TRUE(LogContainsBeginEvent(
333      log.entries(), 1, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
334  EXPECT_TRUE(LogContainsEndEvent(
335      log.entries(), 2, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
336  EXPECT_TRUE(LogContainsBeginEvent(
337      log.entries(), 3, NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
338  EXPECT_TRUE(LogContainsEndEvent(
339      log.entries(), 4, NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
340  EXPECT_TRUE(LogContainsEvent(
341      log.entries(), 5,
342      NetLog::TYPE_INIT_PROXY_RESOLVER_FALLING_BACK_TO_NEXT_PAC_URL,
343      NetLog::PHASE_NONE));
344  EXPECT_TRUE(LogContainsBeginEvent(
345      log.entries(), 6, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
346  EXPECT_TRUE(LogContainsEndEvent(
347      log.entries(), 7, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
348  EXPECT_TRUE(LogContainsBeginEvent(
349      log.entries(), 8, NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
350  EXPECT_TRUE(LogContainsEndEvent(
351      log.entries(), 9, NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
352  EXPECT_TRUE(LogContainsEndEvent(
353      log.entries(), 10, NetLog::TYPE_INIT_PROXY_RESOLVER));
354}
355
356// Fails at WPAD (downloading), and fails at custom PAC (downloading).
357TEST(InitProxyResolverTest, AutodetectFailCustomFails1) {
358  Rules rules;
359  RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
360  RuleBasedProxyScriptFetcher fetcher(&rules);
361
362  ProxyConfig config;
363  config.set_auto_detect(true);
364  config.set_pac_url(GURL("http://custom/proxy.pac"));
365
366  rules.AddFailDownloadRule("http://wpad/wpad.dat");
367  rules.AddFailDownloadRule("http://custom/proxy.pac");
368
369  TestCompletionCallback callback;
370  InitProxyResolver init(&resolver, &fetcher, NULL);
371  EXPECT_EQ(kFailedDownloading,
372            init.Init(config, base::TimeDelta(), NULL, &callback));
373  EXPECT_EQ(NULL, resolver.script_data());
374}
375
376// Fails at WPAD (downloading), and fails at custom PAC (parsing).
377TEST(InitProxyResolverTest, AutodetectFailCustomFails2) {
378  Rules rules;
379  RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
380  RuleBasedProxyScriptFetcher fetcher(&rules);
381
382  ProxyConfig config;
383  config.set_auto_detect(true);
384  config.set_pac_url(GURL("http://custom/proxy.pac"));
385
386  rules.AddFailDownloadRule("http://wpad/wpad.dat");
387  rules.AddFailParsingRule("http://custom/proxy.pac");
388
389  TestCompletionCallback callback;
390  InitProxyResolver init(&resolver, &fetcher, NULL);
391  EXPECT_EQ(kFailedParsing,
392            init.Init(config, base::TimeDelta(), NULL, &callback));
393  EXPECT_EQ(NULL, resolver.script_data());
394}
395
396// Fails at WPAD (parsing), but succeeds in choosing the custom PAC.
397// This is the same as AutodetectFailCustomSuccess2, but using a ProxyResolver
398// that doesn't |expects_pac_bytes|.
399TEST(InitProxyResolverTest, AutodetectFailCustomSuccess2_NoFetch) {
400  Rules rules;
401  RuleBasedProxyResolver resolver(&rules, false /*expects_pac_bytes*/);
402  RuleBasedProxyScriptFetcher fetcher(&rules);
403
404  ProxyConfig config;
405  config.set_auto_detect(true);
406  config.set_pac_url(GURL("http://custom/proxy.pac"));
407
408  rules.AddFailParsingRule("");  // Autodetect.
409  Rules::Rule rule = rules.AddSuccessRule("http://custom/proxy.pac");
410
411  TestCompletionCallback callback;
412  InitProxyResolver init(&resolver, &fetcher, NULL);
413  EXPECT_EQ(OK, init.Init(config, base::TimeDelta(), NULL, &callback));
414  EXPECT_EQ(rule.url, resolver.script_data()->url());
415}
416
417// This is a copy-paste of CustomPacFails1, with the exception that we give it
418// a 1 millisecond delay. This means it will now complete asynchronously.
419// Moreover, we test the NetLog to make sure it logged the pause.
420TEST(InitProxyResolverTest, CustomPacFails1_WithPositiveDelay) {
421  Rules rules;
422  RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
423  RuleBasedProxyScriptFetcher fetcher(&rules);
424
425  ProxyConfig config;
426  config.set_pac_url(GURL("http://custom/proxy.pac"));
427
428  rules.AddFailDownloadRule("http://custom/proxy.pac");
429
430  TestCompletionCallback callback;
431  CapturingNetLog log(CapturingNetLog::kUnbounded);
432  InitProxyResolver init(&resolver, &fetcher, &log);
433  EXPECT_EQ(ERR_IO_PENDING,
434            init.Init(config, base::TimeDelta::FromMilliseconds(1),
435                      NULL, &callback));
436
437  EXPECT_EQ(kFailedDownloading, callback.WaitForResult());
438  EXPECT_EQ(NULL, resolver.script_data());
439
440  // Check the NetLog was filled correctly.
441  EXPECT_EQ(6u, log.entries().size());
442  EXPECT_TRUE(LogContainsBeginEvent(
443      log.entries(), 0, NetLog::TYPE_INIT_PROXY_RESOLVER));
444  EXPECT_TRUE(LogContainsBeginEvent(
445      log.entries(), 1, NetLog::TYPE_INIT_PROXY_RESOLVER_WAIT));
446  EXPECT_TRUE(LogContainsEndEvent(
447      log.entries(), 2, NetLog::TYPE_INIT_PROXY_RESOLVER_WAIT));
448  EXPECT_TRUE(LogContainsBeginEvent(
449      log.entries(), 3, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
450  EXPECT_TRUE(LogContainsEndEvent(
451      log.entries(), 4, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
452  EXPECT_TRUE(LogContainsEndEvent(
453      log.entries(), 5, NetLog::TYPE_INIT_PROXY_RESOLVER));
454}
455
456// This is a copy-paste of CustomPacFails1, with the exception that we give it
457// a -5 second delay instead of a 0 ms delay. This change should have no effect
458// so the rest of the test is unchanged.
459TEST(InitProxyResolverTest, CustomPacFails1_WithNegativeDelay) {
460  Rules rules;
461  RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
462  RuleBasedProxyScriptFetcher fetcher(&rules);
463
464  ProxyConfig config;
465  config.set_pac_url(GURL("http://custom/proxy.pac"));
466
467  rules.AddFailDownloadRule("http://custom/proxy.pac");
468
469  TestCompletionCallback callback;
470  CapturingNetLog log(CapturingNetLog::kUnbounded);
471  InitProxyResolver init(&resolver, &fetcher, &log);
472  EXPECT_EQ(kFailedDownloading,
473            init.Init(config, base::TimeDelta::FromSeconds(-5),
474                      NULL, &callback));
475  EXPECT_EQ(NULL, resolver.script_data());
476
477  // Check the NetLog was filled correctly.
478  EXPECT_EQ(4u, log.entries().size());
479  EXPECT_TRUE(LogContainsBeginEvent(
480      log.entries(), 0, NetLog::TYPE_INIT_PROXY_RESOLVER));
481  EXPECT_TRUE(LogContainsBeginEvent(
482      log.entries(), 1, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
483  EXPECT_TRUE(LogContainsEndEvent(
484      log.entries(), 2, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
485  EXPECT_TRUE(LogContainsEndEvent(
486      log.entries(), 3, NetLog::TYPE_INIT_PROXY_RESOLVER));
487}
488
489}  // namespace
490}  // namespace net
491