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