• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors
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_resolution/win/dhcp_pac_file_adapter_fetcher_win.h"
6 
7 #include <memory>
8 
9 #include "base/memory/ptr_util.h"
10 #include "base/memory/raw_ptr.h"
11 #include "base/run_loop.h"
12 #include "base/synchronization/waitable_event.h"
13 #include "base/task/thread_pool.h"
14 #include "base/test/task_environment.h"
15 #include "base/test/test_timeouts.h"
16 #include "base/threading/thread_restrictions.h"
17 #include "base/time/time.h"
18 #include "base/timer/elapsed_timer.h"
19 #include "base/timer/timer.h"
20 #include "net/base/net_errors.h"
21 #include "net/base/test_completion_callback.h"
22 #include "net/proxy_resolution/mock_pac_file_fetcher.h"
23 #include "net/proxy_resolution/pac_file_fetcher_impl.h"
24 #include "net/test/embedded_test_server/embedded_test_server.h"
25 #include "net/test/gtest_util.h"
26 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
27 #include "net/url_request/url_request_context.h"
28 #include "net/url_request/url_request_context_builder.h"
29 #include "net/url_request/url_request_test_util.h"
30 #include "testing/gmock/include/gmock/gmock.h"
31 #include "testing/gtest/include/gtest/gtest.h"
32 
33 using net::test::IsError;
34 using net::test::IsOk;
35 
36 namespace net {
37 
38 namespace {
39 
40 const char kPacUrl[] = "http://pacserver/script.pac";
41 
42 // In dhcp_pac_file_fetcher_win_unittest.cc there are
43 // a few tests that exercise DhcpPacFileAdapterFetcher end-to-end along with
44 // DhcpPacFileFetcherWin, i.e. it tests the end-to-end usage of Win32 APIs
45 // and the network.  In this file we test only by stubbing out functionality.
46 
47 // Version of DhcpPacFileAdapterFetcher that mocks out dependencies
48 // to allow unit testing.
49 class MockDhcpPacFileAdapterFetcher : public DhcpPacFileAdapterFetcher {
50  public:
MockDhcpPacFileAdapterFetcher(URLRequestContext * context,scoped_refptr<base::TaskRunner> task_runner)51   explicit MockDhcpPacFileAdapterFetcher(
52       URLRequestContext* context,
53       scoped_refptr<base::TaskRunner> task_runner)
54       : DhcpPacFileAdapterFetcher(context, task_runner),
55         timeout_(TestTimeouts::action_timeout()),
56         pac_script_("bingo") {}
57 
Cancel()58   void Cancel() override {
59     DhcpPacFileAdapterFetcher::Cancel();
60     fetcher_ = nullptr;
61   }
62 
ImplCreateScriptFetcher()63   std::unique_ptr<PacFileFetcher> ImplCreateScriptFetcher() override {
64     // We don't maintain ownership of the fetcher, it is transferred to
65     // the caller.
66     auto fetcher = std::make_unique<MockPacFileFetcher>();
67     fetcher_ = fetcher.get();
68     if (fetcher_delay_ms_ != -1) {
69       fetcher_timer_.Start(FROM_HERE, base::Milliseconds(fetcher_delay_ms_),
70                            this,
71                            &MockDhcpPacFileAdapterFetcher::OnFetcherTimer);
72     }
73     return fetcher;
74   }
75 
76   class DelayingDhcpQuery : public DhcpQuery {
77    public:
DelayingDhcpQuery()78     explicit DelayingDhcpQuery()
79         : DhcpQuery(),
80           test_finished_event_(
81               base::WaitableEvent::ResetPolicy::MANUAL,
82               base::WaitableEvent::InitialState::NOT_SIGNALED) {}
83 
ImplGetPacURLFromDhcp(const std::string & adapter_name)84     std::string ImplGetPacURLFromDhcp(
85         const std::string& adapter_name) override {
86       base::ElapsedTimer timer;
87       {
88         base::ScopedAllowBaseSyncPrimitivesForTesting
89             scoped_allow_base_sync_primitives;
90         test_finished_event_.TimedWait(dhcp_delay_);
91       }
92       return configured_url_;
93     }
94 
95     base::WaitableEvent test_finished_event_;
96     base::TimeDelta dhcp_delay_;
97     std::string configured_url_;
98 
99    private:
~DelayingDhcpQuery()100     ~DelayingDhcpQuery() override {}
101   };
102 
ImplCreateDhcpQuery()103   scoped_refptr<DhcpQuery> ImplCreateDhcpQuery() override {
104     dhcp_query_ = base::MakeRefCounted<DelayingDhcpQuery>();
105     dhcp_query_->dhcp_delay_ = dhcp_delay_;
106     dhcp_query_->configured_url_ = configured_url_;
107     return dhcp_query_;
108   }
109 
110   // Use a shorter timeout so tests can finish more quickly.
ImplGetTimeout() const111   base::TimeDelta ImplGetTimeout() const override { return timeout_; }
112 
OnFetcherTimer()113   void OnFetcherTimer() {
114     // Note that there is an assumption by this mock implementation that
115     // DhcpPacFileAdapterFetcher::Fetch will call ImplCreateScriptFetcher
116     // and call Fetch on the fetcher before the message loop is re-entered.
117     // This holds true today, but if you hit this DCHECK the problem can
118     // possibly be resolved by having a separate subclass of
119     // MockPacFileFetcher that adds the delay internally (instead of
120     // the simple approach currently used in ImplCreateScriptFetcher above).
121     DCHECK(fetcher_ && fetcher_->has_pending_request());
122     fetcher_->NotifyFetchCompletion(fetcher_result_, pac_script_);
123     fetcher_ = nullptr;
124   }
125 
IsWaitingForFetcher() const126   bool IsWaitingForFetcher() const {
127     return state() == STATE_WAIT_URL;
128   }
129 
WasCancelled() const130   bool WasCancelled() const {
131     return state() == STATE_CANCEL;
132   }
133 
FinishTest()134   void FinishTest() {
135     DCHECK(dhcp_query_.get());
136     dhcp_query_->test_finished_event_.Signal();
137   }
138 
139   base::TimeDelta dhcp_delay_ = base::Milliseconds(1);
140   base::TimeDelta timeout_;
141   std::string configured_url_{kPacUrl};
142   int fetcher_delay_ms_ = 1;
143   int fetcher_result_ = OK;
144   std::string pac_script_;
145   raw_ptr<MockPacFileFetcher, DanglingUntriaged> fetcher_;
146   base::OneShotTimer fetcher_timer_;
147   scoped_refptr<DelayingDhcpQuery> dhcp_query_;
148 };
149 
150 class FetcherClient {
151  public:
FetcherClient()152   FetcherClient()
153       : url_request_context_(CreateTestURLRequestContextBuilder()->Build()),
154         fetcher_(std::make_unique<MockDhcpPacFileAdapterFetcher>(
155             url_request_context_.get(),
156             base::ThreadPool::CreateSequencedTaskRunner(
157                 {base::MayBlock(),
158                  base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}))) {}
159 
WaitForResult(int expected_error)160   void WaitForResult(int expected_error) {
161     EXPECT_EQ(expected_error, callback_.WaitForResult());
162   }
163 
RunTest()164   void RunTest() {
165     fetcher_->Fetch("adapter name", callback_.callback(),
166                     TRAFFIC_ANNOTATION_FOR_TESTS);
167   }
168 
FinishTestAllowCleanup()169   void FinishTestAllowCleanup() {
170     fetcher_->FinishTest();
171     base::RunLoop().RunUntilIdle();
172   }
173 
174   TestCompletionCallback callback_;
175   std::unique_ptr<URLRequestContext> url_request_context_;
176   std::unique_ptr<MockDhcpPacFileAdapterFetcher> fetcher_;
177   std::u16string pac_text_;
178 };
179 
TEST(DhcpPacFileAdapterFetcher,NormalCaseURLNotInDhcp)180 TEST(DhcpPacFileAdapterFetcher, NormalCaseURLNotInDhcp) {
181   base::test::TaskEnvironment task_environment;
182 
183   FetcherClient client;
184   client.fetcher_->configured_url_ = "";
185   client.RunTest();
186   client.WaitForResult(ERR_PAC_NOT_IN_DHCP);
187   ASSERT_TRUE(client.fetcher_->DidFinish());
188   EXPECT_THAT(client.fetcher_->GetResult(), IsError(ERR_PAC_NOT_IN_DHCP));
189   EXPECT_EQ(std::u16string(), client.fetcher_->GetPacScript());
190 }
191 
TEST(DhcpPacFileAdapterFetcher,NormalCaseURLInDhcp)192 TEST(DhcpPacFileAdapterFetcher, NormalCaseURLInDhcp) {
193   base::test::TaskEnvironment task_environment;
194 
195   FetcherClient client;
196   client.RunTest();
197   client.WaitForResult(OK);
198   ASSERT_TRUE(client.fetcher_->DidFinish());
199   EXPECT_THAT(client.fetcher_->GetResult(), IsOk());
200   EXPECT_EQ(std::u16string(u"bingo"), client.fetcher_->GetPacScript());
201   EXPECT_EQ(GURL(kPacUrl), client.fetcher_->GetPacURL());
202 }
203 
TEST(DhcpPacFileAdapterFetcher,TimeoutDuringDhcp)204 TEST(DhcpPacFileAdapterFetcher, TimeoutDuringDhcp) {
205   base::test::TaskEnvironment task_environment;
206 
207   // Does a Fetch() with a long enough delay on accessing DHCP that the
208   // fetcher should time out.  This is to test a case manual testing found,
209   // where under certain circumstances (e.g. adapter enabled for DHCP and
210   // needs to retrieve its configuration from DHCP, but no DHCP server
211   // present on the network) accessing DHCP can take on the order of tens
212   // of seconds.
213   FetcherClient client;
214   client.fetcher_->dhcp_delay_ = TestTimeouts::action_max_timeout();
215   client.fetcher_->timeout_ = base::Milliseconds(25);
216 
217   base::ElapsedTimer timer;
218   client.RunTest();
219   // An error different from this would be received if the timeout didn't
220   // kick in.
221   client.WaitForResult(ERR_TIMED_OUT);
222 
223   ASSERT_TRUE(client.fetcher_->DidFinish());
224   EXPECT_THAT(client.fetcher_->GetResult(), IsError(ERR_TIMED_OUT));
225   EXPECT_EQ(std::u16string(), client.fetcher_->GetPacScript());
226   EXPECT_EQ(GURL(), client.fetcher_->GetPacURL());
227   client.FinishTestAllowCleanup();
228 }
229 
TEST(DhcpPacFileAdapterFetcher,CancelWhileDhcp)230 TEST(DhcpPacFileAdapterFetcher, CancelWhileDhcp) {
231   base::test::TaskEnvironment task_environment;
232 
233   FetcherClient client;
234   client.RunTest();
235   client.fetcher_->Cancel();
236   base::RunLoop().RunUntilIdle();
237   ASSERT_FALSE(client.fetcher_->DidFinish());
238   ASSERT_TRUE(client.fetcher_->WasCancelled());
239   EXPECT_THAT(client.fetcher_->GetResult(), IsError(ERR_ABORTED));
240   EXPECT_EQ(std::u16string(), client.fetcher_->GetPacScript());
241   EXPECT_EQ(GURL(), client.fetcher_->GetPacURL());
242   client.FinishTestAllowCleanup();
243 }
244 
TEST(DhcpPacFileAdapterFetcher,CancelWhileFetcher)245 TEST(DhcpPacFileAdapterFetcher, CancelWhileFetcher) {
246   base::test::TaskEnvironment task_environment;
247 
248   FetcherClient client;
249   // This causes the mock fetcher not to pretend the
250   // fetcher finishes after a timeout.
251   client.fetcher_->fetcher_delay_ms_ = -1;
252   client.RunTest();
253   int max_loops = 4;
254   while (!client.fetcher_->IsWaitingForFetcher() && max_loops--) {
255     base::PlatformThread::Sleep(base::Milliseconds(10));
256     base::RunLoop().RunUntilIdle();
257   }
258   client.fetcher_->Cancel();
259   base::RunLoop().RunUntilIdle();
260   ASSERT_FALSE(client.fetcher_->DidFinish());
261   ASSERT_TRUE(client.fetcher_->WasCancelled());
262   EXPECT_THAT(client.fetcher_->GetResult(), IsError(ERR_ABORTED));
263   EXPECT_EQ(std::u16string(), client.fetcher_->GetPacScript());
264   // GetPacURL() still returns the URL fetched in this case.
265   EXPECT_EQ(GURL(kPacUrl), client.fetcher_->GetPacURL());
266   client.FinishTestAllowCleanup();
267 }
268 
TEST(DhcpPacFileAdapterFetcher,CancelAtCompletion)269 TEST(DhcpPacFileAdapterFetcher, CancelAtCompletion) {
270   base::test::TaskEnvironment task_environment;
271 
272   FetcherClient client;
273   client.RunTest();
274   client.WaitForResult(OK);
275   client.fetcher_->Cancel();
276   // Canceling after you're done should have no effect, so these
277   // are identical expectations to the NormalCaseURLInDhcp test.
278   ASSERT_TRUE(client.fetcher_->DidFinish());
279   EXPECT_THAT(client.fetcher_->GetResult(), IsOk());
280   EXPECT_EQ(std::u16string(u"bingo"), client.fetcher_->GetPacScript());
281   EXPECT_EQ(GURL(kPacUrl), client.fetcher_->GetPacURL());
282   client.FinishTestAllowCleanup();
283 }
284 
285 // Does a real fetch on a mock DHCP configuration.
286 class MockDhcpRealFetchPacFileAdapterFetcher
287     : public MockDhcpPacFileAdapterFetcher {
288  public:
MockDhcpRealFetchPacFileAdapterFetcher(URLRequestContext * context,scoped_refptr<base::TaskRunner> task_runner)289   explicit MockDhcpRealFetchPacFileAdapterFetcher(
290       URLRequestContext* context,
291       scoped_refptr<base::TaskRunner> task_runner)
292       : MockDhcpPacFileAdapterFetcher(context, task_runner),
293         url_request_context_(context) {}
294 
295   // Returns a real PAC file fetcher.
ImplCreateScriptFetcher()296   std::unique_ptr<PacFileFetcher> ImplCreateScriptFetcher() override {
297     return PacFileFetcherImpl::Create(url_request_context_);
298   }
299 
300   raw_ptr<URLRequestContext, DanglingUntriaged> url_request_context_;
301 };
302 
TEST(DhcpPacFileAdapterFetcher,MockDhcpRealFetch)303 TEST(DhcpPacFileAdapterFetcher, MockDhcpRealFetch) {
304   base::test::TaskEnvironment task_environment;
305 
306   EmbeddedTestServer test_server;
307   test_server.ServeFilesFromSourceDirectory(
308       "net/data/pac_file_fetcher_unittest");
309   ASSERT_TRUE(test_server.Start());
310 
311   GURL configured_url = test_server.GetURL("/downloadable.pac");
312 
313   FetcherClient client;
314   auto url_request_context = CreateTestURLRequestContextBuilder()->Build();
315   client.fetcher_ = std::make_unique<MockDhcpRealFetchPacFileAdapterFetcher>(
316       url_request_context.get(),
317       base::ThreadPool::CreateTaskRunner(
318           {base::MayBlock(),
319            base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}));
320   client.fetcher_->configured_url_ = configured_url.spec();
321   client.RunTest();
322   client.WaitForResult(OK);
323   ASSERT_TRUE(client.fetcher_->DidFinish());
324   EXPECT_THAT(client.fetcher_->GetResult(), IsOk());
325   EXPECT_EQ(std::u16string(u"-downloadable.pac-\n"),
326             client.fetcher_->GetPacScript());
327   EXPECT_EQ(configured_url,
328             client.fetcher_->GetPacURL());
329 }
330 
331 #define BASE_URL "http://corpserver/proxy.pac"
332 
TEST(DhcpPacFileAdapterFetcher,SanitizeDhcpApiString)333 TEST(DhcpPacFileAdapterFetcher, SanitizeDhcpApiString) {
334   base::test::TaskEnvironment task_environment;
335 
336   const size_t kBaseUrlLen = strlen(BASE_URL);
337 
338   // Default case.
339   EXPECT_EQ(BASE_URL, DhcpPacFileAdapterFetcher::SanitizeDhcpApiString(
340                           BASE_URL, kBaseUrlLen));
341 
342   // Trailing \n and no null-termination.
343   EXPECT_EQ(BASE_URL, DhcpPacFileAdapterFetcher::SanitizeDhcpApiString(
344                           BASE_URL "\nblablabla", kBaseUrlLen + 1));
345 
346   // Embedded NULLs.
347   EXPECT_EQ(BASE_URL, DhcpPacFileAdapterFetcher::SanitizeDhcpApiString(
348                           BASE_URL "\0foo\0blat", kBaseUrlLen + 9));
349 }
350 
351 #undef BASE_URL
352 
353 }  // namespace
354 
355 }  // namespace net
356