| // 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 "content/browser/web_package/bundled_exchanges_reader.h" |
| |
| #include "base/bind.h" |
| #include "base/callback.h" |
| #include "base/files/file_util.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/logging.h" |
| #include "base/macros.h" |
| #include "base/numerics/safe_conversions.h" |
| #include "base/optional.h" |
| #include "base/test/scoped_task_environment.h" |
| #include "content/browser/web_package/bundled_exchanges_source.h" |
| #include "content/browser/web_package/mock_bundled_exchanges_reader_factory.h" |
| #include "mojo/public/c/system/data_pipe.h" |
| #include "services/data_decoder/public/mojom/bundled_exchanges_parser.mojom.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "url/gurl.h" |
| |
| namespace content { |
| |
| namespace { |
| |
| class BundledExchangesReaderTest : public testing::Test { |
| public: |
| void SetUp() override { |
| reader_factory_ = MockBundledExchangesReaderFactory::Create(); |
| reader_ = reader_factory_->CreateReader(body_); |
| } |
| |
| protected: |
| void ReadMetadata() { |
| base::flat_map<GURL, data_decoder::mojom::BundleIndexValuePtr> items; |
| data_decoder::mojom::BundleIndexValuePtr item = |
| data_decoder::mojom::BundleIndexValue::New(); |
| item->response_locations.push_back( |
| data_decoder::mojom::BundleResponseLocation::New(573u, 765u)); |
| items.insert({primary_url_, std::move(item)}); |
| |
| data_decoder::mojom::BundleMetadataPtr metadata = |
| data_decoder::mojom::BundleMetadata::New(primary_url_, std::move(items), |
| GURL() /* manifest_url */); |
| reader_factory_->ReadAndFullfillMetadata( |
| reader_.get(), std::move(metadata), |
| base::BindOnce( |
| [](data_decoder::mojom::BundleMetadataParseErrorPtr error) { |
| EXPECT_FALSE(error); |
| })); |
| } |
| BundledExchangesReader* GetReader() { return reader_.get(); } |
| MockBundledExchangesReaderFactory* GetMockFactory() { |
| return reader_factory_.get(); |
| } |
| const GURL& GetPrimaryURL() const { return primary_url_; } |
| const std::string& GetBody() const { return body_; } |
| |
| private: |
| base::test::TaskEnvironment task_environment_; |
| std::unique_ptr<MockBundledExchangesReaderFactory> reader_factory_; |
| std::unique_ptr<BundledExchangesReader> reader_; |
| const GURL primary_url_ = GURL("https://test.example.org/"); |
| const std::string body_ = std::string("hello new open world."); |
| }; |
| |
| TEST_F(BundledExchangesReaderTest, ReadMetadata) { |
| ReadMetadata(); |
| EXPECT_EQ(GetPrimaryURL(), GetReader()->GetPrimaryURL()); |
| EXPECT_TRUE(GetReader()->HasEntry(GetPrimaryURL())); |
| EXPECT_TRUE( |
| GetReader()->HasEntry(GURL("https://user:pass@test.example.org/"))); |
| EXPECT_TRUE(GetReader()->HasEntry(GURL("https://test.example.org/#ref"))); |
| EXPECT_FALSE(GetReader()->HasEntry(GURL("https://test.example.org/404"))); |
| } |
| |
| TEST_F(BundledExchangesReaderTest, ReadResponse) { |
| ReadMetadata(); |
| ASSERT_TRUE(GetReader()->HasEntry(GetPrimaryURL())); |
| |
| data_decoder::mojom::BundleResponsePtr response = |
| data_decoder::mojom::BundleResponse::New(); |
| response->response_code = 200; |
| response->payload_offset = 0xdead; |
| response->payload_length = 0xbeaf; |
| |
| GetMockFactory()->ReadAndFullfillResponse( |
| GetReader(), GetPrimaryURL(), std::move(response), |
| base::BindOnce([](data_decoder::mojom::BundleResponsePtr response) { |
| EXPECT_TRUE(response); |
| if (response) { |
| EXPECT_EQ(200, response->response_code); |
| EXPECT_EQ(0xdeadu, response->payload_offset); |
| EXPECT_EQ(0xbeafu, response->payload_length); |
| } |
| })); |
| } |
| |
| TEST_F(BundledExchangesReaderTest, ReadResponseForURLContainingUserAndPass) { |
| GURL url = GURL("https://user:pass@test.example.org/"); |
| |
| ReadMetadata(); |
| ASSERT_TRUE(GetReader()->HasEntry(url)); |
| |
| data_decoder::mojom::BundleResponsePtr response = |
| data_decoder::mojom::BundleResponse::New(); |
| response->response_code = 200; |
| response->payload_offset = 0xdead; |
| response->payload_length = 0xbeaf; |
| |
| GetMockFactory()->ReadAndFullfillResponse( |
| GetReader(), url, std::move(response), |
| base::BindOnce([](data_decoder::mojom::BundleResponsePtr response) { |
| EXPECT_TRUE(response); |
| if (response) { |
| EXPECT_EQ(200, response->response_code); |
| EXPECT_EQ(0xdeadu, response->payload_offset); |
| EXPECT_EQ(0xbeafu, response->payload_length); |
| } |
| })); |
| } |
| |
| TEST_F(BundledExchangesReaderTest, ReadResponseForURLContainingFragment) { |
| GURL url = GURL("https://test.example.org/#fragment"); |
| |
| ReadMetadata(); |
| ASSERT_TRUE(GetReader()->HasEntry(url)); |
| |
| data_decoder::mojom::BundleResponsePtr response = |
| data_decoder::mojom::BundleResponse::New(); |
| response->response_code = 200; |
| response->payload_offset = 0xdead; |
| response->payload_length = 0xbeaf; |
| |
| GetMockFactory()->ReadAndFullfillResponse( |
| GetReader(), url, std::move(response), |
| base::BindOnce([](data_decoder::mojom::BundleResponsePtr response) { |
| EXPECT_TRUE(response); |
| if (response) { |
| EXPECT_EQ(200, response->response_code); |
| EXPECT_EQ(0xdeadu, response->payload_offset); |
| EXPECT_EQ(0xbeafu, response->payload_length); |
| } |
| })); |
| } |
| |
| TEST_F(BundledExchangesReaderTest, ReadResponseBody) { |
| ReadMetadata(); |
| |
| data_decoder::mojom::BundleResponsePtr response = |
| data_decoder::mojom::BundleResponse::New(); |
| constexpr size_t expected_offset = 4; |
| const size_t expected_length = GetBody().size() - 8; |
| response->payload_offset = expected_offset; |
| response->payload_length = expected_length; |
| |
| mojo::ScopedDataPipeProducerHandle producer; |
| mojo::ScopedDataPipeConsumerHandle consumer; |
| MojoCreateDataPipeOptions options; |
| options.struct_size = sizeof(MojoCreateDataPipeOptions); |
| options.element_num_bytes = 1; |
| options.capacity_num_bytes = response->payload_length + 1; |
| ASSERT_EQ(MOJO_RESULT_OK, |
| mojo::CreateDataPipe(&options, &producer, &consumer)); |
| |
| base::RunLoop run_loop; |
| MojoResult callback_result; |
| GetReader()->ReadResponseBody( |
| std::move(response), std::move(producer), |
| base::BindOnce( |
| [](base::Closure quit_closure, MojoResult* callback_result, |
| MojoResult result) { |
| *callback_result = result; |
| std::move(quit_closure).Run(); |
| }, |
| run_loop.QuitClosure(), &callback_result)); |
| run_loop.Run(); |
| ASSERT_EQ(MOJO_RESULT_OK, callback_result); |
| |
| std::vector<char> buffer(expected_length); |
| uint32_t bytes_read = buffer.size(); |
| MojoResult read_result = |
| consumer->ReadData(buffer.data(), &bytes_read, /*flags=*/0); |
| ASSERT_EQ(MOJO_RESULT_OK, read_result); |
| ASSERT_EQ(buffer.size(), bytes_read); |
| EXPECT_EQ(GetBody().substr(expected_offset, expected_length), |
| std::string(buffer.data(), bytes_read)); |
| } |
| |
| } // namespace |
| |
| } // namespace content |