summaryrefslogtreecommitdiff
path: root/chromium/chrome/browser/signin/chrome_signin_proxying_url_loader_factory_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/chrome/browser/signin/chrome_signin_proxying_url_loader_factory_unittest.cc')
-rw-r--r--chromium/chrome/browser/signin/chrome_signin_proxying_url_loader_factory_unittest.cc359
1 files changed, 359 insertions, 0 deletions
diff --git a/chromium/chrome/browser/signin/chrome_signin_proxying_url_loader_factory_unittest.cc b/chromium/chrome/browser/signin/chrome_signin_proxying_url_loader_factory_unittest.cc
new file mode 100644
index 00000000000..2d9772cc070
--- /dev/null
+++ b/chromium/chrome/browser/signin/chrome_signin_proxying_url_loader_factory_unittest.cc
@@ -0,0 +1,359 @@
+// Copyright 2018 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/signin/chrome_signin_proxying_url_loader_factory.h"
+
+#include <algorithm>
+#include <memory>
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/run_loop.h"
+#include "base/test/mock_callback.h"
+#include "chrome/browser/signin/chrome_signin_helper.h"
+#include "chrome/browser/signin/header_modification_delegate.h"
+#include "content/public/test/browser_task_environment.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+#include "services/network/public/mojom/fetch_api.mojom-shared.h"
+#include "services/network/public/mojom/url_response_head.mojom.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::Invoke;
+using testing::_;
+
+namespace signin {
+
+namespace {
+
+class MockDelegate : public HeaderModificationDelegate {
+ public:
+ MockDelegate() = default;
+
+ MockDelegate(const MockDelegate&) = delete;
+ MockDelegate& operator=(const MockDelegate&) = delete;
+
+ ~MockDelegate() override = default;
+
+ MOCK_METHOD1(ShouldInterceptNavigation, bool(content::WebContents* contents));
+ MOCK_METHOD2(ProcessRequest,
+ void(ChromeRequestAdapter* request_adapter,
+ const GURL& redirect_url));
+ MOCK_METHOD2(ProcessResponse,
+ void(ResponseAdapter* response_adapter,
+ const GURL& redirect_url));
+
+ base::WeakPtr<MockDelegate> GetWeakPtr() {
+ return weak_factory_.GetWeakPtr();
+ }
+
+ private:
+ base::WeakPtrFactory<MockDelegate> weak_factory_{this};
+};
+
+content::WebContents::Getter NullWebContentsGetter() {
+ return base::BindRepeating([]() -> content::WebContents* { return nullptr; });
+}
+
+} // namespace
+
+class ChromeSigninProxyingURLLoaderFactoryTest : public testing::Test {
+ public:
+ ChromeSigninProxyingURLLoaderFactoryTest()
+ : test_factory_receiver_(&test_factory_) {}
+
+ ChromeSigninProxyingURLLoaderFactoryTest(
+ const ChromeSigninProxyingURLLoaderFactoryTest&) = delete;
+ ChromeSigninProxyingURLLoaderFactoryTest& operator=(
+ const ChromeSigninProxyingURLLoaderFactoryTest&) = delete;
+
+ ~ChromeSigninProxyingURLLoaderFactoryTest() override {}
+
+ base::WeakPtr<MockDelegate> StartRequest(
+ std::unique_ptr<network::ResourceRequest> request) {
+ loader_ = network::SimpleURLLoader::Create(std::move(request),
+ TRAFFIC_ANNOTATION_FOR_TESTS);
+
+ mojo::Remote<network::mojom::URLLoaderFactory> factory_remote;
+ auto factory_request = factory_remote.BindNewPipeAndPassReceiver();
+ loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+ factory_remote.get(),
+ base::BindOnce(
+ &ChromeSigninProxyingURLLoaderFactoryTest::OnDownloadComplete,
+ base::Unretained(this)));
+
+ auto delegate = std::make_unique<MockDelegate>();
+ base::WeakPtr<MockDelegate> delegate_weak = delegate->GetWeakPtr();
+
+ proxying_factory_ = std::make_unique<ProxyingURLLoaderFactory>(
+ std::move(delegate), NullWebContentsGetter(),
+ std::move(factory_request),
+ test_factory_receiver_.BindNewPipeAndPassRemote(),
+ base::BindOnce(&ChromeSigninProxyingURLLoaderFactoryTest::OnDisconnect,
+ base::Unretained(this)));
+
+ return delegate_weak;
+ }
+
+ void CloseFactoryReceiver() { test_factory_receiver_.reset(); }
+
+ network::TestURLLoaderFactory* factory() { return &test_factory_; }
+ network::SimpleURLLoader* loader() { return loader_.get(); }
+ std::string* response_body() { return response_body_.get(); }
+
+ void OnDownloadComplete(std::unique_ptr<std::string> body) {
+ response_body_ = std::move(body);
+ }
+
+ private:
+ void OnDisconnect(ProxyingURLLoaderFactory* factory) {
+ EXPECT_EQ(factory, proxying_factory_.get());
+ proxying_factory_.reset();
+ }
+
+ content::BrowserTaskEnvironment task_environment_;
+ std::unique_ptr<network::SimpleURLLoader> loader_;
+ std::unique_ptr<ProxyingURLLoaderFactory> proxying_factory_;
+ network::TestURLLoaderFactory test_factory_;
+ mojo::Receiver<network::mojom::URLLoaderFactory> test_factory_receiver_;
+ std::unique_ptr<std::string> response_body_;
+};
+
+TEST_F(ChromeSigninProxyingURLLoaderFactoryTest, NoModification) {
+ auto request = std::make_unique<network::ResourceRequest>();
+ request->url = GURL("https://google.com/");
+
+ factory()->AddResponse("https://google.com/", "Hello.");
+ base::WeakPtr<MockDelegate> delegate = StartRequest(std::move(request));
+
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(net::OK, loader()->NetError());
+ ASSERT_TRUE(response_body());
+ EXPECT_EQ("Hello.", *response_body());
+}
+
+TEST_F(ChromeSigninProxyingURLLoaderFactoryTest, ModifyHeaders) {
+ const GURL kTestURL("https://google.com/index.html");
+ const GURL kTestReferrer("https://chrome.com/referrer.html");
+ const GURL kTestRedirectURL("https://youtube.com/index.html");
+
+ // Set up the request.
+ auto request = std::make_unique<network::ResourceRequest>();
+ request->url = kTestURL;
+ request->referrer = kTestReferrer;
+ request->destination = network::mojom::RequestDestination::kDocument;
+ request->is_main_frame = true;
+ request->headers.SetHeader("X-Request-1", "Foo");
+
+ base::WeakPtr<MockDelegate> delegate = StartRequest(std::move(request));
+
+ // The first destruction callback added by ProcessRequest is expected to be
+ // called. The second (added after a redirect) will not be.
+ base::MockCallback<base::OnceClosure> destruction_callback;
+ EXPECT_CALL(destruction_callback, Run()).Times(1);
+ base::MockCallback<base::OnceClosure> ignored_destruction_callback;
+ EXPECT_CALL(ignored_destruction_callback, Run()).Times(0);
+
+ // The delegate will be called twice to process a request, first when the
+ // request is started and again when the request is redirected.
+ EXPECT_CALL(*delegate, ProcessRequest(_, _))
+ .WillOnce(
+ Invoke([&](ChromeRequestAdapter* adapter, const GURL& redirect_url) {
+ EXPECT_EQ(kTestURL, adapter->GetUrl());
+ EXPECT_EQ(network::mojom::RequestDestination::kDocument,
+ adapter->GetRequestDestination());
+ EXPECT_EQ(GURL("https://chrome.com"), adapter->GetReferrerOrigin());
+
+ EXPECT_TRUE(adapter->HasHeader("X-Request-1"));
+ adapter->RemoveRequestHeaderByName("X-Request-1");
+ EXPECT_FALSE(adapter->HasHeader("X-Request-1"));
+
+ adapter->SetExtraHeaderByName("X-Request-2", "Bar");
+ EXPECT_TRUE(adapter->HasHeader("X-Request-2"));
+
+ EXPECT_EQ(GURL(), redirect_url);
+
+ adapter->SetDestructionCallback(destruction_callback.Get());
+ }))
+ .WillOnce(
+ Invoke([&](ChromeRequestAdapter* adapter, const GURL& redirect_url) {
+ EXPECT_EQ(network::mojom::RequestDestination::kDocument,
+ adapter->GetRequestDestination());
+
+ // Changes to the URL and referrer take effect after the redirect
+ // is followed.
+ EXPECT_EQ(kTestURL, adapter->GetUrl());
+ EXPECT_EQ(GURL("https://chrome.com"), adapter->GetReferrerOrigin());
+
+ // X-Request-1 and X-Request-2 were modified in the previous call to
+ // ProcessRequest(). These changes should still be present.
+ EXPECT_FALSE(adapter->HasHeader("X-Request-1"));
+ EXPECT_TRUE(adapter->HasHeader("X-Request-2"));
+
+ adapter->RemoveRequestHeaderByName("X-Request-2");
+ EXPECT_FALSE(adapter->HasHeader("X-Request-2"));
+
+ adapter->SetExtraHeaderByName("X-Request-3", "Baz");
+ EXPECT_TRUE(adapter->HasHeader("X-Request-3"));
+
+ EXPECT_EQ(kTestRedirectURL, redirect_url);
+
+ adapter->SetDestructionCallback(ignored_destruction_callback.Get());
+ }));
+
+ const void* const kResponseUserDataKey = &kResponseUserDataKey;
+ std::unique_ptr<base::SupportsUserData::Data> response_user_data =
+ std::make_unique<base::SupportsUserData::Data>();
+ base::SupportsUserData::Data* response_user_data_ptr =
+ response_user_data.get();
+
+ // The delegate will also be called twice to process a response, first when
+ // the redirect is received and again for the redirect response.
+ EXPECT_CALL(*delegate, ProcessResponse(_, _))
+ .WillOnce(Invoke([&](ResponseAdapter* adapter, const GURL& redirect_url) {
+ EXPECT_EQ(GURL("https://google.com"), adapter->GetOrigin());
+ EXPECT_TRUE(adapter->IsMainFrame());
+
+ adapter->SetUserData(kResponseUserDataKey,
+ std::move(response_user_data));
+ EXPECT_EQ(response_user_data_ptr,
+ adapter->GetUserData(kResponseUserDataKey));
+
+ const net::HttpResponseHeaders* headers = adapter->GetHeaders();
+ EXPECT_TRUE(headers->HasHeader("X-Response-1"));
+ EXPECT_TRUE(headers->HasHeader("X-Response-2"));
+ adapter->RemoveHeader("X-Response-2");
+
+ EXPECT_EQ(kTestRedirectURL, redirect_url);
+ }))
+ .WillOnce(Invoke([&](ResponseAdapter* adapter, const GURL& redirect_url) {
+ EXPECT_EQ(GURL("https://youtube.com"), adapter->GetOrigin());
+ EXPECT_TRUE(adapter->IsMainFrame());
+
+ EXPECT_EQ(response_user_data_ptr,
+ adapter->GetUserData(kResponseUserDataKey));
+
+ const net::HttpResponseHeaders* headers = adapter->GetHeaders();
+ // This is a new response and so previous headers should not carry over.
+ EXPECT_FALSE(headers->HasHeader("X-Response-1"));
+ EXPECT_FALSE(headers->HasHeader("X-Response-2"));
+
+ EXPECT_TRUE(headers->HasHeader("X-Response-3"));
+ EXPECT_TRUE(headers->HasHeader("X-Response-4"));
+ adapter->RemoveHeader("X-Response-3");
+
+ EXPECT_EQ(GURL(), redirect_url);
+ }));
+
+ // Set up a redirect and final response.
+ {
+ net::RedirectInfo redirect_info;
+ redirect_info.new_url = kTestRedirectURL;
+ // An HTTPS to HTTPS redirect such as this wouldn't normally change the
+ // referrer but we do for testing purposes.
+ redirect_info.new_referrer = kTestURL.spec();
+
+ auto redirect_head = network::mojom::URLResponseHead::New();
+ redirect_head->headers = base::MakeRefCounted<net::HttpResponseHeaders>("");
+ redirect_head->headers->SetHeader("X-Response-1", "Foo");
+ redirect_head->headers->SetHeader("X-Response-2", "Bar");
+
+ auto response_head = network::mojom::URLResponseHead::New();
+ response_head->headers = base::MakeRefCounted<net::HttpResponseHeaders>("");
+ response_head->headers->SetHeader("X-Response-3", "Foo");
+ response_head->headers->SetHeader("X-Response-4", "Bar");
+ std::string body("Hello.");
+ network::URLLoaderCompletionStatus status;
+ status.decoded_body_length = body.size();
+
+ network::TestURLLoaderFactory::Redirects redirects;
+ redirects.push_back({redirect_info, std::move(redirect_head)});
+
+ factory()->AddResponse(kTestURL, std::move(response_head), body, status,
+ std::move(redirects));
+ }
+
+ // Wait for the request to complete and check the response.
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(net::OK, loader()->NetError());
+ const network::mojom::URLResponseHead* response_head =
+ loader()->ResponseInfo();
+ ASSERT_TRUE(response_head && response_head->headers);
+ EXPECT_FALSE(response_head->headers->HasHeader("X-Response-3"));
+ EXPECT_TRUE(response_head->headers->HasHeader("X-Response-4"));
+ ASSERT_TRUE(response_body());
+ EXPECT_EQ("Hello.", *response_body());
+
+ // NOTE: TestURLLoaderFactory currently does not expose modifications to
+ // request headers and so we cannot verify that the modifications have been
+ // passed to the target URLLoader.
+}
+
+TEST_F(ChromeSigninProxyingURLLoaderFactoryTest, TargetFactoryFailure) {
+ mojo::Remote<network::mojom::URLLoaderFactory> factory_remote;
+ mojo::PendingRemote<network::mojom::URLLoaderFactory>
+ pending_target_factory_remote;
+ auto target_factory_receiver =
+ pending_target_factory_remote.InitWithNewPipeAndPassReceiver();
+
+ // Without a target factory the proxy will process no requests.
+ auto delegate = std::make_unique<MockDelegate>();
+ EXPECT_CALL(*delegate, ProcessRequest(_, _)).Times(0);
+
+ auto proxying_factory = std::make_unique<ProxyingURLLoaderFactory>(
+ std::move(delegate), NullWebContentsGetter(),
+ factory_remote.BindNewPipeAndPassReceiver(),
+ std::move(pending_target_factory_remote), base::DoNothing());
+
+ // Close |target_factory_receiver| instead of binding it to a
+ // URLLoaderFactory. Spin the message loop so that the connection error
+ // handler can run.
+ target_factory_receiver = mojo::NullReceiver();
+ base::RunLoop().RunUntilIdle();
+
+ auto request = std::make_unique<network::ResourceRequest>();
+ request->url = GURL("https://google.com");
+ auto loader = network::SimpleURLLoader::Create(std::move(request),
+ TRAFFIC_ANNOTATION_FOR_TESTS);
+ loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+ factory_remote.get(),
+ base::BindOnce(
+ &ChromeSigninProxyingURLLoaderFactoryTest::OnDownloadComplete,
+ base::Unretained(this)));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_FALSE(response_body());
+ EXPECT_EQ(net::ERR_FAILED, loader->NetError());
+}
+
+TEST_F(ChromeSigninProxyingURLLoaderFactoryTest, RequestKeepAlive) {
+ // Start the request.
+ auto request = std::make_unique<network::ResourceRequest>();
+ request->url = GURL("https://google.com");
+ base::WeakPtr<MockDelegate> delegate = StartRequest(std::move(request));
+ base::RunLoop().RunUntilIdle();
+
+ // Close the factory receiver and spin the message loop again to allow the
+ // connection error handler to be called.
+ CloseFactoryReceiver();
+ base::RunLoop().RunUntilIdle();
+
+ // The ProxyingURLLoaderFactory should not have been destroyed yet because
+ // there is still an in progress request that has not been completed.
+ EXPECT_TRUE(delegate);
+
+ // Complete the request.
+ factory()->AddResponse("https://google.com", "Hello.");
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_FALSE(delegate);
+ EXPECT_EQ(net::OK, loader()->NetError());
+ ASSERT_TRUE(response_body());
+ EXPECT_EQ("Hello.", *response_body());
+}
+
+} // namespace signin