123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887 |
- // Copyright 2015 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 CertNetFetcherURLRequest. This is an implementation
- // of CertNetFetcher that provides a service for fetching network requests.
- //
- // The interface for CertNetFetcher is synchronous, however allows
- // overlapping requests. When starting a request CertNetFetcherURLRequest
- // returns a CertNetFetcher::Request (CertNetFetcherRequestImpl) that the
- // caller can use to cancel the fetch, or wait for it to complete
- // (blocking).
- //
- // The CertNetFetcherURLRequest is shared between a network thread and a
- // caller thread that waits for fetches to happen on the network thread.
- //
- // The classes are mainly organized based on their thread affinity:
- //
- // ---------------
- // Straddles caller thread and network thread
- // ---------------
- //
- // CertNetFetcherURLRequest (implements CertNetFetcher)
- // * Main entry point. Must be created and shutdown from the network thread.
- // * Provides a service to start/cancel/wait for URL fetches, to be
- // used on the caller thread.
- // * Returns callers a CertNetFetcher::Request as a handle
- // * Requests can run in parallel, however will block the current thread when
- // reading results.
- // * Posts tasks to network thread to coordinate actual work
- //
- // RequestCore
- // * Reference-counted bridge between CertNetFetcherRequestImpl and the
- // dependencies on the network thread
- // * Holds the result of the request, a WaitableEvent for signaling
- // completion, and pointers for canceling work on network thread.
- //
- // ---------------
- // Lives on caller thread
- // ---------------
- //
- // CertNetFetcherRequestImpl (implements CertNetFetcher::Request)
- // * Wrapper for cancelling events, or waiting for a request to complete
- // * Waits on a WaitableEvent to complete requests.
- //
- // ---------------
- // Lives on network thread
- // ---------------
- //
- // AsyncCertNetFetcherURLRequest
- // * 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 "net/cert_net/cert_net_fetcher_url_request.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/numerics/safe_math.h"
- #include "base/synchronization/waitable_event.h"
- #include "base/task/single_thread_task_runner.h"
- #include "base/threading/thread_task_runner_handle.h"
- #include "base/time/time.h"
- #include "base/timer/timer.h"
- #include "net/base/io_buffer.h"
- #include "net/base/isolation_info.h"
- #include "net/base/load_flags.h"
- #include "net/cert/cert_net_fetcher.h"
- #include "net/cookies/site_for_cookies.h"
- #include "net/dns/public/secure_dns_policy.h"
- #include "net/traffic_annotation/network_traffic_annotation.h"
- #include "net/url_request/redirect_info.h"
- #include "net/url_request/url_request_context.h"
- #include "url/origin.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 net {
- namespace {
- // The size of the buffer used for reading the response body of the URLRequest.
- const int kReadBufferSizeInBytes = 4096;
- // 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>;
- } // namespace
- // AsyncCertNetFetcherURLRequest manages URLRequests in an async fashion on the
- // URLRequestContexts's task runner thread.
- //
- // * Schedules
- // * De-duplicates requests
- // * Handles timeouts
- class CertNetFetcherURLRequest::AsyncCertNetFetcherURLRequest {
- public:
- // Initializes AsyncCertNetFetcherURLRequest using the specified
- // URLRequestContext for issuing requests. |context| must remain valid until
- // Shutdown() is called or the AsyncCertNetFetcherURLRequest is destroyed.
- explicit AsyncCertNetFetcherURLRequest(URLRequestContext* context);
- AsyncCertNetFetcherURLRequest(const AsyncCertNetFetcherURLRequest&) = delete;
- AsyncCertNetFetcherURLRequest& operator=(
- const AsyncCertNetFetcherURLRequest&) = delete;
- // The AsyncCertNetFetcherURLRequest is expected to be kept alive until all
- // requests have completed or Shutdown() is called.
- ~AsyncCertNetFetcherURLRequest();
- // 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:
- // 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_;
- // Not owned. |context_| must outlive the AsyncCertNetFetcherURLRequest.
- raw_ptr<URLRequestContext> context_ = nullptr;
- THREAD_CHECKER(thread_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.
- Error CanFetchUrl(const GURL& url) {
- if (!url.SchemeIs("http"))
- return ERR_DISALLOWED_URL_SCHEME;
- return OK;
- }
- base::TimeDelta GetTimeout(int timeout_milliseconds) {
- if (timeout_milliseconds == 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 == CertNetFetcher::DEFAULT)
- return default_max_response_bytes;
- // Ensure that the specified limit is not negative, and cannot result in an
- // overflow while reading.
- base::CheckedNumeric<size_t> check(max_response_bytes);
- check += kReadBufferSizeInBytes;
- DCHECK(check.IsValid());
- 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 CertNetFetcherURLRequest::RequestCore
- : public base::RefCountedThreadSafe<RequestCore> {
- public:
- explicit RequestCore(scoped_refptr<base::SingleThreadTaskRunner> 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_, ERR_ABORTED);
- job_ = job;
- }
- void OnJobCompleted(Job* job,
- Error error,
- const std::vector<uint8_t>& response_body) {
- DCHECK(task_runner_->RunsTasksInCurrentSequence());
- DCHECK_EQ(job_, job);
- job_ = nullptr;
- error_ = error;
- bytes_ = response_body;
- completion_event_.Signal();
- }
- // 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();
- // 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(Error* error, std::vector<uint8_t>* bytes) {
- DCHECK(!task_runner_->RunsTasksInCurrentSequence());
- completion_event_.Wait();
- *bytes = std::move(bytes_);
- *error = error_;
- error_ = ERR_UNEXPECTED;
- }
- private:
- friend class base::RefCountedThreadSafe<RequestCore>;
- ~RequestCore() {
- // Requests should have been cancelled prior to destruction.
- DCHECK(!job_);
- }
- // 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.
- Error error_ = OK;
- std::vector<uint8_t> bytes_;
- // Indicates when |error_| and |bytes_| have been written to.
- base::WaitableEvent completion_event_;
- scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
- };
- struct CertNetFetcherURLRequest::RequestParams {
- RequestParams();
- RequestParams(const RequestParams&) = delete;
- RequestParams& operator=(const RequestParams&) = delete;
- bool operator<(const RequestParams& other) const;
- GURL url;
- HttpMethod http_method = HTTP_METHOD_GET;
- size_t max_response_bytes = 0;
- // If set to a value <= 0 then means "no timeout".
- base::TimeDelta timeout;
- // IMPORTANT: When adding fields to this structure, update operator<().
- };
- CertNetFetcherURLRequest::RequestParams::RequestParams() = default;
- bool CertNetFetcherURLRequest::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 URLRequest as well as all of the pending requests
- // for it.
- class Job : public URLRequest::Delegate {
- public:
- Job(std::unique_ptr<CertNetFetcherURLRequest::RequestParams> request_params,
- CertNetFetcherURLRequest::AsyncCertNetFetcherURLRequest* parent);
- Job(const Job&) = delete;
- Job& operator=(const Job&) = delete;
- ~Job() override;
- const CertNetFetcherURLRequest::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<CertNetFetcherURLRequest::RequestCore> request);
- // Removes |request| from the job.
- void DetachRequest(CertNetFetcherURLRequest::RequestCore* request);
- // Creates and starts a URLRequest for the job. After the URLRequest has
- // completed, OnJobCompleted() will be invoked and all the registered requests
- // notified of completion.
- void StartURLRequest(URLRequestContext* context);
- // 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
- // AsyncCertNetFetcherURLRequest.
- void Cancel();
- private:
- // Implementation of URLRequest::Delegate
- void OnReceivedRedirect(URLRequest* request,
- const RedirectInfo& redirect_info,
- bool* defer_redirect) override;
- void OnResponseStarted(URLRequest* request, int net_error) override;
- void OnReadCompleted(URLRequest* request, int bytes_read) override;
- // Clears the URLRequest and timer. Helper for doing work common to
- // cancellation and job completion.
- void Stop();
- // Reads as much data as available from |request|.
- void ReadBody(URLRequest* request);
- // Helper to copy the partial bytes read from the read IOBuffer to an
- // aggregated buffer.
- bool ConsumeBytesRead(URLRequest* request, int num_bytes);
- // Called when the URLRequest has completed (either success or failure).
- void OnUrlRequestCompleted(int net_error);
- // Called when the Job has completed. The job may finish in response to a
- // timeout, an invalid URL, or the URLRequest completing. By the time this
- // method is called, the |response_body_| variable have been assigned.
- void OnJobCompleted(Error error);
- // Calls r->OnJobCompleted() for each RequestCore |r| currently attached
- // to this job, and then clears |requests_|.
- void CompleteAndClearRequests(Error error);
- // Cancels a request with a specified error code and calls
- // OnUrlRequestCompleted().
- void FailRequest(Error error);
- // The requests attached to this job.
- std::vector<scoped_refptr<CertNetFetcherURLRequest::RequestCore>> requests_;
- // The input parameters for starting a URLRequest.
- std::unique_ptr<CertNetFetcherURLRequest::RequestParams> request_params_;
- // The URLRequest response information.
- std::vector<uint8_t> response_body_;
- std::unique_ptr<URLRequest> url_request_;
- scoped_refptr<IOBuffer> read_buffer_;
- // Used to timeout the job when the URLRequest takes too long. This timer is
- // also used for notifying a failure to start the URLRequest.
- base::OneShotTimer timer_;
- // Non-owned pointer to the AsyncCertNetFetcherURLRequest that created this
- // job.
- raw_ptr<CertNetFetcherURLRequest::AsyncCertNetFetcherURLRequest> parent_;
- };
- } // namespace
- void CertNetFetcherURLRequest::RequestCore::CancelJob() {
- if (!task_runner_->RunsTasksInCurrentSequence()) {
- task_runner_->PostTask(FROM_HERE,
- base::BindOnce(&RequestCore::CancelJob, this));
- return;
- }
- if (job_) {
- auto* job = job_.get();
- job_ = nullptr;
- job->DetachRequest(this);
- }
- SignalImmediateError();
- }
- void CertNetFetcherURLRequest::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_ = ERR_ABORTED;
- bytes_.clear();
- completion_event_.Signal();
- }
- namespace {
- Job::Job(
- std::unique_ptr<CertNetFetcherURLRequest::RequestParams> request_params,
- CertNetFetcherURLRequest::AsyncCertNetFetcherURLRequest* parent)
- : request_params_(std::move(request_params)), parent_(parent) {}
- Job::~Job() {
- DCHECK(requests_.empty());
- Stop();
- }
- void Job::AttachRequest(
- scoped_refptr<CertNetFetcherURLRequest::RequestCore> request) {
- request->AttachedToJob(this);
- requests_.push_back(std::move(request));
- }
- void Job::DetachRequest(CertNetFetcherURLRequest::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::StartURLRequest(URLRequestContext* context) {
- Error error = CanFetchUrl(request_params_->url);
- if (error != OK) {
- OnJobCompleted(error);
- return;
- }
- // Start the URLRequest.
- read_buffer_ = base::MakeRefCounted<IOBuffer>(kReadBufferSizeInBytes);
- NetworkTrafficAnnotationTag traffic_annotation =
- DefineNetworkTrafficAnnotation("certificate_verifier_url_request",
- 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."
- "NOTE: this path is being deprecated. Please see the"
- "certificate_verifier_url_loader annotation for the new path."
- 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."
- })");
- url_request_ = context->CreateRequest(request_params_->url, DEFAULT_PRIORITY,
- this, traffic_annotation);
- if (request_params_->http_method == HTTP_METHOD_POST)
- url_request_->set_method("POST");
- url_request_->set_allow_credentials(false);
- // Disable secure DNS for hostname lookups triggered by certificate network
- // fetches to prevent deadlock.
- url_request_->SetSecureDnsPolicy(SecureDnsPolicy::kDisable);
- // Create IsolationInfo based on the origin of the requested URL.
- // TODO(https://crbug.com/1016890): Cert validation needs to either be
- // double-keyed or based on a static database, to protect it from being used
- // as a cross-site user tracking vector. For now, just treat it as if it were
- // a subresource request of the origin used for the request. This allows the
- // result to still be cached in the HTTP cache, and lets URLRequest DCHECK
- // that all requests have non-empty IsolationInfos.
- url::Origin origin = url::Origin::Create(request_params_->url);
- url_request_->set_isolation_info(IsolationInfo::Create(
- IsolationInfo::RequestType::kOther, origin /* top_frame_origin */,
- origin /* frame_origin */, SiteForCookies()));
- url_request_->Start();
- // Start a timer to limit how long the job runs for.
- if (request_params_->timeout.is_positive()) {
- timer_.Start(FROM_HERE, request_params_->timeout,
- base::BindOnce(&Job::FailRequest, base::Unretained(this),
- ERR_TIMED_OUT));
- }
- }
- void Job::Cancel() {
- // Stop the timer and clear the URLRequest.
- Stop();
- // Signal attached requests that they've been completed.
- CompleteAndClearRequests(static_cast<Error>(ERR_ABORTED));
- }
- void Job::OnReceivedRedirect(URLRequest* request,
- const RedirectInfo& redirect_info,
- bool* defer_redirect) {
- DCHECK_EQ(url_request_.get(), request);
- // Ensure that the new URL matches the policy.
- Error error = CanFetchUrl(redirect_info.new_url);
- if (error != OK) {
- FailRequest(error);
- return;
- }
- }
- void Job::OnResponseStarted(URLRequest* request, int net_error) {
- DCHECK_EQ(url_request_.get(), request);
- DCHECK_NE(ERR_IO_PENDING, net_error);
- if (net_error != OK) {
- OnUrlRequestCompleted(net_error);
- return;
- }
- if (request->GetResponseCode() != 200) {
- FailRequest(ERR_HTTP_RESPONSE_CODE_FAILURE);
- return;
- }
- ReadBody(request);
- }
- void Job::OnReadCompleted(URLRequest* request, int bytes_read) {
- DCHECK_EQ(url_request_.get(), request);
- DCHECK_NE(ERR_IO_PENDING, bytes_read);
- // Keep reading the response body.
- if (ConsumeBytesRead(request, bytes_read))
- ReadBody(request);
- }
- void Job::Stop() {
- timer_.Stop();
- url_request_.reset();
- }
- void Job::ReadBody(URLRequest* request) {
- // Read as many bytes as are available synchronously.
- int num_bytes = 0;
- while (num_bytes >= 0) {
- num_bytes = request->Read(read_buffer_.get(), kReadBufferSizeInBytes);
- if (num_bytes == ERR_IO_PENDING)
- return;
- if (!ConsumeBytesRead(request, num_bytes))
- return;
- }
- OnUrlRequestCompleted(num_bytes);
- }
- bool Job::ConsumeBytesRead(URLRequest* request, int num_bytes) {
- DCHECK_NE(ERR_IO_PENDING, num_bytes);
- if (num_bytes <= 0) {
- // Error while reading, or EOF.
- OnUrlRequestCompleted(num_bytes);
- return false;
- }
- // Enforce maximum size bound.
- if (num_bytes + response_body_.size() > request_params_->max_response_bytes) {
- FailRequest(ERR_FILE_TOO_BIG);
- return false;
- }
- // Append the data to |response_body_|.
- response_body_.reserve(num_bytes);
- response_body_.insert(response_body_.end(), read_buffer_->data(),
- read_buffer_->data() + num_bytes);
- return true;
- }
- void Job::OnUrlRequestCompleted(int net_error) {
- DCHECK_NE(ERR_IO_PENDING, net_error);
- Error result = static_cast<Error>(net_error);
- OnJobCompleted(result);
- }
- void Job::OnJobCompleted(Error error) {
- DCHECK_NE(ERR_IO_PENDING, error);
- // Stop the timer and clear the URLRequest.
- Stop();
- std::unique_ptr<Job> delete_this = parent_->RemoveJob(this);
- CompleteAndClearRequests(error);
- }
- void Job::CompleteAndClearRequests(Error error) {
- for (const auto& request : requests_) {
- request->OnJobCompleted(this, error, response_body_);
- }
- requests_.clear();
- }
- void Job::FailRequest(Error error) {
- DCHECK_NE(ERR_IO_PENDING, error);
- int result = url_request_->CancelWithError(error);
- OnUrlRequestCompleted(result);
- }
- } // namespace
- CertNetFetcherURLRequest::AsyncCertNetFetcherURLRequest::
- AsyncCertNetFetcherURLRequest(URLRequestContext* context)
- : context_(context) {
- // Allow creation to happen from another thread.
- DETACH_FROM_THREAD(thread_checker_);
- }
- CertNetFetcherURLRequest::AsyncCertNetFetcherURLRequest::
- ~AsyncCertNetFetcherURLRequest() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- jobs_.clear();
- }
- bool JobComparator::operator()(const Job* job1, const Job* job2) const {
- return job1->request_params() < job2->request_params();
- }
- void CertNetFetcherURLRequest::AsyncCertNetFetcherURLRequest::Fetch(
- std::unique_ptr<RequestParams> request_params,
- scoped_refptr<RequestCore> request) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_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;
- }
- auto new_job = std::make_unique<Job>(std::move(request_params), this);
- job = new_job.get();
- jobs_[job] = std::move(new_job);
- // Attach the request before calling StartURLRequest; this ensures that the
- // request will get signalled if StartURLRequest completes the job
- // synchronously.
- job->AttachRequest(std::move(request));
- job->StartURLRequest(context_);
- }
- void CertNetFetcherURLRequest::AsyncCertNetFetcherURLRequest::Shutdown() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- for (const auto& job : jobs_) {
- job.first->Cancel();
- }
- jobs_.clear();
- }
- namespace {
- struct JobToRequestParamsComparator {
- bool operator()(const JobSet::value_type& job,
- const CertNetFetcherURLRequest::RequestParams& value) const {
- return job.first->request_params() < value;
- }
- };
- } // namespace
- Job* CertNetFetcherURLRequest::AsyncCertNetFetcherURLRequest::FindJob(
- const RequestParams& params) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_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>
- CertNetFetcherURLRequest::AsyncCertNetFetcherURLRequest::RemoveJob(Job* job) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_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 CertNetFetcher::Request {
- public:
- explicit CertNetFetcherRequestImpl(
- scoped_refptr<CertNetFetcherURLRequest::RequestCore> core)
- : core_(std::move(core)) {
- DCHECK(core_);
- }
- void WaitForResult(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<CertNetFetcherURLRequest::RequestCore> core_;
- };
- } // namespace
- CertNetFetcherURLRequest::CertNetFetcherURLRequest()
- : task_runner_(base::ThreadTaskRunnerHandle::Get()) {}
- CertNetFetcherURLRequest::~CertNetFetcherURLRequest() {
- // The fetcher must be shutdown (at which point |context_| will be set to
- // null) before destruction.
- DCHECK(!context_);
- }
- void CertNetFetcherURLRequest::SetURLRequestContext(
- URLRequestContext* context) {
- DCHECK(task_runner_->RunsTasksInCurrentSequence());
- context_ = context;
- }
- // static
- base::TimeDelta CertNetFetcherURLRequest::GetDefaultTimeoutForTesting() {
- return GetTimeout(CertNetFetcher::DEFAULT);
- }
- void CertNetFetcherURLRequest::Shutdown() {
- DCHECK(task_runner_->RunsTasksInCurrentSequence());
- if (impl_) {
- impl_->Shutdown();
- impl_.reset();
- }
- context_ = nullptr;
- }
- std::unique_ptr<CertNetFetcher::Request>
- CertNetFetcherURLRequest::FetchCaIssuers(const GURL& url,
- int timeout_milliseconds,
- int max_response_bytes) {
- auto request_params = std::make_unique<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<CertNetFetcher::Request> CertNetFetcherURLRequest::FetchCrl(
- const GURL& url,
- int timeout_milliseconds,
- int max_response_bytes) {
- auto request_params = std::make_unique<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<CertNetFetcher::Request> CertNetFetcherURLRequest::FetchOcsp(
- const GURL& url,
- int timeout_milliseconds,
- int max_response_bytes) {
- auto request_params = std::make_unique<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 CertNetFetcherURLRequest::DoFetchOnNetworkSequence(
- std::unique_ptr<RequestParams> request_params,
- scoped_refptr<RequestCore> request) {
- DCHECK(task_runner_->RunsTasksInCurrentSequence());
- if (!context_) {
- // 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;
- }
- if (!impl_) {
- impl_ = std::make_unique<AsyncCertNetFetcherURLRequest>(context_);
- }
- impl_->Fetch(std::move(request_params), request);
- }
- std::unique_ptr<CertNetFetcherURLRequest::Request>
- CertNetFetcherURLRequest::DoFetch(
- std::unique_ptr<RequestParams> request_params) {
- auto request_core = base::MakeRefCounted<RequestCore>(task_runner_);
- // If the fetcher has already been shutdown, DoFetchOnNetworkSequence will
- // signal the request with an error. However, if the fetcher shuts down
- // before DoFetchOnNetworkSequence 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(&CertNetFetcherURLRequest::DoFetchOnNetworkSequence,
- this, std::move(request_params), request_core))) {
- request_core->SignalImmediateError();
- }
- return std::make_unique<CertNetFetcherRequestImpl>(std::move(request_core));
- }
- } // namespace net
|