blob: 83ce44aa6c3fc610c242bff2b6a717d1282b7832 [file] [log] [blame]
// 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 "content/renderer/loader/sync_load_context.h"
#include "base/bind.h"
#include "base/threading/thread.h"
#include "content/public/renderer/fixed_received_data.h"
#include "content/renderer/loader/sync_load_response.h"
#include "mojo/public/cpp/system/data_pipe_utils.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/test/test_url_loader_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
namespace {
class TestSharedURLLoaderFactory : public network::TestURLLoaderFactory,
public network::SharedURLLoaderFactory {
public:
// mojom::URLLoaderFactory implementation.
void CreateLoaderAndStart(network::mojom::URLLoaderRequest request,
int32_t routing_id,
int32_t request_id,
uint32_t options,
const network::ResourceRequest& url_request,
network::mojom::URLLoaderClientPtr client,
const net::MutableNetworkTrafficAnnotationTag&
traffic_annotation) override {
network::TestURLLoaderFactory::CreateLoaderAndStart(
std::move(request), routing_id, request_id, options, url_request,
std::move(client), traffic_annotation);
}
void Clone(network::mojom::URLLoaderFactoryRequest) override { NOTREACHED(); }
std::unique_ptr<network::SharedURLLoaderFactoryInfo> Clone() override {
NOTREACHED();
return nullptr;
}
private:
friend class base::RefCounted<TestSharedURLLoaderFactory>;
~TestSharedURLLoaderFactory() override = default;
};
class MockSharedURLLoaderFactoryInfo
: public network::SharedURLLoaderFactoryInfo {
public:
explicit MockSharedURLLoaderFactoryInfo()
: factory_(base::MakeRefCounted<TestSharedURLLoaderFactory>()) {}
scoped_refptr<TestSharedURLLoaderFactory> factory() const { return factory_; }
protected:
scoped_refptr<network::SharedURLLoaderFactory> CreateFactory() override {
return factory_;
}
scoped_refptr<TestSharedURLLoaderFactory> factory_;
};
class MockResourceDispatcher : public ResourceDispatcher {
public:
int CreatePendingRequest(std::unique_ptr<RequestPeer> peer) {
peers_.push_back(std::move(peer));
return peers_.size() - 1;
}
bool RemovePendingRequest(
int request_id,
scoped_refptr<base::SingleThreadTaskRunner>) override {
if (request_id < 0 || static_cast<int>(peers_.size()) <= request_id)
return false;
peers_[request_id].reset();
return true;
}
private:
std::vector<std::unique_ptr<RequestPeer>> peers_;
};
} // namespace
class SyncLoadContextTest : public testing::Test {
public:
SyncLoadContextTest() : loading_thread_("loading thread") {}
void SetUp() override {
ASSERT_TRUE(loading_thread_.StartAndWaitForTesting());
}
void StartAsyncWithWaitableEventOnLoadingThread(
std::unique_ptr<network::ResourceRequest> request,
std::unique_ptr<network::SharedURLLoaderFactoryInfo> factory_info,
SyncLoadResponse* out_response,
base::WaitableEvent* redirect_or_response_event) {
loading_thread_.task_runner()->PostTask(
FROM_HERE,
base::BindOnce(
&SyncLoadContext::StartAsyncWithWaitableEvent, std::move(request),
MSG_ROUTING_NONE, loading_thread_.task_runner(),
TRAFFIC_ANNOTATION_FOR_TESTS, std::move(factory_info),
std::vector<std::unique_ptr<URLLoaderThrottle>>(), out_response,
redirect_or_response_event, nullptr /* terminate_sync_load_event */,
base::TimeDelta::FromSeconds(60) /* timeout */,
nullptr /* download_to_blob_registry */));
}
static void RunSyncLoadContextViaNonDataPipe(
network::ResourceRequest* request,
SyncLoadResponse* response,
std::string expected_data,
base::WaitableEvent* redirect_or_response_event,
scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
DCHECK(task_runner->BelongsToCurrentThread());
auto* context = new SyncLoadContext(
request, std::make_unique<MockSharedURLLoaderFactoryInfo>(), response,
redirect_or_response_event, nullptr /* terminate_sync_load_event */,
base::TimeDelta::FromSeconds(60) /* timeout */,
nullptr /* download_to_blob_registry */, task_runner);
// Override |resource_dispatcher_| for testing.
auto dispatcher = std::make_unique<MockResourceDispatcher>();
context->request_id_ =
dispatcher->CreatePendingRequest(base::WrapUnique(context));
context->resource_dispatcher_ = std::move(dispatcher);
// Simulate the response.
context->OnReceivedResponse(network::ResourceResponseInfo());
context->OnReceivedData(std::make_unique<FixedReceivedData>(
expected_data.data(), expected_data.size()));
context->OnCompletedRequest(network::URLLoaderCompletionStatus(net::OK));
}
static void RunSyncLoadContextViaDataPipe(
network::ResourceRequest* request,
SyncLoadResponse* response,
std::string expected_data,
base::WaitableEvent* redirect_or_response_event,
scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
DCHECK(task_runner->BelongsToCurrentThread());
auto* context = new SyncLoadContext(
request, std::make_unique<MockSharedURLLoaderFactoryInfo>(), response,
redirect_or_response_event, nullptr /* terminate_sync_load_event */,
base::TimeDelta::FromSeconds(60) /* timeout */,
nullptr /* download_to_blob_registry */, task_runner);
// Override |resource_dispatcher_| for testing.
auto dispatcher = std::make_unique<MockResourceDispatcher>();
context->request_id_ =
dispatcher->CreatePendingRequest(base::WrapUnique(context));
context->resource_dispatcher_ = std::move(dispatcher);
// Simulate the response.
context->OnReceivedResponse(network::ResourceResponseInfo());
mojo::ScopedDataPipeProducerHandle producer_handle;
mojo::ScopedDataPipeConsumerHandle consumer_handle;
EXPECT_EQ(MOJO_RESULT_OK,
mojo::CreateDataPipe(nullptr /* options */, &producer_handle,
&consumer_handle));
context->OnStartLoadingResponseBody(std::move(consumer_handle));
context->OnCompletedRequest(network::URLLoaderCompletionStatus(net::OK));
mojo::BlockingCopyFromString(expected_data, producer_handle);
}
base::Thread loading_thread_;
};
TEST_F(SyncLoadContextTest, StartAsyncWithWaitableEvent) {
GURL expected_url = GURL("https://example.com");
std::string expected_data = "foobarbaz";
// Create and exercise SyncLoadContext on the |loading_thread_|.
auto request = std::make_unique<network::ResourceRequest>();
request->url = expected_url;
auto factory_info = std::make_unique<MockSharedURLLoaderFactoryInfo>();
factory_info->factory()->AddResponse(expected_url.spec(), expected_data);
SyncLoadResponse response;
base::WaitableEvent redirect_or_response_event(
base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
StartAsyncWithWaitableEventOnLoadingThread(std::move(request),
std::move(factory_info), &response,
&redirect_or_response_event);
// Wait until the response is received.
redirect_or_response_event.Wait();
// Check if |response| is set properly after the WaitableEvent fires.
EXPECT_EQ(net::OK, response.error_code);
EXPECT_EQ(expected_data, response.data);
}
TEST_F(SyncLoadContextTest, ResponseBodyViaNonDataPipe) {
GURL expected_url = GURL("https://example.com");
std::string expected_data = "foobarbaz";
// Create and exercise SyncLoadContext on the |loading_thread_|.
auto request = std::make_unique<network::ResourceRequest>();
request->url = expected_url;
SyncLoadResponse response;
base::WaitableEvent redirect_or_response_event(
base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
loading_thread_.task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&SyncLoadContextTest::RunSyncLoadContextViaNonDataPipe,
request.get(), &response, expected_data,
&redirect_or_response_event,
loading_thread_.task_runner()));
// Wait until the response is received.
redirect_or_response_event.Wait();
// Check if |response| is set properly after the WaitableEvent fires.
EXPECT_EQ(net::OK, response.error_code);
EXPECT_EQ(expected_data, response.data);
}
TEST_F(SyncLoadContextTest, ResponseBodyViaDataPipe) {
GURL expected_url = GURL("https://example.com");
std::string expected_data = "foobarbaz";
// Create and exercise SyncLoadContext on the |loading_thread_|.
auto request = std::make_unique<network::ResourceRequest>();
request->url = expected_url;
SyncLoadResponse response;
base::WaitableEvent redirect_or_response_event(
base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
loading_thread_.task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&SyncLoadContextTest::RunSyncLoadContextViaDataPipe,
request.get(), &response, expected_data,
&redirect_or_response_event,
loading_thread_.task_runner()));
// Wait until the response is received.
redirect_or_response_event.Wait();
// Check if |response| is set properly after the WaitableEvent fires.
EXPECT_EQ(net::OK, response.error_code);
EXPECT_EQ(expected_data, response.data);
}
} // namespace content