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