blob: d4d012d1ac32519f8875c3ef21e6fe82b28c6404 [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/memory/ptr_util.h"
#include "base/threading/thread.h"
#include "content/renderer/loader/sync_load_response.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.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(
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 {
network::TestURLLoaderFactory::CreateLoaderAndStart(
std::move(receiver), routing_id, request_id, options, url_request,
std::move(client), traffic_annotation);
}
void Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory>) override {
NOTREACHED();
}
std::unique_ptr<network::PendingSharedURLLoaderFactory> Clone() override {
NOTREACHED();
return nullptr;
}
private:
friend class base::RefCounted<TestSharedURLLoaderFactory>;
~TestSharedURLLoaderFactory() override = default;
};
class MockPendingSharedURLLoaderFactory
: public network::PendingSharedURLLoaderFactory {
public:
explicit MockPendingSharedURLLoaderFactory()
: 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::PendingSharedURLLoaderFactory> pending_factory,
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, 0 /* loader_options */,
std::move(pending_factory),
std::vector<std::unique_ptr<blink::URLLoaderThrottle>>(),
out_response, redirect_or_response_event,
nullptr /* terminate_sync_load_event */,
base::TimeDelta::FromSeconds(60) /* timeout */,
mojo::NullRemote() /* download_to_blob_registry */));
}
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<MockPendingSharedURLLoaderFactory>(),
response, redirect_or_response_event,
nullptr /* terminate_sync_load_event */,
base::TimeDelta::FromSeconds(60) /* timeout */,
mojo::NullRemote() /* 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::mojom::URLResponseHead::New());
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 pending_factory = std::make_unique<MockPendingSharedURLLoaderFactory>();
pending_factory->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(pending_factory), &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, 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