Adds a --deterministic-fetch flag to headless_shell
Adds a -deterministic-fetch flag to headless_shell which causes network
reuests to complete in the same order they're created in. This removes
a significant source of network related non-determinism at the cost of
slower page loads.
BUG=546953
Review-Url: https://codereview.chromium.org/2352663003
Cr-Commit-Position: refs/heads/master@{#420657}
diff --git a/headless/BUILD.gn b/headless/BUILD.gn
index 572b116..a8a01a3 100644
--- a/headless/BUILD.gn
+++ b/headless/BUILD.gn
@@ -202,12 +202,16 @@
"public/util/black_hole_protocol_handler.h",
"public/util/deterministic_dispatcher.cc",
"public/util/deterministic_dispatcher.h",
+ "public/util/deterministic_http_protocol_handler.cc",
+ "public/util/deterministic_http_protocol_handler.h",
"public/util/error_reporter.cc",
"public/util/error_reporter.h",
"public/util/expedited_dispatcher.cc",
"public/util/expedited_dispatcher.h",
"public/util/generic_url_request_job.cc",
"public/util/generic_url_request_job.h",
+ "public/util/http_url_fetcher.cc",
+ "public/util/http_url_fetcher.h",
"public/util/in_memory_protocol_handler.cc",
"public/util/in_memory_protocol_handler.h",
"public/util/in_memory_request_job.cc",
diff --git a/headless/app/headless_shell.cc b/headless/app/headless_shell.cc
index 73e4d62..0c9ed16 100644
--- a/headless/app/headless_shell.cc
+++ b/headless/app/headless_shell.cc
@@ -25,6 +25,8 @@
#include "headless/public/headless_devtools_client.h"
#include "headless/public/headless_devtools_target.h"
#include "headless/public/headless_web_contents.h"
+#include "headless/public/util/deterministic_dispatcher.h"
+#include "headless/public/util/deterministic_http_protocol_handler.h"
#include "net/base/file_stream.h"
#include "net/base/io_buffer.h"
#include "net/base/ip_address.h"
@@ -73,7 +75,25 @@
void OnStart(HeadlessBrowser* browser) {
browser_ = browser;
- browser_context_ = browser_->CreateBrowserContextBuilder().Build();
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+ headless::switches::kDeterministicFetch)) {
+ deterministic_dispatcher_.reset(
+ new headless::DeterministicDispatcher(browser_->BrowserIOThread()));
+
+ headless::ProtocolHandlerMap protocol_handlers;
+ protocol_handlers[url::kHttpScheme] =
+ base::MakeUnique<headless::DeterministicHttpProtocolHandler>(
+ deterministic_dispatcher_.get(), browser->BrowserIOThread());
+ protocol_handlers[url::kHttpsScheme] =
+ base::MakeUnique<headless::DeterministicHttpProtocolHandler>(
+ deterministic_dispatcher_.get(), browser->BrowserIOThread());
+
+ browser_context_ = browser_->CreateBrowserContextBuilder()
+ .SetProtocolHandlers(std::move(protocol_handlers))
+ .Build();
+ } else {
+ browser_context_ = browser_->CreateBrowserContextBuilder().Build();
+ }
HeadlessWebContents::Builder builder(
browser_context_->CreateWebContentsBuilder());
@@ -335,6 +355,7 @@
bool processed_page_ready_;
std::unique_ptr<net::FileStream> screenshot_file_stream_;
HeadlessBrowserContext* browser_context_;
+ std::unique_ptr<headless::DeterministicDispatcher> deterministic_dispatcher_;
DISALLOW_COPY_AND_ASSIGN(HeadlessShell);
};
diff --git a/headless/app/headless_shell_switches.cc b/headless/app/headless_shell_switches.cc
index a0afc69..c67b5df 100644
--- a/headless/app/headless_shell_switches.cc
+++ b/headless/app/headless_shell_switches.cc
@@ -7,6 +7,11 @@
namespace headless {
namespace switches {
+// Instructs headless_shell to cause network fetches to complete in order of
+// creation. This removes a significant source of network related
+// non-determinism at the cost of slower page loads.
+const char kDeterministicFetch[] = "deterministic-fetch";
+
// Instructs headless_shell to print document.body.innerHTML to stdout.
const char kDumpDom[] = "dump-dom";
diff --git a/headless/app/headless_shell_switches.h b/headless/app/headless_shell_switches.h
index f995cfd..a803c3c 100644
--- a/headless/app/headless_shell_switches.h
+++ b/headless/app/headless_shell_switches.h
@@ -7,6 +7,7 @@
namespace headless {
namespace switches {
+extern const char kDeterministicFetch[];
extern const char kDumpDom[];
extern const char kProxyServer[];
extern const char kRemoteDebuggingAddress[];
diff --git a/headless/public/util/deterministic_http_protocol_handler.cc b/headless/public/util/deterministic_http_protocol_handler.cc
new file mode 100644
index 0000000..bb2c111
--- /dev/null
+++ b/headless/public/util/deterministic_http_protocol_handler.cc
@@ -0,0 +1,80 @@
+// 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 "headless/public/util/deterministic_http_protocol_handler.h"
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "headless/public/headless_browser_context.h"
+#include "headless/public/util/deterministic_dispatcher.h"
+#include "headless/public/util/generic_url_request_job.h"
+#include "headless/public/util/http_url_fetcher.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_job_factory_impl.h"
+
+namespace headless {
+
+class DeterministicHttpProtocolHandler::NopGenericURLRequestJobDelegate
+ : public GenericURLRequestJob::Delegate {
+ public:
+ NopGenericURLRequestJobDelegate() {}
+ ~NopGenericURLRequestJobDelegate() override {}
+
+ // GenericURLRequestJob::Delegate methods:
+ bool BlockOrRewriteRequest(
+ const GURL& url,
+ const std::string& method,
+ const std::string& referrer,
+ GenericURLRequestJob::RewriteCallback callback) override {
+ return false;
+ }
+
+ const GenericURLRequestJob::HttpResponse* MaybeMatchResource(
+ const GURL& url,
+ const std::string& method,
+ const net::HttpRequestHeaders& request_headers) override {
+ return nullptr;
+ }
+
+ void OnResourceLoadComplete(const GURL& final_url,
+ const std::string& mime_type,
+ int http_response_code) override {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NopGenericURLRequestJobDelegate);
+};
+
+DeterministicHttpProtocolHandler::DeterministicHttpProtocolHandler(
+ DeterministicDispatcher* deterministic_dispatcher,
+ scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
+ : deterministic_dispatcher_(deterministic_dispatcher),
+ io_task_runner_(io_task_runner),
+ nop_delegate_(new NopGenericURLRequestJobDelegate()) {}
+
+DeterministicHttpProtocolHandler::~DeterministicHttpProtocolHandler() {
+ if (url_request_context_)
+ io_task_runner_->DeleteSoon(FROM_HERE, url_request_context_.release());
+ if (url_request_job_factory_)
+ io_task_runner_->DeleteSoon(FROM_HERE, url_request_job_factory_.release());
+}
+
+net::URLRequestJob* DeterministicHttpProtocolHandler::MaybeCreateJob(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate) const {
+ if (!url_request_context_) {
+ DCHECK(io_task_runner_->BelongsToCurrentThread());
+ // Create our own URLRequestContext with an empty URLRequestJobFactoryImpl
+ // which lets us use the default http(s) RequestJobs.
+ url_request_context_.reset(new net::URLRequestContext());
+ url_request_context_->CopyFrom(request->context());
+ url_request_job_factory_.reset(new net::URLRequestJobFactoryImpl());
+ url_request_context_->set_job_factory(url_request_job_factory_.get());
+ }
+ return new GenericURLRequestJob(
+ request, network_delegate, deterministic_dispatcher_,
+ base::MakeUnique<HttpURLFetcher>(url_request_context_.get()),
+ nop_delegate_.get());
+}
+
+} // namespace headless
diff --git a/headless/public/util/deterministic_http_protocol_handler.h b/headless/public/util/deterministic_http_protocol_handler.h
new file mode 100644
index 0000000..da0a70d
--- /dev/null
+++ b/headless/public/util/deterministic_http_protocol_handler.h
@@ -0,0 +1,59 @@
+// 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.
+
+#ifndef HEADLESS_PUBLIC_UTIL_DETERMINISTIC_HTTP_PROTOCOL_HANDLER_H_
+#define HEADLESS_PUBLIC_UTIL_DETERMINISTIC_HTTP_PROTOCOL_HANDLER_H_
+
+#include <memory>
+
+#include "base/single_thread_task_runner.h"
+#include "net/url_request/url_request_job_factory.h"
+
+namespace net {
+class URLRequestContext;
+class URLRequestJobFactory;
+} // namespace
+
+namespace headless {
+class DeterministicDispatcher;
+class HeadlessBrowserContext;
+
+// A deterministic protocol handler. Requests made to this protocol handler
+// will return in order of creation, regardless of what order the network
+// returns them in. This helps remove one large source of network related
+// non determinism at the cost of slower page loads.
+class DeterministicHttpProtocolHandler
+ : public net::URLRequestJobFactory::ProtocolHandler {
+ public:
+ // Note |deterministic_dispatcher| is expected to be shared across a number of
+ // protocol handlers, e.g. for http & https protocols.
+ DeterministicHttpProtocolHandler(
+ DeterministicDispatcher* deterministic_dispatcher,
+ scoped_refptr<base::SingleThreadTaskRunner> io_task_runner);
+ ~DeterministicHttpProtocolHandler() override;
+
+ net::URLRequestJob* MaybeCreateJob(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate) const override;
+
+ private:
+ class NopGenericURLRequestJobDelegate;
+
+ DeterministicDispatcher* deterministic_dispatcher_; // NOT OWNED.
+ scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
+ std::unique_ptr<NopGenericURLRequestJobDelegate> nop_delegate_;
+
+ // |url_request_context_| and |url_request_job_factory_| are lazily created on
+ // the IO thread. The URLRequestContext is setup to bypass any user-specified
+ // protocol handlers including this one. This is necessary to actually fetch
+ // http resources.
+ mutable std::unique_ptr<net::URLRequestContext> url_request_context_;
+ mutable std::unique_ptr<net::URLRequestJobFactory> url_request_job_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeterministicHttpProtocolHandler);
+};
+
+} // namespace headless
+
+#endif // HEADLESS_PUBLIC_UTIL_DETERMINISTIC_HTTP_PROTOCOL_HANDLER_H_
diff --git a/headless/public/util/generic_url_request_job.cc b/headless/public/util/generic_url_request_job.cc
index 0228ae84..9c7245b 100644
--- a/headless/public/util/generic_url_request_job.cc
+++ b/headless/public/util/generic_url_request_job.cc
@@ -48,12 +48,13 @@
}
void GenericURLRequestJob::Start() {
- auto callback = [this](RewriteResult result, const GURL& url) {
+ auto callback = [this](RewriteResult result, const GURL& url,
+ const std::string& method) {
switch (result) {
case RewriteResult::kAllow:
// Note that we use the rewritten url for selecting cookies.
// Also, rewriting does not affect the request initiator.
- PrepareCookies(url, url::Origin(url));
+ PrepareCookies(url, method, url::Origin(url));
break;
case RewriteResult::kDeny:
DispatchStartError(net::ERR_FILE_NOT_FOUND);
@@ -66,14 +67,15 @@
}
};
- if (!delegate_->BlockOrRewriteRequest(request_->url(), request_->referrer(),
- callback)) {
- PrepareCookies(request()->url(),
+ if (!delegate_->BlockOrRewriteRequest(request_->url(), request_->method(),
+ request_->referrer(), callback)) {
+ PrepareCookies(request_->url(), request_->method(),
url::Origin(request_->first_party_for_cookies()));
}
}
void GenericURLRequestJob::PrepareCookies(const GURL& rewritten_url,
+ const std::string& method,
const url::Origin& site_for_cookies) {
net::CookieStore* cookie_store = request_->context()->cookie_store();
net::CookieOptions options;
@@ -98,11 +100,12 @@
cookie_store->GetCookieListWithOptionsAsync(
rewritten_url, options,
base::Bind(&GenericURLRequestJob::OnCookiesAvailable,
- weak_factory_.GetWeakPtr(), rewritten_url));
+ weak_factory_.GetWeakPtr(), rewritten_url, method));
}
void GenericURLRequestJob::OnCookiesAvailable(
const GURL& rewritten_url,
+ const std::string& method,
const net::CookieList& cookie_list) {
// TODO(alexclarke): Set user agent.
// Pass cookies, the referrer and any extra headers into the fetch request.
@@ -114,15 +117,16 @@
request_->referrer());
// The resource may have been supplied in the request.
- const HttpResponse* matched_resource =
- delegate_->MaybeMatchResource(rewritten_url, extra_request_headers_);
+ const HttpResponse* matched_resource = delegate_->MaybeMatchResource(
+ rewritten_url, method, extra_request_headers_);
if (matched_resource) {
OnFetchCompleteExtractHeaders(
matched_resource->final_url, matched_resource->http_response_code,
matched_resource->response_data, matched_resource->response_data_size);
} else {
- url_fetcher_->StartFetch(rewritten_url, extra_request_headers_, this);
+ url_fetcher_->StartFetch(rewritten_url, method, extra_request_headers_,
+ this);
}
}
diff --git a/headless/public/util/generic_url_request_job.h b/headless/public/util/generic_url_request_job.h
index 63e5d8ee..514a5e1 100644
--- a/headless/public/util/generic_url_request_job.h
+++ b/headless/public/util/generic_url_request_job.h
@@ -38,8 +38,8 @@
public URLFetcher::ResultListener {
public:
enum class RewriteResult { kAllow, kDeny, kFailure };
- using RewriteCallback =
- std::function<void(RewriteResult result, const GURL& url)>;
+ using RewriteCallback = std::function<
+ void(RewriteResult result, const GURL& url, const std::string& method)>;
struct HttpResponse {
GURL final_url;
@@ -58,6 +58,7 @@
// with the result, or false to indicate that no rewriting is necessary.
// Called on an arbitrary thread.
virtual bool BlockOrRewriteRequest(const GURL& url,
+ const std::string& method,
const std::string& referrer,
RewriteCallback callback) = 0;
@@ -65,6 +66,7 @@
// Called on an arbitrary thread.
virtual const HttpResponse* MaybeMatchResource(
const GURL& url,
+ const std::string& method,
const net::HttpRequestHeaders& request_headers) = 0;
// Signals that a resource load has finished. Called on an arbitrary thread.
@@ -104,9 +106,11 @@
private:
void PrepareCookies(const GURL& rewritten_url,
+ const std::string& method,
const url::Origin& site_for_cookies);
void OnCookiesAvailable(const GURL& rewritten_url,
+ const std::string& method,
const net::CookieList& cookie_list);
std::unique_ptr<URLFetcher> url_fetcher_;
diff --git a/headless/public/util/generic_url_request_job_test.cc b/headless/public/util/generic_url_request_job_test.cc
index 474b03ee..989b5f6 100644
--- a/headless/public/util/generic_url_request_job_test.cc
+++ b/headless/public/util/generic_url_request_job_test.cc
@@ -53,10 +53,12 @@
~MockFetcher() override {}
void StartFetch(const GURL& url,
+ const std::string& method,
const net::HttpRequestHeaders& request_headers,
ResultListener* result_listener) override {
// Record the request.
fetch_request_->SetString("url", url.spec());
+ fetch_request_->SetString("method", method);
std::unique_ptr<base::DictionaryValue> headers(new base::DictionaryValue);
for (net::HttpRequestHeaders::Iterator it(request_headers); it.GetNext();) {
headers->SetString(it.name(), it.value());
@@ -183,6 +185,7 @@
std::string expected_request_json =
"{\"url\": \"https://example.com/\","
+ " \"method\": \"GET\","
" \"headers\": {"
" \"Accept\": \"text/plain\","
" \"Cookie\": \"\","
@@ -334,6 +337,7 @@
std::string expected_request_json =
"{\"url\": \"https://example.com/\","
+ " \"method\": \"GET\","
" \"headers\": {"
" \"Cookie\": \"basic_cookie=1; secure_cookie=2; http_only_cookie=3\","
" \"Referer\": \"\""
diff --git a/headless/public/util/http_url_fetcher.cc b/headless/public/util/http_url_fetcher.cc
new file mode 100644
index 0000000..179c338
--- /dev/null
+++ b/headless/public/util/http_url_fetcher.cc
@@ -0,0 +1,187 @@
+// 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 "headless/public/util/http_url_fetcher.h"
+
+#include "net/base/io_buffer.h"
+#include "net/cert/cert_status_flags.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_context.h"
+
+namespace headless {
+
+class HttpUrlFetcher::Delegate : public net::URLRequest::Delegate {
+ public:
+ Delegate(const GURL& rewritten_url,
+ const std::string& method,
+ const net::HttpRequestHeaders& request_headers,
+ const net::URLRequestContext* url_request_context,
+ ResultListener* result_listener);
+ ~Delegate() override;
+
+ // URLRequest::Delegate methods:
+ void OnAuthRequired(net::URLRequest* request,
+ net::AuthChallengeInfo* auth_info) override;
+ void OnSSLCertificateError(net::URLRequest* request,
+ const net::SSLInfo& ssl_info,
+ bool fatal) override;
+ void OnResponseStarted(net::URLRequest* request, int net_error) override;
+ void OnReadCompleted(net::URLRequest* request, int num_bytes) override;
+
+ private:
+ enum { kBufSize = 4096 };
+
+ bool ConsumeBytesRead(net::URLRequest* request, int num_bytes);
+ void ReadBody(net::URLRequest* request);
+ void OnResponseCompleted(net::URLRequest* request, int net_error);
+
+ // Holds the error condition that was hit by the request, or OK.
+ int result_code_;
+
+ // Buffer that URLRequest writes into.
+ scoped_refptr<net::IOBuffer> buf_;
+
+ // The HTTP fetch.
+ std::unique_ptr<net::URLRequest> request_;
+
+ // Holds the bytes read so far.
+ std::string bytes_read_so_far_;
+
+ // The interface kn which to report any results.
+ ResultListener* result_listener_; // NOT OWNED.
+};
+
+HttpUrlFetcher::Delegate::Delegate(
+ const GURL& rewritten_url,
+ const std::string& method,
+ const net::HttpRequestHeaders& request_headers,
+ const net::URLRequestContext* url_request_context,
+ ResultListener* result_listener)
+ : result_code_(net::OK),
+ buf_(new net::IOBuffer(kBufSize)),
+ request_(url_request_context->CreateRequest(rewritten_url,
+ net::DEFAULT_PRIORITY,
+ this)),
+ result_listener_(result_listener) {
+ request_->set_method(method);
+ request_->SetExtraRequestHeaders(request_headers);
+ request_->Start();
+}
+
+HttpUrlFetcher::Delegate::~Delegate() {}
+
+void HttpUrlFetcher::Delegate::OnAuthRequired(
+ net::URLRequest* request,
+ net::AuthChallengeInfo* auth_info) {
+ DCHECK_EQ(request, request_.get());
+ LOG(WARNING) << "Auth required to fetch URL, aborting.";
+ result_code_ = net::ERR_NOT_IMPLEMENTED;
+ request->CancelAuth();
+}
+
+void HttpUrlFetcher::Delegate::OnSSLCertificateError(
+ net::URLRequest* request,
+ const net::SSLInfo& ssl_info,
+ bool fatal) {
+ DCHECK_EQ(request, request_.get());
+
+ // Revocation check failures are not fatal.
+ if (net::IsCertStatusMinorError(ssl_info.cert_status)) {
+ request->ContinueDespiteLastError();
+ return;
+ }
+ LOG(WARNING) << "SSL certificate error, aborting.";
+
+ // Certificate errors are in same space as net errors.
+ result_code_ = net::MapCertStatusToNetError(ssl_info.cert_status);
+ request->Cancel();
+}
+
+void HttpUrlFetcher::Delegate::OnResponseStarted(net::URLRequest* request,
+ int net_error) {
+ DCHECK_EQ(request, request_.get());
+ DCHECK_NE(net::ERR_IO_PENDING, net_error);
+
+ if (net_error != net::OK) {
+ OnResponseCompleted(request, net_error);
+ return;
+ }
+
+ ReadBody(request);
+}
+
+void HttpUrlFetcher::Delegate::OnReadCompleted(net::URLRequest* request,
+ int num_bytes) {
+ DCHECK_EQ(request, request_.get());
+ DCHECK_NE(net::ERR_IO_PENDING, num_bytes);
+
+ if (ConsumeBytesRead(request, num_bytes)) {
+ // Keep reading.
+ ReadBody(request);
+ }
+}
+
+void HttpUrlFetcher::Delegate::ReadBody(net::URLRequest* request) {
+ // Read as many bytes as are available synchronously.
+ while (true) {
+ int num_bytes = request->Read(buf_.get(), kBufSize);
+ if (num_bytes == net::ERR_IO_PENDING)
+ return;
+
+ if (num_bytes < 0) {
+ OnResponseCompleted(request, num_bytes);
+ return;
+ }
+
+ if (!ConsumeBytesRead(request, num_bytes))
+ return;
+ }
+}
+
+bool HttpUrlFetcher::Delegate::ConsumeBytesRead(net::URLRequest* request,
+ int num_bytes) {
+ if (num_bytes <= 0) {
+ // Error while reading, or EOF.
+ OnResponseCompleted(request, num_bytes);
+ return false;
+ }
+
+ bytes_read_so_far_.append(buf_->data(), num_bytes);
+ return true;
+}
+
+void HttpUrlFetcher::Delegate::OnResponseCompleted(net::URLRequest* request,
+ int net_error) {
+ DCHECK_EQ(request, request_.get());
+
+ if (result_code_ != net::OK) {
+ result_listener_->OnFetchStartError(static_cast<net::Error>(result_code_));
+ return;
+ }
+
+ if (net_error != net::OK) {
+ result_listener_->OnFetchStartError(static_cast<net::Error>(net_error));
+ return;
+ }
+
+ result_listener_->OnFetchCompleteExtractHeaders(
+ request->url(), request->GetResponseCode(), bytes_read_so_far_.c_str(),
+ bytes_read_so_far_.size());
+}
+
+HttpUrlFetcher::HttpUrlFetcher(
+ const net::URLRequestContext* url_request_context)
+ : url_request_context_(url_request_context) {}
+
+HttpUrlFetcher::~HttpUrlFetcher() {}
+
+void HttpUrlFetcher::StartFetch(const GURL& rewritten_url,
+ const std::string& method,
+ const net::HttpRequestHeaders& request_headers,
+ ResultListener* result_listener) {
+ delagate_.reset(new Delegate(rewritten_url, method, request_headers,
+ url_request_context_, result_listener));
+}
+
+} // namespace headless
diff --git a/headless/public/util/http_url_fetcher.h b/headless/public/util/http_url_fetcher.h
new file mode 100644
index 0000000..2b17287
--- /dev/null
+++ b/headless/public/util/http_url_fetcher.h
@@ -0,0 +1,42 @@
+// 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.
+
+#ifndef HEADLESS_PUBLIC_UTIL_HTTP_URL_FETCHER_H_
+#define HEADLESS_PUBLIC_UTIL_HTTP_URL_FETCHER_H_
+
+#include "base/macros.h"
+#include "headless/public/util/url_fetcher.h"
+
+namespace net {
+class URLRequestContext;
+class URLRequestJobFactory;
+} // namespace
+
+namespace headless {
+
+// A simple URLFetcher that uses a net::URLRequestContext to as a back end for
+// http(s) fetches.
+class HttpURLFetcher : public URLFetcher {
+ public:
+ explicit HttpURLFetcher(const net::URLRequestContext* url_request_context);
+ ~HttpURLFetcher() override;
+
+ // URLFetcher implementation:
+ void StartFetch(const GURL& rewritten_url,
+ const std::string& method,
+ const net::HttpRequestHeaders& request_headers,
+ ResultListener* result_listener) override;
+
+ private:
+ class Delegate;
+
+ const net::URLRequestContext* url_request_context_;
+ std::unique_ptr<Delegate> delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(HttpURLFetcher);
+};
+
+} // namespace headless
+
+#endif // HEADLESS_PUBLIC_UTIL_HTTP_URL_FETCHER_H_
diff --git a/headless/public/util/testing/generic_url_request_mocks.cc b/headless/public/util/testing/generic_url_request_mocks.cc
index 4060950..08ff33d 100644
--- a/headless/public/util/testing/generic_url_request_mocks.cc
+++ b/headless/public/util/testing/generic_url_request_mocks.cc
@@ -19,16 +19,18 @@
bool MockGenericURLRequestJobDelegate::BlockOrRewriteRequest(
const GURL& url,
+ const std::string& method,
const std::string& referrer,
GenericURLRequestJob::RewriteCallback callback) {
if (should_block_)
- callback(GenericURLRequestJob::RewriteResult::kDeny, GURL());
+ callback(GenericURLRequestJob::RewriteResult::kDeny, GURL(), method);
return should_block_;
}
const GenericURLRequestJob::HttpResponse*
MockGenericURLRequestJobDelegate::MaybeMatchResource(
const GURL& url,
+ const std::string& method,
const net::HttpRequestHeaders& request_headers) {
return nullptr;
}
diff --git a/headless/public/util/testing/generic_url_request_mocks.h b/headless/public/util/testing/generic_url_request_mocks.h
index 0af68d0..7dc683f 100644
--- a/headless/public/util/testing/generic_url_request_mocks.h
+++ b/headless/public/util/testing/generic_url_request_mocks.h
@@ -33,11 +33,13 @@
bool BlockOrRewriteRequest(
const GURL& url,
+ const std::string& method,
const std::string& referrer,
GenericURLRequestJob::RewriteCallback callback) override;
const GenericURLRequestJob::HttpResponse* MaybeMatchResource(
const GURL& url,
+ const std::string& method,
const net::HttpRequestHeaders& request_headers) override;
void OnResourceLoadComplete(const GURL& final_url,
diff --git a/headless/public/util/url_fetcher.h b/headless/public/util/url_fetcher.h
index 115302f..ac4bbc5 100644
--- a/headless/public/util/url_fetcher.h
+++ b/headless/public/util/url_fetcher.h
@@ -22,8 +22,6 @@
namespace headless {
// An interface for fetching URLs. Note these are only intended to be used once.
-// TODO(alexclarke): Implement a URLFetcher that can backend onto a URLRequest
-// and hook this up in headless_shell under a flag.
class URLFetcher {
public:
URLFetcher() {}
@@ -63,6 +61,7 @@
// Instructs the sub-class to fetch the resource.
virtual void StartFetch(const GURL& rewritten_url,
+ const std::string& method,
const net::HttpRequestHeaders& request_headers,
ResultListener* result_listener) = 0;