xref: /aosp_15_r20/external/cronet/net/cert/coalescing_cert_verifier.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1*6777b538SAndroid Build Coastguard Worker // Copyright 2019 The Chromium Authors
2*6777b538SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*6777b538SAndroid Build Coastguard Worker // found in the LICENSE file.
4*6777b538SAndroid Build Coastguard Worker 
5*6777b538SAndroid Build Coastguard Worker #include "net/cert/coalescing_cert_verifier.h"
6*6777b538SAndroid Build Coastguard Worker 
7*6777b538SAndroid Build Coastguard Worker #include "base/containers/linked_list.h"
8*6777b538SAndroid Build Coastguard Worker #include "base/containers/unique_ptr_adapters.h"
9*6777b538SAndroid Build Coastguard Worker #include "base/functional/bind.h"
10*6777b538SAndroid Build Coastguard Worker #include "base/memory/raw_ptr.h"
11*6777b538SAndroid Build Coastguard Worker #include "base/memory/weak_ptr.h"
12*6777b538SAndroid Build Coastguard Worker #include "base/metrics/histogram_macros.h"
13*6777b538SAndroid Build Coastguard Worker #include "base/ranges/algorithm.h"
14*6777b538SAndroid Build Coastguard Worker #include "base/strings/string_number_conversions.h"
15*6777b538SAndroid Build Coastguard Worker #include "base/time/time.h"
16*6777b538SAndroid Build Coastguard Worker #include "net/base/net_errors.h"
17*6777b538SAndroid Build Coastguard Worker #include "net/cert/cert_verify_result.h"
18*6777b538SAndroid Build Coastguard Worker #include "net/cert/crl_set.h"
19*6777b538SAndroid Build Coastguard Worker #include "net/cert/x509_certificate_net_log_param.h"
20*6777b538SAndroid Build Coastguard Worker #include "net/log/net_log_event_type.h"
21*6777b538SAndroid Build Coastguard Worker #include "net/log/net_log_source.h"
22*6777b538SAndroid Build Coastguard Worker #include "net/log/net_log_source_type.h"
23*6777b538SAndroid Build Coastguard Worker #include "net/log/net_log_values.h"
24*6777b538SAndroid Build Coastguard Worker #include "net/log/net_log_with_source.h"
25*6777b538SAndroid Build Coastguard Worker #include "third_party/boringssl/src/pki/pem.h"
26*6777b538SAndroid Build Coastguard Worker 
27*6777b538SAndroid Build Coastguard Worker namespace net {
28*6777b538SAndroid Build Coastguard Worker 
29*6777b538SAndroid Build Coastguard Worker // DESIGN OVERVIEW:
30*6777b538SAndroid Build Coastguard Worker //
31*6777b538SAndroid Build Coastguard Worker // The CoalescingCertVerifier implements an algorithm to group multiple calls
32*6777b538SAndroid Build Coastguard Worker // to Verify() into a single Job. This avoids overloading the underlying
33*6777b538SAndroid Build Coastguard Worker // CertVerifier, particularly those that are expensive to talk to (e.g.
34*6777b538SAndroid Build Coastguard Worker // talking to the system verifier or across processes), batching multiple
35*6777b538SAndroid Build Coastguard Worker // requests to CoaleacingCertVerifier::Verify() into a single underlying call.
36*6777b538SAndroid Build Coastguard Worker //
37*6777b538SAndroid Build Coastguard Worker // However, this makes lifetime management a bit more complex.
38*6777b538SAndroid Build Coastguard Worker //   - The Job object represents all of the state for a single verification to
39*6777b538SAndroid Build Coastguard Worker //     the CoalescingCertVerifier's underlying CertVerifier.
40*6777b538SAndroid Build Coastguard Worker //       * It keeps the CertVerifyResult alive, which is required as long as
41*6777b538SAndroid Build Coastguard Worker //         there is a pending verification.
42*6777b538SAndroid Build Coastguard Worker //       * It keeps the CertVerify::Request to the underlying verifier alive,
43*6777b538SAndroid Build Coastguard Worker //         as long as there is a pending Request attached to the Job.
44*6777b538SAndroid Build Coastguard Worker //       * It keeps track of every CoalescingCertVerifier::Request that is
45*6777b538SAndroid Build Coastguard Worker //         interested in receiving notification. However, it does NOT own
46*6777b538SAndroid Build Coastguard Worker //         these objects, and thus needs to coordinate with the Request (via
47*6777b538SAndroid Build Coastguard Worker //         AddRequest/AbortRequest) to make sure it never has a stale
48*6777b538SAndroid Build Coastguard Worker //         pointer.
49*6777b538SAndroid Build Coastguard Worker //         NB: It would have also been possible for the Job to only
50*6777b538SAndroid Build Coastguard Worker //         hold WeakPtr<Request>s, rather than Request*, but that seemed less
51*6777b538SAndroid Build Coastguard Worker //         clear as to the lifetime invariants, even if it was more clear
52*6777b538SAndroid Build Coastguard Worker //         about how the pointers are used.
53*6777b538SAndroid Build Coastguard Worker //  - The Job object is always owned by the CoalescingCertVerifier. If the
54*6777b538SAndroid Build Coastguard Worker //    CoalescingCertVerifier is deleted, all in-flight requests to the
55*6777b538SAndroid Build Coastguard Worker //    underlying verifier should be cancelled. When the Job goes away, all the
56*6777b538SAndroid Build Coastguard Worker //    Requests will be orphaned.
57*6777b538SAndroid Build Coastguard Worker //  - The Request object is always owned by the CALLER. It is a handle to
58*6777b538SAndroid Build Coastguard Worker //    allow a caller to cancel a request, per the CertVerifier interface. If
59*6777b538SAndroid Build Coastguard Worker //    the Request goes away, no caller callbacks should be invoked if the Job
60*6777b538SAndroid Build Coastguard Worker //    it was (previously) attached to completes.
61*6777b538SAndroid Build Coastguard Worker //  - Per the CertVerifier interface, when the CoalescingCertVerifier is
62*6777b538SAndroid Build Coastguard Worker //    deleted, then regardless of there being any live Requests, none of those
63*6777b538SAndroid Build Coastguard Worker //    caller callbacks should be invoked.
64*6777b538SAndroid Build Coastguard Worker //
65*6777b538SAndroid Build Coastguard Worker // Finally, to add to the complexity, it's possible that, during the handling
66*6777b538SAndroid Build Coastguard Worker // of a result from the underlying CertVerifier, a Job may begin dispatching
67*6777b538SAndroid Build Coastguard Worker // to its Requests. The Request may delete the CoalescingCertVerifier. If that
68*6777b538SAndroid Build Coastguard Worker // happens, then the Job being processed is also deleted, and none of the
69*6777b538SAndroid Build Coastguard Worker // other Requests should be notified.
70*6777b538SAndroid Build Coastguard Worker 
71*6777b538SAndroid Build Coastguard Worker namespace {
72*6777b538SAndroid Build Coastguard Worker 
CertVerifierParams(const CertVerifier::RequestParams & params)73*6777b538SAndroid Build Coastguard Worker base::Value::Dict CertVerifierParams(
74*6777b538SAndroid Build Coastguard Worker     const CertVerifier::RequestParams& params) {
75*6777b538SAndroid Build Coastguard Worker   base::Value::Dict dict;
76*6777b538SAndroid Build Coastguard Worker   dict.Set("certificates",
77*6777b538SAndroid Build Coastguard Worker            NetLogX509CertificateList(params.certificate().get()));
78*6777b538SAndroid Build Coastguard Worker   if (!params.ocsp_response().empty()) {
79*6777b538SAndroid Build Coastguard Worker     dict.Set("ocsp_response",
80*6777b538SAndroid Build Coastguard Worker              bssl::PEMEncode(params.ocsp_response(), "NETLOG OCSP RESPONSE"));
81*6777b538SAndroid Build Coastguard Worker   }
82*6777b538SAndroid Build Coastguard Worker   if (!params.sct_list().empty()) {
83*6777b538SAndroid Build Coastguard Worker     dict.Set("sct_list", bssl::PEMEncode(params.sct_list(), "NETLOG SCT LIST"));
84*6777b538SAndroid Build Coastguard Worker   }
85*6777b538SAndroid Build Coastguard Worker   dict.Set("host", NetLogStringValue(params.hostname()));
86*6777b538SAndroid Build Coastguard Worker   dict.Set("verifier_flags", params.flags());
87*6777b538SAndroid Build Coastguard Worker 
88*6777b538SAndroid Build Coastguard Worker   return dict;
89*6777b538SAndroid Build Coastguard Worker }
90*6777b538SAndroid Build Coastguard Worker 
91*6777b538SAndroid Build Coastguard Worker }  // namespace
92*6777b538SAndroid Build Coastguard Worker 
93*6777b538SAndroid Build Coastguard Worker // Job contains all the state for a single verification using the underlying
94*6777b538SAndroid Build Coastguard Worker // verifier.
95*6777b538SAndroid Build Coastguard Worker class CoalescingCertVerifier::Job {
96*6777b538SAndroid Build Coastguard Worker  public:
97*6777b538SAndroid Build Coastguard Worker   Job(CoalescingCertVerifier* parent,
98*6777b538SAndroid Build Coastguard Worker       const CertVerifier::RequestParams& params,
99*6777b538SAndroid Build Coastguard Worker       NetLog* net_log,
100*6777b538SAndroid Build Coastguard Worker       bool is_first_job);
101*6777b538SAndroid Build Coastguard Worker   ~Job();
102*6777b538SAndroid Build Coastguard Worker 
params() const103*6777b538SAndroid Build Coastguard Worker   const CertVerifier::RequestParams& params() const { return params_; }
verify_result() const104*6777b538SAndroid Build Coastguard Worker   const CertVerifyResult& verify_result() const { return verify_result_; }
105*6777b538SAndroid Build Coastguard Worker 
106*6777b538SAndroid Build Coastguard Worker   // Attaches |request|, causing it to be notified once this Job completes.
107*6777b538SAndroid Build Coastguard Worker   void AddRequest(CoalescingCertVerifier::Request* request);
108*6777b538SAndroid Build Coastguard Worker 
109*6777b538SAndroid Build Coastguard Worker   // Stops |request| from being notified. If there are no Requests remaining,
110*6777b538SAndroid Build Coastguard Worker   // the Job will be cancelled.
111*6777b538SAndroid Build Coastguard Worker   // NOTE: It's only necessary to call this if the Job has not yet completed.
112*6777b538SAndroid Build Coastguard Worker   // If the Request has been notified of completion, this should not be called.
113*6777b538SAndroid Build Coastguard Worker   void AbortRequest(CoalescingCertVerifier::Request* request);
114*6777b538SAndroid Build Coastguard Worker 
115*6777b538SAndroid Build Coastguard Worker   // Starts a verification using |underlying_verifier|. If this completes
116*6777b538SAndroid Build Coastguard Worker   // synchronously, returns the result code, with the associated result being
117*6777b538SAndroid Build Coastguard Worker   // available via |verify_result()|. Otherwise, it will complete
118*6777b538SAndroid Build Coastguard Worker   // asynchronously, notifying any Requests associated via |AttachRequest|.
119*6777b538SAndroid Build Coastguard Worker   int Start(CertVerifier* underlying_verifier);
120*6777b538SAndroid Build Coastguard Worker 
121*6777b538SAndroid Build Coastguard Worker  private:
122*6777b538SAndroid Build Coastguard Worker   void OnVerifyComplete(int result);
123*6777b538SAndroid Build Coastguard Worker 
124*6777b538SAndroid Build Coastguard Worker   void LogMetrics();
125*6777b538SAndroid Build Coastguard Worker 
126*6777b538SAndroid Build Coastguard Worker   raw_ptr<CoalescingCertVerifier> parent_verifier_;
127*6777b538SAndroid Build Coastguard Worker   const CertVerifier::RequestParams params_;
128*6777b538SAndroid Build Coastguard Worker   const NetLogWithSource net_log_;
129*6777b538SAndroid Build Coastguard Worker   bool is_first_job_ = false;
130*6777b538SAndroid Build Coastguard Worker   CertVerifyResult verify_result_;
131*6777b538SAndroid Build Coastguard Worker 
132*6777b538SAndroid Build Coastguard Worker   base::TimeTicks start_time_;
133*6777b538SAndroid Build Coastguard Worker   std::unique_ptr<CertVerifier::Request> pending_request_;
134*6777b538SAndroid Build Coastguard Worker 
135*6777b538SAndroid Build Coastguard Worker   base::LinkedList<CoalescingCertVerifier::Request> attached_requests_;
136*6777b538SAndroid Build Coastguard Worker   base::WeakPtrFactory<Job> weak_ptr_factory_{this};
137*6777b538SAndroid Build Coastguard Worker };
138*6777b538SAndroid Build Coastguard Worker 
139*6777b538SAndroid Build Coastguard Worker // Tracks the state associated with a single CoalescingCertVerifier::Verify
140*6777b538SAndroid Build Coastguard Worker // request.
141*6777b538SAndroid Build Coastguard Worker //
142*6777b538SAndroid Build Coastguard Worker // There are two ways for requests to be cancelled:
143*6777b538SAndroid Build Coastguard Worker //   - The caller of Verify() can delete the Request object, indicating
144*6777b538SAndroid Build Coastguard Worker //     they are no longer interested in this particular request.
145*6777b538SAndroid Build Coastguard Worker //   - The caller can delete the CoalescingCertVerifier, which should cause
146*6777b538SAndroid Build Coastguard Worker //     all in-process Jobs to be aborted and deleted. Any Requests attached to
147*6777b538SAndroid Build Coastguard Worker //     Jobs should be orphaned, and do nothing when the Request is (eventually)
148*6777b538SAndroid Build Coastguard Worker //     deleted.
149*6777b538SAndroid Build Coastguard Worker class CoalescingCertVerifier::Request
150*6777b538SAndroid Build Coastguard Worker     : public base::LinkNode<CoalescingCertVerifier::Request>,
151*6777b538SAndroid Build Coastguard Worker       public CertVerifier::Request {
152*6777b538SAndroid Build Coastguard Worker  public:
153*6777b538SAndroid Build Coastguard Worker   // Create a request that will be attached to |job|, and will notify
154*6777b538SAndroid Build Coastguard Worker   // |callback| and fill |verify_result| if the Job completes successfully.
155*6777b538SAndroid Build Coastguard Worker   // If the Request is deleted, or the Job is deleted, |callback| will not
156*6777b538SAndroid Build Coastguard Worker   // be notified.
157*6777b538SAndroid Build Coastguard Worker   Request(CoalescingCertVerifier::Job* job,
158*6777b538SAndroid Build Coastguard Worker           CertVerifyResult* verify_result,
159*6777b538SAndroid Build Coastguard Worker           CompletionOnceCallback callback,
160*6777b538SAndroid Build Coastguard Worker           const NetLogWithSource& net_log);
161*6777b538SAndroid Build Coastguard Worker 
162*6777b538SAndroid Build Coastguard Worker   ~Request() override;
163*6777b538SAndroid Build Coastguard Worker 
net_log() const164*6777b538SAndroid Build Coastguard Worker   const NetLogWithSource& net_log() const { return net_log_; }
165*6777b538SAndroid Build Coastguard Worker 
166*6777b538SAndroid Build Coastguard Worker   // Called by Job to complete the requests (either successfully or as a sign
167*6777b538SAndroid Build Coastguard Worker   // that the underlying Job is going away).
168*6777b538SAndroid Build Coastguard Worker   void Complete(int result);
169*6777b538SAndroid Build Coastguard Worker 
170*6777b538SAndroid Build Coastguard Worker   // Called when |job_| is being deleted, to ensure that the Request does not
171*6777b538SAndroid Build Coastguard Worker   // attempt to access the Job further. No callbacks will be invoked,
172*6777b538SAndroid Build Coastguard Worker   // consistent with the CoalescingCertVerifier's contract.
173*6777b538SAndroid Build Coastguard Worker   void OnJobAbort();
174*6777b538SAndroid Build Coastguard Worker 
175*6777b538SAndroid Build Coastguard Worker  private:
176*6777b538SAndroid Build Coastguard Worker   raw_ptr<CoalescingCertVerifier::Job> job_;
177*6777b538SAndroid Build Coastguard Worker 
178*6777b538SAndroid Build Coastguard Worker   raw_ptr<CertVerifyResult> verify_result_;
179*6777b538SAndroid Build Coastguard Worker   CompletionOnceCallback callback_;
180*6777b538SAndroid Build Coastguard Worker   const NetLogWithSource net_log_;
181*6777b538SAndroid Build Coastguard Worker };
182*6777b538SAndroid Build Coastguard Worker 
Job(CoalescingCertVerifier * parent,const CertVerifier::RequestParams & params,NetLog * net_log,bool is_first_job)183*6777b538SAndroid Build Coastguard Worker CoalescingCertVerifier::Job::Job(CoalescingCertVerifier* parent,
184*6777b538SAndroid Build Coastguard Worker                                  const CertVerifier::RequestParams& params,
185*6777b538SAndroid Build Coastguard Worker                                  NetLog* net_log,
186*6777b538SAndroid Build Coastguard Worker                                  bool is_first_job)
187*6777b538SAndroid Build Coastguard Worker     : parent_verifier_(parent),
188*6777b538SAndroid Build Coastguard Worker       params_(params),
189*6777b538SAndroid Build Coastguard Worker       net_log_(
190*6777b538SAndroid Build Coastguard Worker           NetLogWithSource::Make(net_log, NetLogSourceType::CERT_VERIFIER_JOB)),
191*6777b538SAndroid Build Coastguard Worker       is_first_job_(is_first_job) {}
192*6777b538SAndroid Build Coastguard Worker 
~Job()193*6777b538SAndroid Build Coastguard Worker CoalescingCertVerifier::Job::~Job() {
194*6777b538SAndroid Build Coastguard Worker   // If there was at least one outstanding Request still pending, then this
195*6777b538SAndroid Build Coastguard Worker   // Job was aborted, rather than being completed normally and cleaned up.
196*6777b538SAndroid Build Coastguard Worker   if (!attached_requests_.empty() && pending_request_) {
197*6777b538SAndroid Build Coastguard Worker     net_log_.AddEvent(NetLogEventType::CANCELLED);
198*6777b538SAndroid Build Coastguard Worker     net_log_.EndEvent(NetLogEventType::CERT_VERIFIER_JOB);
199*6777b538SAndroid Build Coastguard Worker   }
200*6777b538SAndroid Build Coastguard Worker 
201*6777b538SAndroid Build Coastguard Worker   while (!attached_requests_.empty()) {
202*6777b538SAndroid Build Coastguard Worker     auto* link_node = attached_requests_.head();
203*6777b538SAndroid Build Coastguard Worker     link_node->RemoveFromList();
204*6777b538SAndroid Build Coastguard Worker     link_node->value()->OnJobAbort();
205*6777b538SAndroid Build Coastguard Worker   }
206*6777b538SAndroid Build Coastguard Worker }
207*6777b538SAndroid Build Coastguard Worker 
AddRequest(CoalescingCertVerifier::Request * request)208*6777b538SAndroid Build Coastguard Worker void CoalescingCertVerifier::Job::AddRequest(
209*6777b538SAndroid Build Coastguard Worker     CoalescingCertVerifier::Request* request) {
210*6777b538SAndroid Build Coastguard Worker   // There must be a pending asynchronous verification in process.
211*6777b538SAndroid Build Coastguard Worker   DCHECK(pending_request_);
212*6777b538SAndroid Build Coastguard Worker 
213*6777b538SAndroid Build Coastguard Worker   request->net_log().AddEventReferencingSource(
214*6777b538SAndroid Build Coastguard Worker       NetLogEventType::CERT_VERIFIER_REQUEST_BOUND_TO_JOB, net_log_.source());
215*6777b538SAndroid Build Coastguard Worker   attached_requests_.Append(request);
216*6777b538SAndroid Build Coastguard Worker }
217*6777b538SAndroid Build Coastguard Worker 
AbortRequest(CoalescingCertVerifier::Request * request)218*6777b538SAndroid Build Coastguard Worker void CoalescingCertVerifier::Job::AbortRequest(
219*6777b538SAndroid Build Coastguard Worker     CoalescingCertVerifier::Request* request) {
220*6777b538SAndroid Build Coastguard Worker   // Check to make sure |request| hasn't already been removed.
221*6777b538SAndroid Build Coastguard Worker   DCHECK(request->previous() || request->next());
222*6777b538SAndroid Build Coastguard Worker 
223*6777b538SAndroid Build Coastguard Worker   request->RemoveFromList();
224*6777b538SAndroid Build Coastguard Worker 
225*6777b538SAndroid Build Coastguard Worker   // If there are no more pending requests, abort. This isn't strictly
226*6777b538SAndroid Build Coastguard Worker   // necessary; the request could be allowed to run to completion (and
227*6777b538SAndroid Build Coastguard Worker   // potentially to allow later Requests to join in), but in keeping with the
228*6777b538SAndroid Build Coastguard Worker   // idea of providing more stable guarantees about resources, clean up early.
229*6777b538SAndroid Build Coastguard Worker   if (attached_requests_.empty()) {
230*6777b538SAndroid Build Coastguard Worker     // If this was the last Request, then the Job had not yet completed; this
231*6777b538SAndroid Build Coastguard Worker     // matches the logic in the dtor, which handles when it's the Job that is
232*6777b538SAndroid Build Coastguard Worker     // deleted first, rather than the last Request.
233*6777b538SAndroid Build Coastguard Worker     net_log_.AddEvent(NetLogEventType::CANCELLED);
234*6777b538SAndroid Build Coastguard Worker     net_log_.EndEvent(NetLogEventType::CERT_VERIFIER_JOB);
235*6777b538SAndroid Build Coastguard Worker 
236*6777b538SAndroid Build Coastguard Worker     // DANGER: This will cause |this_| to be deleted!
237*6777b538SAndroid Build Coastguard Worker     parent_verifier_->RemoveJob(this);
238*6777b538SAndroid Build Coastguard Worker     return;
239*6777b538SAndroid Build Coastguard Worker   }
240*6777b538SAndroid Build Coastguard Worker }
241*6777b538SAndroid Build Coastguard Worker 
Start(CertVerifier * underlying_verifier)242*6777b538SAndroid Build Coastguard Worker int CoalescingCertVerifier::Job::Start(CertVerifier* underlying_verifier) {
243*6777b538SAndroid Build Coastguard Worker   // Requests are only attached for asynchronous completion, so they must
244*6777b538SAndroid Build Coastguard Worker   // always be attached after Start() has been called.
245*6777b538SAndroid Build Coastguard Worker   DCHECK(attached_requests_.empty());
246*6777b538SAndroid Build Coastguard Worker   // There should not be a pending request already started (e.g. Start called
247*6777b538SAndroid Build Coastguard Worker   // multiple times).
248*6777b538SAndroid Build Coastguard Worker   DCHECK(!pending_request_);
249*6777b538SAndroid Build Coastguard Worker 
250*6777b538SAndroid Build Coastguard Worker   net_log_.BeginEvent(NetLogEventType::CERT_VERIFIER_JOB,
251*6777b538SAndroid Build Coastguard Worker                       [&] { return CertVerifierParams(params_); });
252*6777b538SAndroid Build Coastguard Worker 
253*6777b538SAndroid Build Coastguard Worker   verify_result_.Reset();
254*6777b538SAndroid Build Coastguard Worker 
255*6777b538SAndroid Build Coastguard Worker   start_time_ = base::TimeTicks::Now();
256*6777b538SAndroid Build Coastguard Worker   int result = underlying_verifier->Verify(
257*6777b538SAndroid Build Coastguard Worker       params_, &verify_result_,
258*6777b538SAndroid Build Coastguard Worker       // Safe, because |verify_request_| is self-owned and guarantees the
259*6777b538SAndroid Build Coastguard Worker       // callback won't be called if |this| is deleted.
260*6777b538SAndroid Build Coastguard Worker       base::BindOnce(&CoalescingCertVerifier::Job::OnVerifyComplete,
261*6777b538SAndroid Build Coastguard Worker                      base::Unretained(this)),
262*6777b538SAndroid Build Coastguard Worker       &pending_request_, net_log_);
263*6777b538SAndroid Build Coastguard Worker   if (result != ERR_IO_PENDING) {
264*6777b538SAndroid Build Coastguard Worker     LogMetrics();
265*6777b538SAndroid Build Coastguard Worker     net_log_.EndEvent(NetLogEventType::CERT_VERIFIER_JOB,
266*6777b538SAndroid Build Coastguard Worker                       [&] { return verify_result_.NetLogParams(result); });
267*6777b538SAndroid Build Coastguard Worker   }
268*6777b538SAndroid Build Coastguard Worker 
269*6777b538SAndroid Build Coastguard Worker   return result;
270*6777b538SAndroid Build Coastguard Worker }
271*6777b538SAndroid Build Coastguard Worker 
OnVerifyComplete(int result)272*6777b538SAndroid Build Coastguard Worker void CoalescingCertVerifier::Job::OnVerifyComplete(int result) {
273*6777b538SAndroid Build Coastguard Worker   LogMetrics();
274*6777b538SAndroid Build Coastguard Worker 
275*6777b538SAndroid Build Coastguard Worker   pending_request_.reset();  // Reset to signal clean completion.
276*6777b538SAndroid Build Coastguard Worker   net_log_.EndEvent(NetLogEventType::CERT_VERIFIER_JOB,
277*6777b538SAndroid Build Coastguard Worker                     [&] { return verify_result_.NetLogParams(result); });
278*6777b538SAndroid Build Coastguard Worker 
279*6777b538SAndroid Build Coastguard Worker   // It's possible that during the process of invoking a callback for a
280*6777b538SAndroid Build Coastguard Worker   // Request, |this| may get deleted (along with the associated parent). If
281*6777b538SAndroid Build Coastguard Worker   // that happens, it's important to ensure that processing of the Job is
282*6777b538SAndroid Build Coastguard Worker   // stopped - i.e. no other callbacks are invoked for other Requests, nor is
283*6777b538SAndroid Build Coastguard Worker   // |this| accessed.
284*6777b538SAndroid Build Coastguard Worker   //
285*6777b538SAndroid Build Coastguard Worker   // To help detect and protect against this, a WeakPtr to |this| is taken. If
286*6777b538SAndroid Build Coastguard Worker   // |this| is deleted, the destructor will have invalidated the WeakPtr.
287*6777b538SAndroid Build Coastguard Worker   //
288*6777b538SAndroid Build Coastguard Worker   // Note that if a Job had already been deleted, this method would not have
289*6777b538SAndroid Build Coastguard Worker   // been invoked in the first place, as the Job (via |pending_request_|) owns
290*6777b538SAndroid Build Coastguard Worker   // the underlying CertVerifier::Request that this method was bound to as a
291*6777b538SAndroid Build Coastguard Worker   // callback. This is why it's OK to grab the WeakPtr from |this| initially.
292*6777b538SAndroid Build Coastguard Worker   base::WeakPtr<Job> weak_this = weak_ptr_factory_.GetWeakPtr();
293*6777b538SAndroid Build Coastguard Worker   while (!attached_requests_.empty()) {
294*6777b538SAndroid Build Coastguard Worker     // Note: It's also possible for additional Requests to be attached to the
295*6777b538SAndroid Build Coastguard Worker     // current Job while processing a Request.
296*6777b538SAndroid Build Coastguard Worker     auto* link_node = attached_requests_.head();
297*6777b538SAndroid Build Coastguard Worker     link_node->RemoveFromList();
298*6777b538SAndroid Build Coastguard Worker 
299*6777b538SAndroid Build Coastguard Worker     // Note: |this| MAY be deleted here.
300*6777b538SAndroid Build Coastguard Worker     //   - If the CoalescingCertVerifier is deleted, it will delete the
301*6777b538SAndroid Build Coastguard Worker     //     Jobs (including |this|)
302*6777b538SAndroid Build Coastguard Worker     //   - If this is the second-to-last Request, and the completion of this
303*6777b538SAndroid Build Coastguard Worker     //     event causes the other Request to be deleted, detaching that Request
304*6777b538SAndroid Build Coastguard Worker     //     from this Job will lead to this Job being deleted (via
305*6777b538SAndroid Build Coastguard Worker     //     Job::AbortRequest())
306*6777b538SAndroid Build Coastguard Worker     link_node->value()->Complete(result);
307*6777b538SAndroid Build Coastguard Worker 
308*6777b538SAndroid Build Coastguard Worker     // Check if |this| has been deleted (which implicitly includes
309*6777b538SAndroid Build Coastguard Worker     // |parent_verifier_|), and abort if so, since no further cleanup is
310*6777b538SAndroid Build Coastguard Worker     // needed.
311*6777b538SAndroid Build Coastguard Worker     if (!weak_this)
312*6777b538SAndroid Build Coastguard Worker       return;
313*6777b538SAndroid Build Coastguard Worker   }
314*6777b538SAndroid Build Coastguard Worker 
315*6777b538SAndroid Build Coastguard Worker   // DANGER: |this| will be invalidated (deleted) after this point.
316*6777b538SAndroid Build Coastguard Worker   return parent_verifier_->RemoveJob(this);
317*6777b538SAndroid Build Coastguard Worker }
318*6777b538SAndroid Build Coastguard Worker 
LogMetrics()319*6777b538SAndroid Build Coastguard Worker void CoalescingCertVerifier::Job::LogMetrics() {
320*6777b538SAndroid Build Coastguard Worker   base::TimeDelta latency = base::TimeTicks::Now() - start_time_;
321*6777b538SAndroid Build Coastguard Worker   UMA_HISTOGRAM_CUSTOM_TIMES("Net.CertVerifier_Job_Latency", latency,
322*6777b538SAndroid Build Coastguard Worker                              base::Milliseconds(1), base::Minutes(10), 100);
323*6777b538SAndroid Build Coastguard Worker   if (is_first_job_) {
324*6777b538SAndroid Build Coastguard Worker     UMA_HISTOGRAM_CUSTOM_TIMES("Net.CertVerifier_First_Job_Latency", latency,
325*6777b538SAndroid Build Coastguard Worker                                base::Milliseconds(1), base::Minutes(10), 100);
326*6777b538SAndroid Build Coastguard Worker   }
327*6777b538SAndroid Build Coastguard Worker }
328*6777b538SAndroid Build Coastguard Worker 
Request(CoalescingCertVerifier::Job * job,CertVerifyResult * verify_result,CompletionOnceCallback callback,const NetLogWithSource & net_log)329*6777b538SAndroid Build Coastguard Worker CoalescingCertVerifier::Request::Request(CoalescingCertVerifier::Job* job,
330*6777b538SAndroid Build Coastguard Worker                                          CertVerifyResult* verify_result,
331*6777b538SAndroid Build Coastguard Worker                                          CompletionOnceCallback callback,
332*6777b538SAndroid Build Coastguard Worker                                          const NetLogWithSource& net_log)
333*6777b538SAndroid Build Coastguard Worker     : job_(job),
334*6777b538SAndroid Build Coastguard Worker       verify_result_(verify_result),
335*6777b538SAndroid Build Coastguard Worker       callback_(std::move(callback)),
336*6777b538SAndroid Build Coastguard Worker       net_log_(net_log) {
337*6777b538SAndroid Build Coastguard Worker   net_log_.BeginEvent(NetLogEventType::CERT_VERIFIER_REQUEST);
338*6777b538SAndroid Build Coastguard Worker }
339*6777b538SAndroid Build Coastguard Worker 
~Request()340*6777b538SAndroid Build Coastguard Worker CoalescingCertVerifier::Request::~Request() {
341*6777b538SAndroid Build Coastguard Worker   if (job_) {
342*6777b538SAndroid Build Coastguard Worker     net_log_.AddEvent(NetLogEventType::CANCELLED);
343*6777b538SAndroid Build Coastguard Worker     net_log_.EndEvent(NetLogEventType::CERT_VERIFIER_REQUEST);
344*6777b538SAndroid Build Coastguard Worker 
345*6777b538SAndroid Build Coastguard Worker     // Need to null out `job_` before aborting the request to avoid a dangling
346*6777b538SAndroid Build Coastguard Worker     // pointer warning, as aborting the request may delete `job_`.
347*6777b538SAndroid Build Coastguard Worker     auto* job = job_.get();
348*6777b538SAndroid Build Coastguard Worker     job_ = nullptr;
349*6777b538SAndroid Build Coastguard Worker 
350*6777b538SAndroid Build Coastguard Worker     // If the Request is deleted before the Job, then detach from the Job.
351*6777b538SAndroid Build Coastguard Worker     // Note: This may cause |job_| to be deleted.
352*6777b538SAndroid Build Coastguard Worker     job->AbortRequest(this);
353*6777b538SAndroid Build Coastguard Worker   }
354*6777b538SAndroid Build Coastguard Worker }
355*6777b538SAndroid Build Coastguard Worker 
Complete(int result)356*6777b538SAndroid Build Coastguard Worker void CoalescingCertVerifier::Request::Complete(int result) {
357*6777b538SAndroid Build Coastguard Worker   DCHECK(job_);  // There must be a pending/non-aborted job to complete.
358*6777b538SAndroid Build Coastguard Worker 
359*6777b538SAndroid Build Coastguard Worker   *verify_result_ = job_->verify_result();
360*6777b538SAndroid Build Coastguard Worker 
361*6777b538SAndroid Build Coastguard Worker   // On successful completion, the Job removes the Request from its set;
362*6777b538SAndroid Build Coastguard Worker   // similarly, break the association here so that when the Request is
363*6777b538SAndroid Build Coastguard Worker   // deleted, it does not try to abort the (now-completed) Job.
364*6777b538SAndroid Build Coastguard Worker   job_ = nullptr;
365*6777b538SAndroid Build Coastguard Worker 
366*6777b538SAndroid Build Coastguard Worker   // Also need to break the association with `verify_result_`, so that
367*6777b538SAndroid Build Coastguard Worker   // dangling pointer checks the result and the Request be destroyed
368*6777b538SAndroid Build Coastguard Worker   // in any order.
369*6777b538SAndroid Build Coastguard Worker   verify_result_ = nullptr;
370*6777b538SAndroid Build Coastguard Worker 
371*6777b538SAndroid Build Coastguard Worker   net_log_.EndEvent(NetLogEventType::CERT_VERIFIER_REQUEST);
372*6777b538SAndroid Build Coastguard Worker 
373*6777b538SAndroid Build Coastguard Worker   // Run |callback_|, which may delete |this|.
374*6777b538SAndroid Build Coastguard Worker   std::move(callback_).Run(result);
375*6777b538SAndroid Build Coastguard Worker }
376*6777b538SAndroid Build Coastguard Worker 
OnJobAbort()377*6777b538SAndroid Build Coastguard Worker void CoalescingCertVerifier::Request::OnJobAbort() {
378*6777b538SAndroid Build Coastguard Worker   DCHECK(job_);  // There must be a pending job to abort.
379*6777b538SAndroid Build Coastguard Worker 
380*6777b538SAndroid Build Coastguard Worker   // If the Job is deleted before the Request, just clean up. The Request will
381*6777b538SAndroid Build Coastguard Worker   // eventually be deleted by the caller.
382*6777b538SAndroid Build Coastguard Worker   net_log_.AddEvent(NetLogEventType::CANCELLED);
383*6777b538SAndroid Build Coastguard Worker   net_log_.EndEvent(NetLogEventType::CERT_VERIFIER_REQUEST);
384*6777b538SAndroid Build Coastguard Worker 
385*6777b538SAndroid Build Coastguard Worker   job_ = nullptr;
386*6777b538SAndroid Build Coastguard Worker   // Note: May delete |this|, if the caller made |callback_| own the Request.
387*6777b538SAndroid Build Coastguard Worker   callback_.Reset();
388*6777b538SAndroid Build Coastguard Worker }
389*6777b538SAndroid Build Coastguard Worker 
CoalescingCertVerifier(std::unique_ptr<CertVerifier> verifier)390*6777b538SAndroid Build Coastguard Worker CoalescingCertVerifier::CoalescingCertVerifier(
391*6777b538SAndroid Build Coastguard Worker     std::unique_ptr<CertVerifier> verifier)
392*6777b538SAndroid Build Coastguard Worker     : verifier_(std::move(verifier)) {
393*6777b538SAndroid Build Coastguard Worker   verifier_->AddObserver(this);
394*6777b538SAndroid Build Coastguard Worker }
395*6777b538SAndroid Build Coastguard Worker 
~CoalescingCertVerifier()396*6777b538SAndroid Build Coastguard Worker CoalescingCertVerifier::~CoalescingCertVerifier() {
397*6777b538SAndroid Build Coastguard Worker   verifier_->RemoveObserver(this);
398*6777b538SAndroid Build Coastguard Worker }
399*6777b538SAndroid Build Coastguard Worker 
Verify(const RequestParams & params,CertVerifyResult * verify_result,CompletionOnceCallback callback,std::unique_ptr<CertVerifier::Request> * out_req,const NetLogWithSource & net_log)400*6777b538SAndroid Build Coastguard Worker int CoalescingCertVerifier::Verify(
401*6777b538SAndroid Build Coastguard Worker     const RequestParams& params,
402*6777b538SAndroid Build Coastguard Worker     CertVerifyResult* verify_result,
403*6777b538SAndroid Build Coastguard Worker     CompletionOnceCallback callback,
404*6777b538SAndroid Build Coastguard Worker     std::unique_ptr<CertVerifier::Request>* out_req,
405*6777b538SAndroid Build Coastguard Worker     const NetLogWithSource& net_log) {
406*6777b538SAndroid Build Coastguard Worker   DCHECK(verify_result);
407*6777b538SAndroid Build Coastguard Worker   DCHECK(!callback.is_null());
408*6777b538SAndroid Build Coastguard Worker 
409*6777b538SAndroid Build Coastguard Worker   out_req->reset();
410*6777b538SAndroid Build Coastguard Worker   ++requests_;
411*6777b538SAndroid Build Coastguard Worker 
412*6777b538SAndroid Build Coastguard Worker   Job* job = FindJob(params);
413*6777b538SAndroid Build Coastguard Worker   if (job) {
414*6777b538SAndroid Build Coastguard Worker     // An identical request is in-flight and joinable, so just attach the
415*6777b538SAndroid Build Coastguard Worker     // callback.
416*6777b538SAndroid Build Coastguard Worker     ++inflight_joins_;
417*6777b538SAndroid Build Coastguard Worker   } else {
418*6777b538SAndroid Build Coastguard Worker     // No existing Jobs can be used. Create and start a new one.
419*6777b538SAndroid Build Coastguard Worker     std::unique_ptr<Job> new_job =
420*6777b538SAndroid Build Coastguard Worker         std::make_unique<Job>(this, params, net_log.net_log(), requests_ == 1);
421*6777b538SAndroid Build Coastguard Worker     int result = new_job->Start(verifier_.get());
422*6777b538SAndroid Build Coastguard Worker     if (result != ERR_IO_PENDING) {
423*6777b538SAndroid Build Coastguard Worker       *verify_result = new_job->verify_result();
424*6777b538SAndroid Build Coastguard Worker       return result;
425*6777b538SAndroid Build Coastguard Worker     }
426*6777b538SAndroid Build Coastguard Worker 
427*6777b538SAndroid Build Coastguard Worker     job = new_job.get();
428*6777b538SAndroid Build Coastguard Worker     joinable_jobs_[params] = std::move(new_job);
429*6777b538SAndroid Build Coastguard Worker   }
430*6777b538SAndroid Build Coastguard Worker 
431*6777b538SAndroid Build Coastguard Worker   std::unique_ptr<CoalescingCertVerifier::Request> request =
432*6777b538SAndroid Build Coastguard Worker       std::make_unique<CoalescingCertVerifier::Request>(
433*6777b538SAndroid Build Coastguard Worker           job, verify_result, std::move(callback), net_log);
434*6777b538SAndroid Build Coastguard Worker   job->AddRequest(request.get());
435*6777b538SAndroid Build Coastguard Worker   *out_req = std::move(request);
436*6777b538SAndroid Build Coastguard Worker   return ERR_IO_PENDING;
437*6777b538SAndroid Build Coastguard Worker }
438*6777b538SAndroid Build Coastguard Worker 
SetConfig(const CertVerifier::Config & config)439*6777b538SAndroid Build Coastguard Worker void CoalescingCertVerifier::SetConfig(const CertVerifier::Config& config) {
440*6777b538SAndroid Build Coastguard Worker   verifier_->SetConfig(config);
441*6777b538SAndroid Build Coastguard Worker 
442*6777b538SAndroid Build Coastguard Worker   IncrementGenerationAndMakeCurrentJobsUnjoinable();
443*6777b538SAndroid Build Coastguard Worker }
444*6777b538SAndroid Build Coastguard Worker 
AddObserver(CertVerifier::Observer * observer)445*6777b538SAndroid Build Coastguard Worker void CoalescingCertVerifier::AddObserver(CertVerifier::Observer* observer) {
446*6777b538SAndroid Build Coastguard Worker   verifier_->AddObserver(observer);
447*6777b538SAndroid Build Coastguard Worker }
448*6777b538SAndroid Build Coastguard Worker 
RemoveObserver(CertVerifier::Observer * observer)449*6777b538SAndroid Build Coastguard Worker void CoalescingCertVerifier::RemoveObserver(CertVerifier::Observer* observer) {
450*6777b538SAndroid Build Coastguard Worker   verifier_->RemoveObserver(observer);
451*6777b538SAndroid Build Coastguard Worker }
452*6777b538SAndroid Build Coastguard Worker 
FindJob(const RequestParams & params)453*6777b538SAndroid Build Coastguard Worker CoalescingCertVerifier::Job* CoalescingCertVerifier::FindJob(
454*6777b538SAndroid Build Coastguard Worker     const RequestParams& params) {
455*6777b538SAndroid Build Coastguard Worker   auto it = joinable_jobs_.find(params);
456*6777b538SAndroid Build Coastguard Worker   if (it != joinable_jobs_.end())
457*6777b538SAndroid Build Coastguard Worker     return it->second.get();
458*6777b538SAndroid Build Coastguard Worker   return nullptr;
459*6777b538SAndroid Build Coastguard Worker }
460*6777b538SAndroid Build Coastguard Worker 
RemoveJob(Job * job)461*6777b538SAndroid Build Coastguard Worker void CoalescingCertVerifier::RemoveJob(Job* job) {
462*6777b538SAndroid Build Coastguard Worker   // See if this was a job from the current configuration generation.
463*6777b538SAndroid Build Coastguard Worker   // Note: It's also necessary to compare that the underlying pointer is the
464*6777b538SAndroid Build Coastguard Worker   // same, and not merely a Job with the same parameters.
465*6777b538SAndroid Build Coastguard Worker   auto joinable_it = joinable_jobs_.find(job->params());
466*6777b538SAndroid Build Coastguard Worker   if (joinable_it != joinable_jobs_.end() && joinable_it->second.get() == job) {
467*6777b538SAndroid Build Coastguard Worker     joinable_jobs_.erase(joinable_it);
468*6777b538SAndroid Build Coastguard Worker     return;
469*6777b538SAndroid Build Coastguard Worker   }
470*6777b538SAndroid Build Coastguard Worker 
471*6777b538SAndroid Build Coastguard Worker   // Otherwise, it MUST have been a job from a previous generation.
472*6777b538SAndroid Build Coastguard Worker   auto inflight_it =
473*6777b538SAndroid Build Coastguard Worker       base::ranges::find_if(inflight_jobs_, base::MatchesUniquePtr(job));
474*6777b538SAndroid Build Coastguard Worker   DCHECK(inflight_it != inflight_jobs_.end());
475*6777b538SAndroid Build Coastguard Worker   inflight_jobs_.erase(inflight_it);
476*6777b538SAndroid Build Coastguard Worker   return;
477*6777b538SAndroid Build Coastguard Worker }
478*6777b538SAndroid Build Coastguard Worker 
IncrementGenerationAndMakeCurrentJobsUnjoinable()479*6777b538SAndroid Build Coastguard Worker void CoalescingCertVerifier::IncrementGenerationAndMakeCurrentJobsUnjoinable() {
480*6777b538SAndroid Build Coastguard Worker   for (auto& job : joinable_jobs_) {
481*6777b538SAndroid Build Coastguard Worker     inflight_jobs_.emplace_back(std::move(job.second));
482*6777b538SAndroid Build Coastguard Worker   }
483*6777b538SAndroid Build Coastguard Worker   joinable_jobs_.clear();
484*6777b538SAndroid Build Coastguard Worker }
485*6777b538SAndroid Build Coastguard Worker 
OnCertVerifierChanged()486*6777b538SAndroid Build Coastguard Worker void CoalescingCertVerifier::OnCertVerifierChanged() {
487*6777b538SAndroid Build Coastguard Worker   IncrementGenerationAndMakeCurrentJobsUnjoinable();
488*6777b538SAndroid Build Coastguard Worker }
489*6777b538SAndroid Build Coastguard Worker 
490*6777b538SAndroid Build Coastguard Worker }  // namespace net
491