summaryrefslogtreecommitdiffstats
path: root/net/proxy/proxy_resolver_js_bindings_unittest.cc
blob: 97d69bf4bbc1bfba35ce22c70b2a2fb45057b393 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/proxy/proxy_resolver_js_bindings.h"

#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
#include "base/string_util.h"
#include "net/base/address_list.h"
#include "net/base/host_cache.h"
#include "net/base/mock_host_resolver.h"
#include "net/base/net_errors.h"
#include "net/base/net_log.h"
#include "net/base/net_log_unittest.h"
#include "net/base/net_util.h"
#include "net/base/test_completion_callback.h"
#include "net/proxy/proxy_resolver_request_context.h"
#include "net/proxy/sync_host_resolver.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace net {

namespace {

// This is a HostResolver that synchronously resolves all hosts to the
// following address list of length 3:
//     192.168.1.1
//     172.22.34.1
//     200.100.1.2
class MockHostResolverWithMultipleResults : public SyncHostResolver {
 public:
  // HostResolver methods:
  virtual int Resolve(const HostResolver::RequestInfo& info,
                      AddressList* addresses,
                      const BoundNetLog& bound_net_log) OVERRIDE {
    return ParseAddressList("192.168.1.1,172.22.34.1,200.100.1.2", "",
                            addresses);
  }

  virtual void Shutdown() OVERRIDE {}

 private:
  virtual ~MockHostResolverWithMultipleResults() {}
};

class MockFailingHostResolver : public SyncHostResolver {
 public:
  MockFailingHostResolver() : count_(0) {}

  // HostResolver methods:
  virtual int Resolve(const HostResolver::RequestInfo& info,
                      AddressList* addresses,
                      const BoundNetLog& bound_net_log) OVERRIDE {
    count_++;
    return ERR_NAME_NOT_RESOLVED;
  }

  virtual void Shutdown() OVERRIDE {}

  // Returns the number of times Resolve() has been called.
  int count() const { return count_; }
  void ResetCount() { count_ = 0; }

 private:
  int count_;
};

class MockSyncHostResolver : public SyncHostResolver {
 public:
  MockSyncHostResolver() {
    resolver_.set_synchronous_mode(true);
  }

  virtual int Resolve(const HostResolver::RequestInfo& info,
                      AddressList* addresses,
                      const BoundNetLog& bound_net_log) OVERRIDE {
    return resolver_.Resolve(info, addresses, CompletionCallback(), NULL,
                             bound_net_log);
  }

  virtual void Shutdown() OVERRIDE {}

  RuleBasedHostResolverProc* rules() {
    return resolver_.rules();
  }

