• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // Overview
6 //
7 // The main entry point is CertNetFetcherURLRequest. This is an implementation
8 // of CertNetFetcher that provides a service for fetching network requests.
9 //
10 // The interface for CertNetFetcher is synchronous, however allows
11 // overlapping requests. When starting a request CertNetFetcherURLRequest
12 // returns a CertNetFetcher::Request (CertNetFetcherRequestImpl) that the
13 // caller can use to cancel the fetch, or wait for it to complete
14 // (blocking).
15 //
16 // The CertNetFetcherURLRequest is shared between a network thread and a
17 // caller thread that waits for fetches to happen on the network thread.
18 //
19 // The classes are mainly organized based on their thread affinity:
20 //
21 // ---------------
22 // Straddles caller thread and network thread
23 // ---------------
24 //
25 // CertNetFetcherURLRequest (implements CertNetFetcher)
26 //   * Main entry point. Must be created and shutdown from the network thread.
27 //   * Provides a service to start/cancel/wait for URL fetches, to be
28 //     used on the caller thread.
29 //   * Returns callers a CertNetFetcher::Request as a handle
30 //   * Requests can run in parallel, however will block the current thread when
31 //     reading results.
32 //   * Posts tasks to network thread to coordinate actual work
33 //
34 // RequestCore
35 //   * Reference-counted bridge between CertNetFetcherRequestImpl and the
36 //     dependencies on the network thread
37 //   * Holds the result of the request, a WaitableEvent for signaling
38 //     completion, and pointers for canceling work on network thread.
39 //
40 // ---------------
41 // Lives on caller thread
42 // ---------------
43 //
44 // CertNetFetcherRequestImpl (implements CertNetFetcher::Request)
45 //   * Wrapper for cancelling events, or waiting for a request to complete
46 //   * Waits on a WaitableEvent to complete requests.
47 //
48 // ---------------
49 // Lives on network thread
50 // ---------------
51 //
52 // AsyncCertNetFetcherURLRequest
53 //   * Asynchronous manager for outstanding requests. Handles de-duplication,
54 //     timeouts, and actual integration with network stack. This is where the
55 //     majority of the logic lives.
56 //   * Signals completion of requests through RequestCore's WaitableEvent.
57 //   * Attaches requests to Jobs for the purpose of de-duplication
58 
59 #include "net/cert_net/cert_net_fetcher_url_request.h"
60 
61 #include <memory>
62 #include <tuple>
63 #include <utility>
64 
65 #include "base/check_op.h"
66 #include "base/containers/extend.h"
67 #include "base/functional/bind.h"
68 #include "base/functional/callback_helpers.h"
69 #include "base/memory/ptr_util.h"
70 #include "base/memory/raw_ptr.h"
71 #include "base/not_fatal_until.h"
72 #include "base/numerics/safe_math.h"
73 #include "base/ranges/algorithm.h"
74 #include "base/synchronization/waitable_event.h"
75 #include "base/task/single_thread_task_runner.h"
76 #include "base/time/time.h"
77 #include "base/timer/timer.h"
78 #include "net/base/io_buffer.h"
79 #include "net/base/isolation_info.h"
80 #include "net/base/load_flags.h"
81 #include "net/cert/cert_net_fetcher.h"
82 #include "net/cookies/site_for_cookies.h"
83 #include "net/dns/public/secure_dns_policy.h"
84 #include "net/traffic_annotation/network_traffic_annotation.h"
85 #include "net/url_request/redirect_info.h"
86 #include "net/url_request/url_request_context.h"
87 #include "url/origin.h"
88 
89 // TODO(eroman): Add support for POST parameters.
90 // TODO(eroman): Add controls for bypassing the cache.
91 // TODO(eroman): Add a maximum number of in-flight jobs/requests.
92 // TODO(eroman): Add NetLog integration.
93 
94 namespace net {
95 
96 namespace {
97 
98 // The size of the buffer used for reading the response body of the URLRequest.
99 const int kReadBufferSizeInBytes = 4096;
100 
101 // The maximum size in bytes for the response body when fetching a CRL.
102 const int kMaxResponseSizeInBytesForCrl = 5 * 1024 * 1024;
103 
104 // The maximum size in bytes for the response body when fetching an AIA URL
105 // (caIssuers/OCSP).
106 const int kMaxResponseSizeInBytesForAia = 64 * 1024;
107 
108 // The default timeout in seconds for fetch requests.
109 const int kTimeoutSeconds = 15;
110 
111 class Job;
112 
113 struct JobToRequestParamsComparator;
114 
115 struct JobComparator {
116   bool operator()(const Job* job1, const Job* job2) const;
117 };
118 
119 // Would be a set<unique_ptr> but extraction of owned objects from a set of
120 // owned types doesn't come until C++17.
121 using JobSet = std::map<Job*, std::unique_ptr<Job>, JobComparator>;
122 
123 }  // namespace
124 
125 // AsyncCertNetFetcherURLRequest manages URLRequests in an async fashion on the
126 // URLRequestContexts's task runner thread.
127 //
128 //  * Schedules
129 //  * De-duplicates requests
130 //  * Handles timeouts
131 class CertNetFetcherURLRequest::AsyncCertNetFetcherURLRequest {
132  public:
133   // Initializes AsyncCertNetFetcherURLRequest using the specified
134   // URLRequestContext for issuing requests. |context| must remain valid until
135   // Shutdown() is called or the AsyncCertNetFetcherURLRequest is destroyed.
136   explicit AsyncCertNetFetcherURLRequest(URLRequestContext* context);
137 
138   AsyncCertNetFetcherURLRequest(const AsyncCertNetFetcherURLRequest&) = delete;
139   AsyncCertNetFetcherURLRequest& operator=(
140       const AsyncCertNetFetcherURLRequest&) = delete;
141 
142   // The AsyncCertNetFetcherURLRequest is expected to be kept alive until all
143   // requests have completed or Shutdown() is called.
144   ~AsyncCertNetFetcherURLRequest();
145 
146   // Starts an asynchronous request to fetch the given URL. On completion
147   // request->OnJobCompleted() will be invoked.
148   void Fetch(std::unique_ptr<RequestParams> request_params,
149              scoped_refptr<RequestCore> request);
150 
151   // Removes |job| from the in progress jobs and transfers ownership to the
152   // caller.
153   std::unique_ptr<Job> RemoveJob(Job* job);
154 
155   // Cancels outstanding jobs, which stops network requests and signals the
156   // corresponding RequestCores that the requests have completed.
157   void Shutdown();
158 
159  private:
160   // Finds a job with a matching RequestPararms or returns nullptr if there was
161   // no match.
162   Job* FindJob(const RequestParams& params);
163 
164   // The in-progress jobs. This set does not contain the job which is actively
165   // invoking callbacks (OnJobCompleted).
166   JobSet jobs_;
167 
168   // Not owned. |context_| must outlive the AsyncCertNetFetcherURLRequest.
169   raw_ptr<URLRequestContext> context_ = nullptr;
170 
171   THREAD_CHECKER(thread_checker_);
172 };
173 
174 namespace {
175 
176 // Policy for which URLs are allowed to be fetched. This is called both for the
177 // initial URL and for each redirect. Returns OK on success or a net error
178 // code on failure.
CanFetchUrl(const GURL & url)179 Error CanFetchUrl(const GURL& url) {
180   if (!url.SchemeIs("http"))
181     return ERR_DISALLOWED_URL_SCHEME;
182   return OK;
183 }
184 
GetTimeout(int timeout_milliseconds)185 base::TimeDelta GetTimeout(int timeout_milliseconds) {
186   if (timeout_milliseconds == CertNetFetcher::DEFAULT)
187     return base::Seconds(kTimeoutSeconds);
188   return base::Milliseconds(timeout_milliseconds);
189 }
190 
GetMaxResponseBytes(int max_response_bytes,size_t default_max_response_bytes)191 size_t GetMaxResponseBytes(int max_response_bytes,
192                            size_t default_max_response_bytes) {
193   if (max_response_bytes == CertNetFetcher::DEFAULT)
194     return default_max_response_bytes;
195 
196   // Ensure that the specified limit is not negative, and cannot result in an
197   // overflow while reading.
198   base::CheckedNumeric<size_t> check(max_response_bytes);
199   check += kReadBufferSizeInBytes;
200   DCHECK(check.IsValid());
201 
202   return max_response_bytes;
203 }
204 
205 enum HttpMethod {
206   HTTP_METHOD_GET,
207   HTTP_METHOD_POST,
208 };
209 
210 }  // namespace
211 
212 // RequestCore tracks an outstanding call to Fetch(). It is
213 // reference-counted for ease of sharing between threads.
214 class CertNetFetcherURLRequest::RequestCore
215     : public base::RefCountedThreadSafe<RequestCore> {
216  public:
RequestCore(scoped_refptr<base::SingleThreadTaskRunner> task_runner)217   explicit RequestCore(scoped_refptr<base::SingleThreadTaskRunner> task_runner)
218       : completion_event_(base::WaitableEvent::ResetPolicy::MANUAL,
219                           base::WaitableEvent::InitialState::NOT_SIGNALED),
220         task_runner_(std::move(task_runner)) {}
221 
222   RequestCore(const RequestCore&) = delete;
223   RequestCore& operator=(const RequestCore&) = delete;
224 
AttachedToJob(Job * job)225   void AttachedToJob(Job* job) {
226     DCHECK(task_runner_->RunsTasksInCurrentSequence());
227     DCHECK(!job_);
228     // Requests should not be attached to jobs after they have been signalled
229     // with a cancellation error (which happens via either Cancel() or
230     // SignalImmediateError()).
231     DCHECK_NE(error_, ERR_ABORTED);
232     job_ = job;
233   }
234 
OnJobCompleted(Job * job,Error error,const std::vector<uint8_t> & response_body)235   void OnJobCompleted(Job* job,
236                       Error error,
237                       const std::vector<uint8_t>& response_body) {
238     DCHECK(task_runner_->RunsTasksInCurrentSequence());
239 
240     DCHECK_EQ(job_, job);
241     job_ = nullptr;
242 
243     error_ = error;
244     bytes_ = response_body;
245     completion_event_.Signal();
246   }
247 
248   // Detaches this request from its job (if it is attached to any) and
249   // signals completion with ERR_ABORTED. Can be called from any thread.
250   void CancelJob();
251 
252   // Can be used to signal that an error was encountered before the request was
253   // attached to a job. Can be called from any thread.
254   void SignalImmediateError();
255 
256   // Should only be called once.
WaitForResult(Error * error,std::vector<uint8_t> * bytes)257   void WaitForResult(Error* error, std::vector<uint8_t>* bytes) {
258     DCHECK(!task_runner_->RunsTasksInCurrentSequence());
259 
260     completion_event_.Wait();
261     *bytes = std::move(bytes_);
262     *error = error_;
263 
264     error_ = ERR_UNEXPECTED;
265   }
266 
267  private:
268   friend class base::RefCountedThreadSafe<RequestCore>;
269 
~RequestCore()270   ~RequestCore() {
271     // Requests should have been cancelled prior to destruction.
272     DCHECK(!job_);
273   }
274 
275   // A non-owned pointer to the job that is executing the request.
276   raw_ptr<Job> job_ = nullptr;
277 
278   // May be written to from network thread, or from the caller thread only when
279   // there is no work that will be done on the network thread (e.g. when the
280   // network thread has been shutdown before the request begins). See comment in
281   // SignalImmediateError.
282   Error error_ = OK;
283   std::vector<uint8_t> bytes_;
284 
285   // Indicates when |error_| and |bytes_| have been written to.
286   base::WaitableEvent completion_event_;
287 
288   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
289 };
290 
291 struct CertNetFetcherURLRequest::RequestParams {
292   RequestParams();
293 
294   RequestParams(const RequestParams&) = delete;
295   RequestParams& operator=(const RequestParams&) = delete;
296 
297   bool operator<(const RequestParams& other) const;
298 
299   GURL url;
300   HttpMethod http_method = HTTP_METHOD_GET;
301   size_t max_response_bytes = 0;
302 
303   // If set to a value <= 0 then means "no timeout".
304   base::TimeDelta timeout;
305 
306   // IMPORTANT: When adding fields to this structure, update operator<().
307 };
308 
309 CertNetFetcherURLRequest::RequestParams::RequestParams() = default;
310 
operator <(const RequestParams & other) const311 bool CertNetFetcherURLRequest::RequestParams::operator<(
312     const RequestParams& other) const {
313   return std::tie(url, http_method, max_response_bytes, timeout) <
314          std::tie(other.url, other.http_method, other.max_response_bytes,
315                   other.timeout);
316 }
317 
318 namespace {
319 
320 // Job tracks an outstanding URLRequest as well as all of the pending requests
321 // for it.
322 class Job : public URLRequest::Delegate {
323  public:
324   Job(std::unique_ptr<CertNetFetcherURLRequest::RequestParams> request_params,
325       CertNetFetcherURLRequest::AsyncCertNetFetcherURLRequest* parent);
326 
327   Job(const Job&) = delete;
328   Job& operator=(const Job&) = delete;
329 
330   ~Job() override;
331 
request_params() const332   const CertNetFetcherURLRequest::RequestParams& request_params() const {
333     return *request_params_;
334   }
335 
336   // Creates a request and attaches it to the job. When the job completes it
337   // will notify the request of completion through OnJobCompleted.
338   void AttachRequest(
339       scoped_refptr<CertNetFetcherURLRequest::RequestCore> request);
340 
341   // Removes |request| from the job.
342   void DetachRequest(CertNetFetcherURLRequest::RequestCore* request);
343 
344   // Creates and starts a URLRequest for the job. After the URLRequest has
345   // completed, OnJobCompleted() will be invoked and all the registered requests
346   // notified of completion.
347   void StartURLRequest(URLRequestContext* context);
348 
349   // Cancels the request with an ERR_ABORTED error and invokes
350   // RequestCore::OnJobCompleted() to notify the registered requests of the
351   // cancellation. The job is *not* removed from the
352   // AsyncCertNetFetcherURLRequest.
353   void Cancel();
354 
355  private:
356   // Implementation of URLRequest::Delegate
357   void OnReceivedRedirect(URLRequest* request,
358                           const RedirectInfo& redirect_info,
359                           bool* defer_redirect) override;
360   void OnResponseStarted(URLRequest* request, int net_error) override;
361   void OnReadCompleted(URLRequest* request, int bytes_read) override;
362 
363   // Clears the URLRequest and timer. Helper for doing work common to
364   // cancellation and job completion.
365   void Stop();
366 
367   // Reads as much data as available from |request|.
368   void ReadBody(URLRequest* request);
369 
370   // Helper to copy the partial bytes read from the read IOBuffer to an
371   // aggregated buffer.
372   bool ConsumeBytesRead(URLRequest* request, int num_bytes);
373 
374   // Called when the URLRequest has completed (either success or failure).
375   void OnUrlRequestCompleted(int net_error);
376 
377   // Called when the Job has completed. The job may finish in response to a
378   // timeout, an invalid URL, or the URLRequest completing. By the time this
379   // method is called, the |response_body_| variable have been assigned.
380   void OnJobCompleted(Error error);
381 
382   // Calls r->OnJobCompleted() for each RequestCore |r| currently attached
383   // to this job, and then clears |requests_|.
384   void CompleteAndClearRequests(Error error);
385 
386   // Cancels a request with a specified error code and calls
387   // OnUrlRequestCompleted().
388   void FailRequest(Error error);
389 
390   // The requests attached to this job.
391   std::vector<scoped_refptr<CertNetFetcherURLRequest::RequestCore>> requests_;
392 
393   // The input parameters for starting a URLRequest.
394   std::unique_ptr<CertNetFetcherURLRequest::RequestParams> request_params_;
395 
396   // The URLRequest response information.
397   std::vector<uint8_t> response_body_;
398 
399   std::unique_ptr<URLRequest> url_request_;
400   scoped_refptr<IOBuffer> read_buffer_;
401 
402   // Used to timeout the job when the URLRequest takes too long. This timer is
403   // also used for notifying a failure to start the URLRequest.
404   base::OneShotTimer timer_;
405 
406   // Non-owned pointer to the AsyncCertNetFetcherURLRequest that created this
407   // job.
408   raw_ptr<CertNetFetcherURLRequest::AsyncCertNetFetcherURLRequest> parent_;
409 };
410 
411 }  // namespace
412 
CancelJob()413 void CertNetFetcherURLRequest::RequestCore::CancelJob() {
414   if (!task_runner_->RunsTasksInCurrentSequence()) {
415     task_runner_->PostTask(FROM_HERE,
416                            base::BindOnce(&RequestCore::CancelJob, this));
417     return;
418   }
419 
420   if (job_) {
421     auto* job = job_.get();
422     job_ = nullptr;
423     job->DetachRequest(this);
424   }
425 
426   SignalImmediateError();
427 }
428 
SignalImmediateError()429 void CertNetFetcherURLRequest::RequestCore::SignalImmediateError() {
430   // These data members are normally only written on the network thread, but it
431   // is safe to write here from either thread. This is because
432   // SignalImmediateError is only to be called before this request is attached
433   // to a job. In particular, if called from the caller thread, no work will be
434   // done on the network thread for this request, so these variables will only
435   // be written and read on the caller thread. If called from the network
436   // thread, they will only be written to on the network thread and will not be
437   // read on the caller thread until |completion_event_| is signalled (after
438   // which it will be not be written on the network thread again).
439   DCHECK(!job_);
440   error_ = ERR_ABORTED;
441   bytes_.clear();
442   completion_event_.Signal();
443 }
444 
445 namespace {
446 
Job(std::unique_ptr<CertNetFetcherURLRequest::RequestParams> request_params,CertNetFetcherURLRequest::AsyncCertNetFetcherURLRequest * parent)447 Job::Job(
448     std::unique_ptr<CertNetFetcherURLRequest::RequestParams> request_params,
449     CertNetFetcherURLRequest::AsyncCertNetFetcherURLRequest* parent)
450     : request_params_(std::move(request_params)), parent_(parent) {}
451 
~Job()452 Job::~Job() {
453   DCHECK(requests_.empty());
454   Stop();
455 }
456 
AttachRequest(scoped_refptr<CertNetFetcherURLRequest::RequestCore> request)457 void Job::AttachRequest(
458     scoped_refptr<CertNetFetcherURLRequest::RequestCore> request) {
459   request->AttachedToJob(this);
460   requests_.push_back(std::move(request));
461 }
462 
DetachRequest(CertNetFetcherURLRequest::RequestCore * request)463 void Job::DetachRequest(CertNetFetcherURLRequest::RequestCore* request) {
464   std::unique_ptr<Job> delete_this;
465 
466   auto it = base::ranges::find(requests_, request);
467   CHECK(it != requests_.end(), base::NotFatalUntil::M130);
468   requests_.erase(it);
469 
470   // If there are no longer any requests attached to the job then
471   // cancel and delete it.
472   if (requests_.empty())
473     delete_this = parent_->RemoveJob(this);
474 }
475 
StartURLRequest(URLRequestContext * context)476 void Job::StartURLRequest(URLRequestContext* context) {
477   Error error = CanFetchUrl(request_params_->url);
478   if (error != OK) {
479     OnJobCompleted(error);
480     return;
481   }
482 
483   // Start the URLRequest.
484   read_buffer_ = base::MakeRefCounted<IOBufferWithSize>(kReadBufferSizeInBytes);
485   NetworkTrafficAnnotationTag traffic_annotation =
486       DefineNetworkTrafficAnnotation("certificate_verifier_url_request",
487                                      R"(
488         semantics {
489           sender: "Certificate Verifier"
490           description:
491             "When verifying certificates, the browser may need to fetch "
492             "additional URLs that are encoded in the server-provided "
493             "certificate chain. This may be part of revocation checking ("
494             "Online Certificate Status Protocol, Certificate Revocation List), "
495             "or path building (Authority Information Access fetches). Please "
496             "refer to the following for more on above protocols: "
497             "https://tools.ietf.org/html/rfc6960, "
498             "https://tools.ietf.org/html/rfc5280#section-4.2.1.13, and"
499             "https://tools.ietf.org/html/rfc5280#section-5.2.7."
500             "NOTE: this path is being deprecated. Please see the"
501             "certificate_verifier_url_loader annotation for the new path."
502           trigger:
503             "Verifying a certificate (likely in response to navigating to an "
504             "'https://' website)."
505           data:
506             "In the case of OCSP this may divulge the website being viewed. No "
507             "user data in other cases."
508           destination: OTHER
509           destination_other:
510             "The URL specified in the certificate."
511         }
512         policy {
513           cookies_allowed: NO
514           setting: "This feature cannot be disabled by settings."
515           policy_exception_justification: "Not implemented."
516         })");
517   url_request_ = context->CreateRequest(request_params_->url, DEFAULT_PRIORITY,
518                                         this, traffic_annotation);
519   if (request_params_->http_method == HTTP_METHOD_POST)
520     url_request_->set_method("POST");
521   url_request_->set_allow_credentials(false);
522 
523   // Disable secure DNS for hostname lookups triggered by certificate network
524   // fetches to prevent deadlock.
525   url_request_->SetSecureDnsPolicy(SecureDnsPolicy::kDisable);
526 
527   // Create IsolationInfo based on the origin of the requested URL.
528   // TODO(crbug.com/40104280): Cert validation needs to either be
529   // double-keyed or based on a static database, to protect it from being used
530   // as a cross-site user tracking vector. For now, just treat it as if it were
531   // a subresource request of the origin used for the request. This allows the
532   // result to still be cached in the HTTP cache, and lets URLRequest DCHECK
533   // that all requests have non-empty IsolationInfos.
534   url::Origin origin = url::Origin::Create(request_params_->url);
535   url_request_->set_isolation_info(IsolationInfo::Create(
536       IsolationInfo::RequestType::kOther, origin /* top_frame_origin */,
537       origin /* frame_origin */, SiteForCookies()));
538 
539   // Ensure that we bypass HSTS for all requests sent through
540   // CertNetFetcherURLRequest, since AIA/CRL/OCSP requests must be in HTTP to
541   // avoid circular dependencies.
542   url_request_->SetLoadFlags(url_request_->load_flags() |
543                              net::LOAD_SHOULD_BYPASS_HSTS);
544 
545   url_request_->Start();
546 
547   // Start a timer to limit how long the job runs for.
548   if (request_params_->timeout.is_positive()) {
549     timer_.Start(FROM_HERE, request_params_->timeout,
550                  base::BindOnce(&Job::FailRequest, base::Unretained(this),
551                                 ERR_TIMED_OUT));
552   }
553 }
554 
Cancel()555 void Job::Cancel() {
556   // Stop the timer and clear the URLRequest.
557   Stop();
558   // Signal attached requests that they've been completed.
559   CompleteAndClearRequests(static_cast<Error>(ERR_ABORTED));
560 }
561 
562 void Job::OnReceivedRedirect(URLRequest* request,
563                              const RedirectInfo& redirect_info,
564                              bool* defer_redirect) {
565   DCHECK_EQ(url_request_.get(), request);
566 
567   // Ensure that the new URL matches the policy.
568   Error error = CanFetchUrl(redirect_info.new_url);
569   if (error != OK) {
570     FailRequest(error);
571     return;
572   }
573 }
574 
575 void Job::OnResponseStarted(URLRequest* request, int net_error) {
576   DCHECK_EQ(url_request_.get(), request);
577   DCHECK_NE(ERR_IO_PENDING, net_error);
578 
579   if (net_error != OK) {
580     OnUrlRequestCompleted(net_error);
581     return;
582   }
583 
584   if (request->GetResponseCode() != 200) {
585     FailRequest(ERR_HTTP_RESPONSE_CODE_FAILURE);
586     return;
587   }
588 
589   ReadBody(request);
590 }
591 
592 void Job::OnReadCompleted(URLRequest* request, int bytes_read) {
593   DCHECK_EQ(url_request_.get(), request);
594   DCHECK_NE(ERR_IO_PENDING, bytes_read);
595 
596   // Keep reading the response body.
597   if (ConsumeBytesRead(request, bytes_read))
598     ReadBody(request);
599 }
600 
601 void Job::Stop() {
602   timer_.Stop();
603   url_request_.reset();
604 }
605 
606 void Job::ReadBody(URLRequest* request) {
607   // Read as many bytes as are available synchronously.
608   int num_bytes = 0;
609   while (num_bytes >= 0) {
610     num_bytes = request->Read(read_buffer_.get(), kReadBufferSizeInBytes);
611     if (num_bytes == ERR_IO_PENDING)
612       return;
613     if (!ConsumeBytesRead(request, num_bytes))
614       return;
615   }
616 
617   OnUrlRequestCompleted(num_bytes);
618 }
619 
620 bool Job::ConsumeBytesRead(URLRequest* request, int num_bytes) {
621   DCHECK_NE(ERR_IO_PENDING, num_bytes);
622   if (num_bytes <= 0) {
623     // Error while reading, or EOF.
624     OnUrlRequestCompleted(num_bytes);
625     return false;
626   }
627 
628   // Enforce maximum size bound.
629   const auto num_bytes_s = static_cast<size_t>(num_bytes);
630   if (num_bytes_s + response_body_.size() >
631       request_params_->max_response_bytes) {
632     FailRequest(ERR_FILE_TOO_BIG);
633     return false;
634   }
635 
636   // Append the data to |response_body_|.
637   response_body_.reserve(response_body_.size() + num_bytes_s);
638   base::Extend(response_body_, read_buffer_->span().first(num_bytes_s));
639   return true;
640 }
641 
642 void Job::OnUrlRequestCompleted(int net_error) {
643   DCHECK_NE(ERR_IO_PENDING, net_error);
644   Error result = static_cast<Error>(net_error);
645   OnJobCompleted(result);
646 }
647 
648 void Job::OnJobCompleted(Error error) {
649   DCHECK_NE(ERR_IO_PENDING, error);
650   // Stop the timer and clear the URLRequest.
651   Stop();
652 
653   std::unique_ptr<Job> delete_this = parent_->RemoveJob(this);
654   CompleteAndClearRequests(error);
655 }
656 
657 void Job::CompleteAndClearRequests(Error error) {
658   for (const auto& request : requests_) {
659     request->OnJobCompleted(this, error, response_body_);
660   }
661 
662   requests_.clear();
663 }
664 
665 void Job::FailRequest(Error error) {
666   DCHECK_NE(ERR_IO_PENDING, error);
667   int result = url_request_->CancelWithError(error);
668   OnUrlRequestCompleted(result);
669 }
670 
671 }  // namespace
672 
673 CertNetFetcherURLRequest::AsyncCertNetFetcherURLRequest::
674     AsyncCertNetFetcherURLRequest(URLRequestContext* context)
675     : context_(context) {
676   // Allow creation to happen from another thread.
677   DETACH_FROM_THREAD(thread_checker_);
678 }
679 
680 CertNetFetcherURLRequest::AsyncCertNetFetcherURLRequest::
681     ~AsyncCertNetFetcherURLRequest() {
682   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
683   jobs_.clear();
684 }
685 
686 bool JobComparator::operator()(const Job* job1, const Job* job2) const {
687   return job1->request_params() < job2->request_params();
688 }
689 
690 void CertNetFetcherURLRequest::AsyncCertNetFetcherURLRequest::Fetch(
691     std::unique_ptr<RequestParams> request_params,
692     scoped_refptr<RequestCore> request) {
693   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
694 
695   // If there is an in-progress job that matches the request parameters use it.
696   // Otherwise start a new job.
697   Job* job = FindJob(*request_params);
698   if (job) {
699     job->AttachRequest(std::move(request));
700     return;
701   }
702 
703   auto new_job = std::make_unique<Job>(std::move(request_params), this);
704   job = new_job.get();
705   jobs_[job] = std::move(new_job);
706   // Attach the request before calling StartURLRequest; this ensures that the
707   // request will get signalled if StartURLRequest completes the job
708   // synchronously.
709   job->AttachRequest(std::move(request));
710   job->StartURLRequest(context_);
711 }
712 
713 void CertNetFetcherURLRequest::AsyncCertNetFetcherURLRequest::Shutdown() {
714   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
715   for (const auto& job : jobs_) {
716     job.first->Cancel();
717   }
718   jobs_.clear();
719 }
720 
721 namespace {
722 
723 struct JobToRequestParamsComparator {
724   bool operator()(const JobSet::value_type& job,
725                   const CertNetFetcherURLRequest::RequestParams& value) const {
726     return job.first->request_params() < value;
727   }
728 };
729 
730 }  // namespace
731 
732 Job* CertNetFetcherURLRequest::AsyncCertNetFetcherURLRequest::FindJob(
733     const RequestParams& params) {
734   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
735 
736   // The JobSet is kept in sorted order so items can be found using binary
737   // search.
738   auto it = std::lower_bound(jobs_.begin(), jobs_.end(), params,
739                              JobToRequestParamsComparator());
740   if (it != jobs_.end() && !(params < (*it).first->request_params()))
741     return (*it).first;
742   return nullptr;
743 }
744 
745 std::unique_ptr<Job>
746 CertNetFetcherURLRequest::AsyncCertNetFetcherURLRequest::RemoveJob(Job* job) {
747   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
748   auto it = jobs_.find(job);
749   CHECK(it != jobs_.end());
750   std::unique_ptr<Job> owned_job = std::move(it->second);
751   jobs_.erase(it);
752   return owned_job;
753 }
754 
755 namespace {
756 
757 class CertNetFetcherRequestImpl : public CertNetFetcher::Request {
758  public:
759   explicit CertNetFetcherRequestImpl(
760       scoped_refptr<CertNetFetcherURLRequest::RequestCore> core)
761       : core_(std::move(core)) {
762     DCHECK(core_);
763   }
764 
765   void WaitForResult(Error* error, std::vector<uint8_t>* bytes) override {
766     // Should only be called a single time.
767     DCHECK(core_);
768     core_->WaitForResult(error, bytes);
769     core_ = nullptr;
770   }
771 
772   ~CertNetFetcherRequestImpl() override {
773     if (core_)
774       core_->CancelJob();
775   }
776 
777  private:
778   scoped_refptr<CertNetFetcherURLRequest::RequestCore> core_;
779 };
780 
781 }  // namespace
782 
783 CertNetFetcherURLRequest::CertNetFetcherURLRequest()
784     : task_runner_(base::SingleThreadTaskRunner::GetCurrentDefault()) {}
785 
786 CertNetFetcherURLRequest::~CertNetFetcherURLRequest() {
787   // The fetcher must be shutdown (at which point |context_| will be set to
788   // null) before destruction.
789   DCHECK(!context_);
790 }
791 
792 void CertNetFetcherURLRequest::SetURLRequestContext(
793     URLRequestContext* context) {
794   DCHECK(task_runner_->RunsTasksInCurrentSequence());
795   context_ = context;
796 }
797 
798 // static
799 base::TimeDelta CertNetFetcherURLRequest::GetDefaultTimeoutForTesting() {
800   return GetTimeout(CertNetFetcher::DEFAULT);
801 }
802 
803 void CertNetFetcherURLRequest::Shutdown() {
804   DCHECK(task_runner_->RunsTasksInCurrentSequence());
805   if (impl_) {
806     impl_->Shutdown();
807     impl_.reset();
808   }
809   context_ = nullptr;
810 }
811 
812 std::unique_ptr<CertNetFetcher::Request>
813 CertNetFetcherURLRequest::FetchCaIssuers(const GURL& url,
814                                          int timeout_milliseconds,
815                                          int max_response_bytes) {
816   auto request_params = std::make_unique<RequestParams>();
817 
818   request_params->url = url;
819   request_params->http_method = HTTP_METHOD_GET;
820   request_params->timeout = GetTimeout(timeout_milliseconds);
821   request_params->max_response_bytes =
822       GetMaxResponseBytes(max_response_bytes, kMaxResponseSizeInBytesForAia);
823 
824   return DoFetch(std::move(request_params));
825 }
826 
827 std::unique_ptr<CertNetFetcher::Request> CertNetFetcherURLRequest::FetchCrl(
828     const GURL& url,
829     int timeout_milliseconds,
830     int max_response_bytes) {
831   auto request_params = std::make_unique<RequestParams>();
832 
833   request_params->url = url;
834   request_params->http_method = HTTP_METHOD_GET;
835   request_params->timeout = GetTimeout(timeout_milliseconds);
836   request_params->max_response_bytes =
837       GetMaxResponseBytes(max_response_bytes, kMaxResponseSizeInBytesForCrl);
838 
839   return DoFetch(std::move(request_params));
840 }
841 
842 std::unique_ptr<CertNetFetcher::Request> CertNetFetcherURLRequest::FetchOcsp(
843     const GURL& url,
844     int timeout_milliseconds,
845     int max_response_bytes) {
846   auto request_params = std::make_unique<RequestParams>();
847 
848   request_params->url = url;
849   request_params->http_method = HTTP_METHOD_GET;
850   request_params->timeout = GetTimeout(timeout_milliseconds);
851   request_params->max_response_bytes =
852       GetMaxResponseBytes(max_response_bytes, kMaxResponseSizeInBytesForAia);
853 
854   return DoFetch(std::move(request_params));
855 }
856 
857 void CertNetFetcherURLRequest::DoFetchOnNetworkSequence(
858     std::unique_ptr<RequestParams> request_params,
859     scoped_refptr<RequestCore> request) {
860   DCHECK(task_runner_->RunsTasksInCurrentSequence());
861 
862   if (!context_) {
863     // The fetcher might have been shutdown between when this task was posted
864     // and when it is running. In this case, signal the request and do not
865     // start a network request.
866     request->SignalImmediateError();
867     return;
868   }
869 
870   if (!impl_) {
871     impl_ = std::make_unique<AsyncCertNetFetcherURLRequest>(context_);
872   }
873 
874   impl_->Fetch(std::move(request_params), request);
875 }
876 
877 std::unique_ptr<CertNetFetcherURLRequest::Request>
878 CertNetFetcherURLRequest::DoFetch(
879     std::unique_ptr<RequestParams> request_params) {
880   auto request_core = base::MakeRefCounted<RequestCore>(task_runner_);
881 
882   // If the fetcher has already been shutdown, DoFetchOnNetworkSequence will
883   // signal the request with an error. However, if the fetcher shuts down
884   // before DoFetchOnNetworkSequence runs and PostTask still returns true,
885   // then the request will hang (that is, WaitForResult will not return).
886   if (!task_runner_->PostTask(
887           FROM_HERE,
888           base::BindOnce(&CertNetFetcherURLRequest::DoFetchOnNetworkSequence,
889                          this, std::move(request_params), request_core))) {
890     request_core->SignalImmediateError();
891   }
892 
893   return std::make_unique<CertNetFetcherRequestImpl>(std::move(request_core));
894 }
895 
896 }  // namespace net
897