blob: 0569dc79ee930a3530205dc1403feea45a20b98b [file] [log] [blame]
// Copyright 2016 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/renderer/loader/url_loader_client_impl.h"
#include <vector>
#include "base/run_loop.h"
#include "base/test/task_environment.h"
#include "content/renderer/loader/navigation_response_override_parameters.h"
#include "content/renderer/loader/resource_dispatcher.h"
#include "content/renderer/loader/test_request_peer.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "net/url_request/redirect_info.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom.h"
#include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
namespace content {
namespace {
constexpr size_t kDataPipeCapacity = 4096;
std::string ReadOneChunk(mojo::ScopedDataPipeConsumerHandle* handle) {
char buffer[kDataPipeCapacity];
uint32_t read_bytes = kDataPipeCapacity;
MojoResult result =
(*handle)->ReadData(buffer, &read_bytes, MOJO_READ_DATA_FLAG_NONE);
if (result != MOJO_RESULT_OK)
return "";
return std::string(buffer, read_bytes);
}
std::string GetRequestPeerContextBody(TestRequestPeer::Context* context) {
if (context->body_handle) {
context->data += ReadOneChunk(&context->body_handle);
}
return context->data;
}
} // namespace
class URLLoaderClientImplTest : public ::testing::Test,
public network::mojom::URLLoaderFactory {
protected:
URLLoaderClientImplTest() : dispatcher_(new ResourceDispatcher()) {
auto request = std::make_unique<network::ResourceRequest>();
request_id_ = dispatcher_->StartAsync(
std::move(request), 0 /* loader_option */,
blink::scheduler::GetSingleThreadTaskRunnerForTesting(),
TRAFFIC_ANNOTATION_FOR_TESTS, false,
std::make_unique<TestRequestPeer>(dispatcher_.get(),
&request_peer_context_),
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(this),
std::vector<std::unique_ptr<blink::URLLoaderThrottle>>(),
nullptr /* navigation_response_override_params */);
request_peer_context_.request_id = request_id_;
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(url_loader_client_);
}
void TearDown() override { url_loader_client_.reset(); }
void CreateLoaderAndStart(
mojo::PendingReceiver<network::mojom::URLLoader> receiver,
int32_t routing_id,
int32_t request_id,
uint32_t options,
const network::ResourceRequest& url_request,
mojo::PendingRemote<network::mojom::URLLoaderClient> client,
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation)
override {
url_loader_client_.Bind(std::move(client));
}
void Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver)
override {
NOTREACHED();
}
static MojoCreateDataPipeOptions DataPipeOptions() {
MojoCreateDataPipeOptions options;
options.struct_size = sizeof(MojoCreateDataPipeOptions);
options.flags = MOJO_CREATE_DATA_PIPE_FLAG_NONE;
options.element_num_bytes = 1;
options.capacity_num_bytes = kDataPipeCapacity;
return options;
}
base::test::SingleThreadTaskEnvironment task_environment_;
std::unique_ptr<ResourceDispatcher> dispatcher_;
TestRequestPeer::Context request_peer_context_;
int request_id_ = 0;
mojo::Remote<network::mojom::URLLoaderClient> url_loader_client_;
};
TEST_F(URLLoaderClientImplTest, OnReceiveResponse) {
url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New());
EXPECT_FALSE(request_peer_context_.received_response);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(request_peer_context_.received_response);
}
TEST_F(URLLoaderClientImplTest, ResponseBody) {
url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New());
EXPECT_FALSE(request_peer_context_.received_response);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(request_peer_context_.received_response);
mojo::DataPipe data_pipe(DataPipeOptions());
url_loader_client_->OnStartLoadingResponseBody(
std::move(data_pipe.consumer_handle));
uint32_t size = 5;
MojoResult result = data_pipe.producer_handle->WriteData(
"hello", &size, MOJO_WRITE_DATA_FLAG_NONE);
ASSERT_EQ(MOJO_RESULT_OK, result);
EXPECT_EQ(5u, size);
base::RunLoop().RunUntilIdle();
EXPECT_EQ("hello", GetRequestPeerContextBody(&request_peer_context_));
}
TEST_F(URLLoaderClientImplTest, OnReceiveRedirect) {
net::RedirectInfo redirect_info;
url_loader_client_->OnReceiveRedirect(redirect_info,
network::mojom::URLResponseHead::New());
EXPECT_EQ(0, request_peer_context_.seen_redirects);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, request_peer_context_.seen_redirects);
}
TEST_F(URLLoaderClientImplTest, OnReceiveCachedMetadata) {
std::vector<uint8_t> data;
data.push_back('a');
mojo_base::BigBuffer metadata(data);
url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New());
url_loader_client_->OnReceiveCachedMetadata(std::move(metadata));
EXPECT_FALSE(request_peer_context_.received_response);
EXPECT_EQ(0u, request_peer_context_.cached_metadata.size());
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(request_peer_context_.received_response);
ASSERT_EQ(1u, request_peer_context_.cached_metadata.size());
EXPECT_EQ('a', request_peer_context_.cached_metadata.data()[0]);
}
TEST_F(URLLoaderClientImplTest, OnTransferSizeUpdated) {
url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New());
url_loader_client_->OnTransferSizeUpdated(4);
url_loader_client_->OnTransferSizeUpdated(4);
EXPECT_FALSE(request_peer_context_.received_response);
EXPECT_EQ(0, request_peer_context_.total_encoded_data_length);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(request_peer_context_.received_response);
EXPECT_EQ(8, request_peer_context_.total_encoded_data_length);
}
TEST_F(URLLoaderClientImplTest, OnCompleteWithResponseBody) {
network::URLLoaderCompletionStatus status;
url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New());
mojo::DataPipe data_pipe(DataPipeOptions());
url_loader_client_->OnStartLoadingResponseBody(
std::move(data_pipe.consumer_handle));
uint32_t size = 5;
MojoResult result = data_pipe.producer_handle->WriteData(
"hello", &size, MOJO_WRITE_DATA_FLAG_NONE);
ASSERT_EQ(MOJO_RESULT_OK, result);
EXPECT_EQ(5u, size);
data_pipe.producer_handle.reset();
EXPECT_FALSE(request_peer_context_.received_response);
EXPECT_EQ("", GetRequestPeerContextBody(&request_peer_context_));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(request_peer_context_.received_response);
EXPECT_EQ("hello", GetRequestPeerContextBody(&request_peer_context_));
url_loader_client_->OnComplete(status);
EXPECT_FALSE(request_peer_context_.complete);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(request_peer_context_.received_response);
EXPECT_EQ("hello", GetRequestPeerContextBody(&request_peer_context_));
EXPECT_TRUE(request_peer_context_.complete);
}
// Due to the lack of ordering guarantee, it is possible that the response body
// bytes arrives after the completion message. URLLoaderClientImpl should
// restore the order.
TEST_F(URLLoaderClientImplTest, OnCompleteShouldBeTheLastMessage) {
network::URLLoaderCompletionStatus status;
url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New());
mojo::DataPipe data_pipe(DataPipeOptions());
url_loader_client_->OnStartLoadingResponseBody(
std::move(data_pipe.consumer_handle));
url_loader_client_->OnComplete(status);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(request_peer_context_.received_response);
EXPECT_TRUE(request_peer_context_.complete);
uint32_t size = 5;
MojoResult result = data_pipe.producer_handle->WriteData(
"hello", &size, MOJO_WRITE_DATA_FLAG_NONE);
ASSERT_EQ(MOJO_RESULT_OK, result);
EXPECT_EQ(5u, size);
base::RunLoop().RunUntilIdle();
EXPECT_EQ("hello", GetRequestPeerContextBody(&request_peer_context_));
}
TEST_F(URLLoaderClientImplTest, CancelOnReceiveResponse) {
request_peer_context_.cancel_on_receive_response = true;
network::URLLoaderCompletionStatus status;
url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New());
mojo::DataPipe data_pipe(DataPipeOptions());
url_loader_client_->OnStartLoadingResponseBody(
std::move(data_pipe.consumer_handle));
url_loader_client_->OnComplete(status);
EXPECT_FALSE(request_peer_context_.received_response);
EXPECT_FALSE(request_peer_context_.complete);
EXPECT_FALSE(request_peer_context_.cancelled);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(request_peer_context_.received_response);
EXPECT_FALSE(request_peer_context_.complete);
EXPECT_TRUE(request_peer_context_.cancelled);
}
TEST_F(URLLoaderClientImplTest, Defer) {
network::URLLoaderCompletionStatus status;
url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New());
mojo::DataPipe data_pipe;
data_pipe.producer_handle.reset(); // Empty body.
url_loader_client_->OnStartLoadingResponseBody(
std::move(data_pipe.consumer_handle));
url_loader_client_->OnComplete(status);
EXPECT_FALSE(request_peer_context_.received_response);
EXPECT_FALSE(request_peer_context_.complete);
dispatcher_->SetDefersLoading(request_id_, true);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(request_peer_context_.received_response);
EXPECT_FALSE(request_peer_context_.complete);
dispatcher_->SetDefersLoading(request_id_, false);
EXPECT_FALSE(request_peer_context_.received_response);
EXPECT_FALSE(request_peer_context_.complete);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(request_peer_context_.received_response);
EXPECT_TRUE(request_peer_context_.complete);
}
TEST_F(URLLoaderClientImplTest, DeferWithResponseBody) {
network::URLLoaderCompletionStatus status;
url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New());
mojo::DataPipe data_pipe(DataPipeOptions());
uint32_t size = 5;
MojoResult result = data_pipe.producer_handle->WriteData(
"hello", &size, MOJO_WRITE_DATA_FLAG_NONE);
ASSERT_EQ(MOJO_RESULT_OK, result);
EXPECT_EQ(5u, size);
data_pipe.producer_handle.reset();
url_loader_client_->OnStartLoadingResponseBody(
std::move(data_pipe.consumer_handle));
url_loader_client_->OnComplete(status);
EXPECT_FALSE(request_peer_context_.received_response);
EXPECT_FALSE(request_peer_context_.complete);
EXPECT_EQ("", GetRequestPeerContextBody(&request_peer_context_));
dispatcher_->SetDefersLoading(request_id_, true);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(request_peer_context_.received_response);
EXPECT_FALSE(request_peer_context_.complete);
EXPECT_EQ("", GetRequestPeerContextBody(&request_peer_context_));
dispatcher_->SetDefersLoading(request_id_, false);
EXPECT_FALSE(request_peer_context_.received_response);
EXPECT_FALSE(request_peer_context_.complete);
EXPECT_EQ("", GetRequestPeerContextBody(&request_peer_context_));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(request_peer_context_.received_response);
EXPECT_TRUE(request_peer_context_.complete);
EXPECT_EQ("hello", GetRequestPeerContextBody(&request_peer_context_));
}
// As "transfer size update" message is handled specially in the implementation,
// we have a separate test.
TEST_F(URLLoaderClientImplTest, DeferWithTransferSizeUpdated) {
network::URLLoaderCompletionStatus status;
url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New());
mojo::DataPipe data_pipe(DataPipeOptions());
uint32_t size = 5;
MojoResult result = data_pipe.producer_handle->WriteData(
"hello", &size, MOJO_WRITE_DATA_FLAG_NONE);
ASSERT_EQ(MOJO_RESULT_OK, result);
EXPECT_EQ(5u, size);
data_pipe.producer_handle.reset();
url_loader_client_->OnStartLoadingResponseBody(
std::move(data_pipe.consumer_handle));
url_loader_client_->OnTransferSizeUpdated(4);
url_loader_client_->OnComplete(status);
EXPECT_FALSE(request_peer_context_.received_response);
EXPECT_FALSE(request_peer_context_.complete);
EXPECT_EQ("", GetRequestPeerContextBody(&request_peer_context_));
EXPECT_EQ(0, request_peer_context_.total_encoded_data_length);
dispatcher_->SetDefersLoading(request_id_, true);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(request_peer_context_.received_response);
EXPECT_FALSE(request_peer_context_.complete);
EXPECT_EQ("", GetRequestPeerContextBody(&request_peer_context_));
EXPECT_EQ(0, request_peer_context_.total_encoded_data_length);
dispatcher_->SetDefersLoading(request_id_, false);
EXPECT_FALSE(request_peer_context_.received_response);
EXPECT_FALSE(request_peer_context_.complete);
EXPECT_EQ("", GetRequestPeerContextBody(&request_peer_context_));
EXPECT_EQ(0, request_peer_context_.total_encoded_data_length);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(request_peer_context_.received_response);
EXPECT_TRUE(request_peer_context_.complete);
EXPECT_EQ("hello", GetRequestPeerContextBody(&request_peer_context_));
EXPECT_EQ(4, request_peer_context_.total_encoded_data_length);
}
TEST_F(URLLoaderClientImplTest, SetDeferredDuringFlushingDeferredMessage) {
request_peer_context_.defer_on_redirect = true;
net::RedirectInfo redirect_info;
network::URLLoaderCompletionStatus status;
url_loader_client_->OnReceiveRedirect(redirect_info,
network::mojom::URLResponseHead::New());
url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New());
mojo::DataPipe data_pipe(DataPipeOptions());
uint32_t size = 5;
MojoResult result = data_pipe.producer_handle->WriteData(
"hello", &size, MOJO_WRITE_DATA_FLAG_NONE);
ASSERT_EQ(MOJO_RESULT_OK, result);
EXPECT_EQ(5u, size);
data_pipe.producer_handle.reset();
url_loader_client_->OnStartLoadingResponseBody(
std::move(data_pipe.consumer_handle));
url_loader_client_->OnTransferSizeUpdated(4);
url_loader_client_->OnComplete(status);
EXPECT_EQ(0, request_peer_context_.seen_redirects);
EXPECT_FALSE(request_peer_context_.received_response);
EXPECT_FALSE(request_peer_context_.complete);
EXPECT_EQ("", GetRequestPeerContextBody(&request_peer_context_));
EXPECT_EQ(0, request_peer_context_.total_encoded_data_length);
dispatcher_->SetDefersLoading(request_id_, true);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0, request_peer_context_.seen_redirects);
EXPECT_FALSE(request_peer_context_.received_response);
EXPECT_FALSE(request_peer_context_.complete);
EXPECT_EQ("", GetRequestPeerContextBody(&request_peer_context_));
EXPECT_EQ(0, request_peer_context_.total_encoded_data_length);
dispatcher_->SetDefersLoading(request_id_, false);
EXPECT_EQ(0, request_peer_context_.seen_redirects);
EXPECT_FALSE(request_peer_context_.received_response);
EXPECT_FALSE(request_peer_context_.complete);
EXPECT_EQ("", GetRequestPeerContextBody(&request_peer_context_));
EXPECT_EQ(0, request_peer_context_.total_encoded_data_length);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, request_peer_context_.seen_redirects);
EXPECT_FALSE(request_peer_context_.received_response);
EXPECT_FALSE(request_peer_context_.complete);
EXPECT_EQ("", GetRequestPeerContextBody(&request_peer_context_));
EXPECT_EQ(0, request_peer_context_.total_encoded_data_length);
EXPECT_FALSE(request_peer_context_.cancelled);
dispatcher_->SetDefersLoading(request_id_, false);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1, request_peer_context_.seen_redirects);
EXPECT_TRUE(request_peer_context_.received_response);
EXPECT_TRUE(request_peer_context_.complete);
EXPECT_EQ("hello", GetRequestPeerContextBody(&request_peer_context_));
EXPECT_EQ(4, request_peer_context_.total_encoded_data_length);
EXPECT_FALSE(request_peer_context_.cancelled);
}
TEST_F(URLLoaderClientImplTest,
SetDeferredDuringFlushingDeferredMessageOnTransferSizeUpdated) {
request_peer_context_.defer_on_transfer_size_updated = true;
network::URLLoaderCompletionStatus status;
url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New());
mojo::DataPipe data_pipe;
data_pipe.producer_handle.reset(); // Empty body.
url_loader_client_->OnStartLoadingResponseBody(
std::move(data_pipe.consumer_handle));
url_loader_client_->OnTransferSizeUpdated(4);
url_loader_client_->OnComplete(status);
EXPECT_FALSE(request_peer_context_.received_response);
EXPECT_FALSE(request_peer_context_.complete);
EXPECT_EQ(0, request_peer_context_.total_encoded_data_length);
dispatcher_->SetDefersLoading(request_id_, true);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(request_peer_context_.received_response);
EXPECT_FALSE(request_peer_context_.complete);
EXPECT_EQ(0, request_peer_context_.total_encoded_data_length);
dispatcher_->SetDefersLoading(request_id_, false);
EXPECT_FALSE(request_peer_context_.received_response);
EXPECT_FALSE(request_peer_context_.complete);
EXPECT_EQ(0, request_peer_context_.total_encoded_data_length);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(request_peer_context_.received_response);
EXPECT_FALSE(request_peer_context_.complete);
EXPECT_EQ(4, request_peer_context_.total_encoded_data_length);
EXPECT_FALSE(request_peer_context_.cancelled);
dispatcher_->SetDefersLoading(request_id_, false);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(request_peer_context_.received_response);
EXPECT_TRUE(request_peer_context_.complete);
EXPECT_EQ(4, request_peer_context_.total_encoded_data_length);
EXPECT_FALSE(request_peer_context_.cancelled);
}
} // namespace content