xref: /aosp_15_r20/external/cronet/net/cert_net/cert_net_fetcher_url_request.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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<IOBufferWithSize>(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   // Ensure that we bypass HSTS for all requests sent through
538   // CertNetFetcherURLRequest, since AIA/CRL/OCSP requests must be in HTTP to
539   // avoid circular dependencies.
540   url_request_->SetLoadFlags(url_request_->load_flags() |
541                              net::LOAD_SHOULD_BYPASS_HSTS);
542 
543   url_request_->Start();
544 
545   // Start a timer to limit how long the job runs for.
546   if (request_params_->timeout.is_positive()) {
547     timer_.Start(FROM_HERE, request_params_->timeout,
548                  base::BindOnce(&Job::FailRequest, base::Unretained(this),
549                                 ERR_TIMED_OUT));
550   }
551 }
552 
Cancel()553 void Job::Cancel() {
554   // Stop the timer and clear the URLRequest.
555   Stop();
556   // Signal attached requests that they've been completed.
557   CompleteAndClearRequests(static_cast<Error>(ERR_ABORTED));
558 }
559 
560 void Job::OnReceivedRedirect(URLRequest* request,
561                              const RedirectInfo& redirect_info,
562                              bool* defer_redirect) {
563   DCHECK_EQ(url_request_.get(), request);
564 
565   // Ensure that the new URL matches the policy.
566   Error error = CanFetchUrl(redirect_info.new_url);
567   if (error != OK) {
568     FailRequest(error);
569     return;
570   }
571 }
572 
573 void Job::OnResponseStarted(URLRequest* request, int net_error) {
574   DCHECK_EQ(url_request_.get(), request);
575   DCHECK_NE(ERR_IO_PENDING, net_error);
576 
577   if (net_error != OK) {
578     OnUrlRequestCompleted(net_error);
579     return;
580   }
581 
582   if (request->GetResponseCode() != 200) {
583     FailRequest(ERR_HTTP_RESPONSE_CODE_FAILURE);
584     return;
585   }
586 
587   ReadBody(request);
588 }
589 
590 void Job::OnReadCompleted(URLRequest* request, int bytes_read) {
591   DCHECK_EQ(url_request_.get(), request);
592   DCHECK_NE(ERR_IO_PENDING, bytes_read);
593 
594   // Keep reading the response body.
595   if (ConsumeBytesRead(request, bytes_read))
596     ReadBody(request);
597 }
598 
599 void Job::Stop() {
600   timer_.Stop();
601   url_request_.reset();
602 }
603 
604 void Job::ReadBody(URLRequest* request) {
605   // Read as many bytes as are available synchronously.
606   int num_bytes = 0;
607   while (num_bytes >= 0) {
608     num_bytes = request->Read(read_buffer_.get(), kReadBufferSizeInBytes);
609     if (num_bytes == ERR_IO_PENDING)
610       return;
611     if (!ConsumeBytesRead(request, num_bytes))
612       return;
613   }
614 
615   OnUrlRequestCompleted(num_bytes);
616 }
617 
618 bool Job::ConsumeBytesRead(URLRequest* request, int num_bytes) {
619   DCHECK_NE(ERR_IO_PENDING, num_bytes);
620   if (num_bytes <= 0) {
621     // Error while reading, or EOF.
622     OnUrlRequestCompleted(num_bytes);
623     return false;
624   }
625 
626   // Enforce maximum size bound.
627   if (num_bytes + response_body_.size() > request_params_->max_response_bytes) {
628     FailRequest(ERR_FILE_TOO_BIG);
629     return false;
630   }
631 
632   // Append the data to |response_body_|.
633   response_body_.reserve(num_bytes);
634   response_body_.insert(response_body_.end(), read_buffer_->data(),
635                         read_buffer_->data() + num_bytes);
636   return true;
637 }
638 
639 void Job::OnUrlRequestCompleted(int net_error) {
640   DCHECK_NE(ERR_IO_PENDING, net_error);
641   Error result = static_cast<Error>(net_error);
642   OnJobCompleted(result);
643 }
644 
645 void Job::OnJobCompleted(Error error) {
646   DCHECK_NE(ERR_IO_PENDING, error);
647   // Stop the timer and clear the URLRequest.
648   Stop();
649 
650   std::unique_ptr<Job> delete_this = parent_->RemoveJob(this);
651   CompleteAndClearRequests(error);
652 }
653 
654 void Job::CompleteAndClearRequests(Error error) {
655   for (const auto& request : requests_) {
656     request->OnJobCompleted(this, error, response_body_);
657   }
658 
659   requests_.clear();
660 }
661 
662 void Job::FailRequest(Error error) {
663   DCHECK_NE(ERR_IO_PENDING, error);
664   int result = url_request_->CancelWithError(error);
665   OnUrlRequestCompleted(result);
666 }
667 
668 }  // namespace
669 
670 CertNetFetcherURLRequest::AsyncCertNetFetcherURLRequest::
671     AsyncCertNetFetcherURLRequest(URLRequestContext* context)
672     : context_(context) {
673   // Allow creation to happen from another thread.
674   DETACH_FROM_THREAD(thread_checker_);
675 }
676 
677 CertNetFetcherURLRequest::AsyncCertNetFetcherURLRequest::
678     ~AsyncCertNetFetcherURLRequest() {
679   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
680   jobs_.clear();
681 }
682 
683 bool JobComparator::operator()(const Job* job1, const Job* job2) const {
684   return job1->request_params() < job2->request_params();
685 }
686 
687 void CertNetFetcherURLRequest::AsyncCertNetFetcherURLRequest::Fetch(
688     std::unique_ptr<RequestParams> request_params,
689     scoped_refptr<RequestCore> request) {
690   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
691 
692   // If there is an in-progress job that matches the request parameters use it.
693   // Otherwise start a new job.
694   Job* job = FindJob(*request_params);
695   if (job) {
696     job->AttachRequest(std::move(request));
697     return;
698   }
699 
700   auto new_job = std::make_unique<Job>(std::move(request_params), this);
701   job = new_job.get();
702   jobs_[job] = std::move(new_job);
703   // Attach the request before calling StartURLRequest; this ensures that the
704   // request will get signalled if StartURLRequest completes the job
705   // synchronously.
706   job->AttachRequest(std::move(request));
707   job->StartURLRequest(context_);
708 }
709 
710 void CertNetFetcherURLRequest::AsyncCertNetFetcherURLRequest::Shutdown() {
711   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
712   for (const auto& job : jobs_) {
713     job.first->Cancel();
714   }
715   jobs_.clear();
716 }
717 
718 namespace {
719 
720 struct JobToRequestParamsComparator {
721   bool operator()(const JobSet::value_type& job,
722                   const CertNetFetcherURLRequest::RequestParams& value) const {
723     return job.first->request_params() < value;
724   }
725 };
726 
727 }  // namespace
728 
729 Job* CertNetFetcherURLRequest::AsyncCertNetFetcherURLRequest::FindJob(
730     const RequestParams& params) {
731   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
732 
733   // The JobSet is kept in sorted order so items can be found using binary
734   // search.
735   auto it = std::lower_bound(jobs_.begin(), jobs_.end(), params,
736                              JobToRequestParamsComparator());
737   if (it != jobs_.end() && !(params < (*it).first->request_params()))
738     return (*it).first;
739   return nullptr;
740 }
741 
742 std::unique_ptr<Job>
743 CertNetFetcherURLRequest::AsyncCertNetFetcherURLRequest::RemoveJob(Job* job) {
744   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
745   auto it = jobs_.find(job);
746   CHECK(it != jobs_.end());
747   std::unique_ptr<Job> owned_job = std::move(it->second);
748   jobs_.erase(it);
749   return owned_job;
750 }
751 
752 namespace {
753 
754 class CertNetFetcherRequestImpl : public CertNetFetcher::Request {
755  public:
756   explicit CertNetFetcherRequestImpl(
757       scoped_refptr<CertNetFetcherURLRequest::RequestCore> core)
758       : core_(std::move(core)) {
759     DCHECK(core_);
760   }
761 
762   void WaitForResult(Error* error, std::vector<uint8_t>* bytes) override {
763     // Should only be called a single time.
764     DCHECK(core_);
765     core_->WaitForResult(error, bytes);
766     core_ = nullptr;
767   }
768 
769   ~CertNetFetcherRequestImpl() override {
770     if (core_)
771       core_->CancelJob();
772   }
773 
774  private:
775   scoped_refptr<CertNetFetcherURLRequest::RequestCore> core_;
776 };
777 
778 }  // namespace
779 
780 CertNetFetcherURLRequest::CertNetFetcherURLRequest()
781     : task_runner_(base::SingleThreadTaskRunner::GetCurrentDefault()) {}
782 
783 CertNetFetcherURLRequest::~CertNetFetcherURLRequest() {
784   // The fetcher must be shutdown (at which point |context_| will be set to
785   // null) before destruction.
786   DCHECK(!context_);
787 }
788 
789 void CertNetFetcherURLRequest::SetURLRequestContext(
790     URLRequestContext* context) {
791   DCHECK(task_runner_->RunsTasksInCurrentSequence());
792   context_ = context;
793 }
794 
795 // static
796 base::TimeDelta CertNetFetcherURLRequest::GetDefaultTimeoutForTesting() {
797   return GetTimeout(CertNetFetcher::DEFAULT);
798 }
799 
800 void CertNetFetcherURLRequest::Shutdown() {
801   DCHECK(task_runner_->RunsTasksInCurrentSequence());
802   if (impl_) {
803     impl_->Shutdown();
804     impl_.reset();
805   }
806   context_ = nullptr;
807 }
808 
809 std::unique_ptr<CertNetFetcher::Request>
810 CertNetFetcherURLRequest::FetchCaIssuers(const GURL& url,
811                                          int timeout_milliseconds,
812                                          int max_response_bytes) {
813   auto request_params = std::make_unique<RequestParams>();
814 
815   request_params->url = url;
816   request_params->http_method = HTTP_METHOD_GET;
817   request_params->timeout = GetTimeout(timeout_milliseconds);
818   request_params->max_response_bytes =
819       GetMaxResponseBytes(max_response_bytes, kMaxResponseSizeInBytesForAia);
820 
821   return DoFetch(std::move(request_params));
822 }
823 
824 std::unique_ptr<CertNetFetcher::Request> CertNetFetcherURLRequest::FetchCrl(
825     const GURL& url,
826     int timeout_milliseconds,
827     int max_response_bytes) {
828   auto request_params = std::make_unique<RequestParams>();
829 
830   request_params->url = url;
831   request_params->http_method = HTTP_METHOD_GET;
832   request_params->timeout = GetTimeout(timeout_milliseconds);
833   request_params->max_response_bytes =
834       GetMaxResponseBytes(max_response_bytes, kMaxResponseSizeInBytesForCrl);
835 
836   return DoFetch(std::move(request_params));
837 }
838 
839 std::unique_ptr<CertNetFetcher::Request> CertNetFetcherURLRequest::FetchOcsp(
840     const GURL& url,
841     int timeout_milliseconds,
842     int max_response_bytes) {
843   auto request_params = std::make_unique<RequestParams>();
844 
845   request_params->url = url;
846   request_params->http_method = HTTP_METHOD_GET;
847   request_params->timeout = GetTimeout(timeout_milliseconds);
848   request_params->max_response_bytes =
849       GetMaxResponseBytes(max_response_bytes, kMaxResponseSizeInBytesForAia);
850 
851   return DoFetch(std::move(request_params));
852 }
853 
854 void CertNetFetcherURLRequest::DoFetchOnNetworkSequence(
855     std::unique_ptr<RequestParams> request_params,
856     scoped_refptr<RequestCore> request) {
857   DCHECK(task_runner_->RunsTasksInCurrentSequence());
858 
859   if (!context_) {
860     // The fetcher might have been shutdown between when this task was posted
861     // and when it is running. In this case, signal the request and do not
862     // start a network request.
863     request->SignalImmediateError();
864     return;
865   }
866 
867   if (!impl_) {
868     impl_ = std::make_unique<AsyncCertNetFetcherURLRequest>(context_);
869   }
870 
871   impl_->Fetch(std::move(request_params), request);
872 }
873 
874 std::unique_ptr<CertNetFetcherURLRequest::Request>
875 CertNetFetcherURLRequest::DoFetch(
876     std::unique_ptr<RequestParams> request_params) {
877   auto request_core = base::MakeRefCounted<RequestCore>(task_runner_);
878 
879   // If the fetcher has already been shutdown, DoFetchOnNetworkSequence will
880   // signal the request with an error. However, if the fetcher shuts down
881   // before DoFetchOnNetworkSequence runs and PostTask still returns true,
882   // then the request will hang (that is, WaitForResult will not return).
883   if (!task_runner_->PostTask(
884           FROM_HERE,
885           base::BindOnce(&CertNetFetcherURLRequest::DoFetchOnNetworkSequence,
886                          this, std::move(request_params), request_core))) {
887     request_core->SignalImmediateError();
888   }
889 
890   return std::make_unique<CertNetFetcherRequestImpl>(std::move(request_core));
891 }
892 
893 }  // namespace net
894