Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / net / proxy / proxy_resolver_v8_tracing_unittest.cc
1 // Copyright (c) 2013 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_resolver_v8_tracing.h"
6
7 #include "base/files/file_util.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/path_service.h"
10 #include "base/stl_util.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/synchronization/waitable_event.h"
15 #include "base/threading/platform_thread.h"
16 #include "base/values.h"
17 #include "net/base/net_errors.h"
18 #include "net/base/net_log.h"
19 #include "net/base/net_log_unittest.h"
20 #include "net/base/test_completion_callback.h"
21 #include "net/dns/host_cache.h"
22 #include "net/dns/mock_host_resolver.h"
23 #include "net/proxy/proxy_info.h"
24 #include "net/proxy/proxy_resolver_error_observer.h"
25 #include "testing/gtest/include/gtest/gtest.h"
26 #include "url/gurl.h"
27
28 namespace net {
29
30 namespace {
31
32 class ProxyResolverV8TracingTest : public testing::Test {
33  public:
34   virtual void TearDown() OVERRIDE {
35     // Drain any pending messages, which may be left over from cancellation.
36     // This way they get reliably run as part of the current test, rather than
37     // spilling into the next test's execution.
38     base::MessageLoop::current()->RunUntilIdle();
39   }
40 };
41
42 scoped_refptr<ProxyResolverScriptData> LoadScriptData(const char* filename) {
43   base::FilePath path;
44   PathService::Get(base::DIR_SOURCE_ROOT, &path);
45   path = path.AppendASCII("net");
46   path = path.AppendASCII("data");
47   path = path.AppendASCII("proxy_resolver_v8_tracing_unittest");
48   path = path.AppendASCII(filename);
49
50   // Try to read the file from disk.
51   std::string file_contents;
52   bool ok = base::ReadFileToString(path, &file_contents);
53
54   // If we can't load the file from disk, something is misconfigured.
55   EXPECT_TRUE(ok) << "Failed to read file: " << path.value();
56
57   // Load the PAC script into the ProxyResolver.
58   return ProxyResolverScriptData::FromUTF8(file_contents);
59 }
60
61 void InitResolver(ProxyResolverV8Tracing* resolver, const char* filename) {
62   TestCompletionCallback callback;
63   int rv =
64       resolver->SetPacScript(LoadScriptData(filename), callback.callback());
65   EXPECT_EQ(ERR_IO_PENDING, rv);
66   EXPECT_EQ(OK, callback.WaitForResult());
67 }
68
69 class MockErrorObserver : public ProxyResolverErrorObserver {
70  public:
71   MockErrorObserver() : event_(true, false) {}
72
73   virtual void OnPACScriptError(int line_number,
74                                 const base::string16& error) OVERRIDE {
75     {
76       base::AutoLock l(lock_);
77       output += base::StringPrintf("Error: line %d: %s\n", line_number,
78                                    base::UTF16ToASCII(error).c_str());
79     }
80     event_.Signal();
81   }
82
83   std::string GetOutput() {
84     base::AutoLock l(lock_);
85     return output;
86   }
87
88   void WaitForOutput() {
89     event_.Wait();
90   }
91
92  private:
93   base::Lock lock_;
94   std::string output;
95
96   base::WaitableEvent event_;
97 };
98
99 TEST_F(ProxyResolverV8TracingTest, Simple) {
100   CapturingNetLog log;
101   CapturingBoundNetLog request_log;
102   MockCachingHostResolver host_resolver;
103   MockErrorObserver* error_observer = new MockErrorObserver;
104   ProxyResolverV8Tracing resolver(&host_resolver, error_observer, &log);
105
106   InitResolver(&resolver, "simple.js");
107
108   TestCompletionCallback callback;
109   ProxyInfo proxy_info;
110
111   int rv = resolver.GetProxyForURL(
112       GURL("http://foo/"), &proxy_info, callback.callback(),
113       NULL, request_log.bound());
114
115   EXPECT_EQ(ERR_IO_PENDING, rv);
116   EXPECT_EQ(OK, callback.WaitForResult());
117
118   EXPECT_EQ("foo:99", proxy_info.proxy_server().ToURI());
119
120   EXPECT_EQ(0u, host_resolver.num_resolve());
121
122   // There were no errors.
123   EXPECT_EQ("", error_observer->GetOutput());
124
125   // Check the NetLogs -- nothing was logged.
126   EXPECT_EQ(0u, log.GetSize());
127   EXPECT_EQ(0u, request_log.GetSize());
128 }
129
130 TEST_F(ProxyResolverV8TracingTest, JavascriptError) {
131   CapturingNetLog log;
132   CapturingBoundNetLog request_log;
133   MockCachingHostResolver host_resolver;
134   MockErrorObserver* error_observer = new MockErrorObserver;
135   ProxyResolverV8Tracing resolver(&host_resolver, error_observer, &log);
136
137   InitResolver(&resolver, "error.js");
138
139   TestCompletionCallback callback;
140   ProxyInfo proxy_info;
141
142   int rv = resolver.GetProxyForURL(
143       GURL("http://throw-an-error/"), &proxy_info, callback.callback(), NULL,
144       request_log.bound());
145
146   EXPECT_EQ(ERR_IO_PENDING, rv);
147   EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, callback.WaitForResult());
148
149   EXPECT_EQ(0u, host_resolver.num_resolve());
150
151   EXPECT_EQ("Error: line 5: Uncaught TypeError: Cannot read property 'split' "
152             "of null\n", error_observer->GetOutput());
153
154   // Check the NetLogs -- there was 1 alert and 1 javascript error, and they
155   // were output to both the global log, and per-request log.
156   CapturingNetLog::CapturedEntryList entries_list[2];
157   log.GetEntries(&entries_list[0]);
158   request_log.GetEntries(&entries_list[1]);
159
160   for (size_t list_i = 0; list_i < arraysize(entries_list); list_i++) {
161     const CapturingNetLog::CapturedEntryList& entries = entries_list[list_i];
162     EXPECT_EQ(2u, entries.size());
163     EXPECT_TRUE(
164         LogContainsEvent(entries, 0, NetLog::TYPE_PAC_JAVASCRIPT_ALERT,
165                          NetLog::PHASE_NONE));
166     EXPECT_TRUE(
167         LogContainsEvent(entries, 1, NetLog::TYPE_PAC_JAVASCRIPT_ERROR,
168                          NetLog::PHASE_NONE));
169
170     EXPECT_EQ("{\"message\":\"Prepare to DIE!\"}", entries[0].GetParamsJson());
171     EXPECT_EQ("{\"line_number\":5,\"message\":\"Uncaught TypeError: Cannot "
172               "read property 'split' of null\"}", entries[1].GetParamsJson());
173   }
174 }
175
176 TEST_F(ProxyResolverV8TracingTest, TooManyAlerts) {
177   CapturingNetLog log;
178   CapturingBoundNetLog request_log;
179   MockCachingHostResolver host_resolver;
180   MockErrorObserver* error_observer = new MockErrorObserver;
181   ProxyResolverV8Tracing resolver(&host_resolver, error_observer, &log);
182
183   InitResolver(&resolver, "too_many_alerts.js");
184
185   TestCompletionCallback callback;
186   ProxyInfo proxy_info;
187
188   int rv = resolver.GetProxyForURL(
189       GURL("http://foo/"),
190       &proxy_info,
191       callback.callback(),
192       NULL,
193       request_log.bound());
194
195   EXPECT_EQ(ERR_IO_PENDING, rv);
196   EXPECT_EQ(OK, callback.WaitForResult());
197
198   // Iteration1 does a DNS resolve
199   // Iteration2 exceeds the alert buffer
200   // Iteration3 runs in blocking mode and completes
201   EXPECT_EQ("foo:3", proxy_info.proxy_server().ToURI());
202
203   EXPECT_EQ(1u, host_resolver.num_resolve());
204
205   // No errors.
206   EXPECT_EQ("", error_observer->GetOutput());
207
208   // Check the NetLogs -- the script generated 50 alerts, which were mirrored
209   // to both the global and per-request logs.
210   CapturingNetLog::CapturedEntryList entries_list[2];
211   log.GetEntries(&entries_list[0]);
212   request_log.GetEntries(&entries_list[1]);
213
214   for (size_t list_i = 0; list_i < arraysize(entries_list); list_i++) {
215     const CapturingNetLog::CapturedEntryList& entries = entries_list[list_i];
216     EXPECT_EQ(50u, entries.size());
217     for (size_t i = 0; i < entries.size(); ++i) {
218       ASSERT_TRUE(
219           LogContainsEvent(entries, i, NetLog::TYPE_PAC_JAVASCRIPT_ALERT,
220                            NetLog::PHASE_NONE));
221     }
222   }
223 }
224
225 // Verify that buffered alerts cannot grow unboundedly, even when the message is
226 // empty string.
227 TEST_F(ProxyResolverV8TracingTest, TooManyEmptyAlerts) {
228   CapturingNetLog log;
229   CapturingBoundNetLog request_log;
230   MockCachingHostResolver host_resolver;
231   MockErrorObserver* error_observer = new MockErrorObserver;
232   ProxyResolverV8Tracing resolver(&host_resolver, error_observer, &log);
233
234   InitResolver(&resolver, "too_many_empty_alerts.js");
235
236   TestCompletionCallback callback;
237   ProxyInfo proxy_info;
238
239   int rv = resolver.GetProxyForURL(
240       GURL("http://foo/"),
241       &proxy_info,
242       callback.callback(),
243       NULL,
244       request_log.bound());
245
246   EXPECT_EQ(ERR_IO_PENDING, rv);
247   EXPECT_EQ(OK, callback.WaitForResult());
248
249   EXPECT_EQ("foo:3", proxy_info.proxy_server().ToURI());
250
251   EXPECT_EQ(1u, host_resolver.num_resolve());
252
253   // No errors.
254   EXPECT_EQ("", error_observer->GetOutput());
255
256   // Check the NetLogs -- the script generated 50 alerts, which were mirrored
257   // to both the global and per-request logs.
258   CapturingNetLog::CapturedEntryList entries_list[2];
259   log.GetEntries(&entries_list[0]);
260   request_log.GetEntries(&entries_list[1]);
261
262   for (size_t list_i = 0; list_i < arraysize(entries_list); list_i++) {
263     const CapturingNetLog::CapturedEntryList& entries = entries_list[list_i];
264     EXPECT_EQ(1000u, entries.size());
265     for (size_t i = 0; i < entries.size(); ++i) {
266       ASSERT_TRUE(
267           LogContainsEvent(entries, i, NetLog::TYPE_PAC_JAVASCRIPT_ALERT,
268                            NetLog::PHASE_NONE));
269     }
270   }
271 }
272
273 // This test runs a PAC script that issues a sequence of DNS resolves. The test
274 // verifies the final result, and that the underlying DNS resolver received
275 // the correct set of queries.
276 TEST_F(ProxyResolverV8TracingTest, Dns) {
277   CapturingNetLog log;
278   CapturingBoundNetLog request_log;
279   MockCachingHostResolver host_resolver;
280   MockErrorObserver* error_observer = new MockErrorObserver;
281   ProxyResolverV8Tracing resolver(&host_resolver, error_observer, &log);
282
283   host_resolver.rules()->AddRuleForAddressFamily(
284       "host1", ADDRESS_FAMILY_IPV4, "166.155.144.44");
285   host_resolver.rules()
286       ->AddIPLiteralRule("host1", "::1,192.168.1.1", std::string());
287   host_resolver.rules()->AddSimulatedFailure("host2");
288   host_resolver.rules()->AddRule("host3", "166.155.144.33");
289   host_resolver.rules()->AddRule("host5", "166.155.144.55");
290   host_resolver.rules()->AddSimulatedFailure("host6");
291   host_resolver.rules()->AddRuleForAddressFamily(
292       "*", ADDRESS_FAMILY_IPV4, "122.133.144.155");
293   host_resolver.rules()->AddRule("*", "133.122.100.200");
294
295   InitResolver(&resolver, "dns.js");
296
297   TestCompletionCallback callback;
298   ProxyInfo proxy_info;
299
300   int rv = resolver.GetProxyForURL(
301       GURL("http://foo/"),
302       &proxy_info,
303       callback.callback(),
304       NULL,
305       request_log.bound());
306
307   EXPECT_EQ(ERR_IO_PENDING, rv);
308   EXPECT_EQ(OK, callback.WaitForResult());
309
310   // The test does 13 DNS resolution, however only 7 of them are unique.
311   EXPECT_EQ(7u, host_resolver.num_resolve());
312
313   const char* kExpectedResult =
314     "122.133.144.155-"  // myIpAddress()
315     "null-"  // dnsResolve('')
316     "__1_192.168.1.1-"  // dnsResolveEx('host1')
317     "null-"  // dnsResolve('host2')
318     "166.155.144.33-"  // dnsResolve('host3')
319     "122.133.144.155-"  // myIpAddress()
320     "166.155.144.33-"  // dnsResolve('host3')
321     "__1_192.168.1.1-"  // dnsResolveEx('host1')
322     "122.133.144.155-"  // myIpAddress()
323     "null-"  // dnsResolve('host2')
324     "-"  // dnsResolveEx('host6')
325     "133.122.100.200-"  // myIpAddressEx()
326     "166.155.144.44"  // dnsResolve('host1')
327     ":99";
328
329   EXPECT_EQ(kExpectedResult, proxy_info.proxy_server().ToURI());
330
331   // No errors.
332   EXPECT_EQ("", error_observer->GetOutput());
333
334   // Check the NetLogs -- the script generated 1 alert, mirrored to both
335   // the per-request and global logs.
336   CapturingNetLog::CapturedEntryList entries_list[2];
337   log.GetEntries(&entries_list[0]);
338   request_log.GetEntries(&entries_list[1]);
339
340   for (size_t list_i = 0; list_i < arraysize(entries_list); list_i++) {
341     const CapturingNetLog::CapturedEntryList& entries = entries_list[list_i];
342     EXPECT_EQ(1u, entries.size());
343     EXPECT_TRUE(
344         LogContainsEvent(entries, 0, NetLog::TYPE_PAC_JAVASCRIPT_ALERT,
345                          NetLog::PHASE_NONE));
346     EXPECT_EQ("{\"message\":\"iteration: 7\"}", entries[0].GetParamsJson());
347   }
348 }
349
350 // This test runs a PAC script that does "myIpAddress()" followed by
351 // "dnsResolve()". This requires 2 restarts. However once the HostResolver's
352 // cache is warmed, subsequent calls should take 0 restarts.
353 TEST_F(ProxyResolverV8TracingTest, DnsChecksCache) {
354   CapturingNetLog log;
355   CapturingBoundNetLog request_log;
356   MockCachingHostResolver host_resolver;
357   MockErrorObserver* error_observer = new MockErrorObserver;
358   ProxyResolverV8Tracing resolver(&host_resolver, error_observer, &log);
359
360   host_resolver.rules()->AddRule("foopy", "166.155.144.11");
361   host_resolver.rules()->AddRule("*", "122.133.144.155");
362
363   InitResolver(&resolver, "simple_dns.js");
364
365   TestCompletionCallback callback1;
366   TestCompletionCallback callback2;
367   ProxyInfo proxy_info;
368
369   int rv = resolver.GetProxyForURL(
370       GURL("http://foopy/req1"),
371       &proxy_info,
372       callback1.callback(),
373       NULL,
374       request_log.bound());
375
376   EXPECT_EQ(ERR_IO_PENDING, rv);
377   EXPECT_EQ(OK, callback1.WaitForResult());
378
379   // The test does 2 DNS resolutions.
380   EXPECT_EQ(2u, host_resolver.num_resolve());
381
382   // The first request took 2 restarts, hence on g_iteration=3.
383   EXPECT_EQ("166.155.144.11:3", proxy_info.proxy_server().ToURI());
384
385   rv = resolver.GetProxyForURL(
386       GURL("http://foopy/req2"),
387       &proxy_info,
388       callback2.callback(),
389       NULL,
390       request_log.bound());
391
392   EXPECT_EQ(ERR_IO_PENDING, rv);
393   EXPECT_EQ(OK, callback2.WaitForResult());
394
395   EXPECT_EQ(4u, host_resolver.num_resolve());
396
397   // This time no restarts were required, so g_iteration incremented by 1.
398   EXPECT_EQ("166.155.144.11:4", proxy_info.proxy_server().ToURI());
399
400   // No errors.
401   EXPECT_EQ("", error_observer->GetOutput());
402
403   EXPECT_EQ(0u, log.GetSize());
404   EXPECT_EQ(0u, request_log.GetSize());
405 }
406
407 // This test runs a weird PAC script that was designed to defeat the DNS tracing
408 // optimization. The proxy resolver should detect the inconsistency and
409 // fall-back to synchronous mode execution.
410 TEST_F(ProxyResolverV8TracingTest, FallBackToSynchronous1) {
411   CapturingNetLog log;
412   CapturingBoundNetLog request_log;
413   MockCachingHostResolver host_resolver;
414   MockErrorObserver* error_observer = new MockErrorObserver;
415   ProxyResolverV8Tracing resolver(&host_resolver, error_observer, &log);
416
417   host_resolver.rules()->AddRule("host1", "166.155.144.11");
418   host_resolver.rules()->AddRule("crazy4", "133.199.111.4");
419   host_resolver.rules()->AddRule("*", "122.133.144.155");
420
421   InitResolver(&resolver, "global_sideffects1.js");
422
423   TestCompletionCallback callback;
424   ProxyInfo proxy_info;
425
426   int rv = resolver.GetProxyForURL(
427       GURL("http://foo/"), &proxy_info, callback.callback(), NULL,
428       request_log.bound());
429   EXPECT_EQ(ERR_IO_PENDING, rv);
430   EXPECT_EQ(OK, callback.WaitForResult());
431
432   // The script itself only does 2 DNS resolves per execution, however it
433   // constructs the hostname using a global counter which changes on each
434   // invocation.
435   EXPECT_EQ(3u, host_resolver.num_resolve());
436
437   EXPECT_EQ("166.155.144.11-133.199.111.4:100",
438             proxy_info.proxy_server().ToURI());
439
440   // No errors.
441   EXPECT_EQ("", error_observer->GetOutput());
442
443   // Check the NetLogs -- the script generated 1 alert, mirrored to both
444   // the per-request and global logs.
445   CapturingNetLog::CapturedEntryList entries_list[2];
446   log.GetEntries(&entries_list[0]);
447   request_log.GetEntries(&entries_list[1]);
448
449   for (size_t list_i = 0; list_i < arraysize(entries_list); list_i++) {
450     const CapturingNetLog::CapturedEntryList& entries = entries_list[list_i];
451     EXPECT_EQ(1u, entries.size());
452     EXPECT_TRUE(
453         LogContainsEvent(entries, 0, NetLog::TYPE_PAC_JAVASCRIPT_ALERT,
454                          NetLog::PHASE_NONE));
455     EXPECT_EQ("{\"message\":\"iteration: 4\"}", entries[0].GetParamsJson());
456   }
457 }
458
459 // This test runs a weird PAC script that was designed to defeat the DNS tracing
460 // optimization. The proxy resolver should detect the inconsistency and
461 // fall-back to synchronous mode execution.
462 TEST_F(ProxyResolverV8TracingTest, FallBackToSynchronous2) {
463   CapturingNetLog log;
464   CapturingBoundNetLog request_log;
465   MockCachingHostResolver host_resolver;
466   MockErrorObserver* error_observer = new MockErrorObserver;
467   ProxyResolverV8Tracing resolver(&host_resolver, error_observer, &log);
468
469   host_resolver.rules()->AddRule("host1", "166.155.144.11");
470   host_resolver.rules()->AddRule("host2", "166.155.144.22");
471   host_resolver.rules()->AddRule("host3", "166.155.144.33");
472   host_resolver.rules()->AddRule("host4", "166.155.144.44");
473   host_resolver.rules()->AddRule("*", "122.133.144.155");
474
475   InitResolver(&resolver, "global_sideffects2.js");
476
477   TestCompletionCallback callback;
478   ProxyInfo proxy_info;
479
480   int rv = resolver.GetProxyForURL(
481       GURL("http://foo/"), &proxy_info, callback.callback(), NULL,
482       request_log.bound());
483   EXPECT_EQ(ERR_IO_PENDING, rv);
484   EXPECT_EQ(OK, callback.WaitForResult());
485
486   EXPECT_EQ(3u, host_resolver.num_resolve());
487
488   EXPECT_EQ("166.155.144.44:100", proxy_info.proxy_server().ToURI());
489
490   // No errors.
491   EXPECT_EQ("", error_observer->GetOutput());
492
493   // Check the NetLogs -- nothing was logged.
494   EXPECT_EQ(0u, log.GetSize());
495   EXPECT_EQ(0u, request_log.GetSize());
496 }
497
498 // This test runs a weird PAC script that yields a never ending sequence
499 // of DNS resolves when restarting. Running it will hit the maximum
500 // DNS resolves per request limit (20) after which every DNS resolve will
501 // fail.
502 TEST_F(ProxyResolverV8TracingTest, InfiniteDNSSequence) {
503   CapturingNetLog log;
504   CapturingBoundNetLog request_log;
505   MockCachingHostResolver host_resolver;
506   MockErrorObserver* error_observer = new MockErrorObserver;
507   ProxyResolverV8Tracing resolver(&host_resolver, error_observer, &log);
508
509   host_resolver.rules()->AddRule("host*", "166.155.144.11");
510   host_resolver.rules()->AddRule("*", "122.133.144.155");
511
512   InitResolver(&resolver, "global_sideffects3.js");
513
514   TestCompletionCallback callback;
515   ProxyInfo proxy_info;
516
517   int rv = resolver.GetProxyForURL(
518       GURL("http://foo/"), &proxy_info, callback.callback(), NULL,
519       request_log.bound());
520   EXPECT_EQ(ERR_IO_PENDING, rv);
521   EXPECT_EQ(OK, callback.WaitForResult());
522
523   EXPECT_EQ(20u, host_resolver.num_resolve());
524
525   EXPECT_EQ(
526       "166.155.144.11-166.155.144.11-166.155.144.11-166.155.144.11-"
527       "166.155.144.11-166.155.144.11-166.155.144.11-166.155.144.11-"
528       "166.155.144.11-166.155.144.11-166.155.144.11-166.155.144.11-"
529       "166.155.144.11-166.155.144.11-166.155.144.11-166.155.144.11-"
530       "166.155.144.11-166.155.144.11-166.155.144.11-166.155.144.11-"
531       "null:21", proxy_info.proxy_server().ToURI());
532
533   // No errors.
534   EXPECT_EQ("", error_observer->GetOutput());
535
536   // Check the NetLogs -- 1 alert was logged.
537   EXPECT_EQ(1u, log.GetSize());
538   EXPECT_EQ(1u, request_log.GetSize());
539 }
540
541 // This test runs a weird PAC script that yields a never ending sequence
542 // of DNS resolves when restarting. Running it will hit the maximum
543 // DNS resolves per request limit (20) after which every DNS resolve will
544 // fail.
545 TEST_F(ProxyResolverV8TracingTest, InfiniteDNSSequence2) {
546   CapturingNetLog log;
547   CapturingBoundNetLog request_log;
548   MockCachingHostResolver host_resolver;
549   MockErrorObserver* error_observer = new MockErrorObserver;
550   ProxyResolverV8Tracing resolver(&host_resolver, error_observer, &log);
551
552   host_resolver.rules()->AddRule("host*", "166.155.144.11");
553   host_resolver.rules()->AddRule("*", "122.133.144.155");
554
555   InitResolver(&resolver, "global_sideffects4.js");
556
557   TestCompletionCallback callback;
558   ProxyInfo proxy_info;
559
560   int rv = resolver.GetProxyForURL(
561       GURL("http://foo/"), &proxy_info, callback.callback(), NULL,
562       request_log.bound());
563   EXPECT_EQ(ERR_IO_PENDING, rv);
564   EXPECT_EQ(OK, callback.WaitForResult());
565
566   EXPECT_EQ(20u, host_resolver.num_resolve());
567
568   EXPECT_EQ("null21:34", proxy_info.proxy_server().ToURI());
569
570   // No errors.
571   EXPECT_EQ("", error_observer->GetOutput());
572
573   // Check the NetLogs -- 1 alert was logged.
574   EXPECT_EQ(1u, log.GetSize());
575   EXPECT_EQ(1u, request_log.GetSize());
576 }
577
578 void DnsDuringInitHelper(bool synchronous_host_resolver) {
579   CapturingNetLog log;
580   CapturingBoundNetLog request_log;
581   MockCachingHostResolver host_resolver;
582   host_resolver.set_synchronous_mode(synchronous_host_resolver);
583   MockErrorObserver* error_observer = new MockErrorObserver;
584   ProxyResolverV8Tracing resolver(&host_resolver, error_observer, &log);
585
586   host_resolver.rules()->AddRule("host1", "91.13.12.1");
587   host_resolver.rules()->AddRule("host2", "91.13.12.2");
588
589   InitResolver(&resolver, "dns_during_init.js");
590
591   // Initialization did 2 dnsResolves.
592   EXPECT_EQ(2u, host_resolver.num_resolve());
593
594   host_resolver.rules()->ClearRules();
595   host_resolver.GetHostCache()->clear();
596
597   host_resolver.rules()->AddRule("host1", "145.88.13.3");
598   host_resolver.rules()->AddRule("host2", "137.89.8.45");
599
600   TestCompletionCallback callback;
601   ProxyInfo proxy_info;
602
603   int rv = resolver.GetProxyForURL(
604       GURL("http://foo/"), &proxy_info, callback.callback(), NULL,
605       request_log.bound());
606   EXPECT_EQ(ERR_IO_PENDING, rv);
607   EXPECT_EQ(OK, callback.WaitForResult());
608
609   // Fetched host1 and host2 again, since the ones done during initialization
610   // should not have been cached.
611   EXPECT_EQ(4u, host_resolver.num_resolve());
612
613   EXPECT_EQ("91.13.12.1-91.13.12.2-145.88.13.3-137.89.8.45:99",
614             proxy_info.proxy_server().ToURI());
615
616   // Check the NetLogs -- the script generated 2 alerts during initialization.
617   EXPECT_EQ(0u, request_log.GetSize());
618   CapturingNetLog::CapturedEntryList entries;
619   log.GetEntries(&entries);
620
621   ASSERT_EQ(2u, entries.size());
622   EXPECT_TRUE(
623       LogContainsEvent(entries, 0, NetLog::TYPE_PAC_JAVASCRIPT_ALERT,
624                        NetLog::PHASE_NONE));
625   EXPECT_TRUE(
626       LogContainsEvent(entries, 1, NetLog::TYPE_PAC_JAVASCRIPT_ALERT,
627                        NetLog::PHASE_NONE));
628
629   EXPECT_EQ("{\"message\":\"Watsup\"}", entries[0].GetParamsJson());
630   EXPECT_EQ("{\"message\":\"Watsup2\"}", entries[1].GetParamsJson());
631 }
632
633 // Tests a PAC script which does DNS resolves during initialization.
634 TEST_F(ProxyResolverV8TracingTest, DnsDuringInit) {
635   // Test with both both a host resolver that always completes asynchronously,
636   // and then again with one that completes synchronously.
637   DnsDuringInitHelper(false);
638   DnsDuringInitHelper(true);
639 }
640
641 void CrashCallback(int) {
642   // Be extra sure that if the callback ever gets invoked, the test will fail.
643   CHECK(false);
644 }
645
646 // Start some requests, cancel them all, and then destroy the resolver.
647 // Note the execution order for this test can vary. Since multiple
648 // threads are involved, the cancellation may be received a different
649 // times.
650 TEST_F(ProxyResolverV8TracingTest, CancelAll) {
651   MockCachingHostResolver host_resolver;
652   MockErrorObserver* error_observer = new MockErrorObserver;
653   ProxyResolverV8Tracing resolver(&host_resolver, error_observer, NULL);
654
655   host_resolver.rules()->AddSimulatedFailure("*");
656
657   InitResolver(&resolver, "dns.js");
658
659   const size_t kNumRequests = 5;
660   ProxyInfo proxy_info[kNumRequests];
661   ProxyResolver::RequestHandle request[kNumRequests];
662
663   for (size_t i = 0; i < kNumRequests; ++i) {
664     int rv = resolver.GetProxyForURL(
665         GURL("http://foo/"), &proxy_info[i],
666         base::Bind(&CrashCallback), &request[i], BoundNetLog());
667     EXPECT_EQ(ERR_IO_PENDING, rv);
668   }
669
670   for (size_t i = 0; i < kNumRequests; ++i) {
671     resolver.CancelRequest(request[i]);
672   }
673 }
674
675 // Note the execution order for this test can vary. Since multiple
676 // threads are involved, the cancellation may be received a different
677 // times.
678 TEST_F(ProxyResolverV8TracingTest, CancelSome) {
679   MockCachingHostResolver host_resolver;
680   MockErrorObserver* error_observer = new MockErrorObserver;
681   ProxyResolverV8Tracing resolver(&host_resolver, error_observer, NULL);
682
683   host_resolver.rules()->AddSimulatedFailure("*");
684
685   InitResolver(&resolver, "dns.js");
686
687   ProxyInfo proxy_info1;
688   ProxyInfo proxy_info2;
689   ProxyResolver::RequestHandle request1;
690   ProxyResolver::RequestHandle request2;
691   TestCompletionCallback callback;
692
693   int rv = resolver.GetProxyForURL(
694       GURL("http://foo/"), &proxy_info1,
695       base::Bind(&CrashCallback), &request1, BoundNetLog());
696   EXPECT_EQ(ERR_IO_PENDING, rv);
697
698   rv = resolver.GetProxyForURL(
699       GURL("http://foo/"), &proxy_info2,
700       callback.callback(), &request2, BoundNetLog());
701   EXPECT_EQ(ERR_IO_PENDING, rv);
702
703   resolver.CancelRequest(request1);
704
705   EXPECT_EQ(OK, callback.WaitForResult());
706 }
707
708 // Cancel a request after it has finished running on the worker thread, and has
709 // posted a task the completion task back to origin thread.
710 TEST_F(ProxyResolverV8TracingTest, CancelWhilePendingCompletionTask) {
711   MockCachingHostResolver host_resolver;
712   MockErrorObserver* error_observer = new MockErrorObserver;
713   ProxyResolverV8Tracing resolver(&host_resolver, error_observer, NULL);
714
715   host_resolver.rules()->AddSimulatedFailure("*");
716
717   InitResolver(&resolver, "error.js");
718
719   ProxyInfo proxy_info1;
720   ProxyInfo proxy_info2;
721   ProxyInfo proxy_info3;
722   ProxyResolver::RequestHandle request1;
723   ProxyResolver::RequestHandle request2;
724   ProxyResolver::RequestHandle request3;
725   TestCompletionCallback callback;
726
727   int rv = resolver.GetProxyForURL(
728       GURL("http://foo/"), &proxy_info1,
729       base::Bind(&CrashCallback), &request1, BoundNetLog());
730   EXPECT_EQ(ERR_IO_PENDING, rv);
731
732   rv = resolver.GetProxyForURL(
733       GURL("http://throw-an-error/"), &proxy_info2,
734       callback.callback(), &request2, BoundNetLog());
735   EXPECT_EQ(ERR_IO_PENDING, rv);
736
737   // Wait until the first request has finished running on the worker thread.
738   // (The second request will output an error).
739   error_observer->WaitForOutput();
740
741   // Cancel the first request, while it has a pending completion task on
742   // the origin thread.
743   resolver.CancelRequest(request1);
744
745   EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, callback.WaitForResult());
746
747   // Start another request, to make sure it is able to complete.
748   rv = resolver.GetProxyForURL(
749       GURL("http://i-have-no-idea-what-im-doing/"), &proxy_info3,
750       callback.callback(), &request3, BoundNetLog());
751   EXPECT_EQ(ERR_IO_PENDING, rv);
752
753   EXPECT_EQ(OK, callback.WaitForResult());
754
755   EXPECT_EQ("i-approve-this-message:42",
756             proxy_info3.proxy_server().ToURI());
757 }
758
759 // This implementation of HostResolver allows blocking until a resolve request
760 // has been received. The resolve requests it receives will never be completed.
761 class BlockableHostResolver : public HostResolver {
762  public:
763   BlockableHostResolver()
764       : num_cancelled_requests_(0), waiting_for_resolve_(false) {}
765
766   virtual int Resolve(const RequestInfo& info,
767                       RequestPriority priority,
768                       AddressList* addresses,
769                       const CompletionCallback& callback,
770                       RequestHandle* out_req,
771                       const BoundNetLog& net_log) OVERRIDE {
772     EXPECT_FALSE(callback.is_null());
773     EXPECT_TRUE(out_req);
774
775     if (!action_.is_null())
776       action_.Run();
777
778     // Indicate to the caller that a request was received.
779     EXPECT_TRUE(waiting_for_resolve_);
780     base::MessageLoop::current()->Quit();
781
782     // This line is intentionally after action_.Run(), since one of the
783     // tests does a cancellation inside of Resolve(), and it is more
784     // interesting if *out_req hasn't been written yet at that point.
785     *out_req = reinterpret_cast<RequestHandle*>(1);  // Magic value.
786
787     // Return ERR_IO_PENDING as this request will NEVER be completed.
788     // Expectation is for the caller to later cancel the request.
789     return ERR_IO_PENDING;
790   }
791
792   virtual int ResolveFromCache(const RequestInfo& info,
793                                AddressList* addresses,
794                                const BoundNetLog& net_log) OVERRIDE {
795     NOTREACHED();
796     return ERR_DNS_CACHE_MISS;
797   }
798
799   virtual void CancelRequest(RequestHandle req) OVERRIDE {
800     EXPECT_EQ(reinterpret_cast<RequestHandle*>(1), req);
801     num_cancelled_requests_++;
802   }
803
804   void SetAction(const base::Callback<void(void)>& action) {
805     action_ = action;
806   }
807
808   // Waits until Resolve() has been called.
809   void WaitUntilRequestIsReceived() {
810     waiting_for_resolve_ = true;
811     base::MessageLoop::current()->Run();
812     DCHECK(waiting_for_resolve_);
813     waiting_for_resolve_ = false;
814   }
815
816   int num_cancelled_requests() const {
817     return num_cancelled_requests_;
818   }
819
820  private:
821   int num_cancelled_requests_;
822   bool waiting_for_resolve_;
823   base::Callback<void(void)> action_;
824 };
825
826 // This cancellation test exercises a more predictable cancellation codepath --
827 // when the request has an outstanding DNS request in flight.
828 TEST_F(ProxyResolverV8TracingTest, CancelWhileOutstandingNonBlockingDns) {
829   BlockableHostResolver host_resolver;
830   MockErrorObserver* error_observer = new MockErrorObserver;
831   ProxyResolverV8Tracing resolver(&host_resolver, error_observer, NULL);
832
833   InitResolver(&resolver, "dns.js");
834
835   ProxyInfo proxy_info1;
836   ProxyInfo proxy_info2;
837   ProxyResolver::RequestHandle request1;
838   ProxyResolver::RequestHandle request2;
839
840   int rv = resolver.GetProxyForURL(
841       GURL("http://foo/req1"), &proxy_info1,
842       base::Bind(&CrashCallback), &request1, BoundNetLog());
843
844   EXPECT_EQ(ERR_IO_PENDING, rv);
845
846   host_resolver.WaitUntilRequestIsReceived();
847
848   rv = resolver.GetProxyForURL(
849       GURL("http://foo/req2"), &proxy_info2,
850       base::Bind(&CrashCallback), &request2, BoundNetLog());
851
852   EXPECT_EQ(ERR_IO_PENDING, rv);
853
854   host_resolver.WaitUntilRequestIsReceived();
855
856   resolver.CancelRequest(request1);
857   resolver.CancelRequest(request2);
858
859   EXPECT_EQ(2, host_resolver.num_cancelled_requests());
860
861   // After leaving this scope, the ProxyResolver is destroyed.
862   // This should not cause any problems, as the outstanding work
863   // should have been cancelled.
864 }
865
866 void CancelRequestAndPause(ProxyResolverV8Tracing* resolver,
867                            ProxyResolver::RequestHandle request) {
868   resolver->CancelRequest(request);
869
870   // Sleep for a little bit. This makes it more likely for the worker
871   // thread to have returned from its call, and serves as a regression
872   // test for http://crbug.com/173373.
873   base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(30));
874 }
875
876 // In non-blocking mode, the worker thread actually does block for
877 // a short time to see if the result is in the DNS cache. Test
878 // cancellation while the worker thread is waiting on this event.
879 TEST_F(ProxyResolverV8TracingTest, CancelWhileBlockedInNonBlockingDns) {
880   BlockableHostResolver host_resolver;
881   MockErrorObserver* error_observer = new MockErrorObserver;
882   ProxyResolverV8Tracing resolver(&host_resolver, error_observer, NULL);
883
884   InitResolver(&resolver, "dns.js");
885
886   ProxyInfo proxy_info;
887   ProxyResolver::RequestHandle request;
888
889   int rv = resolver.GetProxyForURL(
890       GURL("http://foo/"), &proxy_info,
891       base::Bind(&CrashCallback), &request, BoundNetLog());
892
893   EXPECT_EQ(ERR_IO_PENDING, rv);
894
895   host_resolver.SetAction(
896       base::Bind(CancelRequestAndPause, &resolver, request));
897
898   host_resolver.WaitUntilRequestIsReceived();
899
900   // At this point the host resolver ran Resolve(), and should have cancelled
901   // the request.
902
903   EXPECT_EQ(1, host_resolver.num_cancelled_requests());
904 }
905
906 // Cancel the request while there is a pending DNS request, however before
907 // the request is sent to the host resolver.
908 TEST_F(ProxyResolverV8TracingTest, CancelWhileBlockedInNonBlockingDns2) {
909   MockCachingHostResolver host_resolver;
910   MockErrorObserver* error_observer = new MockErrorObserver;
911   ProxyResolverV8Tracing resolver(&host_resolver, error_observer, NULL);
912
913   InitResolver(&resolver, "dns.js");
914
915   ProxyInfo proxy_info;
916   ProxyResolver::RequestHandle request;
917
918   int rv = resolver.GetProxyForURL(
919       GURL("http://foo/"), &proxy_info,
920       base::Bind(&CrashCallback), &request, BoundNetLog());
921
922   EXPECT_EQ(ERR_IO_PENDING, rv);
923
924   // Wait a bit, so the DNS task has hopefully been posted. The test will
925   // work whatever the delay is here, but it is most useful if the delay
926   // is large enough to allow a task to be posted back.
927   base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10));
928   resolver.CancelRequest(request);
929
930   EXPECT_EQ(0u, host_resolver.num_resolve());
931 }
932
933 TEST_F(ProxyResolverV8TracingTest, CancelSetPacWhileOutstandingBlockingDns) {
934   BlockableHostResolver host_resolver;
935   MockErrorObserver* error_observer = new MockErrorObserver;
936
937   ProxyResolverV8Tracing resolver(&host_resolver, error_observer, NULL);
938
939   int rv =
940       resolver.SetPacScript(LoadScriptData("dns_during_init.js"),
941       base::Bind(&CrashCallback));
942   EXPECT_EQ(ERR_IO_PENDING, rv);
943
944   host_resolver.WaitUntilRequestIsReceived();
945
946   resolver.CancelSetPacScript();
947   EXPECT_EQ(1, host_resolver.num_cancelled_requests());
948 }
949
950 // This tests that the execution of a PAC script is terminated when the DNS
951 // dependencies are missing. If the test fails, then it will hang.
952 TEST_F(ProxyResolverV8TracingTest, Terminate) {
953   CapturingNetLog log;
954   CapturingBoundNetLog request_log;
955   MockCachingHostResolver host_resolver;
956   MockErrorObserver* error_observer = new MockErrorObserver;
957   ProxyResolverV8Tracing resolver(&host_resolver, error_observer, &log);
958
959   host_resolver.rules()->AddRule("host1", "182.111.0.222");
960   host_resolver.rules()->AddRule("host2", "111.33.44.55");
961
962   InitResolver(&resolver, "terminate.js");
963
964   TestCompletionCallback callback;
965   ProxyInfo proxy_info;
966
967   int rv = resolver.GetProxyForURL(
968       GURL("http://foopy/req1"),
969       &proxy_info,
970       callback.callback(),
971       NULL,
972       request_log.bound());
973
974   EXPECT_EQ(ERR_IO_PENDING, rv);
975   EXPECT_EQ(OK, callback.WaitForResult());
976
977   // The test does 2 DNS resolutions.
978   EXPECT_EQ(2u, host_resolver.num_resolve());
979
980   EXPECT_EQ("foopy:3", proxy_info.proxy_server().ToURI());
981
982   // No errors.
983   EXPECT_EQ("", error_observer->GetOutput());
984
985   EXPECT_EQ(0u, log.GetSize());
986   EXPECT_EQ(0u, request_log.GetSize());
987 }
988
989 // Tests that multiple instances of ProxyResolverV8Tracing can coexist and run
990 // correctly at the same time. This is relevant because at the moment (time
991 // this test was written) each ProxyResolverV8Tracing creates its own thread to
992 // run V8 on, however each thread is operating on the same v8::Isolate.
993 TEST_F(ProxyResolverV8TracingTest, MultipleResolvers) {
994   // ------------------------
995   // Setup resolver0
996   // ------------------------
997   MockHostResolver host_resolver0;
998   host_resolver0.rules()->AddRuleForAddressFamily(
999       "host1", ADDRESS_FAMILY_IPV4, "166.155.144.44");
1000   host_resolver0.rules()
1001       ->AddIPLiteralRule("host1", "::1,192.168.1.1", std::string());
1002   host_resolver0.rules()->AddSimulatedFailure("host2");
1003   host_resolver0.rules()->AddRule("host3", "166.155.144.33");
1004   host_resolver0.rules()->AddRule("host5", "166.155.144.55");
1005   host_resolver0.rules()->AddSimulatedFailure("host6");
1006   host_resolver0.rules()->AddRuleForAddressFamily(
1007       "*", ADDRESS_FAMILY_IPV4, "122.133.144.155");
1008   host_resolver0.rules()->AddRule("*", "133.122.100.200");
1009   ProxyResolverV8Tracing resolver0(
1010       &host_resolver0, new MockErrorObserver, NULL);
1011   InitResolver(&resolver0, "dns.js");
1012
1013   // ------------------------
1014   // Setup resolver1
1015   // ------------------------
1016   ProxyResolverV8Tracing resolver1(
1017       &host_resolver0, new MockErrorObserver, NULL);
1018   InitResolver(&resolver1, "dns.js");
1019
1020   // ------------------------
1021   // Setup resolver2
1022   // ------------------------
1023   ProxyResolverV8Tracing resolver2(
1024       &host_resolver0, new MockErrorObserver, NULL);
1025   InitResolver(&resolver2, "simple.js");
1026
1027   // ------------------------
1028   // Setup resolver3
1029   // ------------------------
1030   MockHostResolver host_resolver3;
1031   host_resolver3.rules()->AddRule("foo", "166.155.144.33");
1032   ProxyResolverV8Tracing resolver3(
1033       &host_resolver3, new MockErrorObserver, NULL);
1034   InitResolver(&resolver3, "simple_dns.js");
1035
1036   // ------------------------
1037   // Queue up work for each resolver (which will be running in parallel).
1038   // ------------------------
1039
1040   ProxyResolverV8Tracing* resolver[] = {
1041     &resolver0, &resolver1, &resolver2, &resolver3,
1042   };
1043
1044   const size_t kNumResolvers = arraysize(resolver);
1045   const size_t kNumIterations = 20;
1046   const size_t kNumResults = kNumResolvers * kNumIterations;
1047   TestCompletionCallback callback[kNumResults];
1048   ProxyInfo proxy_info[kNumResults];
1049
1050   for (size_t i = 0; i < kNumResults; ++i) {
1051     size_t resolver_i = i % kNumResolvers;
1052     int rv = resolver[resolver_i]->GetProxyForURL(
1053         GURL("http://foo/"), &proxy_info[i], callback[i].callback(), NULL,
1054         BoundNetLog());
1055     EXPECT_EQ(ERR_IO_PENDING, rv);
1056   }
1057
1058   // ------------------------
1059   // Verify all of the results.
1060   // ------------------------
1061
1062   const char* kExpectedForDnsJs =
1063     "122.133.144.155-"  // myIpAddress()
1064     "null-"  // dnsResolve('')
1065     "__1_192.168.1.1-"  // dnsResolveEx('host1')
1066     "null-"  // dnsResolve('host2')
1067     "166.155.144.33-"  // dnsResolve('host3')
1068     "122.133.144.155-"  // myIpAddress()
1069     "166.155.144.33-"  // dnsResolve('host3')
1070     "__1_192.168.1.1-"  // dnsResolveEx('host1')
1071     "122.133.144.155-"  // myIpAddress()
1072     "null-"  // dnsResolve('host2')
1073     "-"  // dnsResolveEx('host6')
1074     "133.122.100.200-"  // myIpAddressEx()
1075     "166.155.144.44"  // dnsResolve('host1')
1076     ":99";
1077
1078   for (size_t i = 0; i < kNumResults; ++i) {
1079     size_t resolver_i = i % kNumResolvers;
1080     EXPECT_EQ(OK, callback[i].WaitForResult());
1081
1082     std::string proxy_uri = proxy_info[i].proxy_server().ToURI();
1083
1084     if (resolver_i == 0 || resolver_i == 1) {
1085       EXPECT_EQ(kExpectedForDnsJs, proxy_uri);
1086     } else if (resolver_i == 2) {
1087       EXPECT_EQ("foo:99", proxy_uri);
1088     } else if (resolver_i == 3) {
1089       EXPECT_EQ("166.155.144.33:",
1090                 proxy_uri.substr(0, proxy_uri.find(':') + 1));
1091     } else {
1092       NOTREACHED();
1093     }
1094   }
1095 }
1096
1097 }  // namespace
1098
1099 }  // namespace net