0

Let headless embedders disable the ResourceScheduler

A C++ headless embedder may wish to implement their own resource scheduling.

Bug: 761364
Change-Id: I82faf8505611e606dcf1df70f221cd87bc582b10
Reviewed-on: https://chromium-review.googlesource.com/645426
Reviewed-by: Jochen Eisinger <jochen@chromium.org>
Reviewed-by: Randy Smith <rdsmith@chromium.org>
Reviewed-by: Sami Kyöstilä <skyostil@chromium.org>
Commit-Queue: Alex Clarke <alexclarke@chromium.org>
Cr-Commit-Position: refs/heads/master@{#502221}
This commit is contained in:
Alex Clarke
2017-09-15 10:56:59 +00:00
committed by Commit Bot
parent 33d7523978
commit 9155a3460f
17 changed files with 423 additions and 130 deletions

@ -737,7 +737,11 @@ std::unique_ptr<net::ClientCertStore>
}
void ResourceDispatcherHostImpl::OnInit() {
scheduler_.reset(new ResourceScheduler);
// In some tests |delegate_| does not get set, when that happens assume the
// scheduler is enabled.
bool enable_resource_scheduler =
delegate_ ? delegate_->ShouldUseResourceScheduler() : true;
scheduler_.reset(new ResourceScheduler(enable_resource_scheduler));
}
void ResourceDispatcherHostImpl::OnShutdown() {

@ -780,6 +780,9 @@ class ResourceScheduler::Client {
ShouldStartReqResult ShouldStartRequest(
ScheduledResourceRequest* request) const {
if (!resource_scheduler_->enabled())
return START_REQUEST;
const net::URLRequest& url_request = *request->url_request();
// Syncronous requests could block the entire render, which could impact
// user-observable Clients.
@ -993,8 +996,9 @@ class ResourceScheduler::Client {
base::WeakPtrFactory<ResourceScheduler::Client> weak_ptr_factory_;
};
ResourceScheduler::ResourceScheduler()
: priority_requests_delayable_(
ResourceScheduler::ResourceScheduler(bool enabled)
: enabled_(enabled),
priority_requests_delayable_(
base::FeatureList::IsEnabled(kPrioritySupportedRequestsDelayable)),
head_priority_requests_delayable_(base::FeatureList::IsEnabled(
kHeadPrioritySupportedRequestsDelayable)),

@ -75,7 +75,7 @@ class CONTENT_EXPORT ResourceScheduler {
};
typedef std::vector<MaxRequestsForBDPRange> MaxRequestsForBDPRanges;
ResourceScheduler();
explicit ResourceScheduler(bool enabled);
~ResourceScheduler();
// Requests that this ResourceScheduler schedule, and eventually loads, the
@ -167,6 +167,8 @@ class CONTENT_EXPORT ResourceScheduler {
task_runner_ = std::move(sequenced_task_runner);
}
bool enabled() const { return enabled_; }
private:
class Client;
class RequestQueue;
@ -253,6 +255,11 @@ class CONTENT_EXPORT ResourceScheduler {
ClientMap client_map_;
RequestSet unowned_requests_;
// Whether or not to enable ResourceScheduling. This will almost always be
// enabled, except for some C++ headless embedders who may implement their own
// resource scheduling via protocol handlers.
const bool enabled_;
// True if requests to servers that support priorities (e.g., H2/QUIC) can
// be delayed.
bool priority_requests_delayable_;

@ -181,12 +181,12 @@ class ResourceSchedulerTest : public testing::Test {
// Done separately from construction to allow for modification of command
// line flags in tests.
void InitializeScheduler() {
void InitializeScheduler(bool enabled = true) {
CleanupScheduler();
// Destroys previous scheduler, also destroys any previously created
// mock_timer_.
scheduler_.reset(new ResourceScheduler());
scheduler_.reset(new ResourceScheduler(enabled));
scheduler_->OnClientCreated(kChildId, kRouteId,
&network_quality_estimator_);
@ -1966,6 +1966,32 @@ TEST_F(ResourceSchedulerTest, NumDelayableAtStartOfNonDelayableUMA) {
1);
}
TEST_F(ResourceSchedulerTest, SchedulerEnabled) {
std::unique_ptr<TestRequest> high(
NewRequest("http://host/high", net::HIGHEST));
std::unique_ptr<TestRequest> low(NewRequest("http://host/req", net::LOWEST));
std::unique_ptr<TestRequest> request(
NewRequest("http://host/req", net::LOWEST));
EXPECT_FALSE(request->started());
}
TEST_F(ResourceSchedulerTest, SchedulerDisabled) {
InitializeScheduler(false);
std::unique_ptr<TestRequest> high(
NewRequest("http://host/high", net::HIGHEST));
std::unique_ptr<TestRequest> low(NewRequest("http://host/req", net::LOWEST));
std::unique_ptr<TestRequest> request(
NewRequest("http://host/req", net::LOWEST));
// Normally |request| wouldn't start immediately due to the |high| priority
// request, but when the scheduler is disabled it starts immediately.
EXPECT_TRUE(request->started());
}
} // unnamed namespace
} // namespace content

@ -104,6 +104,10 @@ ResourceDispatcherHostDelegate::CreateClientCertStore(
return std::unique_ptr<net::ClientCertStore>();
}
bool ResourceDispatcherHostDelegate::ShouldUseResourceScheduler() const {
return true;
}
ResourceDispatcherHostDelegate::~ResourceDispatcherHostDelegate() {
}

@ -142,6 +142,11 @@ class CONTENT_EXPORT ResourceDispatcherHostDelegate {
virtual std::unique_ptr<net::ClientCertStore> CreateClientCertStore(
ResourceContext* resource_context);
// Whether or not to enable ResourceScheduling. This will almost always be
// enabled, except for some C++ headless embedders who may implement their own
// resource scheduling via protocol handlers.
virtual bool ShouldUseResourceScheduler() const;
protected:
virtual ~ResourceDispatcherHostDelegate();
};

@ -542,6 +542,10 @@ test("headless_unittests") {
"public/util/error_reporter_unittest.cc",
"public/util/expedited_dispatcher_test.cc",
"public/util/generic_url_request_job_test.cc",
"public/util/testing/generic_url_request_mocks.cc",
"public/util/testing/generic_url_request_mocks.h",
"public/util/testing/test_in_memory_protocol_handler.cc",
"public/util/testing/test_in_memory_protocol_handler.h",
"public/util/throttled_dispatcher_test.cc",
"public/util/virtual_time_controller_test.cc",
]
@ -671,6 +675,12 @@ test("headless_browsertests") {
"lib/headless_devtools_client_browsertest.cc",
"lib/headless_web_contents_browsertest.cc",
"lib/virtual_time_browsertest.cc",
"public/util/testing/fake_managed_dispatch_url_request_job.cc",
"public/util/testing/fake_managed_dispatch_url_request_job.h",
"public/util/testing/generic_url_request_mocks.cc",
"public/util/testing/generic_url_request_mocks.h",
"public/util/testing/test_in_memory_protocol_handler.cc",
"public/util/testing/test_in_memory_protocol_handler.h",
"test/headless_browser_test.cc",
"test/headless_browser_test.h",
"test/headless_test_launcher.cc",

@ -292,7 +292,8 @@ void HeadlessContentBrowserClient::AllowCertificateError(
void HeadlessContentBrowserClient::ResourceDispatcherHostCreated() {
resource_dispatcher_host_delegate_.reset(
new HeadlessResourceDispatcherHostDelegate);
new HeadlessResourceDispatcherHostDelegate(
browser_->options()->enable_resource_scheduler));
content::ResourceDispatcherHost::Get()->SetDelegate(
resource_dispatcher_host_delegate_.get());
}

@ -6,10 +6,16 @@
namespace headless {
HeadlessResourceDispatcherHostDelegate::
HeadlessResourceDispatcherHostDelegate() {}
HeadlessResourceDispatcherHostDelegate::HeadlessResourceDispatcherHostDelegate(
bool enable_resource_scheduler)
: enable_resource_scheduler_(enable_resource_scheduler) {}
HeadlessResourceDispatcherHostDelegate::
~HeadlessResourceDispatcherHostDelegate() {}
bool HeadlessResourceDispatcherHostDelegate::ShouldUseResourceScheduler()
const {
return enable_resource_scheduler_;
}
} // namespace headless

@ -12,10 +12,15 @@ namespace headless {
class HeadlessResourceDispatcherHostDelegate
: public content::ResourceDispatcherHostDelegate {
public:
HeadlessResourceDispatcherHostDelegate();
explicit HeadlessResourceDispatcherHostDelegate(
bool enable_resource_scheduler);
~HeadlessResourceDispatcherHostDelegate() override;
bool ShouldUseResourceScheduler() const override;
private:
const bool enable_resource_scheduler_;
DISALLOW_COPY_AND_ASSIGN(HeadlessResourceDispatcherHostDelegate);
};

@ -12,9 +12,7 @@
#include "headless/public/devtools/domains/network.h"
#include "headless/public/devtools/domains/page.h"
#include "headless/public/headless_devtools_client.h"
#include "headless/public/util/expedited_dispatcher.h"
#include "headless/public/util/generic_url_request_job.h"
#include "headless/public/util/url_fetcher.h"
#include "headless/public/util/testing/test_in_memory_protocol_handler.h"
#include "headless/test/headless_browser_test.h"
#include "net/http/http_response_headers.h"
#include "net/url_request/url_request_job_factory.h"
@ -27,118 +25,6 @@ using testing::ContainerEq;
namespace headless {
namespace {
class TestProtocolHandler : public net::URLRequestJobFactory::ProtocolHandler {
public:
explicit TestProtocolHandler(
scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner)
: test_delegate_(new TestDelegate()),
dispatcher_(new ExpeditedDispatcher(io_thread_task_runner)),
headless_browser_context_(nullptr) {}
~TestProtocolHandler() override {}
void SetHeadlessBrowserContext(
HeadlessBrowserContext* headless_browser_context) {
headless_browser_context_ = headless_browser_context;
}
struct Response {
Response() {}
Response(const std::string& body, const std::string& mime_type)
: data("HTTP/1.1 200 OK\r\nContent-Type: " + mime_type + "\r\n\r\n" +
body) {}
std::string data;
};
void InsertResponse(const std::string& url, const Response& response) {
response_map_[url] = response;
}
const Response* GetResponse(const std::string& url) const {
std::map<std::string, Response>::const_iterator find_it =
response_map_.find(url);
if (find_it == response_map_.end())
return nullptr;
return &find_it->second;
}
class MockURLFetcher : public URLFetcher {
public:
explicit MockURLFetcher(TestProtocolHandler* protocol_handler)
: protocol_handler_(protocol_handler) {}
~MockURLFetcher() override {}
// URLFetcher implementation:
void StartFetch(const Request* request,
ResultListener* result_listener) override {
GURL url = request->GetURLRequest()->url();
EXPECT_EQ("GET", request->GetURLRequest()->method());
const Response* response = protocol_handler_->GetResponse(url.spec());
if (!response)
result_listener->OnFetchStartError(net::ERR_FILE_NOT_FOUND);
net::LoadTimingInfo load_timing_info;
result_listener->OnFetchCompleteExtractHeaders(
url, response->data.c_str(), response->data.size(), load_timing_info);
int frame_tree_node_id = request->GetFrameTreeNodeId();
DCHECK_NE(frame_tree_node_id, -1) << " For url " << url;
protocol_handler_->url_to_frame_tree_node_id_[url.spec()] =
frame_tree_node_id;
}
private:
TestProtocolHandler* protocol_handler_;
DISALLOW_COPY_AND_ASSIGN(MockURLFetcher);
};
class TestDelegate : public GenericURLRequestJob::Delegate {
public:
TestDelegate() {}
~TestDelegate() override {}
// GenericURLRequestJob::Delegate implementation:
void OnResourceLoadFailed(const Request* request,
net::Error error) override {}
void OnResourceLoadComplete(
const Request* request,
const GURL& final_url,
scoped_refptr<net::HttpResponseHeaders> response_headers,
const char* body,
size_t body_size) override {}
private:
scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner_;
DISALLOW_COPY_AND_ASSIGN(TestDelegate);
};
// net::URLRequestJobFactory::ProtocolHandler implementation::
net::URLRequestJob* MaybeCreateJob(
net::URLRequest* request,
net::NetworkDelegate* network_delegate) const override {
return new GenericURLRequestJob(
request, network_delegate, dispatcher_.get(),
base::MakeUnique<MockURLFetcher>(
const_cast<TestProtocolHandler*>(this)),
test_delegate_.get(), headless_browser_context_);
}
std::map<std::string, int> url_to_frame_tree_node_id_;
private:
std::unique_ptr<TestDelegate> test_delegate_;
std::unique_ptr<ExpeditedDispatcher> dispatcher_;
std::map<std::string, Response> response_map_;
HeadlessBrowserContext* headless_browser_context_;
DISALLOW_COPY_AND_ASSIGN(TestProtocolHandler);
};
const char* kIndexHtml = R"(
<html>
<head><link rel="stylesheet" type="text/css" href="style1.css"></head>
@ -215,8 +101,9 @@ class FrameIdTest : public HeadlessAsyncDevTooledBrowserTest,
ProtocolHandlerMap GetProtocolHandlers() override {
ProtocolHandlerMap protocol_handlers;
std::unique_ptr<TestProtocolHandler> http_handler(
new TestProtocolHandler(browser()->BrowserIOThread()));
std::unique_ptr<TestInMemoryProtocolHandler> http_handler(
new TestInMemoryProtocolHandler(browser()->BrowserIOThread(),
/* simulate_slow_fetch */ false));
http_handler_ = http_handler.get();
http_handler_->InsertResponse("http://foo.com/index.html",
{kIndexHtml, "text/html"});
@ -243,7 +130,7 @@ class FrameIdTest : public HeadlessAsyncDevTooledBrowserTest,
// page::Observer implementation:
void OnLoadEventFired(const page::LoadEventFiredParams& params) override {
std::map<std::string, std::string> protocol_handler_url_to_frame_id_;
for (const auto& pair : http_handler_->url_to_frame_tree_node_id_) {
for (const auto& pair : http_handler_->url_to_frame_tree_node_id()) {
// TODO(alexclarke): This will probably break with OOPIF, fix this.
// See https://bugs.chromium.org/p/chromium/issues/detail?id=715924
protocol_handler_url_to_frame_id_[pair.first] =
@ -264,7 +151,7 @@ class FrameIdTest : public HeadlessAsyncDevTooledBrowserTest,
private:
std::map<std::string, std::string> url_to_frame_id_;
TestProtocolHandler* http_handler_; // NOT OWNED
TestInMemoryProtocolHandler* http_handler_; // NOT OWNED
};
HEADLESS_ASYNC_DEVTOOLED_TEST_F(FrameIdTest);

@ -22,6 +22,7 @@
#include "headless/public/headless_browser.h"
#include "headless/public/headless_devtools_client.h"
#include "headless/public/headless_web_contents.h"
#include "headless/public/util/testing/test_in_memory_protocol_handler.h"
#include "headless/test/headless_browser_test.h"
#include "headless/test/tab_socket_test.h"
#include "printing/features/features.h"
@ -42,7 +43,10 @@
#endif
using testing::ElementsAre;
using testing::ElementsAreArray;
using testing::Not;
using testing::UnorderedElementsAre;
using testing::UnorderedElementsAreArray;
namespace headless {
@ -913,4 +917,113 @@ IN_PROC_BROWSER_TEST_F(HeadlessWebContentsTest, BrowserOpenInTab) {
EXPECT_EQ(2u, browser_context->GetAllWebContents().size());
browser_context->RemoveObserver(&observer);
}
namespace {
const char* kRequestOrderTestPage = R"(
<html>
<body>
<img src="img0">
<img src="img1">
<img src="img2">
<img src="img3">
<img src="img4">
<img src="img5">
<img src="img6">
<script src='script7' async></script>
<script>
var xhr = new XMLHttpRequest();
xhr.open('GET', 'xhr8');
xhr.send();
</script>
<iframe src=frame9></iframe>
<img src="img10">
<img src="img11">
</body>
</html> )";
const char* kRequestOrderTestPageUrls[] = {
"http://foo.com/index.html", "http://foo.com/img0",
"http://foo.com/img1", "http://foo.com/img2",
"http://foo.com/img3", "http://foo.com/img4",
"http://foo.com/img5", "http://foo.com/img6",
"http://foo.com/script7", "http://foo.com/img10",
"http://foo.com/img11", "http://foo.com/xhr8",
"http://foo.com/frame9"};
} // namespace
class ResourceSchedulerTest : public HeadlessAsyncDevTooledBrowserTest,
public page::Observer {
public:
void SetUp() override {
options()->enable_resource_scheduler = GetEnableResourceScheduler();
HeadlessBrowserTest::SetUp();
}
void RunDevTooledTest() override {
http_handler_->SetHeadlessBrowserContext(browser_context_);
devtools_client_->GetPage()->AddObserver(this);
base::RunLoop run_loop;
devtools_client_->GetPage()->Enable(run_loop.QuitClosure());
base::MessageLoop::ScopedNestableTaskAllower nest_loop(
base::MessageLoop::current());
run_loop.Run();
devtools_client_->GetPage()->Navigate("http://foo.com/index.html");
}
virtual bool GetEnableResourceScheduler() = 0;
ProtocolHandlerMap GetProtocolHandlers() override {
ProtocolHandlerMap protocol_handlers;
std::unique_ptr<TestInMemoryProtocolHandler> http_handler(
new TestInMemoryProtocolHandler(browser()->BrowserIOThread(),
/* simulate_slow_fetch */ true));
http_handler_ = http_handler.get();
http_handler_->InsertResponse("http://foo.com/index.html",
{kRequestOrderTestPage, "text/html"});
protocol_handlers[url::kHttpScheme] = std::move(http_handler);
return protocol_handlers;
}
const TestInMemoryProtocolHandler* http_handler() const {
return http_handler_;
}
private:
TestInMemoryProtocolHandler* http_handler_; // NOT OWNED
};
class DisableResourceSchedulerTest : public ResourceSchedulerTest {
public:
bool GetEnableResourceScheduler() override { return false; }
void OnLoadEventFired(const page::LoadEventFiredParams&) override {
EXPECT_THAT(http_handler()->urls_requested(),
ElementsAreArray(kRequestOrderTestPageUrls));
FinishAsynchronousTest();
}
};
HEADLESS_ASYNC_DEVTOOLED_TEST_F(DisableResourceSchedulerTest);
class EnableResourceSchedulerTest : public ResourceSchedulerTest {
public:
bool GetEnableResourceScheduler() override {
return true; // The default value.
}
void OnLoadEventFired(const page::LoadEventFiredParams&) override {
// We expect a different resource order when the ResourceScheduler is used.
EXPECT_THAT(http_handler()->urls_requested(),
Not(ElementsAreArray(kRequestOrderTestPageUrls)));
// However all the same urls should still be requested.
EXPECT_THAT(http_handler()->urls_requested(),
UnorderedElementsAreArray(kRequestOrderTestPageUrls));
FinishAsynchronousTest();
}
};
HEADLESS_ASYNC_DEVTOOLED_TEST_F(EnableResourceSchedulerTest);
} // namespace headless

@ -39,6 +39,7 @@ Options::Options(int argc, const char** argv)
message_pump(nullptr),
single_process_mode(false),
disable_sandbox(false),
enable_resource_scheduler(true),
#if defined(USE_OZONE)
// TODO(skyostil): Implement SwiftShader backend for headless ozone.
gl_implementation("osmesa"),
@ -125,6 +126,11 @@ Builder& Builder::SetDisableSandbox(bool disable_sandbox) {
return *this;
}
Builder& Builder::SetEnableResourceScheduler(bool enable_resource_scheduler) {
options_.enable_resource_scheduler = enable_resource_scheduler;
return *this;
}
Builder& Builder::SetGLImplementation(const std::string& gl_implementation) {
options_.gl_implementation = gl_implementation;
return *this;

@ -137,6 +137,9 @@ struct HEADLESS_EXPORT HeadlessBrowser::Options {
// a security risk and should be used with caution.
bool disable_sandbox;
// Whether or not to enable content::ResourceScheduler. Enabled by default.
bool enable_resource_scheduler;
// Choose the GL implementation to use for rendering. A suitable
// implementantion is selected by default. Setting this to an empty
// string can be used to disable GL rendering (e.g., WebGL support).
@ -205,6 +208,7 @@ class HEADLESS_EXPORT HeadlessBrowser::Options::Builder {
Builder& SetMessagePump(base::MessagePump* message_pump);
Builder& SetSingleProcessMode(bool single_process_mode);
Builder& SetDisableSandbox(bool disable_sandbox);
Builder& SetEnableResourceScheduler(bool enable_resource_scheduler);
Builder& SetGLImplementation(const std::string& gl_implementation);
Builder& AddMojoServiceName(const std::string& mojo_service_name);
#if defined(OS_WIN)

@ -18,11 +18,11 @@
#include "headless/public/util/managed_dispatch_url_request_job.h"
#include "headless/public/util/url_fetcher.h"
#include "net/base/net_errors.h"
#include "net/http/http_response_headers.h"
#include "net/url_request/url_request.h"
#include "url/gurl.h"
namespace net {
class HttpResponseHeaders;
class IOBuffer;
} // namespace net

@ -0,0 +1,127 @@
// Copyright 2017 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 "headless/public/util/testing/test_in_memory_protocol_handler.h"
#include "base/memory/ptr_util.h"
#include "headless/public/util/expedited_dispatcher.h"
#include "headless/public/util/generic_url_request_job.h"
#include "headless/public/util/url_fetcher.h"
namespace headless {
class TestInMemoryProtocolHandler::MockURLFetcher : public URLFetcher {
public:
MockURLFetcher(
TestInMemoryProtocolHandler* protocol_handler,
scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner)
: protocol_handler_(protocol_handler),
io_thread_task_runner_(io_thread_task_runner) {}
~MockURLFetcher() override {}
// URLFetcher implementation:
void StartFetch(const Request* request,
ResultListener* result_listener) override {
GURL url = request->GetURLRequest()->url();
DCHECK_EQ("GET", request->GetURLRequest()->method());
int frame_tree_node_id = request->GetFrameTreeNodeId();
DCHECK_NE(frame_tree_node_id, -1) << " For url " << url;
protocol_handler_->RegisterUrl(url.spec(), frame_tree_node_id);
if (protocol_handler_->simulate_slow_fetch()) {
io_thread_task_runner_->PostDelayedTask(
FROM_HERE,
base::Bind(&MockURLFetcher::FinishFetch, base::Unretained(this),
result_listener, url),
base::TimeDelta::FromMilliseconds(100));
} else {
FinishFetch(result_listener, url);
}
}
void FinishFetch(ResultListener* result_listener, GURL url) {
const TestInMemoryProtocolHandler::Response* response =
protocol_handler_->GetResponse(url.spec());
if (response) {
net::LoadTimingInfo load_timing_info;
result_listener->OnFetchCompleteExtractHeaders(
url, response->data.c_str(), response->data.size(), load_timing_info);
} else {
result_listener->OnFetchStartError(net::ERR_FILE_NOT_FOUND);
}
}
private:
TestInMemoryProtocolHandler* protocol_handler_; // NOT OWNED.
scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner_;
DISALLOW_COPY_AND_ASSIGN(MockURLFetcher);
};
class TestInMemoryProtocolHandler::TestDelegate
: public GenericURLRequestJob::Delegate {
public:
TestDelegate() {}
~TestDelegate() override {}
// GenericURLRequestJob::Delegate implementation:
void OnResourceLoadFailed(const Request* request, net::Error error) override {
}
void OnResourceLoadComplete(
const Request* request,
const GURL& final_url,
scoped_refptr<net::HttpResponseHeaders> response_headers,
const char* body,
size_t body_size) override {}
private:
scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner_;
DISALLOW_COPY_AND_ASSIGN(TestDelegate);
};
TestInMemoryProtocolHandler::TestInMemoryProtocolHandler(
scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner,
bool simulate_slow_fetch)
: test_delegate_(new TestDelegate()),
dispatcher_(new ExpeditedDispatcher(io_thread_task_runner)),
headless_browser_context_(nullptr),
simulate_slow_fetch_(simulate_slow_fetch),
io_thread_task_runner_(io_thread_task_runner) {}
TestInMemoryProtocolHandler::~TestInMemoryProtocolHandler() {}
void TestInMemoryProtocolHandler::SetHeadlessBrowserContext(
HeadlessBrowserContext* headless_browser_context) {
headless_browser_context_ = headless_browser_context;
}
void TestInMemoryProtocolHandler::InsertResponse(const std::string& url,
const Response& response) {
response_map_[url] = response;
}
const TestInMemoryProtocolHandler::Response*
TestInMemoryProtocolHandler::GetResponse(const std::string& url) const {
std::map<std::string, Response>::const_iterator find_it =
response_map_.find(url);
if (find_it == response_map_.end())
return nullptr;
return &find_it->second;
}
net::URLRequestJob* TestInMemoryProtocolHandler::MaybeCreateJob(
net::URLRequest* request,
net::NetworkDelegate* network_delegate) const {
return new GenericURLRequestJob(
request, network_delegate, dispatcher_.get(),
base::MakeUnique<MockURLFetcher>(
const_cast<TestInMemoryProtocolHandler*>(this),
io_thread_task_runner_),
test_delegate_.get(), headless_browser_context_);
}
} // namespace headless

@ -0,0 +1,84 @@
// Copyright 2017 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.
#ifndef HEADLESS_PUBLIC_UTIL_TESTING_TEST_IN_MEMORY_PROTOCOL_HANDLER_H_
#define HEADLESS_PUBLIC_UTIL_TESTING_TEST_IN_MEMORY_PROTOCOL_HANDLER_H_
#include <map>
#include <vector>
#include "base/macros.h"
#include "base/single_thread_task_runner.h"
#include "net/url_request/url_request_job_factory.h"
namespace headless {
class ExpeditedDispatcher;
class HeadlessBrowserContext;
class TestInMemoryProtocolHandler
: public net::URLRequestJobFactory::ProtocolHandler {
public:
TestInMemoryProtocolHandler(
scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner,
bool simulate_slow_fetch);
~TestInMemoryProtocolHandler() override;
void SetHeadlessBrowserContext(
HeadlessBrowserContext* headless_browser_context);
struct Response {
Response() {}
Response(const std::string& body, const std::string& mime_type)
: data("HTTP/1.1 200 OK\r\nContent-Type: " + mime_type + "\r\n\r\n" +
body) {}
std::string data;
};
void InsertResponse(const std::string& url, const Response& response);
// net::URLRequestJobFactory::ProtocolHandler implementation::
net::URLRequestJob* MaybeCreateJob(
net::URLRequest* request,
net::NetworkDelegate* network_delegate) const override;
const std::map<std::string, int>& url_to_frame_tree_node_id() const {
return url_to_frame_tree_node_id_;
}
const std::vector<std::string>& urls_requested() const {
return urls_requested_;
}
private:
const Response* GetResponse(const std::string& url) const;
void RegisterUrl(const std::string& url, int frame_tree_node_id) {
urls_requested_.push_back(url);
url_to_frame_tree_node_id_[url] = frame_tree_node_id;
}
bool simulate_slow_fetch() const { return simulate_slow_fetch_; }
class TestDelegate;
class MockURLFetcher;
friend class TestDelegate;
friend class MockURLFetcher;
std::unique_ptr<TestDelegate> test_delegate_;
std::unique_ptr<ExpeditedDispatcher> dispatcher_;
std::map<std::string, Response> response_map_;
HeadlessBrowserContext* headless_browser_context_;
std::map<std::string, int> url_to_frame_tree_node_id_;
std::vector<std::string> urls_requested_;
bool simulate_slow_fetch_;
scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner_;
DISALLOW_COPY_AND_ASSIGN(TestInMemoryProtocolHandler);
};
} // namespace headless
#endif // HEADLESS_PUBLIC_UTIL_TESTING_TEST_IN_MEMORY_PROTOCOL_HANDLER_H_