1 // Copyright 2020 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 "chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_web_request_service.h"
6
7 #include <map>
8 #include <memory>
9
10 #include "base/bind.h"
11 #include "base/macros.h"
12 #include "base/memory/ref_counted.h"
13 #include "base/run_loop.h"
14 #include "chrome/browser/browser_process.h"
15 #include "chrome/browser/chromeos/wilco_dtc_supportd/mojo_utils.h"
16 #include "chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_network_context.h"
17 #include "chrome/browser/net/system_network_context_manager.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/ui/browser.h"
20 #include "chrome/services/wilco_dtc_supportd/public/mojom/wilco_dtc_supportd.mojom.h"
21 #include "chrome/test/base/in_process_browser_test.h"
22 #include "content/public/test/browser_test.h"
23 #include "content/public/test/simple_url_loader_test_helper.h"
24 #include "net/cookies/site_for_cookies.h"
25 #include "net/test/embedded_test_server/embedded_test_server.h"
26 #include "net/test/embedded_test_server/http_request.h"
27 #include "net/test/embedded_test_server/http_response.h"
28 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
29 #include "services/network/public/cpp/simple_url_loader.h"
30 #include "testing/gtest/include/gtest/gtest.h"
31 #include "url/gurl.h"
32
33 namespace chromeos {
34
35 namespace {
36
37 // Performs a request once using WilcoDctSupportdWebRequestService, waiting for
38 // the result to be available.
39 class ServiceRequestPerformer {
40 public:
ServiceRequestPerformer(WilcoDtcSupportdWebRequestService * web_request_service)41 explicit ServiceRequestPerformer(
42 WilcoDtcSupportdWebRequestService* web_request_service)
43 : web_request_service_(web_request_service) {
44 DCHECK(web_request_service_);
45 }
46
47 ~ServiceRequestPerformer() = default;
48
49 ServiceRequestPerformer(const ServiceRequestPerformer&) = delete;
50 ServiceRequestPerformer& operator=(const ServiceRequestPerformer&) = delete;
51
PerformRequest(const GURL & url)52 void PerformRequest(const GURL& url) {
53 ASSERT_FALSE(request_performed_);
54
55 request_performed_ = true;
56
57 base::RunLoop run_loop;
58 web_request_service_->PerformRequest(
59 wilco_dtc_supportd::mojom::WilcoDtcSupportdWebRequestHttpMethod::kGet,
60 url, {}, "",
61 base::BindOnce(
62 [](base::OnceClosure callback,
63 wilco_dtc_supportd::mojom::WilcoDtcSupportdWebRequestStatus*
64 out_status,
65 int* out_response, mojo::ScopedHandle* out_response_handle,
66 wilco_dtc_supportd::mojom::WilcoDtcSupportdWebRequestStatus
67 status,
68 int response, mojo::ScopedHandle response_handle) {
69 *out_status = status;
70 *out_response = response;
71 *out_response_handle = std::move(response_handle);
72 std::move(callback).Run();
73 },
74 run_loop.QuitClosure(), &status_, &response_, &response_handle_));
75 run_loop.Run();
76 }
77
status() const78 wilco_dtc_supportd::mojom::WilcoDtcSupportdWebRequestStatus status() const {
79 DCHECK(request_performed_);
80 return status_;
81 }
82
response() const83 int response() const {
84 DCHECK(request_performed_);
85 return response_;
86 }
87
response_body_release()88 std::string response_body_release() {
89 DCHECK(request_performed_);
90 base::ReadOnlySharedMemoryMapping response_shared_memory;
91 return MojoUtils::GetStringPieceFromMojoHandle(std::move(response_handle_),
92 &response_shared_memory)
93 .as_string();
94 }
95
96 private:
97 bool request_performed_ = false;
98
99 // Not owned.
100 WilcoDtcSupportdWebRequestService* const web_request_service_;
101
102 // Results of the request:
103 wilco_dtc_supportd::mojom::WilcoDtcSupportdWebRequestStatus status_ =
104 wilco_dtc_supportd::mojom::WilcoDtcSupportdWebRequestStatus::kOk;
105 int response_ = 0;
106 mojo::ScopedHandle response_handle_;
107 };
108
109 // Performs a request once using network::mojom::URLLoaderFactory, waiting for
110 // the result to be available.
111 class ContextRequestPerformer {
112 public:
ContextRequestPerformer(network::mojom::URLLoaderFactory * url_loader_factory)113 explicit ContextRequestPerformer(
114 network::mojom::URLLoaderFactory* url_loader_factory)
115 : url_loader_factory_(url_loader_factory) {
116 DCHECK(url_loader_factory_);
117 }
118
119 ~ContextRequestPerformer() = default;
120
121 ContextRequestPerformer(const ContextRequestPerformer&) = delete;
122 ContextRequestPerformer& operator=(const ContextRequestPerformer&) = delete;
123
PerformRequest(const GURL & url)124 void PerformRequest(const GURL& url) {
125 ASSERT_FALSE(request_performed_);
126 request_performed_ = true;
127
128 auto request = std::make_unique<network::ResourceRequest>();
129 request->url = url;
130 // Allow access to SameSite cookies.
131 request->site_for_cookies = net::SiteForCookies::FromUrl(url);
132
133 auto url_loader = network::SimpleURLLoader::Create(
134 std::move(request), TRAFFIC_ANNOTATION_FOR_TESTS);
135
136 url_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
137 url_loader_factory_, url_loader_test_helper_.GetCallback());
138 url_loader_test_helper_.WaitForCallback();
139 }
140
response_body() const141 const std::string* response_body() const {
142 DCHECK(request_performed_);
143 return url_loader_test_helper_.response_body();
144 }
145
146 private:
147 bool request_performed_ = false;
148
149 network::mojom::URLLoaderFactory* const url_loader_factory_;
150
151 content::SimpleURLLoaderTestHelper url_loader_test_helper_;
152 };
153
154 } // namespace
155
156 class WilcoDtcSupportdNetworkContextTest : public InProcessBrowserTest {
157 public:
WilcoDtcSupportdNetworkContextTest()158 WilcoDtcSupportdNetworkContextTest()
159 : embedded_test_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
160
161 ~WilcoDtcSupportdNetworkContextTest() override = default;
162
163 WilcoDtcSupportdNetworkContextTest(
164 const WilcoDtcSupportdNetworkContextTest&) = delete;
165 WilcoDtcSupportdNetworkContextTest& operator=(
166 const WilcoDtcSupportdNetworkContextTest&) = delete;
167
SetUpOnMainThread()168 void SetUpOnMainThread() override {
169 auto network_context_impl =
170 std::make_unique<WilcoDtcSupportdNetworkContextImpl>();
171 network_context_impl_ptr_ = network_context_impl.get();
172 web_request_service_ = std::make_unique<WilcoDtcSupportdWebRequestService>(
173 std::move(network_context_impl));
174 }
175
TearDownOnMainThread()176 void TearDownOnMainThread() override {
177 network_context_impl_ptr_ = nullptr;
178 web_request_service_.reset();
179 }
180
StartServer()181 void StartServer() {
182 embedded_test_server()->AddDefaultHandlers(GetChromeTestDataDir());
183 ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
184 embedded_test_server()->StartAcceptingConnections();
185 }
186
embedded_test_server()187 net::EmbeddedTestServer* embedded_test_server() {
188 return &embedded_test_server_;
189 }
190
network_context_impl()191 WilcoDtcSupportdNetworkContextImpl* network_context_impl() {
192 DCHECK(network_context_impl_ptr_);
193 return network_context_impl_ptr_;
194 }
195
web_request_service()196 WilcoDtcSupportdWebRequestService* web_request_service() {
197 DCHECK(web_request_service_);
198 return web_request_service_.get();
199 }
200
201 private:
202 net::EmbeddedTestServer embedded_test_server_;
203
204 // Owned by |web_request_service_|.
205 WilcoDtcSupportdNetworkContextImpl* network_context_impl_ptr_ = nullptr;
206
207 std::unique_ptr<WilcoDtcSupportdWebRequestService> web_request_service_;
208 };
209
210 // Verifies that by default it's not allowed to perform web requests to the
211 // local host.
IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,LocalHostRequestsAreNotAllowed)212 IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,
213 LocalHostRequestsAreNotAllowed) {
214 ASSERT_NO_FATAL_FAILURE(StartServer());
215
216 {
217 ServiceRequestPerformer request_performer(web_request_service());
218 ASSERT_NO_FATAL_FAILURE(request_performer.PerformRequest(
219 embedded_test_server()->GetURL("/echo")));
220 EXPECT_EQ(request_performer.status(),
221 wilco_dtc_supportd::mojom::WilcoDtcSupportdWebRequestStatus::
222 kNetworkError);
223 EXPECT_EQ(request_performer.response(), 0);
224 EXPECT_EQ(request_performer.response_body_release(), "");
225 }
226 }
227
228 // Verifies that client receives non-empty body in case of HTTP error.
IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,NonEmpyResponseBodyOnNetworkError)229 IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,
230 NonEmpyResponseBodyOnNetworkError) {
231 ASSERT_NO_FATAL_FAILURE(StartServer());
232
233 web_request_service()->set_allow_local_requests_for_testing(true /* allow */);
234
235 {
236 ServiceRequestPerformer request_performer(web_request_service());
237 ASSERT_NO_FATAL_FAILURE(request_performer.PerformRequest(
238 embedded_test_server()->GetURL("/echo?status=500")));
239 EXPECT_EQ(request_performer.status(),
240 wilco_dtc_supportd::mojom::WilcoDtcSupportdWebRequestStatus::
241 kHttpError);
242 EXPECT_EQ(request_performer.response(), 500);
243 EXPECT_EQ(request_performer.response_body_release(), "Echo");
244 }
245 }
246
247 // Verifies that client continues request if client SSL certifient was not
248 // requested by server.
IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,NoClientCertificate)249 IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,
250 NoClientCertificate) {
251 ASSERT_NO_FATAL_FAILURE(StartServer());
252
253 web_request_service()->set_allow_local_requests_for_testing(true /* allow */);
254
255 {
256 ServiceRequestPerformer request_performer(web_request_service());
257 ASSERT_NO_FATAL_FAILURE(request_performer.PerformRequest(
258 embedded_test_server()->GetURL("/echo")));
259 EXPECT_EQ(request_performer.status(),
260 wilco_dtc_supportd::mojom::WilcoDtcSupportdWebRequestStatus::kOk);
261 EXPECT_EQ(request_performer.response(), 200);
262 EXPECT_EQ(request_performer.response_body_release(), "Echo");
263 }
264 }
265
266 // Verifies that client continues request if client SSL certifient was
267 // requested by server as optional.
IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,OptionalClientCertificate)268 IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,
269 OptionalClientCertificate) {
270 net::SSLServerConfig ssl_server_config;
271 ssl_server_config.client_cert_type =
272 net::SSLServerConfig::ClientCertType::OPTIONAL_CLIENT_CERT;
273 embedded_test_server()->SetSSLConfig(
274 net::EmbeddedTestServer::ServerCertificate::CERT_OK, ssl_server_config);
275
276 ASSERT_NO_FATAL_FAILURE(StartServer());
277
278 web_request_service()->set_allow_local_requests_for_testing(true /* allow */);
279
280 {
281 ServiceRequestPerformer request_performer(web_request_service());
282 ASSERT_NO_FATAL_FAILURE(request_performer.PerformRequest(
283 embedded_test_server()->GetURL("/echo")));
284 EXPECT_EQ(request_performer.status(),
285 wilco_dtc_supportd::mojom::WilcoDtcSupportdWebRequestStatus::kOk);
286 EXPECT_EQ(request_performer.response(), 200);
287 EXPECT_EQ(request_performer.response_body_release(), "Echo");
288 }
289 }
290
291 // Verifies that client does not continue request if client SSL certifient was
292 // requested by server as required.
IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,RequireClientCertificate)293 IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,
294 RequireClientCertificate) {
295 net::SSLServerConfig ssl_server_config;
296 ssl_server_config.client_cert_type =
297 net::SSLServerConfig::ClientCertType::REQUIRE_CLIENT_CERT;
298 embedded_test_server()->SetSSLConfig(
299 net::EmbeddedTestServer::ServerCertificate::CERT_OK, ssl_server_config);
300
301 ASSERT_NO_FATAL_FAILURE(StartServer());
302
303 web_request_service()->set_allow_local_requests_for_testing(true /* allow */);
304
305 {
306 ServiceRequestPerformer request_performer(web_request_service());
307 ASSERT_NO_FATAL_FAILURE(request_performer.PerformRequest(
308 embedded_test_server()->GetURL("/echo")));
309 EXPECT_EQ(request_performer.status(),
310 wilco_dtc_supportd::mojom::WilcoDtcSupportdWebRequestStatus::
311 kNetworkError);
312 EXPECT_EQ(request_performer.response(), 0);
313 EXPECT_EQ(request_performer.response_body_release(), "");
314 }
315 }
316
317 // Verifies that WilcoDtcSupportdNetworkContext reuses valid connection to
318 // networking service.
IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,ReuseValidNetworkingServiceConnection)319 IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,
320 ReuseValidNetworkingServiceConnection) {
321 ASSERT_NO_FATAL_FAILURE(StartServer());
322
323 web_request_service()->set_allow_local_requests_for_testing(true /* allow */);
324
325 {
326 ServiceRequestPerformer request_performer(web_request_service());
327 ASSERT_NO_FATAL_FAILURE(request_performer.PerformRequest(
328 embedded_test_server()->GetURL("/echo")));
329 EXPECT_EQ(request_performer.status(),
330 wilco_dtc_supportd::mojom::WilcoDtcSupportdWebRequestStatus::kOk);
331 EXPECT_EQ(request_performer.response(), 200);
332 EXPECT_EQ(request_performer.response_body_release(), "Echo");
333 }
334
335 {
336 ServiceRequestPerformer request_performer(web_request_service());
337 ASSERT_NO_FATAL_FAILURE(request_performer.PerformRequest(
338 embedded_test_server()->GetURL("/echo")));
339 EXPECT_EQ(request_performer.status(),
340 wilco_dtc_supportd::mojom::WilcoDtcSupportdWebRequestStatus::kOk);
341 EXPECT_EQ(request_performer.response(), 200);
342 EXPECT_EQ(request_performer.response_body_release(), "Echo");
343 }
344 }
345
346 // Verifies that WilcoDtcSupportdNetworkContext reconnects to network service
347 // on network service crash.
IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,CrashNetworkingService)348 IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,
349 CrashNetworkingService) {
350 ASSERT_NO_FATAL_FAILURE(StartServer());
351
352 web_request_service()->set_allow_local_requests_for_testing(true /* allow */);
353
354 {
355 ServiceRequestPerformer request_performer(web_request_service());
356 ASSERT_NO_FATAL_FAILURE(request_performer.PerformRequest(
357 embedded_test_server()->GetURL("/echo")));
358 EXPECT_EQ(request_performer.status(),
359 wilco_dtc_supportd::mojom::WilcoDtcSupportdWebRequestStatus::kOk);
360 EXPECT_EQ(request_performer.response(), 200);
361 EXPECT_EQ(request_performer.response_body_release(), "Echo");
362 }
363
364 SimulateNetworkServiceCrash();
365 network_context_impl()->FlushForTesting();
366
367 {
368 ServiceRequestPerformer request_performer(web_request_service());
369 ASSERT_NO_FATAL_FAILURE(request_performer.PerformRequest(
370 embedded_test_server()->GetURL("/echo")));
371 EXPECT_EQ(request_performer.status(),
372 wilco_dtc_supportd::mojom::WilcoDtcSupportdWebRequestStatus::kOk);
373 EXPECT_EQ(request_performer.response(), 200);
374 EXPECT_EQ(request_performer.response_body_release(), "Echo");
375 }
376 }
377
378 // Verifies that WilcoDtcSupportdWebRequestService neither saves nor sends
379 // cookies.
IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,NoCookies)380 IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest, NoCookies) {
381 ASSERT_NO_FATAL_FAILURE(StartServer());
382
383 web_request_service()->set_allow_local_requests_for_testing(true /* allow */);
384
385 // Verify that wilco network context neither sends nor saves any cookies.
386 {
387 ServiceRequestPerformer request_performer(web_request_service());
388 ASSERT_NO_FATAL_FAILURE(request_performer.PerformRequest(
389 embedded_test_server()->GetURL("/echoheader?cookie")));
390 EXPECT_EQ(request_performer.response_body_release(), "None");
391 }
392 {
393 const std::string kCookies = "foo=bar";
394
395 ServiceRequestPerformer request_performer(web_request_service());
396 ASSERT_NO_FATAL_FAILURE(request_performer.PerformRequest(
397 embedded_test_server()->GetURL("/set-cookie?" + kCookies)));
398 EXPECT_EQ(request_performer.response_body_release(), kCookies);
399 }
400 {
401 ServiceRequestPerformer request_performer(web_request_service());
402 ASSERT_NO_FATAL_FAILURE(request_performer.PerformRequest(
403 embedded_test_server()->GetURL("/echoheader?cookie")));
404 EXPECT_EQ(request_performer.response_body_release(), "None");
405 }
406 }
407
408 // Verifies that WilcoDtcSupportdNetworkContextImpl neither reads nor saves
409 // cookies in non wilco network contexts.
IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,NotUsingOtherNetworkContexts)410 IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,
411 NotUsingOtherNetworkContexts) {
412 ASSERT_NO_FATAL_FAILURE(StartServer());
413
414 web_request_service()->set_allow_local_requests_for_testing(true /* allow */);
415
416 const std::map<std::string, network::mojom::URLLoaderFactory*>
417 url_loader_factories{
418 {"profile", browser()->profile()->GetURLLoaderFactory().get()},
419 {"system", g_browser_process->system_network_context_manager()
420 ->GetSharedURLLoaderFactory()
421 .get()}};
422
423 for (const auto& iter : url_loader_factories) {
424 LOG(INFO)
425 << "Verifying that wilco web request service does not use cookies from "
426 << iter.first << " network context.";
427
428 const std::string kCookiesFooBar = "foo=bar";
429
430 // Setup foo=bar cookies for non wilco network context.
431 {
432 ContextRequestPerformer context_request_performer(iter.second);
433 ASSERT_NO_FATAL_FAILURE(context_request_performer.PerformRequest(
434 embedded_test_server()->GetURL("/echoheader?cookie")));
435 ASSERT_TRUE(context_request_performer.response_body());
436 EXPECT_EQ(*context_request_performer.response_body(), "None");
437 }
438 {
439 ContextRequestPerformer context_request_performer(iter.second);
440 ASSERT_NO_FATAL_FAILURE(context_request_performer.PerformRequest(
441 embedded_test_server()->GetURL("/set-cookie?" + kCookiesFooBar)));
442 ASSERT_TRUE(context_request_performer.response_body());
443 EXPECT_EQ(*context_request_performer.response_body(), kCookiesFooBar);
444 }
445 {
446 ContextRequestPerformer context_request_performer(iter.second);
447 ASSERT_NO_FATAL_FAILURE(context_request_performer.PerformRequest(
448 embedded_test_server()->GetURL("/echoheader?cookie")));
449 ASSERT_TRUE(context_request_performer.response_body());
450 EXPECT_EQ(*context_request_performer.response_body(), kCookiesFooBar);
451 }
452
453 const std::string kCookiesBarFoo = "bar=foo";
454 WilcoDtcSupportdNetworkContextImpl wilco_network_context;
455
456 // Verify that wilco network context neither reads nor saves cookies in
457 // non wilco network context.
458 {
459 ContextRequestPerformer context_request_performer(
460 wilco_network_context.GetURLLoaderFactory());
461 ASSERT_NO_FATAL_FAILURE(context_request_performer.PerformRequest(
462 embedded_test_server()->GetURL("/echoheader?cookie")));
463 EXPECT_EQ(*context_request_performer.response_body(), "None");
464 }
465 {
466 ContextRequestPerformer context_request_performer(
467 wilco_network_context.GetURLLoaderFactory());
468 ASSERT_NO_FATAL_FAILURE(context_request_performer.PerformRequest(
469 embedded_test_server()->GetURL("/set-cookie?" + kCookiesBarFoo)));
470 EXPECT_EQ(*context_request_performer.response_body(), kCookiesBarFoo);
471 }
472 {
473 ContextRequestPerformer context_request_performer(
474 wilco_network_context.GetURLLoaderFactory());
475 ASSERT_NO_FATAL_FAILURE(context_request_performer.PerformRequest(
476 embedded_test_server()->GetURL("/echoheader?cookie")));
477 EXPECT_EQ(*context_request_performer.response_body(), kCookiesBarFoo);
478 }
479
480 // Verify that we still have foo=bar cookies for non wilco network context.
481 {
482 ContextRequestPerformer context_request_performer(iter.second);
483 ASSERT_NO_FATAL_FAILURE(context_request_performer.PerformRequest(
484 embedded_test_server()->GetURL("/echoheader?cookie")));
485 ASSERT_TRUE(context_request_performer.response_body());
486 EXPECT_EQ(*context_request_performer.response_body(), kCookiesFooBar);
487 }
488 }
489 }
490
491 } // namespace chromeos
492