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