blob: 2cdf7a35b19d0df82deebae5dd375af5001e9d12 [file] [log] [blame]
// Copyright 2020 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 "chrome/browser/ash/wilco_dtc_supportd/wilco_dtc_supportd_web_request_service.h"
#include <map>
#include <memory>
#include "base/bind.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/run_loop.h"
#include "chrome/browser/ash/wilco_dtc_supportd/mojo_utils.h"
#include "chrome/browser/ash/wilco_dtc_supportd/wilco_dtc_supportd_network_context.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/net/system_network_context_manager.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/services/wilco_dtc_supportd/public/mojom/wilco_dtc_supportd.mojom.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/simple_url_loader_test_helper.h"
#include "net/cookies/site_for_cookies.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace ash {
namespace {
// Performs a request once using WilcoDctSupportdWebRequestService, waiting for
// the result to be available.
class ServiceRequestPerformer {
public:
explicit ServiceRequestPerformer(
WilcoDtcSupportdWebRequestService* web_request_service)
: web_request_service_(web_request_service) {
DCHECK(web_request_service_);
}
~ServiceRequestPerformer() = default;
ServiceRequestPerformer(const ServiceRequestPerformer&) = delete;
ServiceRequestPerformer& operator=(const ServiceRequestPerformer&) = delete;
void PerformRequest(const GURL& url) {
ASSERT_FALSE(request_performed_);
request_performed_ = true;
base::RunLoop run_loop;
web_request_service_->PerformRequest(
chromeos::wilco_dtc_supportd::mojom::
WilcoDtcSupportdWebRequestHttpMethod::kGet,
url, {}, "",
base::BindOnce(
[](base::OnceClosure callback,
chromeos::wilco_dtc_supportd::mojom::
WilcoDtcSupportdWebRequestStatus* out_status,
int* out_response, mojo::ScopedHandle* out_response_handle,
chromeos::wilco_dtc_supportd::mojom::
WilcoDtcSupportdWebRequestStatus status,
int response, mojo::ScopedHandle response_handle) {
*out_status = status;
*out_response = response;
*out_response_handle = std::move(response_handle);
std::move(callback).Run();
},
run_loop.QuitClosure(), &status_, &response_, &response_handle_));
run_loop.Run();
}
chromeos::wilco_dtc_supportd::mojom::WilcoDtcSupportdWebRequestStatus status()
const {
DCHECK(request_performed_);
return status_;
}
int response() const {
DCHECK(request_performed_);
return response_;
}
std::string response_body_release() {
DCHECK(request_performed_);
base::ReadOnlySharedMemoryMapping response_shared_memory;
return std::string(MojoUtils::GetStringPieceFromMojoHandle(
std::move(response_handle_), &response_shared_memory));
}
private:
bool request_performed_ = false;
// Not owned.
WilcoDtcSupportdWebRequestService* const web_request_service_;
// Results of the request:
chromeos::wilco_dtc_supportd::mojom::WilcoDtcSupportdWebRequestStatus
status_ = chromeos::wilco_dtc_supportd::mojom::
WilcoDtcSupportdWebRequestStatus::kOk;
int response_ = 0;
mojo::ScopedHandle response_handle_;
};
// Performs a request once using network::mojom::URLLoaderFactory, waiting for
// the result to be available.
class ContextRequestPerformer {
public:
explicit ContextRequestPerformer(
network::mojom::URLLoaderFactory* url_loader_factory)
: url_loader_factory_(url_loader_factory) {
DCHECK(url_loader_factory_);
}
~ContextRequestPerformer() = default;
ContextRequestPerformer(const ContextRequestPerformer&) = delete;
ContextRequestPerformer& operator=(const ContextRequestPerformer&) = delete;
void PerformRequest(const GURL& url) {
ASSERT_FALSE(request_performed_);
request_performed_ = true;
auto request = std::make_unique<network::ResourceRequest>();
request->url = url;
// Allow access to SameSite cookies.
request->site_for_cookies = net::SiteForCookies::FromUrl(url);
auto url_loader = network::SimpleURLLoader::Create(
std::move(request), TRAFFIC_ANNOTATION_FOR_TESTS);
url_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
url_loader_factory_, url_loader_test_helper_.GetCallback());
url_loader_test_helper_.WaitForCallback();
}
const std::string* response_body() const {
DCHECK(request_performed_);
return url_loader_test_helper_.response_body();
}
private:
bool request_performed_ = false;
network::mojom::URLLoaderFactory* const url_loader_factory_;
content::SimpleURLLoaderTestHelper url_loader_test_helper_;
};
} // namespace
class WilcoDtcSupportdNetworkContextTest : public InProcessBrowserTest {
public:
WilcoDtcSupportdNetworkContextTest()
: embedded_test_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
~WilcoDtcSupportdNetworkContextTest() override = default;
WilcoDtcSupportdNetworkContextTest(
const WilcoDtcSupportdNetworkContextTest&) = delete;
WilcoDtcSupportdNetworkContextTest& operator=(
const WilcoDtcSupportdNetworkContextTest&) = delete;
void SetUpOnMainThread() override {
auto network_context_impl =
std::make_unique<WilcoDtcSupportdNetworkContextImpl>();
network_context_impl_ptr_ = network_context_impl.get();
web_request_service_ = std::make_unique<WilcoDtcSupportdWebRequestService>(
std::move(network_context_impl));
}
void TearDownOnMainThread() override {
network_context_impl_ptr_ = nullptr;
web_request_service_.reset();
}
void StartServer() {
embedded_test_server()->AddDefaultHandlers(GetChromeTestDataDir());
ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
embedded_test_server()->StartAcceptingConnections();
}
net::EmbeddedTestServer* embedded_test_server() {
return &embedded_test_server_;
}
WilcoDtcSupportdNetworkContextImpl* network_context_impl() {
DCHECK(network_context_impl_ptr_);
return network_context_impl_ptr_;
}
WilcoDtcSupportdWebRequestService* web_request_service() {
DCHECK(web_request_service_);
return web_request_service_.get();
}
private:
net::EmbeddedTestServer embedded_test_server_;
// Owned by |web_request_service_|.
WilcoDtcSupportdNetworkContextImpl* network_context_impl_ptr_ = nullptr;
std::unique_ptr<WilcoDtcSupportdWebRequestService> web_request_service_;
};
// Verifies that by default it's not allowed to perform web requests to the
// local host.
IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,
LocalHostRequestsAreNotAllowed) {
ASSERT_NO_FATAL_FAILURE(StartServer());
{
ServiceRequestPerformer request_performer(web_request_service());
ASSERT_NO_FATAL_FAILURE(request_performer.PerformRequest(
embedded_test_server()->GetURL("/echo")));
EXPECT_EQ(request_performer.status(),
chromeos::wilco_dtc_supportd::mojom::
WilcoDtcSupportdWebRequestStatus::kNetworkError);
EXPECT_EQ(request_performer.response(), 0);
EXPECT_EQ(request_performer.response_body_release(), "");
}
}
// Verifies that client receives non-empty body in case of HTTP error.
IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,
NonEmpyResponseBodyOnNetworkError) {
ASSERT_NO_FATAL_FAILURE(StartServer());
web_request_service()->set_allow_local_requests_for_testing(true /* allow */);
{
ServiceRequestPerformer request_performer(web_request_service());
ASSERT_NO_FATAL_FAILURE(request_performer.PerformRequest(
embedded_test_server()->GetURL("/echo?status=500")));
EXPECT_EQ(request_performer.status(),
chromeos::wilco_dtc_supportd::mojom::
WilcoDtcSupportdWebRequestStatus::kHttpError);
EXPECT_EQ(request_performer.response(), 500);
EXPECT_EQ(request_performer.response_body_release(), "Echo");
}
}
// Verifies that client continues request if client SSL certifient was not
// requested by server.
IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,
NoClientCertificate) {
ASSERT_NO_FATAL_FAILURE(StartServer());
web_request_service()->set_allow_local_requests_for_testing(true /* allow */);
{
ServiceRequestPerformer request_performer(web_request_service());
ASSERT_NO_FATAL_FAILURE(request_performer.PerformRequest(
embedded_test_server()->GetURL("/echo")));
EXPECT_EQ(request_performer.status(),
chromeos::wilco_dtc_supportd::mojom::
WilcoDtcSupportdWebRequestStatus::kOk);
EXPECT_EQ(request_performer.response(), 200);
EXPECT_EQ(request_performer.response_body_release(), "Echo");
}
}
// Verifies that client continues request if client SSL certifient was
// requested by server as optional.
IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,
OptionalClientCertificate) {
net::SSLServerConfig ssl_server_config;
ssl_server_config.client_cert_type =
net::SSLServerConfig::ClientCertType::OPTIONAL_CLIENT_CERT;
embedded_test_server()->SetSSLConfig(
net::EmbeddedTestServer::ServerCertificate::CERT_OK, ssl_server_config);
ASSERT_NO_FATAL_FAILURE(StartServer());
web_request_service()->set_allow_local_requests_for_testing(true /* allow */);
{
ServiceRequestPerformer request_performer(web_request_service());
ASSERT_NO_FATAL_FAILURE(request_performer.PerformRequest(
embedded_test_server()->GetURL("/echo")));
EXPECT_EQ(request_performer.status(),
chromeos::wilco_dtc_supportd::mojom::
WilcoDtcSupportdWebRequestStatus::kOk);
EXPECT_EQ(request_performer.response(), 200);
EXPECT_EQ(request_performer.response_body_release(), "Echo");
}
}
// Verifies that client does not continue request if client SSL certifient was
// requested by server as required.
IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,
RequireClientCertificate) {
net::SSLServerConfig ssl_server_config;
ssl_server_config.client_cert_type =
net::SSLServerConfig::ClientCertType::REQUIRE_CLIENT_CERT;
embedded_test_server()->SetSSLConfig(
net::EmbeddedTestServer::ServerCertificate::CERT_OK, ssl_server_config);
ASSERT_NO_FATAL_FAILURE(StartServer());
web_request_service()->set_allow_local_requests_for_testing(true /* allow */);
{
ServiceRequestPerformer request_performer(web_request_service());
ASSERT_NO_FATAL_FAILURE(request_performer.PerformRequest(
embedded_test_server()->GetURL("/echo")));
EXPECT_EQ(request_performer.status(),
chromeos::wilco_dtc_supportd::mojom::
WilcoDtcSupportdWebRequestStatus::kNetworkError);
EXPECT_EQ(request_performer.response(), 0);
EXPECT_EQ(request_performer.response_body_release(), "");
}
}
// Verifies that WilcoDtcSupportdNetworkContext reuses valid connection to
// networking service.
IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,
ReuseValidNetworkingServiceConnection) {
ASSERT_NO_FATAL_FAILURE(StartServer());
web_request_service()->set_allow_local_requests_for_testing(true /* allow */);
{
ServiceRequestPerformer request_performer(web_request_service());
ASSERT_NO_FATAL_FAILURE(request_performer.PerformRequest(
embedded_test_server()->GetURL("/echo")));
EXPECT_EQ(request_performer.status(),
chromeos::wilco_dtc_supportd::mojom::
WilcoDtcSupportdWebRequestStatus::kOk);
EXPECT_EQ(request_performer.response(), 200);
EXPECT_EQ(request_performer.response_body_release(), "Echo");
}
{
ServiceRequestPerformer request_performer(web_request_service());
ASSERT_NO_FATAL_FAILURE(request_performer.PerformRequest(
embedded_test_server()->GetURL("/echo")));
EXPECT_EQ(request_performer.status(),
chromeos::wilco_dtc_supportd::mojom::
WilcoDtcSupportdWebRequestStatus::kOk);
EXPECT_EQ(request_performer.response(), 200);
EXPECT_EQ(request_performer.response_body_release(), "Echo");
}
}
// Verifies that WilcoDtcSupportdNetworkContext reconnects to network service
// on network service crash.
IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,
CrashNetworkingService) {
ASSERT_NO_FATAL_FAILURE(StartServer());
web_request_service()->set_allow_local_requests_for_testing(true /* allow */);
{
ServiceRequestPerformer request_performer(web_request_service());
ASSERT_NO_FATAL_FAILURE(request_performer.PerformRequest(
embedded_test_server()->GetURL("/echo")));
EXPECT_EQ(request_performer.status(),
chromeos::wilco_dtc_supportd::mojom::
WilcoDtcSupportdWebRequestStatus::kOk);
EXPECT_EQ(request_performer.response(), 200);
EXPECT_EQ(request_performer.response_body_release(), "Echo");
}
SimulateNetworkServiceCrash();
network_context_impl()->FlushForTesting();
{
ServiceRequestPerformer request_performer(web_request_service());
ASSERT_NO_FATAL_FAILURE(request_performer.PerformRequest(
embedded_test_server()->GetURL("/echo")));
EXPECT_EQ(request_performer.status(),
chromeos::wilco_dtc_supportd::mojom::
WilcoDtcSupportdWebRequestStatus::kOk);
EXPECT_EQ(request_performer.response(), 200);
EXPECT_EQ(request_performer.response_body_release(), "Echo");
}
}
// Verifies that WilcoDtcSupportdWebRequestService neither saves nor sends
// cookies.
IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest, NoCookies) {
ASSERT_NO_FATAL_FAILURE(StartServer());
web_request_service()->set_allow_local_requests_for_testing(true /* allow */);
// Verify that wilco network context neither sends nor saves any cookies.
{
ServiceRequestPerformer request_performer(web_request_service());
ASSERT_NO_FATAL_FAILURE(request_performer.PerformRequest(
embedded_test_server()->GetURL("/echoheader?cookie")));
EXPECT_EQ(request_performer.response_body_release(), "None");
}
{
const std::string kCookies = "foo=bar";
ServiceRequestPerformer request_performer(web_request_service());
ASSERT_NO_FATAL_FAILURE(request_performer.PerformRequest(
embedded_test_server()->GetURL("/set-cookie?" + kCookies)));
EXPECT_EQ(request_performer.response_body_release(), kCookies);
}
{
ServiceRequestPerformer request_performer(web_request_service());
ASSERT_NO_FATAL_FAILURE(request_performer.PerformRequest(
embedded_test_server()->GetURL("/echoheader?cookie")));
EXPECT_EQ(request_performer.response_body_release(), "None");
}
}
// Verifies that WilcoDtcSupportdNetworkContextImpl neither reads nor saves
// cookies in non wilco network contexts.
IN_PROC_BROWSER_TEST_F(WilcoDtcSupportdNetworkContextTest,
NotUsingOtherNetworkContexts) {
ASSERT_NO_FATAL_FAILURE(StartServer());
web_request_service()->set_allow_local_requests_for_testing(true /* allow */);
const std::map<std::string, network::mojom::URLLoaderFactory*>
url_loader_factories{
{"profile", browser()->profile()->GetURLLoaderFactory().get()},
{"system", g_browser_process->system_network_context_manager()
->GetSharedURLLoaderFactory()
.get()}};
for (const auto& iter : url_loader_factories) {
LOG(INFO)
<< "Verifying that wilco web request service does not use cookies from "
<< iter.first << " network context.";
const std::string kCookiesFooBar = "foo=bar";
// Setup foo=bar cookies for non wilco network context.
{
ContextRequestPerformer context_request_performer(iter.second);
ASSERT_NO_FATAL_FAILURE(context_request_performer.PerformRequest(
embedded_test_server()->GetURL("/echoheader?cookie")));
ASSERT_TRUE(context_request_performer.response_body());
EXPECT_EQ(*context_request_performer.response_body(), "None");
}
{
ContextRequestPerformer context_request_performer(iter.second);
ASSERT_NO_FATAL_FAILURE(context_request_performer.PerformRequest(
embedded_test_server()->GetURL("/set-cookie?" + kCookiesFooBar)));
ASSERT_TRUE(context_request_performer.response_body());
EXPECT_EQ(*context_request_performer.response_body(), kCookiesFooBar);
}
{
ContextRequestPerformer context_request_performer(iter.second);
ASSERT_NO_FATAL_FAILURE(context_request_performer.PerformRequest(
embedded_test_server()->GetURL("/echoheader?cookie")));
ASSERT_TRUE(context_request_performer.response_body());
EXPECT_EQ(*context_request_performer.response_body(), kCookiesFooBar);
}
const std::string kCookiesBarFoo = "bar=foo";
WilcoDtcSupportdNetworkContextImpl wilco_network_context;
// Verify that wilco network context neither reads nor saves cookies in
// non wilco network context.
{
ContextRequestPerformer context_request_performer(
wilco_network_context.GetURLLoaderFactory());
ASSERT_NO_FATAL_FAILURE(context_request_performer.PerformRequest(
embedded_test_server()->GetURL("/echoheader?cookie")));
EXPECT_EQ(*context_request_performer.response_body(), "None");
}
{
ContextRequestPerformer context_request_performer(
wilco_network_context.GetURLLoaderFactory());
ASSERT_NO_FATAL_FAILURE(context_request_performer.PerformRequest(
embedded_test_server()->GetURL("/set-cookie?" + kCookiesBarFoo)));
EXPECT_EQ(*context_request_performer.response_body(), kCookiesBarFoo);
}
{
ContextRequestPerformer context_request_performer(
wilco_network_context.GetURLLoaderFactory());
ASSERT_NO_FATAL_FAILURE(context_request_performer.PerformRequest(
embedded_test_server()->GetURL("/echoheader?cookie")));
EXPECT_EQ(*context_request_performer.response_body(), kCookiesBarFoo);
}
// Verify that we still have foo=bar cookies for non wilco network context.
{
ContextRequestPerformer context_request_performer(iter.second);
ASSERT_NO_FATAL_FAILURE(context_request_performer.PerformRequest(
embedded_test_server()->GetURL("/echoheader?cookie")));
ASSERT_TRUE(context_request_performer.response_body());
EXPECT_EQ(*context_request_performer.response_body(), kCookiesFooBar);
}
}
}
} // namespace ash