 private:
  MockHostResolver resolver_;
};

TEST(ProxyResolverJSBindingsTest, DnsResolve) {
  MockSyncHostResolver* host_resolver = new MockSyncHostResolver;

  // Get a hold of a DefaultJSBindings* (it is a hidden impl class).
  scoped_ptr<ProxyResolverJSBindings> bindings(
      ProxyResolverJSBindings::CreateDefault(host_resolver, NULL, NULL));

  std::string ip_address;

  // Empty string is not considered a valid host (even though on some systems
  // requesting this will resolve to localhost).
  host_resolver->rules()->AddSimulatedFailure("");
  EXPECT_FALSE(bindings->DnsResolve("", &ip_address));

  // Should call through to the HostResolver.
  host_resolver->rules()->AddRule("google.com", "192.168.1.1");
  EXPECT_TRUE(bindings->DnsResolve("google.com", &ip_address));
  EXPECT_EQ("192.168.1.1", ip_address);

  // Resolve failures should give empty string.
  host_resolver->rules()->AddSimulatedFailure("fail");
  EXPECT_FALSE(bindings->DnsResolve("fail", &ip_address));

  // TODO(eroman): would be nice to have an IPV6 test here too, but that
  // won't work on all systems.
}

TEST(ProxyResolverJSBindingsTest, MyIpAddress) {
  MockSyncHostResolver* host_resolver = new MockSyncHostResolver;

  // Get a hold of a DefaultJSBindings* (it is a hidden impl class).
  scoped_ptr<ProxyResolverJSBindings> bindings(
      ProxyResolverJSBindings::CreateDefault(host_resolver, NULL, NULL));

  // Our IP address is always going to be 127.0.0.1, since we are using a
  // mock host resolver.
  std::string my_ip_address;
  EXPECT_TRUE(bindings->MyIpAddress(&my_ip_address));

  EXPECT_EQ("127.0.0.1", my_ip_address);
}

// Tests that the regular PAC functions restrict results to IPv4,
// but that the Microsoft extensions to PAC do not. We test this
// by seeing whether ADDRESS_FAMILY_IPV4 or ADDRESS_FAMILY_UNSPECIFIED
// was passed into to the host resolver.
//
//   Restricted to IPv4 address family:
//     myIpAddress()
//     dnsResolve()
//
//   Unrestricted address family:
//     myIpAddressEx()
//     dnsResolveEx()
TEST(ProxyResolverJSBindingsTest, RestrictAddressFamily) {
  MockSyncHostResolver* host_resolver = new MockSyncHostResolver;

  // Get a hold of a DefaultJSBindings* (it is a hidden impl class).
  scoped_ptr<ProxyResolverJSBindings> bindings(
      ProxyResolverJSBindings::CreateDefault(host_resolver, NULL, NULL));

  // Make it so requests resolve to particular address patterns based on family:
  //  IPV4_ONLY --> 192.168.1.*
  //  UNSPECIFIED --> 192.168.2.1
  host_resolver->rules()->AddRuleForAddressFamily(
      "foo", ADDRESS_FAMILY_IPV4, "192.168.1.1");
  host_resolver->rules()->AddRuleForAddressFamily(
      "*", ADDRESS_FAMILY_IPV4, "192.168.1.2");
  host_resolver->rules()->AddRuleForAddressFamily(
      "foo", ADDRESS_FAMILY_UNSPECIFIED, "192.168.2.1");
  host_resolver->rules()->AddRuleForAddressFamily(
      "*", ADDRESS_FAMILY_UNSPECIFIED, "192.168.2.2");

  // Verify that our mock setups works as expected, and we get different results
  // depending if the address family was IPV4_ONLY or not.
  HostResolver::RequestInfo info(HostPortPair("foo", 80));
  AddressList address_list;
  EXPECT_EQ(OK, host_resolver->Resolve(info, &address_list, BoundNetLog()));
  ASSERT_FALSE(address_list.empty());
  EXPECT_EQ("192.168.2.1", address_list.front().ToStringWithoutPort());

  info.set_address_family(ADDRESS_FAMILY_IPV4);
  EXPECT_EQ(OK, host_resolver->Resolve(info, &address_list, BoundNetLog()));
  ASSERT_FALSE(address_list.empty());
  EXPECT_EQ("192.168.1.1", address_list.front().ToStringWithoutPort());

  std::string ip_address;
  // Now the actual test.
  EXPECT_TRUE(bindings->MyIpAddress(&ip_address));
  EXPECT_EQ("192.168.1.2", ip_address);  // IPv4 restricted.

  EXPECT_TRUE(bindings->DnsResolve("foo", &ip_address));
  EXPECT_EQ("192.168.1.1", ip_address);  // IPv4 restricted.

  EXPECT_TRUE(bindings->DnsResolve("foo2", &ip_address));
  EXPECT_EQ("192.168.1.2", ip_address);  // IPv4 restricted.

  EXPECT_TRUE(bindings->MyIpAddressEx(&ip_address));
  EXPECT_EQ("192.168.2.2", ip_address);  // Unrestricted.

  EXPECT_TRUE(bindings->DnsResolveEx("foo", &ip_address));
  EXPECT_EQ("192.168.2.1", ip_address);  // Unrestricted.

  EXPECT_TRUE(bindings->DnsResolveEx("foo2", &ip_address));
  EXPECT_EQ("192.168.2.2", ip_address);  // Unrestricted.
}

// Test that myIpAddressEx() and dnsResolveEx() both return a semi-colon
// separated list of addresses (as opposed to the non-Ex versions which
// just return the first result).
TEST(ProxyResolverJSBindingsTest, ExFunctionsReturnList) {
  SyncHostResolver* host_resolver =
      new MockHostResolverWithMultipleResults;

  // Get a hold of a DefaultJSBindings* (it is a hidden impl class).
  scoped_ptr<ProxyResolverJSBindings> bindings(
      ProxyResolverJSBindings::CreateDefault(host_resolver, NULL, NULL));

  std::string ip_addresses;

  EXPECT_TRUE(bindings->MyIpAddressEx(&ip_addresses));
  EXPECT_EQ("192.168.1.1;172.22.34.1;200.100.1.2", ip_addresses);

  EXPECT_TRUE(bindings->DnsResolveEx("FOO", &ip_addresses));
  EXPECT_EQ("192.168.1.1;172.22.34.1;200.100.1.2", ip_addresses);
}

TEST(ProxyResolverJSBindingsTest, PerRequestDNSCache) {
  MockFailingHostResolver* host_resolver = new MockFailingHostResolver;

  // Get a hold of a DefaultJSBindings* (it is a hidden impl class).
  scoped_ptr<ProxyResolverJSBindings> bindings(
      ProxyResolverJSBindings::CreateDefault(host_resolver, NULL, NULL));

  std::string ip_address;

  // Call DnsResolve() 4 times for the same hostname -- this should issue
  // 4 separate calls to the underlying host resolver, since there is no
  // current request context.
  EXPECT_FALSE(bindings->DnsResolve("foo", &ip_address));
  EXPECT_FALSE(bindings->DnsResolve("foo", &ip_address));
  EXPECT_FALSE(bindings->DnsResolve("foo", &ip_address));
  EXPECT_FALSE(bindings->DnsResolve("foo", &ip_address));
  EXPECT_EQ(4, host_resolver->count());

  host_resolver->ResetCount();

  // Now setup a per-request context, and try the same experiment -- we
  // expect the underlying host resolver to receive only 1 request this time,
  // since it will service the others from the per-request DNS cache.
  const unsigned kMaxCacheEntries = 50;
  HostCache cache(kMaxCacheEntries);
  ProxyResolverRequestContext context(NULL, &cache);
  bindings->set_current_request_context(&context);

  EXPECT_FALSE(bindings->DnsResolve("foo", &ip_address));
  EXPECT_FALSE(bindings->DnsResolve("foo", &ip_address));
  EXPECT_FALSE(bindings->DnsResolve("foo", &ip_address));
  EXPECT_FALSE(bindings->DnsResolve("foo", &ip_address));
  EXPECT_EQ(1, host_resolver->count());

  host_resolver->ResetCount();

  // The "Ex" version shares this same cache, however since the flags
  // are different it won't reuse this particular entry.
  EXPECT_FALSE(bindings->DnsResolveEx("foo", &ip_address));
  EXPECT_EQ(1, host_resolver->count());
  EXPECT_FALSE(bindings->DnsResolveEx("foo", &ip_address));
  EXPECT_FALSE(bindings->DnsResolveEx("foo", &ip_address));
  EXPECT_EQ(1, host_resolver->count());

  bindings->set_current_request_context(NULL);
}

// Test that when a binding is called, it logs to the per-request NetLog.
TEST(ProxyResolverJSBindingsTest, NetLog) {
  MockFailingHostResolver* host_resolver = new MockFailingHostResolver;

  CapturingNetLog global_log;

  // Get a hold of a DefaultJSBindings* (it is a hidden impl class).
  scoped_ptr<ProxyResolverJSBindings> bindings(
      ProxyResolverJSBindings::CreateDefault(
          host_resolver, &global_log, NULL));

  // Attach a capturing NetLog as the current request's log stream.
  CapturingNetLog log;
  BoundNetLog bound_log(BoundNetLog::Make(&log, NetLog::SOURCE_NONE));
  ProxyResolverRequestContext context(&bound_log, NULL);
  bindings->set_current_request_context(&context);

  std::string ip_address;
  net::CapturingNetLog::CapturedEntryList entries;
  log.GetEntries(&entries);
  ASSERT_EQ(0u, entries.size());

  // Call all the bindings. Each call should be logging something to
  // our NetLog.

  bindings->MyIpAddress(&ip_address);

  log.GetEntries(&entries);
  EXPECT_EQ(2u, entries.size());
  EXPECT_TRUE(LogContainsBeginEvent(
      entries, 0, NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS));
  EXPECT_TRUE(LogContainsEndEvent(
      entries, 1, NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS));

  bindings->MyIpAddressEx(&ip_address);

  log.GetEntries(&entries);
  EXPECT_EQ(4u, entries.size());
  EXPECT_TRUE(LogContainsBeginEvent(
      entries, 2, NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS_EX));
  EXPECT_TRUE(LogContainsEndEvent(
      entries, 3, NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS_EX));

  bindings->DnsResolve("foo", &ip_address);

  log.GetEntries(&entries);
  EXPECT_EQ(6u, entries.size());
  EXPECT_TRUE(LogContainsBeginEvent(
      entries, 4, NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE));
  EXPECT_TRUE(LogContainsEndEvent(
      entries, 5, NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE));

  bindings->DnsResolveEx("foo", &ip_address);

  log.GetEntries(&entries);
  EXPECT_EQ(8u, entries.size());
  EXPECT_TRUE(LogContainsBeginEvent(
      entries, 6, NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE_EX));
  EXPECT_TRUE(LogContainsEndEvent(
      entries, 7, NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE_EX));

  // Nothing has been emitted globally yet.
  net::CapturingNetLog::CapturedEntryList global_log_entries;
  global_log.GetEntries(&global_log_entries);
  EXPECT_EQ(0u, global_log_entries.size());

  bindings->OnError(30, string16());

  log.GetEntries(&entries);
  EXPECT_EQ(9u, entries.size());
  EXPECT_TRUE(LogContainsEvent(
      entries, 8, NetLog::TYPE_PAC_JAVASCRIPT_ERROR,
      NetLog::PHASE_NONE));

  // We also emit errors to the top-level log stream.
  global_log.GetEntries(&global_log_entries);
  EXPECT_EQ(1u, global_log_entries.size());
  EXPECT_TRUE(LogContainsEvent(
      global_log_entries, 0, NetLog::TYPE_PAC_JAVASCRIPT_ERROR,
      NetLog::PHASE_NONE));

  bindings->Alert(string16());

  log.GetEntries(&entries);
  EXPECT_EQ(10u, entries.size());
  EXPECT_TRUE(LogContainsEvent(
      entries, 9, NetLog::TYPE_PAC_JAVASCRIPT_ALERT,
      NetLog::PHASE_NONE));

  // We also emit javascript alerts to the top-level log stream.
  global_log.GetEntries(&global_log_entries);
  EXPECT_EQ(2u, global_log_entries.size());
  EXPECT_TRUE(LogContainsEvent(
      global_log_entries, 1, NetLog::TYPE_PAC_JAVASCRIPT_ALERT,
      NetLog::PHASE_NONE));
}

}  // namespace

}  // namespace net