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