1 // Copyright 2015 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 "services/network/proxy_service_mojo.h"
6 
7 #include <algorithm>
8 #include <memory>
9 #include <string>
10 #include <utility>
11 
12 #include "base/callback_helpers.h"
13 #include "base/memory/ptr_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/test/task_environment.h"
16 #include "base/values.h"
17 #include "build/build_config.h"
18 #include "net/base/completion_once_callback.h"
19 #include "net/base/network_delegate_impl.h"
20 #include "net/base/network_isolation_key.h"
21 #include "net/base/test_completion_callback.h"
22 #include "net/dns/mock_host_resolver.h"
23 #include "net/log/net_log_event_type.h"
24 #include "net/log/net_log_with_source.h"
25 #include "net/log/test_net_log.h"
26 #include "net/log/test_net_log_util.h"
27 #include "net/proxy_resolution/configured_proxy_resolution_service.h"
28 #include "net/proxy_resolution/dhcp_pac_file_fetcher.h"
29 #include "net/proxy_resolution/mock_pac_file_fetcher.h"
30 #include "net/proxy_resolution/proxy_config_service_fixed.h"
31 #include "net/proxy_resolution/proxy_resolution_request.h"
32 #include "net/test/event_waiter.h"
33 #include "net/test/gtest_util.h"
34 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
35 #include "services/network/test_mojo_proxy_resolver_factory.h"
36 #include "services/proxy_resolver/public/mojom/proxy_resolver.mojom.h"
37 #include "testing/gmock/include/gmock/gmock.h"
38 #include "testing/gtest/include/gtest/gtest.h"
39 #include "url/gurl.h"
40 
41 using net::test::IsOk;
42 
43 namespace network {
44 
45 namespace {
46 
47 const char kPacUrl[] = "http://example.com/proxy.pac";
48 const char kSimplePacScript[] =
49     "function FindProxyForURL(url, host) {\n"
50     "  return 'PROXY foo:1234';\n"
51     "}";
52 const char kDnsResolvePacScript[] =
53     "function FindProxyForURL(url, host) {\n"
54     "  if (dnsResolveEx('example.com') != '1.2.3.4')\n"
55     "    return 'DIRECT';\n"
56     "  return 'QUIC bar:4321';\n"
57     "}";
58 const char kThrowingPacScript[] =
59     "function FindProxyForURL(url, host) {\n"
60     "  alert('alert: ' + host);\n"
61     "  throw new Error('error: ' + url);\n"
62     "}";
63 const char kThrowingOnLoadPacScript[] =
64     "function FindProxyForURL(url, host) {}\n"
65     "alert('alert: foo');\n"
66     "throw new Error('error: http://foo');";
67 
68 class TestNetworkDelegate : public net::NetworkDelegateImpl {
69  public:
70   enum Event {
71     PAC_SCRIPT_ERROR,
72   };
73 
event_waiter()74   net::EventWaiter<Event>& event_waiter() { return event_waiter_; }
75 
76   void OnPACScriptError(int line_number, const base::string16& error) override;
77 
78  private:
79   net::EventWaiter<Event> event_waiter_;
80 };
81 
OnPACScriptError(int line_number,const base::string16 & error)82 void TestNetworkDelegate::OnPACScriptError(int line_number,
83                                            const base::string16& error) {
84   event_waiter_.NotifyEvent(PAC_SCRIPT_ERROR);
85   EXPECT_EQ(3, line_number);
86   EXPECT_TRUE(base::UTF16ToUTF8(error).find("error: http://foo") !=
87               std::string::npos);
88 }
89 
CheckCapturedNetLogEntries(const std::vector<net::NetLogEntry> & entries)90 void CheckCapturedNetLogEntries(const std::vector<net::NetLogEntry>& entries) {
91   ASSERT_GT(entries.size(), 2u);
92   size_t i = 0;
93   // ProxyResolutionService records its own NetLog entries, so skip forward
94   // until the expected event type.
95   while (i < entries.size() &&
96          entries[i].type != net::NetLogEventType::PAC_JAVASCRIPT_ALERT) {
97     i++;
98   }
99   ASSERT_LT(i, entries.size());
100   EXPECT_EQ("alert: foo", net::GetStringValueFromParams(entries[i], "message"));
101   ASSERT_FALSE(entries[i].params.FindKey("line_number"));
102 
103   while (i < entries.size() &&
104          entries[i].type != net::NetLogEventType::PAC_JAVASCRIPT_ERROR) {
105     i++;
106   }
107   ASSERT_LT(i, entries.size());
108   EXPECT_THAT(net::GetStringValueFromParams(entries[i], "message"),
109               testing::HasSubstr("error: http://foo"));
110   EXPECT_EQ(3, net::GetIntegerValueFromParams(entries[i], "line_number"));
111 }
112 
113 }  // namespace
114 
115 class ProxyServiceMojoTest : public testing::Test {
116  protected:
SetUp()117   void SetUp() override {
118     mock_host_resolver_.rules()->AddRule("example.com", "1.2.3.4");
119 
120     fetcher_ = new net::MockPacFileFetcher;
121     proxy_resolution_service_ =
122         CreateConfiguredProxyResolutionServiceUsingMojoFactory(
123             test_mojo_proxy_resolver_factory_.CreateFactoryRemote(),
124             std::make_unique<net::ProxyConfigServiceFixed>(
125                 net::ProxyConfigWithAnnotation(
126                     net::ProxyConfig::CreateFromCustomPacURL(GURL(kPacUrl)),
127                     TRAFFIC_ANNOTATION_FOR_TESTS)),
128             base::WrapUnique(fetcher_),
129             std::make_unique<net::DoNothingDhcpPacFileFetcher>(),
130             &mock_host_resolver_, &net_log_, true /* pac_quick_check_enabled */,
131             &network_delegate_);
132   }
133 
134   base::test::TaskEnvironment task_environment_;
135   TestMojoProxyResolverFactory test_mojo_proxy_resolver_factory_;
136   TestNetworkDelegate network_delegate_;
137   net::MockHostResolver mock_host_resolver_;
138   // Owned by |proxy_resolution_service_|.
139   net::MockPacFileFetcher* fetcher_;
140   net::RecordingTestNetLog net_log_;
141   std::unique_ptr<net::ConfiguredProxyResolutionService>
142       proxy_resolution_service_;
143 };
144 
TEST_F(ProxyServiceMojoTest,Basic)145 TEST_F(ProxyServiceMojoTest, Basic) {
146   net::ProxyInfo info;
147   net::TestCompletionCallback callback;
148   std::unique_ptr<net::ProxyResolutionRequest> request;
149   EXPECT_EQ(net::ERR_IO_PENDING,
150             proxy_resolution_service_->ResolveProxy(
151                 GURL("http://foo"), std::string(), net::NetworkIsolationKey(),
152                 &info, callback.callback(), &request, net::NetLogWithSource()));
153 
154   // PAC file fetcher should have a fetch triggered by the first
155   // |ResolveProxy()| request.
156   EXPECT_TRUE(fetcher_->has_pending_request());
157   EXPECT_EQ(GURL(kPacUrl), fetcher_->pending_request_url());
158   fetcher_->NotifyFetchCompletion(net::OK, kSimplePacScript);
159 
160   EXPECT_THAT(callback.WaitForResult(), IsOk());
161   EXPECT_EQ("PROXY foo:1234", info.ToPacString());
162   EXPECT_EQ(0u, mock_host_resolver_.num_resolve());
163   proxy_resolution_service_.reset();
164 }
165 
TEST_F(ProxyServiceMojoTest,DnsResolution)166 TEST_F(ProxyServiceMojoTest, DnsResolution) {
167   net::ProxyInfo info;
168   net::TestCompletionCallback callback;
169   std::unique_ptr<net::ProxyResolutionRequest> request;
170   EXPECT_EQ(net::ERR_IO_PENDING,
171             proxy_resolution_service_->ResolveProxy(
172                 GURL("http://foo"), std::string(), net::NetworkIsolationKey(),
173                 &info, callback.callback(), &request, net::NetLogWithSource()));
174 
175   // PAC file fetcher should have a fetch triggered by the first
176   // |ResolveProxy()| request.
177   EXPECT_TRUE(fetcher_->has_pending_request());
178   EXPECT_EQ(GURL(kPacUrl), fetcher_->pending_request_url());
179 
180   fetcher_->NotifyFetchCompletion(net::OK, kDnsResolvePacScript);
181 
182   EXPECT_THAT(callback.WaitForResult(), IsOk());
183   EXPECT_EQ("QUIC bar:4321", info.ToPacString());
184   EXPECT_EQ(1u, mock_host_resolver_.num_resolve());
185   proxy_resolution_service_.reset();
186 }
187 
TEST_F(ProxyServiceMojoTest,Error)188 TEST_F(ProxyServiceMojoTest, Error) {
189   net::ProxyInfo info;
190   net::TestCompletionCallback callback;
191   net::RecordingBoundTestNetLog test_net_log;
192   std::unique_ptr<net::ProxyResolutionRequest> request;
193   EXPECT_EQ(net::ERR_IO_PENDING,
194             proxy_resolution_service_->ResolveProxy(
195                 GURL("http://foo"), std::string(), net::NetworkIsolationKey(),
196                 &info, callback.callback(), &request, test_net_log.bound()));
197 
198   // PAC file fetcher should have a fetch triggered by the first
199   // |ResolveProxy()| request.
200   EXPECT_TRUE(fetcher_->has_pending_request());
201   EXPECT_EQ(GURL(kPacUrl), fetcher_->pending_request_url());
202   fetcher_->NotifyFetchCompletion(net::OK, kThrowingPacScript);
203 
204   network_delegate_.event_waiter().WaitForEvent(
205       TestNetworkDelegate::PAC_SCRIPT_ERROR);
206 
207   EXPECT_THAT(callback.WaitForResult(), IsOk());
208   EXPECT_EQ("DIRECT", info.ToPacString());
209   EXPECT_EQ(0u, mock_host_resolver_.num_resolve());
210 
211   CheckCapturedNetLogEntries(test_net_log.GetEntries());
212   CheckCapturedNetLogEntries(net_log_.GetEntries());
213 }
214 
TEST_F(ProxyServiceMojoTest,ErrorOnInitialization)215 TEST_F(ProxyServiceMojoTest, ErrorOnInitialization) {
216   net::ProxyInfo info;
217   net::TestCompletionCallback callback;
218   std::unique_ptr<net::ProxyResolutionRequest> request;
219   EXPECT_EQ(net::ERR_IO_PENDING,
220             proxy_resolution_service_->ResolveProxy(
221                 GURL("http://foo"), std::string(), net::NetworkIsolationKey(),
222                 &info, callback.callback(), &request, net::NetLogWithSource()));
223 
224   // PAC file fetcher should have a fetch triggered by the first
225   // |ResolveProxy()| request.
226   EXPECT_TRUE(fetcher_->has_pending_request());
227   EXPECT_EQ(GURL(kPacUrl), fetcher_->pending_request_url());
228   fetcher_->NotifyFetchCompletion(net::OK, kThrowingOnLoadPacScript);
229 
230   network_delegate_.event_waiter().WaitForEvent(
231       TestNetworkDelegate::PAC_SCRIPT_ERROR);
232 
233   EXPECT_THAT(callback.WaitForResult(), IsOk());
234   EXPECT_EQ("DIRECT", info.ToPacString());
235   EXPECT_EQ(0u, mock_host_resolver_.num_resolve());
236 
237   CheckCapturedNetLogEntries(net_log_.GetEntries());
238 }
239 
240 }  // namespace network
241