1 //
2 // Copyright (C) 2011 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 
17 #include "update_engine/chrome_browser_proxy_resolver.h"
18 
19 #include <deque>
20 #include <string>
21 #include <vector>
22 
23 #include <gtest/gtest.h>
24 
25 #include <base/bind.h>
26 #include <brillo/make_unique_ptr.h>
27 #include <brillo/message_loops/fake_message_loop.h>
28 
29 #include "libcros/dbus-proxies.h"
30 #include "libcros/dbus-proxy-mocks.h"
31 #include "update_engine/dbus_test_utils.h"
32 
33 using ::testing::Return;
34 using ::testing::StrEq;
35 using ::testing::_;
36 using brillo::MessageLoop;
37 using org::chromium::LibCrosServiceInterfaceProxyMock;
38 using org::chromium::UpdateEngineLibcrosProxyResolvedInterfaceProxyMock;
39 using std::deque;
40 using std::string;
41 using std::vector;
42 
43 namespace chromeos_update_engine {
44 
45 class ChromeBrowserProxyResolverTest : public ::testing::Test {
46  protected:
ChromeBrowserProxyResolverTest()47   ChromeBrowserProxyResolverTest()
48       : service_interface_mock_(new LibCrosServiceInterfaceProxyMock()),
49         ue_proxy_resolved_interface_mock_(
50             new UpdateEngineLibcrosProxyResolvedInterfaceProxyMock()),
51         libcros_proxy_(
52             brillo::make_unique_ptr(service_interface_mock_),
53             brillo::make_unique_ptr(ue_proxy_resolved_interface_mock_)) {}
54 
SetUp()55   void SetUp() override {
56     loop_.SetAsCurrent();
57     // The ProxyResolved signal should be subscribed to.
58     MOCK_SIGNAL_HANDLER_EXPECT_SIGNAL_HANDLER(
59         ue_proxy_resolved_signal_,
60         *ue_proxy_resolved_interface_mock_,
61         ProxyResolved);
62 
63     EXPECT_TRUE(resolver_.Init());
64     // Run the loop once to dispatch the successfully registered signal handler.
65     EXPECT_TRUE(loop_.RunOnce(false));
66   }
67 
TearDown()68   void TearDown() override {
69     EXPECT_FALSE(loop_.PendingTasks());
70   }
71 
72   // Send the signal to the callback passed during registration of the
73   // ProxyResolved.
74   void SendReplySignal(const string& source_url,
75                        const string& proxy_info,
76                        const string& error_message);
77 
78   void RunTest(bool chrome_replies, bool chrome_alive);
79 
80   brillo::FakeMessageLoop loop_{nullptr};
81 
82   // Local pointers to the mocks. The instances are owned by the
83   // |libcros_proxy_|.
84   LibCrosServiceInterfaceProxyMock* service_interface_mock_;
85   UpdateEngineLibcrosProxyResolvedInterfaceProxyMock*
86       ue_proxy_resolved_interface_mock_;
87 
88   // The registered signal handler for the signal
89   // UpdateEngineLibcrosProxyResolvedInterface.ProxyResolved.
90   chromeos_update_engine::dbus_test_utils::MockSignalHandler<
91       void(const string&, const string&, const string&)>
92       ue_proxy_resolved_signal_;
93 
94   LibCrosProxy libcros_proxy_;
95   ChromeBrowserProxyResolver resolver_{&libcros_proxy_};
96 };
97 
98 
SendReplySignal(const string & source_url,const string & proxy_info,const string & error_message)99 void ChromeBrowserProxyResolverTest::SendReplySignal(
100     const string& source_url,
101     const string& proxy_info,
102     const string& error_message) {
103   ASSERT_TRUE(ue_proxy_resolved_signal_.IsHandlerRegistered());
104   ue_proxy_resolved_signal_.signal_callback().Run(
105       source_url, proxy_info, error_message);
106 }
107 
108 namespace {
CheckResponseResolved(const deque<string> & proxies)109 void CheckResponseResolved(const deque<string>& proxies) {
110   EXPECT_EQ(2U, proxies.size());
111   EXPECT_EQ("socks5://192.168.52.83:5555", proxies[0]);
112   EXPECT_EQ(kNoProxy, proxies[1]);
113   MessageLoop::current()->BreakLoop();
114 }
115 
CheckResponseNoReply(const deque<string> & proxies)116 void CheckResponseNoReply(const deque<string>& proxies) {
117   EXPECT_EQ(1U, proxies.size());
118   EXPECT_EQ(kNoProxy, proxies[0]);
119   MessageLoop::current()->BreakLoop();
120 }
121 }  // namespace
122 
123 // chrome_replies should be set to whether or not we fake a reply from
124 // chrome. If there's no reply, the resolver should time out.
125 // If chrome_alive is false, assume that sending to chrome fails.
RunTest(bool chrome_replies,bool chrome_alive)126 void ChromeBrowserProxyResolverTest::RunTest(bool chrome_replies,
127                                              bool chrome_alive) {
128   char kUrl[] = "http://example.com/blah";
129   char kProxyConfig[] = "SOCKS5 192.168.52.83:5555;DIRECT";
130 
131   EXPECT_CALL(*service_interface_mock_,
132               ResolveNetworkProxy(StrEq(kUrl),
133                                   StrEq(kLibCrosProxyResolveSignalInterface),
134                                   StrEq(kLibCrosProxyResolveName),
135                                   _,
136                                   _))
137       .WillOnce(Return(chrome_alive));
138 
139   ProxiesResolvedFn get_proxies_response = base::Bind(&CheckResponseNoReply);
140   if (chrome_replies) {
141     get_proxies_response = base::Bind(&CheckResponseResolved);
142     MessageLoop::current()->PostDelayedTask(
143         FROM_HERE,
144         base::Bind(&ChromeBrowserProxyResolverTest::SendReplySignal,
145                    base::Unretained(this),
146                    kUrl,
147                    kProxyConfig,
148                    ""),
149         base::TimeDelta::FromSeconds(1));
150   }
151 
152   EXPECT_NE(kProxyRequestIdNull,
153             resolver_.GetProxiesForUrl(kUrl, get_proxies_response));
154   MessageLoop::current()->Run();
155 }
156 
157 
TEST_F(ChromeBrowserProxyResolverTest,ParseTest)158 TEST_F(ChromeBrowserProxyResolverTest, ParseTest) {
159   // Test ideas from
160   // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_list_unittest.cc
161   vector<string> inputs = {
162       "PROXY foopy:10",
163       " DIRECT",  // leading space.
164       "PROXY foopy1 ; proxy foopy2;\t DIRECT",
165       "proxy foopy1 ; SOCKS foopy2",
166       "DIRECT ; proxy foopy1 ; DIRECT ; SOCKS5 foopy2;DIRECT ",
167       "DIRECT ; proxy foopy1:80; DIRECT ; DIRECT",
168       "PROXY-foopy:10",
169       "PROXY",
170       "PROXY foopy1 ; JUNK ; JUNK ; SOCKS5 foopy2 ; ;",
171       "HTTP foopy1; SOCKS5 foopy2"};
172   vector<deque<string>> outputs = {
173       {"http://foopy:10", kNoProxy},
174       {kNoProxy},
175       {"http://foopy1", "http://foopy2", kNoProxy},
176       {"http://foopy1", "socks4://foopy2", kNoProxy},
177       {kNoProxy, "http://foopy1", kNoProxy, "socks5://foopy2", kNoProxy},
178       {kNoProxy, "http://foopy1:80", kNoProxy, kNoProxy},
179       {kNoProxy},
180       {kNoProxy},
181       {"http://foopy1", "socks5://foopy2", kNoProxy},
182       {"socks5://foopy2", kNoProxy}};
183   ASSERT_EQ(inputs.size(), outputs.size());
184 
185   for (size_t i = 0; i < inputs.size(); i++) {
186     deque<string> results =
187         ChromeBrowserProxyResolver::ParseProxyString(inputs[i]);
188     deque<string>& expected = outputs[i];
189     EXPECT_EQ(results.size(), expected.size()) << "i = " << i;
190     if (expected.size() != results.size())
191       continue;
192     for (size_t j = 0; j < expected.size(); j++) {
193       EXPECT_EQ(expected[j], results[j]) << "i = " << i;
194     }
195   }
196 }
197 
TEST_F(ChromeBrowserProxyResolverTest,SuccessTest)198 TEST_F(ChromeBrowserProxyResolverTest, SuccessTest) {
199   RunTest(true, true);
200 }
201 
TEST_F(ChromeBrowserProxyResolverTest,NoReplyTest)202 TEST_F(ChromeBrowserProxyResolverTest, NoReplyTest) {
203   RunTest(false, true);
204 }
205 
TEST_F(ChromeBrowserProxyResolverTest,NoChromeTest)206 TEST_F(ChromeBrowserProxyResolverTest, NoChromeTest) {
207   RunTest(false, false);
208 }
209 
TEST_F(ChromeBrowserProxyResolverTest,CancelCallbackTest)210 TEST_F(ChromeBrowserProxyResolverTest, CancelCallbackTest) {
211   int called = 0;
212   auto callback = base::Bind(
213       [](int* called, const deque<string>& proxies) { (*called)++; }, &called);
214 
215   EXPECT_CALL(*service_interface_mock_, ResolveNetworkProxy(_, _, _, _, _))
216       .Times(4)
217       .WillRepeatedly(Return(true));
218 
219   EXPECT_NE(kProxyRequestIdNull,
220             resolver_.GetProxiesForUrl("http://urlA", callback));
221   ProxyRequestId req_b = resolver_.GetProxiesForUrl("http://urlB", callback);
222   // Note that we add twice the same url.
223   ProxyRequestId req_c = resolver_.GetProxiesForUrl("http://urlC", callback);
224   EXPECT_NE(kProxyRequestIdNull,
225             resolver_.GetProxiesForUrl("http://urlC", callback));
226 
227   EXPECT_EQ(0, called);
228   EXPECT_TRUE(resolver_.CancelProxyRequest(req_b));
229   EXPECT_TRUE(resolver_.CancelProxyRequest(req_c));
230   // Canceling the same request twice should fail even if there's another
231   // request for the same URL.
232   EXPECT_FALSE(resolver_.CancelProxyRequest(req_c));
233 
234   loop_.Run();
235   EXPECT_EQ(2, called);
236 }
237 
238 }  // namespace chromeos_update_engine
239