| // Copyright 2023 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "net/http/http_transaction_test_util.h" |
| |
| #include <string> |
| #include <string_view> |
| |
| #include "base/test/bind.h" |
| #include "base/test/task_environment.h" |
| #include "net/base/test_completion_callback.h" |
| #include "net/test/gtest_util.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace net { |
| |
| namespace { |
| |
| // Default transaction. |
| const MockTransaction kBasicTransaction = { |
| .url = "http://www.example.com/", |
| .method = "GET", |
| .request_time = base::Time(), |
| .request_headers = "", |
| .load_flags = LOAD_NORMAL, |
| .transport_info = TransportInfo(TransportType::kDirect, |
| IPEndPoint(IPAddress::IPv4Localhost(), 80), |
| /*accept_ch_frame_arg=*/"", |
| /*cert_is_issued_by_known_root=*/false, |
| kProtoUnknown), |
| .status = "HTTP/1.1 200 OK", |
| .response_headers = "Cache-Control: max-age=10000\n", |
| .response_time = base::Time(), |
| .data = "<html><body>Hello world!</body></html>", |
| .dns_aliases = {}, |
| .fps_cache_filter = absl::nullopt, |
| .browser_run_id = absl::nullopt, |
| .test_mode = TEST_MODE_NORMAL, |
| .handler = MockTransactionHandler(), |
| .read_handler = MockTransactionReadHandler(), |
| .cert = nullptr, |
| .cert_status = 0, |
| .ssl_connection_status = 0, |
| .start_return_code = OK, |
| .read_return_code = OK, |
| }; |
| const size_t kDefaultBufferSize = 1024; |
| |
| } // namespace |
| |
| class MockNetworkTransactionTest : public ::testing::Test { |
| public: |
| MockNetworkTransactionTest() |
| : network_layer_(std::make_unique<MockNetworkLayer>()) {} |
| ~MockNetworkTransactionTest() override = default; |
| |
| MockNetworkTransactionTest(const MockNetworkTransactionTest&) = delete; |
| MockNetworkTransactionTest& operator=(const MockNetworkTransactionTest&) = |
| delete; |
| |
| protected: |
| std::unique_ptr<HttpTransaction> CreateNetworkTransaction() { |
| std::unique_ptr<HttpTransaction> network_transaction; |
| network_layer_->CreateTransaction(DEFAULT_PRIORITY, &network_transaction); |
| return network_transaction; |
| } |
| |
| void RunUntilIdle() { task_environment_.RunUntilIdle(); } |
| |
| MockNetworkLayer& network_layer() { return *network_layer_.get(); } |
| |
| private: |
| std::unique_ptr<MockNetworkLayer> network_layer_; |
| base::test::TaskEnvironment task_environment_{ |
| base::test::TaskEnvironment::TimeSource::MOCK_TIME}; |
| }; |
| |
| TEST_F(MockNetworkTransactionTest, Basic) { |
| AddMockTransaction(&kBasicTransaction); |
| HttpRequestInfo request = MockHttpRequest(kBasicTransaction); |
| |
| auto transaction = CreateNetworkTransaction(); |
| TestCompletionCallback start_callback; |
| ASSERT_THAT(transaction->Start(&request, start_callback.callback(), |
| NetLogWithSource()), |
| test::IsError(ERR_IO_PENDING)); |
| EXPECT_THAT(start_callback.WaitForResult(), test::IsError(OK)); |
| |
| EXPECT_FALSE(transaction->GetResponseInfo()->was_cached); |
| EXPECT_TRUE(transaction->GetResponseInfo()->network_accessed); |
| EXPECT_EQ(kBasicTransaction.transport_info.endpoint, |
| transaction->GetResponseInfo()->remote_endpoint); |
| EXPECT_FALSE(transaction->GetResponseInfo()->was_fetched_via_proxy); |
| |
| scoped_refptr<IOBufferWithSize> buf = |
| base::MakeRefCounted<IOBufferWithSize>(kDefaultBufferSize); |
| TestCompletionCallback read_callback; |
| ASSERT_THAT( |
| transaction->Read(buf.get(), buf->size(), read_callback.callback()), |
| test::IsError(ERR_IO_PENDING)); |
| int read_result = read_callback.WaitForResult(); |
| ASSERT_THAT(read_result, std::string_view(kBasicTransaction.data).size()); |
| EXPECT_EQ(std::string_view(kBasicTransaction.data), |
| std::string_view(buf->data(), read_result)); |
| } |
| |
| TEST_F(MockNetworkTransactionTest, SyncNetStart) { |
| MockTransaction new_mock_transaction = kBasicTransaction; |
| new_mock_transaction.test_mode = TEST_MODE_SYNC_NET_START; |
| AddMockTransaction(&new_mock_transaction); |
| HttpRequestInfo request = MockHttpRequest(new_mock_transaction); |
| |
| auto transaction = CreateNetworkTransaction(); |
| TestCompletionCallback start_callback; |
| ASSERT_THAT(transaction->Start(&request, start_callback.callback(), |
| NetLogWithSource()), |
| test::IsError(OK)); |
| |
| scoped_refptr<IOBufferWithSize> buf = |
| base::MakeRefCounted<IOBufferWithSize>(kDefaultBufferSize); |
| TestCompletionCallback read_callback; |
| ASSERT_THAT( |
| transaction->Read(buf.get(), buf->size(), read_callback.callback()), |
| test::IsError(ERR_IO_PENDING)); |
| int read_result = read_callback.WaitForResult(); |
| ASSERT_THAT(read_result, std::string_view(new_mock_transaction.data).size()); |
| EXPECT_EQ(std::string_view(new_mock_transaction.data), |
| std::string_view(buf->data(), read_result)); |
| } |
| |
| TEST_F(MockNetworkTransactionTest, AsyncNetStartFailure) { |
| MockTransaction new_mock_transaction = kBasicTransaction; |
| new_mock_transaction.start_return_code = ERR_NETWORK_ACCESS_DENIED; |
| AddMockTransaction(&new_mock_transaction); |
| HttpRequestInfo request = MockHttpRequest(new_mock_transaction); |
| |
| auto transaction = CreateNetworkTransaction(); |
| TestCompletionCallback start_callback; |
| ASSERT_THAT(transaction->Start(&request, start_callback.callback(), |
| NetLogWithSource()), |
| test::IsError(ERR_IO_PENDING)); |
| EXPECT_THAT(start_callback.WaitForResult(), |
| test::IsError(ERR_NETWORK_ACCESS_DENIED)); |
| } |
| |
| TEST_F(MockNetworkTransactionTest, SyncNetStartFailure) { |
| MockTransaction new_mock_transaction = kBasicTransaction; |
| new_mock_transaction.test_mode = TEST_MODE_SYNC_NET_START; |
| new_mock_transaction.start_return_code = ERR_NETWORK_ACCESS_DENIED; |
| AddMockTransaction(&new_mock_transaction); |
| HttpRequestInfo request = MockHttpRequest(new_mock_transaction); |
| |
| auto transaction = CreateNetworkTransaction(); |
| TestCompletionCallback start_callback; |
| ASSERT_THAT(transaction->Start(&request, start_callback.callback(), |
| NetLogWithSource()), |
| test::IsError(ERR_NETWORK_ACCESS_DENIED)); |
| } |
| |
| TEST_F(MockNetworkTransactionTest, BeforeNetworkStartCallback) { |
| AddMockTransaction(&kBasicTransaction); |
| HttpRequestInfo request = MockHttpRequest(kBasicTransaction); |
| |
| auto transaction = CreateNetworkTransaction(); |
| bool before_network_start_callback_called = false; |
| transaction->SetBeforeNetworkStartCallback(base::BindLambdaForTesting( |
| [&](bool* defer) { before_network_start_callback_called = true; })); |
| |
| TestCompletionCallback start_callback; |
| ASSERT_THAT(transaction->Start(&request, start_callback.callback(), |
| NetLogWithSource()), |
| test::IsError(ERR_IO_PENDING)); |
| EXPECT_THAT(start_callback.WaitForResult(), test::IsError(OK)); |
| EXPECT_TRUE(before_network_start_callback_called); |
| } |
| |
| TEST_F(MockNetworkTransactionTest, BeforeNetworkStartCallbackDeferAndResume) { |
| AddMockTransaction(&kBasicTransaction); |
| HttpRequestInfo request = MockHttpRequest(kBasicTransaction); |
| |
| auto transaction = CreateNetworkTransaction(); |
| bool before_network_start_callback_called = false; |
| transaction->SetBeforeNetworkStartCallback( |
| base::BindLambdaForTesting([&](bool* defer) { |
| before_network_start_callback_called = true; |
| *defer = true; |
| })); |
| |
| TestCompletionCallback start_callback; |
| ASSERT_THAT(transaction->Start(&request, start_callback.callback(), |
| NetLogWithSource()), |
| test::IsError(ERR_IO_PENDING)); |
| EXPECT_TRUE(before_network_start_callback_called); |
| RunUntilIdle(); |
| EXPECT_FALSE(start_callback.have_result()); |
| transaction->ResumeNetworkStart(); |
| EXPECT_FALSE(start_callback.have_result()); |
| EXPECT_THAT(start_callback.WaitForResult(), test::IsError(OK)); |
| } |
| |
| TEST_F(MockNetworkTransactionTest, AsyncConnectedCallback) { |
| AddMockTransaction(&kBasicTransaction); |
| HttpRequestInfo request = MockHttpRequest(kBasicTransaction); |
| |
| auto transaction = CreateNetworkTransaction(); |
| bool connected_callback_called = false; |
| CompletionOnceCallback callback_for_connected_callback; |
| transaction->SetConnectedCallback(base::BindLambdaForTesting( |
| [&](const TransportInfo& info, CompletionOnceCallback callback) -> int { |
| EXPECT_EQ(kBasicTransaction.transport_info, info); |
| connected_callback_called = true; |
| callback_for_connected_callback = std::move(callback); |
| return ERR_IO_PENDING; |
| })); |
| |
| TestCompletionCallback start_callback; |
| ASSERT_THAT(transaction->Start(&request, start_callback.callback(), |
| NetLogWithSource()), |
| test::IsError(ERR_IO_PENDING)); |
| RunUntilIdle(); |
| EXPECT_TRUE(connected_callback_called); |
| EXPECT_FALSE(start_callback.have_result()); |
| std::move(callback_for_connected_callback).Run(OK); |
| EXPECT_THAT(start_callback.WaitForResult(), test::IsError(OK)); |
| } |
| |
| TEST_F(MockNetworkTransactionTest, AsyncConnectedCallbackFailure) { |
| AddMockTransaction(&kBasicTransaction); |
| HttpRequestInfo request = MockHttpRequest(kBasicTransaction); |
| |
| auto transaction = CreateNetworkTransaction(); |
| bool connected_callback_called = false; |
| CompletionOnceCallback callback_for_connected_callback; |
| transaction->SetConnectedCallback(base::BindLambdaForTesting( |
| [&](const TransportInfo& info, CompletionOnceCallback callback) -> int { |
| EXPECT_EQ(kBasicTransaction.transport_info, info); |
| connected_callback_called = true; |
| callback_for_connected_callback = std::move(callback); |
| return ERR_IO_PENDING; |
| })); |
| |
| TestCompletionCallback start_callback; |
| ASSERT_THAT(transaction->Start(&request, start_callback.callback(), |
| NetLogWithSource()), |
| test::IsError(ERR_IO_PENDING)); |
| RunUntilIdle(); |
| EXPECT_TRUE(connected_callback_called); |
| EXPECT_FALSE(start_callback.have_result()); |
| std::move(callback_for_connected_callback).Run(ERR_INSUFFICIENT_RESOURCES); |
| EXPECT_THAT(start_callback.WaitForResult(), |
| test::IsError(ERR_INSUFFICIENT_RESOURCES)); |
| } |
| |
| TEST_F(MockNetworkTransactionTest, SyncConnectedCallback) { |
| AddMockTransaction(&kBasicTransaction); |
| HttpRequestInfo request = MockHttpRequest(kBasicTransaction); |
| |
| auto transaction = CreateNetworkTransaction(); |
| bool connected_callback_called = false; |
| transaction->SetConnectedCallback(base::BindLambdaForTesting( |
| [&](const TransportInfo& info, CompletionOnceCallback callback) -> int { |
| EXPECT_EQ(kBasicTransaction.transport_info, info); |
| connected_callback_called = true; |
| return OK; |
| })); |
| |
| TestCompletionCallback start_callback; |
| ASSERT_THAT(transaction->Start(&request, start_callback.callback(), |
| NetLogWithSource()), |
| test::IsError(ERR_IO_PENDING)); |
| RunUntilIdle(); |
| EXPECT_TRUE(connected_callback_called); |
| EXPECT_THAT(start_callback.WaitForResult(), test::IsError(OK)); |
| } |
| |
| TEST_F(MockNetworkTransactionTest, SyncConnectedCallbackFailure) { |
| AddMockTransaction(&kBasicTransaction); |
| HttpRequestInfo request = MockHttpRequest(kBasicTransaction); |
| |
| auto transaction = CreateNetworkTransaction(); |
| bool connected_callback_called = false; |
| transaction->SetConnectedCallback(base::BindLambdaForTesting( |
| [&](const TransportInfo& info, CompletionOnceCallback callback) -> int { |
| EXPECT_EQ(kBasicTransaction.transport_info, info); |
| connected_callback_called = true; |
| return ERR_INSUFFICIENT_RESOURCES; |
| })); |
| |
| TestCompletionCallback start_callback; |
| ASSERT_THAT(transaction->Start(&request, start_callback.callback(), |
| NetLogWithSource()), |
| test::IsError(ERR_IO_PENDING)); |
| RunUntilIdle(); |
| EXPECT_TRUE(connected_callback_called); |
| EXPECT_THAT(start_callback.WaitForResult(), |
| test::IsError(ERR_INSUFFICIENT_RESOURCES)); |
| } |
| |
| TEST_F(MockNetworkTransactionTest, ModifyRequestHeadersCallback) { |
| const std::string kTestResponseData = "hello"; |
| MockTransaction new_mock_transaction = kBasicTransaction; |
| new_mock_transaction.request_headers = "Foo: Bar\r\n"; |
| |
| bool transaction_handler_called = false; |
| new_mock_transaction.handler = base::BindLambdaForTesting( |
| [&](const HttpRequestInfo* request, std::string* response_status, |
| std::string* response_headers, std::string* response_data) { |
| EXPECT_EQ("Foo: Bar\r\nHoge: Piyo\r\n\r\n", |
| request->extra_headers.ToString()); |
| *response_data = kTestResponseData; |
| transaction_handler_called = true; |
| }); |
| AddMockTransaction(&new_mock_transaction); |
| HttpRequestInfo request = MockHttpRequest(new_mock_transaction); |
| |
| auto transaction = CreateNetworkTransaction(); |
| bool modify_request_headers_callback_called_ = false; |
| transaction->SetModifyRequestHeadersCallback( |
| base::BindLambdaForTesting([&](HttpRequestHeaders* request_headers) { |
| modify_request_headers_callback_called_ = true; |
| request_headers->SetHeader("Hoge", "Piyo"); |
| })); |
| |
| TestCompletionCallback start_callback; |
| ASSERT_THAT(transaction->Start(&request, start_callback.callback(), |
| NetLogWithSource()), |
| test::IsError(ERR_IO_PENDING)); |
| EXPECT_THAT(start_callback.WaitForResult(), test::IsError(OK)); |
| EXPECT_TRUE(modify_request_headers_callback_called_); |
| EXPECT_TRUE(transaction_handler_called); |
| |
| scoped_refptr<IOBufferWithSize> buf = |
| base::MakeRefCounted<IOBufferWithSize>(kDefaultBufferSize); |
| TestCompletionCallback read_callback; |
| ASSERT_THAT( |
| transaction->Read(buf.get(), buf->size(), read_callback.callback()), |
| test::IsError(ERR_IO_PENDING)); |
| int read_result = read_callback.WaitForResult(); |
| ASSERT_THAT(read_result, kTestResponseData.size()); |
| EXPECT_EQ(kTestResponseData, std::string_view(buf->data(), read_result)); |
| } |
| |
| TEST_F(MockNetworkTransactionTest, CallbackOrder) { |
| const std::string kTestResponseData = "hello"; |
| MockTransaction new_mock_transaction = kBasicTransaction; |
| new_mock_transaction.request_headers = "Foo: Bar\r\n"; |
| |
| bool before_network_start_callback_called = false; |
| bool connected_callback_called = false; |
| bool modify_request_headers_callback_called_ = false; |
| bool transaction_handler_called = false; |
| |
| new_mock_transaction.handler = base::BindLambdaForTesting( |
| [&](const HttpRequestInfo* request, std::string* response_status, |
| std::string* response_headers, std::string* response_data) { |
| EXPECT_TRUE(before_network_start_callback_called); |
| EXPECT_TRUE(connected_callback_called); |
| EXPECT_TRUE(modify_request_headers_callback_called_); |
| EXPECT_FALSE(transaction_handler_called); |
| |
| *response_data = kTestResponseData; |
| transaction_handler_called = true; |
| }); |
| |
| AddMockTransaction(&new_mock_transaction); |
| HttpRequestInfo request = MockHttpRequest(new_mock_transaction); |
| |
| auto transaction = CreateNetworkTransaction(); |
| transaction->SetBeforeNetworkStartCallback( |
| base::BindLambdaForTesting([&](bool* defer) { |
| EXPECT_FALSE(before_network_start_callback_called); |
| EXPECT_FALSE(connected_callback_called); |
| EXPECT_FALSE(modify_request_headers_callback_called_); |
| EXPECT_FALSE(transaction_handler_called); |
| |
| before_network_start_callback_called = true; |
| *defer = true; |
| })); |
| |
| CompletionOnceCallback callback_for_connected_callback; |
| transaction->SetConnectedCallback(base::BindLambdaForTesting( |
| [&](const TransportInfo& info, CompletionOnceCallback callback) -> int { |
| EXPECT_TRUE(before_network_start_callback_called); |
| EXPECT_FALSE(connected_callback_called); |
| EXPECT_FALSE(modify_request_headers_callback_called_); |
| EXPECT_FALSE(transaction_handler_called); |
| |
| connected_callback_called = true; |
| callback_for_connected_callback = std::move(callback); |
| return ERR_IO_PENDING; |
| })); |
| |
| transaction->SetModifyRequestHeadersCallback( |
| base::BindLambdaForTesting([&](HttpRequestHeaders* request_headers) { |
| EXPECT_TRUE(before_network_start_callback_called); |
| EXPECT_TRUE(connected_callback_called); |
| EXPECT_FALSE(modify_request_headers_callback_called_); |
| EXPECT_FALSE(transaction_handler_called); |
| |
| modify_request_headers_callback_called_ = true; |
| })); |
| |
| EXPECT_FALSE(before_network_start_callback_called); |
| TestCompletionCallback start_callback; |
| ASSERT_THAT(transaction->Start(&request, start_callback.callback(), |
| NetLogWithSource()), |
| test::IsError(ERR_IO_PENDING)); |
| |
| EXPECT_TRUE(before_network_start_callback_called); |
| |
| EXPECT_FALSE(connected_callback_called); |
| transaction->ResumeNetworkStart(); |
| RunUntilIdle(); |
| EXPECT_TRUE(connected_callback_called); |
| |
| EXPECT_FALSE(modify_request_headers_callback_called_); |
| std::move(callback_for_connected_callback).Run(OK); |
| EXPECT_TRUE(modify_request_headers_callback_called_); |
| EXPECT_TRUE(transaction_handler_called); |
| |
| EXPECT_TRUE(start_callback.have_result()); |
| EXPECT_THAT(start_callback.WaitForResult(), test::IsError(OK)); |
| } |
| |
| } // namespace net |