blob: f0d2810d7c02199a390b16a6522a08b07657c3d1 [file] [log] [blame]
// Copyright 2019 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.
//
// Overview
//
// The main entry point is CertNetFetcherURLLoader. This is an implementation of
// net::CertNetFetcher that provides a service for fetching network requests.
//
// The interface for net::CertNetFetcher is synchronous, however allows
// overlapping requests. When starting a request CertNetFetcherURLLoader
// returns a net::CertNetFetcher::Request (CertNetFetcherRequestImpl) that the
// caller can use to cancel the fetch, or wait for it to complete
// (blocking).
//
// The CertNetFetcherURLLoader is shared between a creation thread and a
// caller thread that waits for fetches to happen on the creation thread.
//
// The classes are mainly organized based on their thread affinity:
//
// ---------------
// Straddles caller thread and creation thread
// ---------------
//
// CertNetFetcherURLLoader (implements CertNetFetcher)
// * Main entry point. Must be created and shutdown from the creation thread.
// * Provides a service to start/cancel/wait for URL fetches, to be
// used on the caller thread.
// * Returns callers a net::CertNetFetcher::Request as a handle
// * Requests can run in parallel, however will block the current thread when
// reading results.
// * Posts tasks to creation thread to coordinate actual work
//
// RequestCore
// * Reference-counted bridge between CertNetFetcherRequestImpl and the
// dependencies on the creation thread
// * Holds the result of the request, a WaitableEvent for signaling
// completion, and pointers for canceling work on creation thread.
//
// ---------------
// Lives on caller thread
// ---------------
//
// CertNetFetcherRequestImpl (implements net::CertNetFetcher::Request)
// * Wrapper for cancelling events, or waiting for a request to complete
// * Waits on a WaitableEvent to complete requests.
//
// ---------------
// Lives on creation thread
// ---------------
//
// AsyncCertNetFetcherURLLoader
// * Asynchronous manager for outstanding requests. Handles de-duplication,
// timeouts, and actual integration with network stack. This is where the
// majority of the logic lives.
// * Signals completion of requests through RequestCore's WaitableEvent.
// * Attaches requests to Jobs for the purpose of de-duplication
#include "services/cert_verifier/cert_net_url_loader/cert_net_fetcher_url_loader.h"
#include <memory>
#include <tuple>
#include <utility>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/check_op.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/numerics/safe_math.h"
#include "base/sequence_checker.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/timer/timer.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "net/base/load_flags.h"
#include "net/cert/cert_net_fetcher.h"
#include "net/http/http_request_headers.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "net/url_request/redirect_info.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
// TODO(eroman): Add support for POST parameters.
// TODO(eroman): Add controls for bypassing the cache.
// TODO(eroman): Add a maximum number of in-flight jobs/requests.
// TODO(eroman): Add NetLog integration.
namespace cert_verifier {
namespace {
// NOTE: This code uses the SimpleURLLoader interface, so the maximum response
// sizes must be smaller than the limits imposed on
// SimpleURLLoader::DownloadToString.
// The maximum size in bytes for the response body when fetching a CRL.
const int kMaxResponseSizeInBytesForCrl = 5 * 1024 * 1024;
// The maximum size in bytes for the response body when fetching an AIA URL
// (caIssuers/OCSP).
const int kMaxResponseSizeInBytesForAia = 64 * 1024;
// The default timeout in seconds for fetch requests.
const int kTimeoutSeconds = 15;
class Job;
struct JobToRequestParamsComparator;
struct JobComparator {
bool operator()(const Job* job1, const Job* job2) const;
};
// Would be a set<unique_ptr> but extraction of owned objects from a set of
// owned types doesn't come until C++17.
using JobSet = std::map<Job*, std::unique_ptr<Job>, JobComparator>;
using BindNewURLLoaderFactoryCallback = base::RepeatingCallback<void(
mojo::PendingReceiver<network::mojom::URLLoaderFactory>)>;
} // namespace
// AsyncCertNetFetcherURLLoader manages URLLoaders in an async fashion on the
// creation thread.
//
// * Schedules
// * De-duplicates requests
class CertNetFetcherURLLoader::AsyncCertNetFetcherURLLoader {
public:
// Initializes AsyncCertNetFetcherURLLoader, binding the
// PendingRemote<URLLoaderFactory> into a Remote<URLLoaderFactory> for
// creating URLLoaders.
explicit AsyncCertNetFetcherURLLoader(
mojo::PendingRemote<network::mojom::URLLoaderFactory>
factory_pending_remote,
BindNewURLLoaderFactoryCallback bind_new_url_loader_factory_cb);
AsyncCertNetFetcherURLLoader(const AsyncCertNetFetcherURLLoader&) = delete;
AsyncCertNetFetcherURLLoader& operator=(const AsyncCertNetFetcherURLLoader&) =
delete;
// The AsyncCertNetFetcherURLLoader is expected to be kept alive until all
// requests have completed or Shutdown() is called.
~AsyncCertNetFetcherURLLoader();
// Disconnects |factory_|, and calls FlushForTesting() in order to
// synchronously disconnect.
void DisconnectURLLoaderFactoryForTesting();
// Starts an asynchronous request to fetch the given URL. On completion
// request->OnJobCompleted() will be invoked.
void Fetch(std::unique_ptr<RequestParams> request_params,
scoped_refptr<RequestCore> request);
// Removes |job| from the in progress jobs and transfers ownership to the
// caller.
std::unique_ptr<Job> RemoveJob(Job* job);
// Cancels outstanding jobs, which stops network requests and signals the
// corresponding RequestCores that the requests have completed.
void Shutdown();
private:
// Attempts to use |bind_new_url_loader_factory_cb_| to reconnect |factory_|.
// There's no guarantee that |bind_new_url_loader_factory_cb_| will succeed.
void RebindURLLoaderFactory();
// Finds a job with a matching RequestPararms or returns nullptr if there was
// no match.
Job* FindJob(const RequestParams& params);
// The in-progress jobs. This set does not contain the job which is actively
// invoking callbacks (OnJobCompleted).
JobSet jobs_;
// |factory_| may disconnect. On disconnection, immediately try to reconnect
// in case the disconnection was deliberate. If the disconnection was due to a
// crash, the CertNetFetcherURLLoader will likely be destroyed and recreated
// for a new CertVerifier. It can then be reconnected with
// |bind_new_url_loader_factory_cb_|.
mojo::Remote<network::mojom::URLLoaderFactory> factory_;
// A callback provided by the creator of the CertNetFetcherURLLoader that can
// be used to bind a new URLLoaderFactory if the current one disconnects.
BindNewURLLoaderFactoryCallback bind_new_url_loader_factory_cb_;
SEQUENCE_CHECKER(sequence_checker_);
};
namespace {
// Policy for which URLs are allowed to be fetched. This is called both for the
// initial URL and for each redirect. Returns OK on success or a net error
// code on failure.
net::Error CanFetchUrl(const GURL& url) {
if (!url.SchemeIs("http"))
return net::ERR_DISALLOWED_URL_SCHEME;
return net::OK;
}
base::TimeDelta GetTimeout(int timeout_milliseconds) {
if (timeout_milliseconds == net::CertNetFetcher::DEFAULT)
return base::Seconds(kTimeoutSeconds);
return base::Milliseconds(timeout_milliseconds);
}
size_t GetMaxResponseBytes(int max_response_bytes,
size_t default_max_response_bytes) {
if (max_response_bytes == net::CertNetFetcher::DEFAULT)
return default_max_response_bytes;
return max_response_bytes;
}
enum HttpMethod {
HTTP_METHOD_GET,
HTTP_METHOD_POST,
};
} // namespace
// RequestCore tracks an outstanding call to Fetch(). It is
// reference-counted for ease of sharing between threads.
class CertNetFetcherURLLoader::RequestCore
: public base::RefCountedThreadSafe<RequestCore> {
public:
explicit RequestCore(scoped_refptr<base::SequencedTaskRunner> task_runner)
: completion_event_(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED),
task_runner_(std::move(task_runner)) {}
RequestCore(const RequestCore&) = delete;
RequestCore& operator=(const RequestCore&) = delete;
void AttachedToJob(Job* job) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
DCHECK(!job_);
// Requests should not be attached to jobs after they have been signalled
// with a cancellation error (which happens via either Cancel() or
// SignalImmediateError()).
DCHECK_NE(error_, net::ERR_ABORTED);
job_ = job;
}
void OnJobCompleted(Job* job,
net::Error error,
const std::string* response_body) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
DCHECK_EQ(job_, job);
job_ = nullptr;
const uint8_t* string_data =
reinterpret_cast<const uint8_t*>(response_body->data());
error_ = error;
bytes_ =
std::vector<uint8_t>(string_data, string_data + response_body->size());
completion_event_.Signal();
}
// Can be used to signal that an error was encountered before the request was
// attached to a job. Can be called from any thread.
void SignalImmediateError();
// Should only be called once.
void WaitForResult(net::Error* error, std::vector<uint8_t>* bytes) {
DCHECK(!task_runner_->RunsTasksInCurrentSequence());
completion_event_.Wait();
*bytes = std::move(bytes_);
*error = error_;
error_ = net::ERR_UNEXPECTED;
}
// Detaches this request from its job (if it is attached to any) and
// signals completion with ERR_ABORTED. Can be called from any thread.
void CancelJob();
private:
friend class base::RefCountedThreadSafe<RequestCore>;
~RequestCore() {
// Requests should have been cancelled prior to destruction.
DCHECK(!job_);
}
// See comments on CancelJob().
void CancelJobOnTaskRunner();
// A non-owned pointer to the job that is executing the request.
raw_ptr<Job> job_ = nullptr;
// May be written to from network thread, or from the caller thread only when
// there is no work that will be done on the network thread (e.g. when the
// network thread has been shutdown before the request begins). See comment in
// SignalImmediateError.
net::Error error_ = net::OK;
std::vector<uint8_t> bytes_;
// Indicates when |error_| and |bytes_| have been written to.
base::WaitableEvent completion_event_;
// The task runner of the creation thread.
scoped_refptr<base::SequencedTaskRunner> task_runner_;
};
struct CertNetFetcherURLLoader::RequestParams {
RequestParams();
RequestParams(const RequestParams&) = delete;
RequestParams& operator=(const RequestParams&) = delete;
bool operator<(const RequestParams& other) const;
GURL url;
HttpMethod http_method;
size_t max_response_bytes;
// If set to a value == 0 then means "no timeout".
base::TimeDelta timeout;
// IMPORTANT: When adding fields to this structure, update operator<().
};
CertNetFetcherURLLoader::RequestParams::RequestParams()
: http_method(HTTP_METHOD_GET), max_response_bytes(0) {}
bool CertNetFetcherURLLoader::RequestParams::operator<(
const RequestParams& other) const {
return std::tie(url, http_method, max_response_bytes, timeout) <
std::tie(other.url, other.http_method, other.max_response_bytes,
other.timeout);
}
namespace {
// Job tracks an outstanding URLLoader as well as all of the pending requests
// for it.
class Job {
public:
Job(std::unique_ptr<CertNetFetcherURLLoader::RequestParams> request_params,
CertNetFetcherURLLoader::AsyncCertNetFetcherURLLoader* parent);
Job(const Job&) = delete;
Job& operator=(const Job&) = delete;
~Job();
const CertNetFetcherURLLoader::RequestParams& request_params() const {
return *request_params_;
}
// Creates a request and attaches it to the job. When the job completes it
// will notify the request of completion through OnJobCompleted.
void AttachRequest(
scoped_refptr<CertNetFetcherURLLoader::RequestCore> request);
// Removes |request| from the job.
void DetachRequest(CertNetFetcherURLLoader::RequestCore* request);
// Creates and starts a URLLoader for the job. After the URLLoader has
// completed, OnJobCompleted() will be invoked and all the registered requests
// notified of completion.
void StartURLLoader(network::mojom::URLLoaderFactory* factory);
// Cancels the request with an ERR_ABORTED error and invokes
// RequestCore::OnJobCompleted() to notify the registered requests of the
// cancellation. The job is *not* removed from the
// AsyncCertNetFetcherURLLoader.
void Cancel();
private:
void OnReceivedRedirect(const net::RedirectInfo& redirect_info,
const network::mojom::URLResponseHead& response_head,
std::vector<std::string>* removed_headers);
void OnResponseStarted(const GURL& final_url,
const network::mojom::URLResponseHead& response_head);
// Callback for when |url_loader_| has finished.
void OnUrlLoaderCompleted(std::unique_ptr<std::string> response_body);
// Called when the Job has completed. The job may finish in response to a
// timeout, an invalid URL, or the SimpleURLLoader completing.
// This will delete the Job after calling |CompleteAndClearRequests()|.
void OnJobCompleted(net::Error error,
std::unique_ptr<std::string> response_body);
// Calls r->OnJobCompleted() for each RequestCore |r| currently attached
// to this job, and then clears |requests_|.
void CompleteAndClearRequests(net::Error error,
std::unique_ptr<std::string> response_body);
// Cancels a request with a specified error code and calls
// OnUrlRequestCompleted().
void FailRequest(net::Error error);
// The requests attached to this job.
std::vector<scoped_refptr<CertNetFetcherURLLoader::RequestCore>> requests_;
// The input parameters for starting a URLLoader.
std::unique_ptr<CertNetFetcherURLLoader::RequestParams> request_params_;
// SimpleURLLoader for the request. Can be deleted to cancel the request.
std::unique_ptr<network::SimpleURLLoader> url_loader_;
// Non-owned pointer to the AsyncCertNetFetcherURLLoader that created this
// job.
raw_ptr<CertNetFetcherURLLoader::AsyncCertNetFetcherURLLoader> parent_;
};
} // namespace
void CertNetFetcherURLLoader::RequestCore::SignalImmediateError() {
// These data members are normally only written on the network thread, but it
// is safe to write here from either thread. This is because
// SignalImmediateError is only to be called before this request is attached
// to a job. In particular, if called from the caller thread, no work will be
// done on the network thread for this request, so these variables will only
// be written and read on the caller thread. If called from the network
// thread, they will only be written to on the network thread and will not be
// read on the caller thread until |completion_event_| is signalled (after
// which it will be not be written on the network thread again).
DCHECK(!job_);
error_ = net::ERR_ABORTED;
bytes_.clear();
completion_event_.Signal();
}
void CertNetFetcherURLLoader::RequestCore::CancelJob() {
DCHECK(!task_runner_->RunsTasksInCurrentSequence());
task_runner_->PostTask(
FROM_HERE, base::BindOnce(&RequestCore::CancelJobOnTaskRunner, this));
}
void CertNetFetcherURLLoader::RequestCore::CancelJobOnTaskRunner() {
if (job_) {
auto* job = job_.get();
job_ = nullptr;
job->DetachRequest(this);
}
SignalImmediateError();
}
namespace {
Job::Job(std::unique_ptr<CertNetFetcherURLLoader::RequestParams> request_params,
CertNetFetcherURLLoader::AsyncCertNetFetcherURLLoader* parent)
: request_params_(std::move(request_params)), parent_(parent) {}
Job::~Job() {
DCHECK(requests_.empty());
}
void Job::AttachRequest(
scoped_refptr<CertNetFetcherURLLoader::RequestCore> request) {
request->AttachedToJob(this);
requests_.push_back(std::move(request));
}
void Job::DetachRequest(CertNetFetcherURLLoader::RequestCore* request) {
std::unique_ptr<Job> delete_this;
auto it = std::find(requests_.begin(), requests_.end(), request);
DCHECK(it != requests_.end());
requests_.erase(it);
// If there are no longer any requests attached to the job then
// cancel and delete it.
if (requests_.empty())
delete_this = parent_->RemoveJob(this);
}
void Job::StartURLLoader(network::mojom::URLLoaderFactory* factory) {
net::Error error = CanFetchUrl(request_params_->url);
if (error != net::OK) {
FailRequest(error);
return;
}
// Start the SimpleURLLoader.
net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation("certificate_verifier_url_loader", R"(
semantics {
sender: "Certificate Verifier"
description:
"When verifying certificates, the browser may need to fetch "
"additional URLs that are encoded in the server-provided "
"certificate chain. This may be part of revocation checking ("
"Online Certificate Status Protocol, Certificate Revocation List), "
"or path building (Authority Information Access fetches). Please "
"refer to the following for more on above protocols: "
"https://tools.ietf.org/html/rfc6960, "
"https://tools.ietf.org/html/rfc5280#section-4.2.1.13, and"
"https://tools.ietf.org/html/rfc5280#section-5.2.7."
trigger:
"Verifying a certificate (likely in response to navigating to an "
"'https://' website)."
data:
"In the case of OCSP this may divulge the website being viewed. No "
"user data in other cases."
destination: OTHER
destination_other:
"The URL specified in the certificate."
}
policy {
cookies_allowed: NO
setting: "This feature cannot be disabled by settings."
policy_exception_justification: "Not implemented."
})");
auto request = std::make_unique<network::ResourceRequest>();
request->url = request_params_->url;
if (request_params_->http_method == HTTP_METHOD_POST)
request->method = net::HttpRequestHeaders::kPostMethod;
// Disable secure DNS for hostname lookups triggered by certificate network
// fetches to prevent deadlock.
request->trusted_params = network::ResourceRequest::TrustedParams();
request->trusted_params->disable_secure_dns = true;
request->credentials_mode = network::mojom::CredentialsMode::kOmit;
url_loader_ =
network::SimpleURLLoader::Create(std::move(request), traffic_annotation);
// base::Unretained(this) is safe because |this| owns |url_loader_|, which
// will not call the callback if it is deleted.
url_loader_->SetOnRedirectCallback(
base::BindRepeating(&Job::OnReceivedRedirect, base::Unretained(this)));
url_loader_->SetOnResponseStartedCallback(
base::BindOnce(&Job::OnResponseStarted, base::Unretained(this)));
url_loader_->SetTimeoutDuration(request_params_->timeout);
url_loader_->DownloadToString(
factory,
base::BindOnce(&Job::OnUrlLoaderCompleted, base::Unretained(this)),
request_params_->max_response_bytes);
}
void Job::Cancel() {
// Reset the SimpleURLLoader.
url_loader_.reset();
// Signal attached requests that they've been completed.
CompleteAndClearRequests(net::ERR_ABORTED, nullptr);
}
void Job::OnReceivedRedirect(
const net::RedirectInfo& redirect_info,
const network::mojom::URLResponseHead& response_head,
std::vector<std::string>* removed_headers) {
// Ensure that the new URL matches the policy.
net::Error error = CanFetchUrl(redirect_info.new_url);
if (error != net::OK) {
FailRequest(error);
}
}
void Job::OnResponseStarted(
const GURL& final_url,
const network::mojom::URLResponseHead& response_head) {
if (!response_head.headers || response_head.headers->response_code() != 200) {
FailRequest(net::ERR_HTTP_RESPONSE_CODE_FAILURE);
}
}
void Job::OnUrlLoaderCompleted(std::unique_ptr<std::string> response_body) {
net::Error error = static_cast<net::Error>(url_loader_->NetError());
OnJobCompleted(error, std::move(response_body));
}
void Job::OnJobCompleted(net::Error error,
std::unique_ptr<std::string> response_body) {
// Reset the SimpleURLLoader.
url_loader_.reset();
std::unique_ptr<Job> delete_this = parent_->RemoveJob(this);
CompleteAndClearRequests(error, std::move(response_body));
}
void Job::CompleteAndClearRequests(net::Error error,
std::unique_ptr<std::string> response_body) {
for (const auto& request : requests_) {
std::string empty_str;
request->OnJobCompleted(this, error,
response_body ? response_body.get() : &empty_str);
}
requests_.clear();
}
void Job::FailRequest(net::Error error) {
OnJobCompleted(error, nullptr);
}
} // namespace
CertNetFetcherURLLoader::AsyncCertNetFetcherURLLoader::
AsyncCertNetFetcherURLLoader(
mojo::PendingRemote<network::mojom::URLLoaderFactory>
factory_pending_remote,
BindNewURLLoaderFactoryCallback bind_new_url_loader_factory_cb)
: factory_(std::move(factory_pending_remote)),
bind_new_url_loader_factory_cb_(
std::move(bind_new_url_loader_factory_cb)) {}
CertNetFetcherURLLoader::AsyncCertNetFetcherURLLoader::
~AsyncCertNetFetcherURLLoader() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// The fetcher must be shutdown (at which point |factory_| will be reset and
// unbound) before destruction.
DCHECK(!factory_);
DCHECK(jobs_.empty());
}
void CertNetFetcherURLLoader::AsyncCertNetFetcherURLLoader::
DisconnectURLLoaderFactoryForTesting() {
// Sadly, there's no good way to disconnect a Mojo remote other than resetting
// it, binding it to a new pipe, and dropping the PendingReceiver on the
// floor.
factory_.reset();
std::ignore = factory_.BindNewPipeAndPassReceiver();
factory_.FlushForTesting();
}
bool JobComparator::operator()(const Job* job1, const Job* job2) const {
return job1->request_params() < job2->request_params();
}
void CertNetFetcherURLLoader::AsyncCertNetFetcherURLLoader::Fetch(
std::unique_ptr<RequestParams> request_params,
scoped_refptr<RequestCore> request) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// If there is an in-progress job that matches the request parameters use it.
// Otherwise start a new job.
Job* job = FindJob(*request_params);
if (job) {
job->AttachRequest(std::move(request));
return;
}
if (!factory_.is_connected()) {
RebindURLLoaderFactory();
}
job = new Job(std::move(request_params), this);
jobs_[job] = base::WrapUnique(job);
// Attach the request before calling |StartURLLoader()|; this ensures that the
// request will get signalled if |StartURLLoader()| completes the job
// synchronously.
job->AttachRequest(std::move(request));
job->StartURLLoader(factory_.get());
}
void CertNetFetcherURLLoader::AsyncCertNetFetcherURLLoader::Shutdown() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
for (const auto& job : jobs_) {
job.first->Cancel();
}
jobs_.clear();
factory_.reset();
}
void CertNetFetcherURLLoader::AsyncCertNetFetcherURLLoader::
RebindURLLoaderFactory() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// If Shutdown() was called, the AsyncCertNetFetcherURLLoader should not be
// trying to reconnect a URLLoaderFactory.
DCHECK(factory_);
if (!bind_new_url_loader_factory_cb_) {
return;
}
factory_.reset();
bind_new_url_loader_factory_cb_.Run(factory_.BindNewPipeAndPassReceiver());
}
namespace {
struct JobToRequestParamsComparator {
bool operator()(const JobSet::value_type& job,
const CertNetFetcherURLLoader::RequestParams& value) const {
return job.first->request_params() < value;
}
};
} // namespace
Job* CertNetFetcherURLLoader::AsyncCertNetFetcherURLLoader::FindJob(
const RequestParams& params) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// The JobSet is kept in sorted order so items can be found using binary
// search.
auto it = std::lower_bound(jobs_.begin(), jobs_.end(), params,
JobToRequestParamsComparator());
if (it != jobs_.end() && !(params < (*it).first->request_params()))
return (*it).first;
return nullptr;
}
std::unique_ptr<Job>
CertNetFetcherURLLoader::AsyncCertNetFetcherURLLoader::RemoveJob(Job* job) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto it = jobs_.find(job);
CHECK(it != jobs_.end());
std::unique_ptr<Job> owned_job = std::move(it->second);
jobs_.erase(it);
return owned_job;
}
namespace {
class CertNetFetcherRequestImpl : public net::CertNetFetcher::Request {
public:
explicit CertNetFetcherRequestImpl(
scoped_refptr<CertNetFetcherURLLoader::RequestCore> core)
: core_(std::move(core)) {
DCHECK(core_);
}
void WaitForResult(net::Error* error, std::vector<uint8_t>* bytes) override {
// Should only be called a single time.
DCHECK(core_);
core_->WaitForResult(error, bytes);
core_ = nullptr;
}
~CertNetFetcherRequestImpl() override {
if (core_)
core_->CancelJob();
}
private:
scoped_refptr<CertNetFetcherURLLoader::RequestCore> core_;
};
} // namespace
CertNetFetcherURLLoader::CertNetFetcherURLLoader()
: task_runner_(base::SequencedTaskRunnerHandle::Get()) {}
CertNetFetcherURLLoader::~CertNetFetcherURLLoader() = default;
void CertNetFetcherURLLoader::SetURLLoaderFactoryAndReconnector(
mojo::PendingRemote<network::mojom::URLLoaderFactory> factory,
base::RepeatingCallback<
void(mojo::PendingReceiver<network::mojom::URLLoaderFactory>)>
bind_new_url_loader_factory_cb) {
DCHECK(!impl_);
impl_ = std::make_unique<AsyncCertNetFetcherURLLoader>(
std::move(factory), std::move(bind_new_url_loader_factory_cb));
}
// static
base::TimeDelta CertNetFetcherURLLoader::GetDefaultTimeoutForTesting() {
return GetTimeout(CertNetFetcher::DEFAULT);
}
void CertNetFetcherURLLoader::DisconnectURLLoaderFactoryForTesting() {
impl_->DisconnectURLLoaderFactoryForTesting();
}
void CertNetFetcherURLLoader::Shutdown() {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
if (impl_) {
impl_->Shutdown();
impl_.reset();
}
}
std::unique_ptr<net::CertNetFetcher::Request>
CertNetFetcherURLLoader::FetchCaIssuers(const GURL& url,
int timeout_milliseconds,
int max_response_bytes) {
std::unique_ptr<RequestParams> request_params(new RequestParams);
request_params->url = url;
request_params->http_method = HTTP_METHOD_GET;
request_params->timeout = GetTimeout(timeout_milliseconds);
request_params->max_response_bytes =
GetMaxResponseBytes(max_response_bytes, kMaxResponseSizeInBytesForAia);
return DoFetch(std::move(request_params));
}
std::unique_ptr<net::CertNetFetcher::Request> CertNetFetcherURLLoader::FetchCrl(
const GURL& url,
int timeout_milliseconds,
int max_response_bytes) {
std::unique_ptr<RequestParams> request_params(new RequestParams);
request_params->url = url;
request_params->http_method = HTTP_METHOD_GET;
request_params->timeout = GetTimeout(timeout_milliseconds);
request_params->max_response_bytes =
GetMaxResponseBytes(max_response_bytes, kMaxResponseSizeInBytesForCrl);
return DoFetch(std::move(request_params));
}
std::unique_ptr<net::CertNetFetcher::Request>
CertNetFetcherURLLoader::FetchOcsp(const GURL& url,
int timeout_milliseconds,
int max_response_bytes) {
std::unique_ptr<RequestParams> request_params(new RequestParams);
request_params->url = url;
request_params->http_method = HTTP_METHOD_GET;
request_params->timeout = GetTimeout(timeout_milliseconds);
request_params->max_response_bytes =
GetMaxResponseBytes(max_response_bytes, kMaxResponseSizeInBytesForAia);
return DoFetch(std::move(request_params));
}
void CertNetFetcherURLLoader::DoFetchOnTaskRunner(
std::unique_ptr<RequestParams> request_params,
scoped_refptr<RequestCore> request) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
if (!impl_) {
// |SetURLLoaderFactoryAndReconnector| may not have been called yet, or the
// fetcher might have been shutdown between when this task was posted and
// when it is running. In this case, signal the request and do not start a
// network request.
request->SignalImmediateError();
return;
}
impl_->Fetch(std::move(request_params), request);
}
std::unique_ptr<CertNetFetcherURLLoader::Request>
CertNetFetcherURLLoader::DoFetch(
std::unique_ptr<RequestParams> request_params) {
// Caller thread cannot be the same as the creation thread.
DCHECK(!task_runner_->RunsTasksInCurrentSequence());
auto request_core = base::MakeRefCounted<RequestCore>(task_runner_);
// If the fetcher has already been shutdown, DoFetchOnTaskRunner will
// signal the request with an error. However, if the fetcher shuts down
// before DoFetchOnTaskRunner runs and PostTask still returns true,
// then the request will hang (that is, WaitForResult will not return).
if (!task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&CertNetFetcherURLLoader::DoFetchOnTaskRunner, this,
std::move(request_params), request_core))) {
request_core->SignalImmediateError();
}
return std::make_unique<CertNetFetcherRequestImpl>(std::move(request_core));
}
} // namespace cert_verifier