Create WebPushSender for sending web-push messages
- Created WebPushSender for sending out web-push messages using VAPID
(https://developers.google.com/web/ilt/pwa/introduction-to-push-notifications#using_vapid)
- Created WebPushMessage struct to encapsulate message_id into messages
to make callbacks easier
- Refactored across GCMEncryptionProvider / GCMDriver to use
WebPushMessage and provide callback mechanism
Bug: 966037
Change-Id: I1e8fe6d418e57660ecfe7e8d493b7ae9e71a68f3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1649165
Commit-Queue: Alex Chau <[email protected]>
Reviewed-by: Max Moroz <[email protected]>
Reviewed-by: Adam Langley <[email protected]>
Reviewed-by: Scott Violet <[email protected]>
Reviewed-by: Daniel Cheng <[email protected]>
Reviewed-by: Peter Beverloo <[email protected]>
Reviewed-by: Richard Knoll <[email protected]>
Auto-Submit: Alex Chau <[email protected]>
Cr-Commit-Position: refs/heads/master@{#672534}
diff --git a/components/gcm_driver/gcm_driver_unittest.cc b/components/gcm_driver/gcm_driver_unittest.cc
new file mode 100644
index 0000000..71809442
--- /dev/null
+++ b/components/gcm_driver/gcm_driver_unittest.cc
@@ -0,0 +1,285 @@
+// Copyright 2019 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 "components/gcm_driver/gcm_driver_desktop.h"
+
+#include <stdint.h>
+
+#include "base/base64.h"
+#include "base/bind.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop_current.h"
+#include "base/optional.h"
+#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/gcm_driver/crypto/gcm_encryption_provider.h"
+#include "components/gcm_driver/fake_gcm_client_factory.h"
+#include "components/gcm_driver/gcm_channel_status_request.h"
+#include "components/gcm_driver/gcm_channel_status_syncer.h"
+#include "components/gcm_driver/gcm_client_factory.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/testing_pref_service.h"
+#include "crypto/ec_private_key.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "net/url_request/url_request_test_util.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_network_connection_tracker.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "services/network/test/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gcm {
+
+namespace {
+
+const char kTestChannelStatusRequestURL[] = "http://channel.status.request.com";
+const char kTestAppID1[] = "TestApp1";
+
+// P-256 point in uncompressed X9.62 format. Used for Diffie-Hellman.
+const char kP256dh[] =
+ "BIYKm1hwEtmAUNfeX3V4cs089gVo0ng+"
+ "UdRgpTFnnv7lojCKRwXgHQZKvjO34fSb7HxQlSRoldcE2iBG2aO468U=";
+// A base64 encoded authentication secret of 128 bits.
+const char kAuthSecret[] = "JX7sXZrnCvwEkepEzP8eAQ==";
+// PKCS #8 encoded P-256 private key.
+const char kPrivateKey[] =
+ "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgS8wRbDOWz0lKExvIVQiRKtPAP8"
+ "dgHUHAw5gyOd5d4jKhRANCAARZb49Va5MD/KcWtc0oiWc2e8njBDtQzj0mzcOl1fDSt16Pvu6p"
+ "fTU3MTWnImDNnkPxtXm58K7Uax8jFxA4TeXJ";
+const char kFCMToken[] = "fcm_token";
+
+void PumpCurrentLoop() {
+ base::RunLoop(base::RunLoop::Type::kNestableTasksAllowed).RunUntilIdle();
+}
+
+} // namespace
+
+class GCMDriverBaseTest : public testing::Test {
+ public:
+ enum WaitToFinish { DO_NOT_WAIT, WAIT };
+
+ GCMDriverBaseTest();
+ ~GCMDriverBaseTest() override;
+
+ // testing::Test:
+ void SetUp() override;
+ void TearDown() override;
+
+ GCMDriverDesktop* driver() { return driver_.get(); }
+ const std::string& send_web_push_message_id() const {
+ return send_web_push_message_id_;
+ }
+ bool send_web_push_message_result() const {
+ return send_web_push_message_result_;
+ }
+ const std::string& p256dh() const { return p256dh_; }
+ const std::string& auth_secret() const { return auth_secret_; }
+ network::TestURLLoaderFactory& loader() { return test_url_loader_factory_; }
+
+ void PumpIOLoop();
+
+ void CreateDriver();
+ void ShutdownDriver();
+
+ void SendWebPushMessage(const std::string& app_id,
+ WebPushMessage message,
+ base::Optional<net::HttpStatusCode> completion_status,
+ WaitToFinish wait_to_finish);
+ void GetEncryptionInfo(const std::string& app_id,
+ WaitToFinish wait_to_finish);
+
+ void SendWebPushMessageCompleted(const std::string& message_id, bool result);
+ void GetEncryptionInfoCompleted(const std::string& p256dh,
+ const std::string& auth_secret);
+ void UnregisterCompleted(GCMClient::Result result);
+
+ private:
+ base::ScopedTempDir temp_dir_;
+ TestingPrefServiceSimple prefs_;
+ base::test::ScopedTaskEnvironment task_environment_{
+ base::test::ScopedTaskEnvironment::MainThreadType::UI};
+ base::Thread io_thread_;
+ network::TestURLLoaderFactory test_url_loader_factory_;
+
+ std::unique_ptr<GCMDriverDesktop> driver_;
+
+ base::Closure async_operation_completed_callback_;
+
+ std::string send_web_push_message_id_;
+ bool send_web_push_message_result_;
+ std::string p256dh_;
+ std::string auth_secret_;
+
+ DISALLOW_COPY_AND_ASSIGN(GCMDriverBaseTest);
+};
+
+GCMDriverBaseTest::GCMDriverBaseTest() : io_thread_("IOThread") {}
+
+GCMDriverBaseTest::~GCMDriverBaseTest() = default;
+
+void GCMDriverBaseTest::SetUp() {
+ GCMChannelStatusSyncer::RegisterPrefs(prefs_.registry());
+ io_thread_.Start();
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+
+ CreateDriver();
+ PumpIOLoop();
+ PumpCurrentLoop();
+}
+
+void GCMDriverBaseTest::TearDown() {
+ if (!driver_)
+ return;
+
+ ShutdownDriver();
+ driver_.reset();
+ PumpIOLoop();
+
+ io_thread_.Stop();
+}
+
+void GCMDriverBaseTest::PumpIOLoop() {
+ base::RunLoop run_loop;
+ io_thread_.task_runner()->PostTaskAndReply(
+ FROM_HERE, base::BindOnce(&PumpCurrentLoop), run_loop.QuitClosure());
+ run_loop.Run();
+}
+
+void GCMDriverBaseTest::CreateDriver() {
+ scoped_refptr<net::URLRequestContextGetter> request_context =
+ new net::TestURLRequestContextGetter(io_thread_.task_runner());
+ GCMClient::ChromeBuildInfo chrome_build_info;
+ chrome_build_info.product_category_for_subtypes = "com.chrome.macosx";
+ driver_ = std::make_unique<GCMDriverDesktop>(
+ std::make_unique<FakeGCMClientFactory>(
+ base::ThreadTaskRunnerHandle::Get(), io_thread_.task_runner()),
+ chrome_build_info, kTestChannelStatusRequestURL, "user-agent-string",
+ &prefs_, temp_dir_.GetPath(), base::DoNothing(),
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_url_loader_factory_),
+ network::TestNetworkConnectionTracker::GetInstance(),
+ base::ThreadTaskRunnerHandle::Get(), io_thread_.task_runner(),
+ task_environment_.GetMainThreadTaskRunner());
+}
+
+void GCMDriverBaseTest::ShutdownDriver() {
+ driver()->Shutdown();
+}
+
+void GCMDriverBaseTest::SendWebPushMessage(
+ const std::string& app_id,
+ WebPushMessage message,
+ base::Optional<net::HttpStatusCode> completion_status,
+ WaitToFinish wait_to_finish) {
+ std::string p256dh, auth_secret;
+ ASSERT_TRUE(base::Base64Decode(kP256dh, &p256dh));
+ ASSERT_TRUE(base::Base64Decode(kAuthSecret, &auth_secret));
+
+ std::string private_key_info;
+ ASSERT_TRUE(base::Base64Decode(kPrivateKey, &private_key_info));
+ std::unique_ptr<crypto::ECPrivateKey> private_key =
+ crypto::ECPrivateKey::CreateFromPrivateKeyInfo(std::vector<uint8_t>(
+ private_key_info.begin(), private_key_info.end()));
+ ASSERT_TRUE(private_key);
+
+ base::RunLoop run_loop;
+ async_operation_completed_callback_ = run_loop.QuitClosure();
+ driver_->SendWebPushMessage(
+ app_id, /* authorized_entity= */ "", p256dh, auth_secret, kFCMToken,
+ private_key.get(), std::move(message),
+ base::BindOnce(&GCMDriverBaseTest::SendWebPushMessageCompleted,
+ base::Unretained(this)));
+
+ if (completion_status) {
+ ASSERT_EQ(test_url_loader_factory_.NumPending(), 1);
+ test_url_loader_factory_.SimulateResponseForPendingRequest(
+ test_url_loader_factory_.GetPendingRequest(0)->request.url,
+ network::URLLoaderCompletionStatus(net::OK),
+ network::CreateResourceResponseHead(*completion_status), "");
+ }
+
+ if (wait_to_finish == WAIT)
+ run_loop.Run();
+}
+
+void GCMDriverBaseTest::GetEncryptionInfo(const std::string& app_id,
+ WaitToFinish wait_to_finish) {
+ base::RunLoop run_loop;
+ async_operation_completed_callback_ = run_loop.QuitClosure();
+ driver_->GetEncryptionInfo(
+ app_id, base::Bind(&GCMDriverBaseTest::GetEncryptionInfoCompleted,
+ base::Unretained(this)));
+ if (wait_to_finish == WAIT)
+ run_loop.Run();
+}
+
+void GCMDriverBaseTest::SendWebPushMessageCompleted(
+ const std::string& message_id,
+ bool result) {
+ send_web_push_message_id_ = message_id;
+ send_web_push_message_result_ = result;
+ if (!async_operation_completed_callback_.is_null())
+ async_operation_completed_callback_.Run();
+}
+
+void GCMDriverBaseTest::GetEncryptionInfoCompleted(
+ const std::string& p256dh,
+ const std::string& auth_secret) {
+ p256dh_ = p256dh;
+ auth_secret_ = auth_secret;
+ if (!async_operation_completed_callback_.is_null())
+ async_operation_completed_callback_.Run();
+}
+
+TEST_F(GCMDriverBaseTest, SendWebPushMessage) {
+ GetEncryptionInfo(kTestAppID1, GCMDriverBaseTest::WAIT);
+
+ WebPushMessage message;
+ message.id = "message_id";
+ message.time_to_live = 3600;
+ message.payload = "payload";
+ ASSERT_NO_FATAL_FAILURE(SendWebPushMessage(kTestAppID1, std::move(message),
+ base::make_optional(net::HTTP_OK),
+ GCMDriverBaseTest::WAIT));
+
+ EXPECT_EQ("message_id", send_web_push_message_id());
+ EXPECT_TRUE(send_web_push_message_result());
+}
+
+TEST_F(GCMDriverBaseTest, SendWebPushMessageEncryptionError) {
+ // Intentionally not creating encryption info.
+
+ WebPushMessage message;
+ message.id = "message_id";
+ message.time_to_live = 3600;
+ message.payload = "payload";
+ ASSERT_NO_FATAL_FAILURE(SendWebPushMessage(
+ kTestAppID1, std::move(message), base::nullopt, GCMDriverBaseTest::WAIT));
+
+ EXPECT_EQ("message_id", send_web_push_message_id());
+ EXPECT_FALSE(send_web_push_message_result());
+}
+
+TEST_F(GCMDriverBaseTest, SendWebPushMessageServerError) {
+ GetEncryptionInfo(kTestAppID1, GCMDriverBaseTest::WAIT);
+
+ WebPushMessage message;
+ message.id = "message_id";
+ message.time_to_live = 3600;
+ message.payload = "payload";
+ ASSERT_NO_FATAL_FAILURE(
+ SendWebPushMessage(kTestAppID1, std::move(message),
+ base::make_optional(net::HTTP_INTERNAL_SERVER_ERROR),
+ GCMDriverBaseTest::WAIT));
+
+ EXPECT_EQ("message_id", send_web_push_message_id());
+ EXPECT_FALSE(send_web_push_message_result());
+}
+
+} // namespace gcm