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