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 "net/base/net_errors.h"
8#include "net/base/load_log.h"
9#include "net/base/load_log_util.h"
10#include "net/base/load_log_unittest.h"
11#include "net/base/test_completion_callback.h"
12#include "net/proxy/init_proxy_resolver.h"
13#include "net/proxy/proxy_config.h"
14#include "net/proxy/proxy_resolver.h"
15#include "net/proxy/proxy_script_fetcher.h"
16#include "testing/gtest/include/gtest/gtest.h"
17
18namespace net {
19namespace {
20
21enum Error {
22  kFailedDownloading = -100,
23  kFailedParsing = -200,
24};
25
26class Rules {
27 public:
28  struct Rule {
29    Rule(const GURL& url,
30         int fetch_error,
31         int set_pac_error)
32        : url(url),
33          fetch_error(fetch_error),
34          set_pac_error(set_pac_error) {
35    }
36
37    std::string bytes() const {
38      if (set_pac_error == OK)
39        return url.spec() + "!valid-script";
40      if (fetch_error == OK)
41        return url.spec() + "!invalid-script";
42      return std::string();
43    }
44
45    GURL url;
46    int fetch_error;
47    int set_pac_error;
48  };
49
50  Rule AddSuccessRule(const char* url) {
51    Rule rule(GURL(url), OK /*fetch_error*/, OK /*set_pac_error*/);
52    rules_.push_back(rule);
53    return rule;
54  }
55
56  void AddFailDownloadRule(const char* url) {
57    rules_.push_back(Rule(GURL(url), kFailedDownloading /*fetch_error*/,
58        ERR_UNEXPECTED /*set_pac_error*/));
59  }
60
61  void AddFailParsingRule(const char* url) {
62    rules_.push_back(Rule(GURL(url), OK /*fetch_error*/,
63        kFailedParsing /*set_pac_error*/));
64  }
65
66  const Rule& GetRuleByUrl(const GURL& url) const {
67    for (RuleList::const_iterator it = rules_.begin(); it != rules_.end();
68         ++it) {
69      if (it->url == url)
70        return *it;
71    }
72    CHECK(false) << "Rule not found for " << url;
73    return rules_[0];
74  }
75
76  const Rule& GetRuleByBytes(const std::string& bytes) const {
77    for (RuleList::const_iterator it = rules_.begin(); it != rules_.end();
78         ++it) {
79      if (it->bytes() == bytes)
80        return *it;
81    }
82    CHECK(false) << "Rule not found for " << bytes;
83    return rules_[0];
84  }
85
86 private:
87  typedef std::vector<Rule> RuleList;
88  RuleList rules_;
89};
90
91class RuleBasedProxyScriptFetcher : public ProxyScriptFetcher {
92 public:
93  explicit RuleBasedProxyScriptFetcher(const Rules* rules) : rules_(rules) {}
94
95  // ProxyScriptFetcher implementation.
96  virtual int Fetch(const GURL& url,
97                    std::string* bytes,
98                    CompletionCallback* callback) {
99    const Rules::Rule& rule = rules_->GetRuleByUrl(url);
100    int rv = rule.fetch_error;
101    EXPECT_NE(ERR_UNEXPECTED, rv);
102    if (rv == OK)
103      *bytes = rule.bytes();
104    return rv;
105  }
106
107  virtual void Cancel() {}
108
109 private:
110  const Rules* rules_;
111};
112
113class RuleBasedProxyResolver : public ProxyResolver {
114 public:
115  RuleBasedProxyResolver(const Rules* rules, bool expects_pac_bytes)
116      : ProxyResolver(expects_pac_bytes), rules_(rules) {}
117
118  // ProxyResolver implementation:
119  virtual int GetProxyForURL(const GURL& /*url*/,
120                             ProxyInfo* /*results*/,
121                             CompletionCallback* /*callback*/,
122                             RequestHandle* /*request_handle*/,
123                             LoadLog* /*load_log*/) {
124    NOTREACHED();
125    return ERR_UNEXPECTED;
126  }
127
128  virtual void CancelRequest(RequestHandle request_handle) {
129    NOTREACHED();
130  }
131
132  virtual int SetPacScript(const GURL& pac_url,
133                           const std::string& pac_bytes,
134                           CompletionCallback* callback) {
135    const Rules::Rule& rule = expects_pac_bytes() ?
136        rules_->GetRuleByBytes(pac_bytes) :
137        rules_->GetRuleByUrl(pac_url);
138
139    int rv = rule.set_pac_error;
140    EXPECT_NE(ERR_UNEXPECTED, rv);
141
142    if (expects_pac_bytes())
143      EXPECT_EQ(rule.bytes(), pac_bytes);
144    else
145      EXPECT_EQ(rule.url, pac_url);
146
147    if (rv == OK) {
148      pac_bytes_ = pac_bytes;
149      pac_url_ = pac_url;
150    }
151    return rv;
152  }
153
154  const std::string& pac_bytes() const { return pac_bytes_; }
155  const GURL& pac_url() const { return pac_url_; }
156
157 private:
158  const Rules* rules_;
159  std::string pac_bytes_;
160  GURL pac_url_;
161};
162
163// Succeed using custom PAC script.
164TEST(InitProxyResolverTest, CustomPacSucceeds) {
165  Rules rules;
166  RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
167  RuleBasedProxyScriptFetcher fetcher(&rules);
168
169  ProxyConfig config;
170  config.pac_url = GURL("http://custom/proxy.pac");
171
172  Rules::Rule rule = rules.AddSuccessRule("http://custom/proxy.pac");
173
174  TestCompletionCallback callback;
175  scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
176  InitProxyResolver init(&resolver, &fetcher);
177  EXPECT_EQ(OK, init.Init(config, &callback, log));
178  EXPECT_EQ(rule.bytes(), resolver.pac_bytes());
179
180  // Check the LoadLog was filled correctly.
181  EXPECT_EQ(9u, log->entries().size());
182  EXPECT_TRUE(LogContainsBeginEvent(
183      *log, 0, LoadLog::TYPE_INIT_PROXY_RESOLVER));
184  EXPECT_TRUE(LogContainsBeginEvent(
185      *log, 1, LoadLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
186  EXPECT_TRUE(LogContainsEndEvent(
187      *log, 4, LoadLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
188  EXPECT_TRUE(LogContainsBeginEvent(
189      *log, 5, LoadLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
190  EXPECT_TRUE(LogContainsEndEvent(
191      *log, 7, LoadLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
192  EXPECT_TRUE(LogContainsEndEvent(*log, 8, LoadLog::TYPE_INIT_PROXY_RESOLVER));
193}
194
195// Fail downloading the custom PAC script.
196TEST(InitProxyResolverTest, CustomPacFails1) {
197  Rules rules;
198  RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
199  RuleBasedProxyScriptFetcher fetcher(&rules);
200
201  ProxyConfig config;
202  config.pac_url = GURL("http://custom/proxy.pac");
203
204  rules.AddFailDownloadRule("http://custom/proxy.pac");
205
206  TestCompletionCallback callback;
207  scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
208  InitProxyResolver init(&resolver, &fetcher);
209  EXPECT_EQ(kFailedDownloading, init.Init(config, &callback, log));
210  EXPECT_EQ("", resolver.pac_bytes());
211
212  // Check the LoadLog was filled correctly.
213  EXPECT_EQ(6u, log->entries().size());
214  EXPECT_TRUE(LogContainsBeginEvent(
215      *log, 0, LoadLog::TYPE_INIT_PROXY_RESOLVER));
216  EXPECT_TRUE(LogContainsBeginEvent(
217      *log, 1, LoadLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
218  EXPECT_TRUE(LogContainsEndEvent(
219      *log, 4, LoadLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
220  EXPECT_TRUE(LogContainsEndEvent(*log, 5, LoadLog::TYPE_INIT_PROXY_RESOLVER));
221}
222
223// Fail parsing the custom PAC script.
224TEST(InitProxyResolverTest, CustomPacFails2) {
225  Rules rules;
226  RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
227  RuleBasedProxyScriptFetcher fetcher(&rules);
228
229  ProxyConfig config;
230  config.pac_url = GURL("http://custom/proxy.pac");
231
232  rules.AddFailParsingRule("http://custom/proxy.pac");
233
234  TestCompletionCallback callback;
235  InitProxyResolver init(&resolver, &fetcher);
236  EXPECT_EQ(kFailedParsing, init.Init(config, &callback, NULL));
237  EXPECT_EQ("", resolver.pac_bytes());
238}
239
240// Fail downloading the custom PAC script, because the fetcher was NULL.
241TEST(InitProxyResolverTest, HasNullProxyScriptFetcher) {
242  Rules rules;
243  RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
244
245  ProxyConfig config;
246  config.pac_url = GURL("http://custom/proxy.pac");
247
248  TestCompletionCallback callback;
249  InitProxyResolver init(&resolver, NULL);
250  EXPECT_EQ(ERR_UNEXPECTED, init.Init(config, &callback, NULL));
251  EXPECT_EQ("", resolver.pac_bytes());
252}
253
254// Succeeds in choosing autodetect (wpad).
255TEST(InitProxyResolverTest, AutodetectSuccess) {
256  Rules rules;
257  RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
258  RuleBasedProxyScriptFetcher fetcher(&rules);
259
260  ProxyConfig config;
261  config.auto_detect = true;
262
263  Rules::Rule rule = rules.AddSuccessRule("http://wpad/wpad.dat");
264
265  TestCompletionCallback callback;
266  InitProxyResolver init(&resolver, &fetcher);
267  EXPECT_EQ(OK, init.Init(config, &callback, NULL));
268  EXPECT_EQ(rule.bytes(), resolver.pac_bytes());
269}
270
271// Fails at WPAD (downloading), but succeeds in choosing the custom PAC.
272TEST(InitProxyResolverTest, AutodetectFailCustomSuccess1) {
273  Rules rules;
274  RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
275  RuleBasedProxyScriptFetcher fetcher(&rules);
276
277  ProxyConfig config;
278  config.auto_detect = true;
279  config.pac_url = GURL("http://custom/proxy.pac");
280
281  rules.AddFailDownloadRule("http://wpad/wpad.dat");
282  Rules::Rule rule = rules.AddSuccessRule("http://custom/proxy.pac");
283
284  TestCompletionCallback callback;
285  InitProxyResolver init(&resolver, &fetcher);
286  EXPECT_EQ(OK, init.Init(config, &callback, NULL));
287  EXPECT_EQ(rule.bytes(), resolver.pac_bytes());
288}
289
290// Fails at WPAD (parsing), but succeeds in choosing the custom PAC.
291TEST(InitProxyResolverTest, AutodetectFailCustomSuccess2) {
292  Rules rules;
293  RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
294  RuleBasedProxyScriptFetcher fetcher(&rules);
295
296  ProxyConfig config;
297  config.auto_detect = true;
298  config.pac_url = GURL("http://custom/proxy.pac");
299
300  rules.AddFailParsingRule("http://wpad/wpad.dat");
301  Rules::Rule rule = rules.AddSuccessRule("http://custom/proxy.pac");
302
303  TestCompletionCallback callback;
304  scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
305  InitProxyResolver init(&resolver, &fetcher);
306  EXPECT_EQ(OK, init.Init(config, &callback, log));
307  EXPECT_EQ(rule.bytes(), resolver.pac_bytes());
308
309  // Check the LoadLog was filled correctly.
310  // (Note that the Fetch and Set states are repeated since both WPAD and custom
311  // PAC scripts are tried).
312  EXPECT_EQ(17u, log->entries().size());
313  EXPECT_TRUE(LogContainsBeginEvent(
314      *log, 0, LoadLog::TYPE_INIT_PROXY_RESOLVER));
315  EXPECT_TRUE(LogContainsBeginEvent(
316      *log, 1, LoadLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
317  EXPECT_TRUE(LogContainsEndEvent(
318      *log, 4, LoadLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
319  EXPECT_TRUE(LogContainsBeginEvent(
320      *log, 5, LoadLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
321  EXPECT_TRUE(LogContainsEndEvent(
322      *log, 7, LoadLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
323  EXPECT_TRUE(LogContainsBeginEvent(
324      *log, 9, LoadLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
325  EXPECT_TRUE(LogContainsEndEvent(
326      *log, 12, LoadLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
327  EXPECT_TRUE(LogContainsBeginEvent(
328      *log, 13, LoadLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
329  EXPECT_TRUE(LogContainsEndEvent(
330      *log, 15, LoadLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
331  EXPECT_TRUE(LogContainsEndEvent(*log, 16, LoadLog::TYPE_INIT_PROXY_RESOLVER));
332}
333
334// Fails at WPAD (downloading), and fails at custom PAC (downloading).
335TEST(InitProxyResolverTest, AutodetectFailCustomFails1) {
336  Rules rules;
337  RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
338  RuleBasedProxyScriptFetcher fetcher(&rules);
339
340  ProxyConfig config;
341  config.auto_detect = true;
342  config.pac_url = GURL("http://custom/proxy.pac");
343
344  rules.AddFailDownloadRule("http://wpad/wpad.dat");
345  rules.AddFailDownloadRule("http://custom/proxy.pac");
346
347  TestCompletionCallback callback;
348  InitProxyResolver init(&resolver, &fetcher);
349  EXPECT_EQ(kFailedDownloading, init.Init(config, &callback, NULL));
350  EXPECT_EQ("", resolver.pac_bytes());
351}
352
353// Fails at WPAD (downloading), and fails at custom PAC (parsing).
354TEST(InitProxyResolverTest, AutodetectFailCustomFails2) {
355  Rules rules;
356  RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
357  RuleBasedProxyScriptFetcher fetcher(&rules);
358
359  ProxyConfig config;
360  config.auto_detect = true;
361  config.pac_url = GURL("http://custom/proxy.pac");
362
363  rules.AddFailDownloadRule("http://wpad/wpad.dat");
364  rules.AddFailParsingRule("http://custom/proxy.pac");
365
366  TestCompletionCallback callback;
367  InitProxyResolver init(&resolver, &fetcher);
368  EXPECT_EQ(kFailedParsing, init.Init(config, &callback, NULL));
369  EXPECT_EQ("", resolver.pac_bytes());
370}
371
372// Fails at WPAD (parsing), but succeeds in choosing the custom PAC.
373// This is the same as AutodetectFailCustomSuccess2, but using a ProxyResolver
374// that doesn't |expects_pac_bytes|.
375TEST(InitProxyResolverTest, AutodetectFailCustomSuccess2_NoFetch) {
376  Rules rules;
377  RuleBasedProxyResolver resolver(&rules, false /*expects_pac_bytes*/);
378  RuleBasedProxyScriptFetcher fetcher(&rules);
379
380  ProxyConfig config;
381  config.auto_detect = true;
382  config.pac_url = GURL("http://custom/proxy.pac");
383
384  rules.AddFailParsingRule("");  // Autodetect.
385  Rules::Rule rule = rules.AddSuccessRule("http://custom/proxy.pac");
386
387  TestCompletionCallback callback;
388  InitProxyResolver init(&resolver, &fetcher);
389  EXPECT_EQ(OK, init.Init(config, &callback, NULL));
390  EXPECT_EQ(rule.url, resolver.pac_url());
391}
392
393}  // namespace
394}  // namespace net
395