Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "net/base/cert_verifier.h" | 5 #include "net/base/cert_verifier.h" |
| 6 | 6 |
| 7 #if defined(USE_NSS) | 7 #include "base/compiler_specific.h" |
| 8 #include <private/pprthred.h> // PR_DetatchThread | |
| 9 #endif | |
| 10 | |
| 11 #include "base/lock.h" | 8 #include "base/lock.h" |
| 12 #include "base/message_loop_proxy.h" | 9 #include "base/message_loop.h" |
| 13 #include "base/scoped_ptr.h" | 10 #include "base/stl_util-inl.h" |
| 14 #include "base/worker_pool.h" | 11 #include "base/worker_pool.h" |
| 15 #include "net/base/cert_verify_result.h" | |
| 16 #include "net/base/net_errors.h" | 12 #include "net/base/net_errors.h" |
| 17 #include "net/base/x509_certificate.h" | 13 #include "net/base/x509_certificate.h" |
| 18 | 14 |
| 15 #if defined(USE_NSS) | |
| 16 #include <private/pprthred.h> // PR_DetachThread | |
| 17 #endif | |
| 18 | |
| 19 namespace net { | 19 namespace net { |
| 20 | 20 |
| 21 class CertVerifier::Request : | 21 //////////////////////////////////////////////////////////////////////////// |
| 22 public base::RefCountedThreadSafe<CertVerifier::Request> { | 22 |
| 23 // Life of a request: | |
| 24 // | |
| 25 // CertVerifier CertVerifierJob CertVerifierWorker Request | |
| 26 // | (origin loop) (worker loop) | |
| 27 // | | |
| 28 // Verify() | |
| 29 // |---->-------------------<creates> | |
| 30 // | | |
| 31 // |---->----<creates> | |
| 32 // | | |
| 33 // |---->---------------------------------------------------<creates> | |
| 34 // | | |
| 35 // |---->--------------------Start | |
| 36 // | | | |
| 37 // | PostTask | |
| 38 // | | |
| 39 // | <starts verifying> | |
| 40 // |---->-----AddRequest | | |
| 41 // | | |
| 42 // | | |
| 43 // | | |
| 44 // Finish | |
| 45 // | | |
| 46 // PostTask | |
| 47 // | |
| 48 // | | |
| 49 // DoReply | |
| 50 // |----<-----------------------| | |
| 51 // HandleResult | |
| 52 // | | |
| 53 // |---->-----HandleResult | |
| 54 // | | |
| 55 // |------>-----------------------------------Post | |
| 56 // | |
| 57 // | |
| 58 // | |
| 59 // On a cache hit, CertVerifier::Verify() returns synchronously without | |
| 60 // posting a task to a worker thread. | |
| 61 | |
| 62 // The number of CachedCertVerifyResult objects that we'll cache. | |
| 63 static const unsigned kMaxCacheEntries = 64; | |
|
agl
2010/12/13 16:25:04
My feeling is that this is probably a little low?
| |
| 64 | |
| 65 // The number of seconds for which we'll cache a cache entry. | |
| 66 static const unsigned kTTLSecs = 1800; // 30 minutes. | |
| 67 | |
| 68 namespace { | |
| 69 | |
| 70 class DefaultTimeService : public CertVerifier::TimeService { | |
| 23 public: | 71 public: |
| 24 Request(CertVerifier* verifier, | 72 // CertVerifier::TimeService methods: |
| 25 X509Certificate* cert, | 73 virtual base::Time Now() { return base::Time::Now(); } |
| 26 const std::string& hostname, | 74 }; |
| 27 int flags, | 75 |
| 28 CertVerifyResult* verify_result, | 76 } // namespace |
| 29 CompletionCallback* callback) | 77 |
| 78 CachedCertVerifyResult::CachedCertVerifyResult() : error(ERR_FAILED) { | |
| 79 } | |
| 80 | |
| 81 CachedCertVerifyResult::~CachedCertVerifyResult() {} | |
| 82 | |
| 83 bool CachedCertVerifyResult::HasExpired(const base::Time current_time) const { | |
| 84 return current_time >= expiry; | |
| 85 } | |
| 86 | |
| 87 // Represents the output and result callback of a request. | |
| 88 class CertVerifierRequest { | |
|
willchan no longer on Chromium
2010/12/13 09:30:53
Should this be within the anonymous namespace? I
| |
| 89 public: | |
| 90 CertVerifierRequest(CompletionCallback* callback, | |
| 91 CertVerifyResult* verify_result) | |
| 92 : callback_(callback), | |
| 93 verify_result_(verify_result) { | |
| 94 } | |
| 95 | |
| 96 // Ensures that the result callback will never be made. | |
| 97 void Cancel() { | |
| 98 callback_ = NULL; | |
| 99 verify_result_ = NULL; | |
| 100 } | |
| 101 | |
| 102 // Copies the contents of |verify_result| to the caller's | |
| 103 // CertVerifyResult and calls the callback. | |
| 104 void Post(const CachedCertVerifyResult& verify_result) { | |
| 105 if (callback_) { | |
| 106 *verify_result_ = verify_result.result; | |
| 107 callback_->Run(verify_result.error); | |
| 108 } | |
| 109 delete this; | |
| 110 } | |
| 111 | |
| 112 private: | |
| 113 CompletionCallback* callback_; | |
| 114 CertVerifyResult* verify_result_; | |
| 115 }; | |
| 116 | |
| 117 | |
| 118 // CertVerifierWorker runs on a worker thread and takes care of the blocking | |
| 119 // process of performing the certificate verification. Deletes itself | |
| 120 // eventually if Start() succeeds. | |
| 121 class CertVerifierWorker { | |
| 122 public: | |
| 123 CertVerifierWorker(X509Certificate* cert, | |
| 124 const std::string& hostname, | |
| 125 int flags, | |
| 126 CertVerifier* cert_verifier) | |
|
willchan no longer on Chromium
2010/12/13 09:30:53
I think you should just pass in a callback. Then
| |
| 30 : cert_(cert), | 127 : cert_(cert), |
| 31 hostname_(hostname), | 128 hostname_(hostname), |
| 32 flags_(flags), | 129 flags_(flags), |
| 33 verifier_(verifier), | 130 origin_loop_(MessageLoop::current()), |
| 34 verify_result_(verify_result), | 131 cert_verifier_(cert_verifier), |
| 35 callback_(callback), | 132 canceled_(false), |
| 36 origin_loop_proxy_(base::MessageLoopProxy::CreateForCurrentThread()), | 133 error_(ERR_FAILED) { |
| 37 error_(OK) { | |
| 38 } | 134 } |
| 39 | 135 |
| 40 void DoVerify() { | 136 bool Start() { |
| 41 // Running on the worker thread | 137 DCHECK_EQ(MessageLoop::current(), origin_loop_); |
|
willchan no longer on Chromium
2010/12/13 09:30:53
You may want to consider using ThreadChecker inste
| |
| 42 error_ = cert_->Verify(hostname_, flags_, &result_); | 138 |
| 139 return WorkerPool::PostTask( | |
| 140 FROM_HERE, NewRunnableMethod(this, &CertVerifierWorker::Run), | |
| 141 true /* task is slow */); | |
| 142 } | |
| 143 | |
| 144 // Cancel is called from the origin loop when the CertVerifier is getting | |
| 145 // deleted. | |
| 146 void Cancel() { | |
| 147 DCHECK_EQ(MessageLoop::current(), origin_loop_); | |
| 148 AutoLock locked(lock_); | |
| 149 canceled_ = true; | |
| 150 } | |
| 151 | |
| 152 private: | |
| 153 void Run() { | |
| 154 // Runs on a worker thread. | |
| 155 error_ = cert_->Verify(hostname_, flags_, &verify_result_); | |
| 43 #if defined(USE_NSS) | 156 #if defined(USE_NSS) |
| 44 // Detach the thread from NSPR. | 157 // Detach the thread from NSPR. |
| 45 // Calling NSS functions attaches the thread to NSPR, which stores | 158 // Calling NSS functions attaches the thread to NSPR, which stores |
| 46 // the NSPR thread ID in thread-specific data. | 159 // the NSPR thread ID in thread-specific data. |
| 47 // The threads in our thread pool terminate after we have called | 160 // The threads in our thread pool terminate after we have called |
| 48 // PR_Cleanup. Unless we detach them from NSPR, net_unittests gets | 161 // PR_Cleanup. Unless we detach them from NSPR, net_unittests gets |
| 49 // segfaults on shutdown when the threads' thread-specific data | 162 // segfaults on shutdown when the threads' thread-specific data |
| 50 // destructors run. | 163 // destructors run. |
| 51 PR_DetachThread(); | 164 PR_DetachThread(); |
| 52 #endif | 165 #endif |
| 53 | 166 Finish(); |
| 54 scoped_ptr<Task> reply(NewRunnableMethod(this, &Request::DoCallback)); | 167 } |
| 55 | 168 |
| 56 // The origin loop could go away while we are trying to post to it, so we | 169 // DoReply runs on the origin thread. |
| 57 // need to call its PostTask method inside a lock. See ~CertVerifier. | 170 void DoReply() { |
| 58 AutoLock locked(origin_loop_proxy_lock_); | 171 DCHECK_EQ(MessageLoop::current(), origin_loop_); |
| 59 if (origin_loop_proxy_) { | 172 { |
| 60 bool posted = origin_loop_proxy_->PostTask(FROM_HERE, reply.release()); | 173 // We lock here because the worker thread could still be in Finished, |
| 61 // TODO(willchan): Fix leaks and then change this to a DCHECK. | 174 // after the PostTask, but before unlocking |lock_|. If we do not lock in |
| 62 LOG_IF(ERROR, !posted) << "Leaked CertVerifier!"; | 175 // this case, we will end up deleting a locked Lock, which can lead to |
| 63 } | 176 // memory leaks or worse errors. |
| 64 } | 177 AutoLock locked(lock_); |
| 65 | 178 if (!canceled_) { |
| 66 void DoCallback() { | 179 cert_verifier_->HandleResult(cert_, hostname_, flags_, |
| 67 // Running on the origin thread. | 180 error_, verify_result_); |
| 68 | 181 } |
| 69 // We may have been cancelled! | 182 } |
| 70 if (!verifier_) | 183 delete this; |
| 71 return; | 184 } |
| 72 | 185 |
| 73 *verify_result_ = result_; | 186 void Finish() { |
| 74 | 187 // Runs on the worker thread. |
| 75 // Drop the verifier's reference to us. Do this before running the | 188 // We assume that the origin loop outlives the CertVerifier. If the |
|
willchan no longer on Chromium
2010/12/13 09:30:53
See my note below for avoiding this complexity by
| |
| 76 // callback since the callback might result in the verifier being | 189 // CertVerifier is deleted, it will call Cancel on us. If it does so |
| 77 // destroyed. | 190 // before the Acquire, we'll delete ourselves and return. If it's trying to |
| 78 verifier_->request_ = NULL; | 191 // do so concurrently, then it'll block on the lock and we'll call PostTask |
| 79 | 192 // while the CertVerifier (and therefore the MessageLoop) is still alive. |
| 80 callback_->Run(error_); | 193 // If it does so after this function, we assume that the MessageLoop will |
| 81 } | 194 // process pending tasks. In which case we'll notice the |canceled_| flag |
| 82 | 195 // in DoReply. |
| 83 void Cancel() { | 196 |
| 84 verifier_ = NULL; | 197 bool canceled; |
| 85 | 198 { |
| 86 AutoLock locked(origin_loop_proxy_lock_); | 199 AutoLock locked(lock_); |
| 87 origin_loop_proxy_ = NULL; | 200 canceled = canceled_; |
| 201 if (!canceled) { | |
| 202 origin_loop_->PostTask( | |
| 203 FROM_HERE, NewRunnableMethod(this, &CertVerifierWorker::DoReply)); | |
| 204 } | |
| 205 } | |
| 206 | |
| 207 if (canceled) | |
| 208 delete this; | |
| 209 } | |
| 210 | |
| 211 scoped_refptr<X509Certificate> cert_; | |
| 212 const std::string hostname_; | |
| 213 const int flags_; | |
| 214 MessageLoop* const origin_loop_; | |
|
willchan no longer on Chromium
2010/12/13 09:30:53
Note that if an object is leaked (not canceled pro
| |
| 215 CertVerifier* const cert_verifier_; | |
| 216 | |
| 217 Lock lock_; | |
|
willchan no longer on Chromium
2010/12/13 09:30:53
You should document what |lock_| locks. In partic
agl
2010/12/13 16:25:04
I used to do this by indenting the protected varia
| |
| 218 bool canceled_; | |
| 219 | |
| 220 int error_; | |
| 221 CertVerifyResult verify_result_; | |
| 222 | |
| 223 DISALLOW_COPY_AND_ASSIGN(CertVerifierWorker); | |
| 224 }; | |
| 225 | |
| 226 // A CertVerifierJob is a one-to-one counterpart of a CertVerifierWorker. It | |
| 227 // lives only on the CertVerifier's origin message loop. | |
| 228 class CertVerifierJob { | |
| 229 public: | |
| 230 explicit CertVerifierJob(CertVerifierWorker* worker) : worker_(worker) { | |
| 231 } | |
| 232 | |
| 233 ~CertVerifierJob() { | |
| 234 if (worker_) | |
| 235 worker_->Cancel(); | |
| 236 } | |
| 237 | |
| 238 void AddRequest(CertVerifierRequest* request) { | |
| 239 requests_.push_back(request); | |
| 240 } | |
| 241 | |
| 242 void HandleResult(const CachedCertVerifyResult& verify_result) { | |
| 243 worker_ = NULL; | |
| 244 PostAll(verify_result); | |
| 88 } | 245 } |
| 89 | 246 |
| 90 private: | 247 private: |
| 91 friend class base::RefCountedThreadSafe<CertVerifier::Request>; | 248 void PostAll(const CachedCertVerifyResult& verify_result) { |
| 92 | 249 std::vector<CertVerifierRequest*> requests; |
| 93 ~Request() {} | 250 requests_.swap(requests); |
| 94 | 251 |
| 95 // Set on the origin thread, read on the worker thread. | 252 for (std::vector<CertVerifierRequest*>::iterator |
| 96 scoped_refptr<X509Certificate> cert_; | 253 i = requests.begin(); i != requests.end(); i++) { |
| 97 std::string hostname_; | 254 (*i)->Post(verify_result); |
| 98 // bitwise OR'd of X509Certificate::VerifyFlags. | 255 // Post() causes the CertVerifierRequest to delete itself. |
| 99 int flags_; | 256 } |
| 100 | 257 } |
| 101 // Only used on the origin thread (where Verify was called). | 258 |
| 102 CertVerifier* verifier_; | 259 std::vector<CertVerifierRequest*> requests_; |
| 103 CertVerifyResult* verify_result_; | 260 CertVerifierWorker* worker_; |
| 104 CompletionCallback* callback_; | |
| 105 | |
| 106 // Used to post ourselves onto the origin thread. | |
| 107 Lock origin_loop_proxy_lock_; | |
| 108 // Use a MessageLoopProxy in case the owner of the CertVerifier is leaked, so | |
| 109 // this code won't crash: http://crbug.com/42275. If this is leaked, then it | |
| 110 // doesn't get Cancel()'d, so |origin_loop_proxy_| doesn't get NULL'd out. If | |
| 111 // the MessageLoop goes away, then if we had used a MessageLoop, this would | |
| 112 // crash. | |
| 113 scoped_refptr<base::MessageLoopProxy> origin_loop_proxy_; | |
| 114 | |
| 115 // Assigned on the worker thread, read on the origin thread. | |
| 116 int error_; | |
| 117 CertVerifyResult result_; | |
| 118 }; | 261 }; |
| 119 | 262 |
| 120 //----------------------------------------------------------------------------- | 263 |
| 121 | 264 CertVerifier::CertVerifier() |
| 122 CertVerifier::CertVerifier() { | 265 : time_service_(new DefaultTimeService), |
| 266 requests_(0), | |
| 267 cache_hits_(0), | |
| 268 inflight_joins_(0) { | |
| 269 } | |
| 270 | |
| 271 CertVerifier::CertVerifier(TimeService* time_service) | |
| 272 : time_service_(time_service), | |
| 273 requests_(0), | |
| 274 cache_hits_(0), | |
| 275 inflight_joins_(0) { | |
| 123 } | 276 } |
| 124 | 277 |
| 125 CertVerifier::~CertVerifier() { | 278 CertVerifier::~CertVerifier() { |
| 126 if (request_) | 279 STLDeleteValues(&inflight_); |
| 127 request_->Cancel(); | |
| 128 } | 280 } |
| 129 | 281 |
| 130 int CertVerifier::Verify(X509Certificate* cert, | 282 int CertVerifier::Verify(X509Certificate* cert, |
| 131 const std::string& hostname, | 283 const std::string& hostname, |
| 132 int flags, | 284 int flags, |
| 133 CertVerifyResult* verify_result, | 285 CertVerifyResult* verify_result, |
| 134 CompletionCallback* callback) { | 286 CompletionCallback* callback, |
| 135 DCHECK(!request_) << "verifier already in use"; | 287 RequestHandle* out_req) { |
| 288 DCHECK(CalledOnValidThread()); | |
| 289 | |
| 290 if (!callback || !verify_result || hostname.empty()) { | |
|
willchan no longer on Chromium
2010/12/13 09:30:53
You should probably add a NOTREACHED(). These rep
| |
| 291 *out_req = NULL; | |
| 292 return ERR_INVALID_ARGUMENT; | |
| 293 } | |
| 294 | |
| 295 requests_++; | |
| 296 | |
| 297 const RequestParams key = {cert->fingerprint(), hostname, flags}; | |
| 298 // First check the cache. | |
| 299 std::map<RequestParams, CachedCertVerifyResult>::iterator i; | |
| 300 i = cache_.find(key); | |
| 301 if (i != cache_.end()) { | |
| 302 if (!i->second.HasExpired(time_service_->Now())) { | |
| 303 cache_hits_++; | |
| 304 *out_req = NULL; | |
| 305 *verify_result = i->second.result; | |
| 306 return i->second.error; | |
| 307 } | |
| 308 // Cache entry has expired. | |
| 309 cache_.erase(i); | |
| 310 } | |
| 311 | |
| 312 // No cache hit. See if an identical request is currently in flight. | |
| 313 CertVerifierJob* job; | |
| 314 std::map<RequestParams, CertVerifierJob*>::const_iterator j; | |
| 315 j = inflight_.find(key); | |
| 316 if (j != inflight_.end()) { | |
| 317 // An identical request is in flight already. We'll just attach our | |
| 318 // callback. | |
| 319 inflight_joins_++; | |
| 320 job = j->second; | |
| 321 } else { | |
| 322 // Need to make a new request. | |
| 323 CertVerifierWorker* worker = new CertVerifierWorker(cert, hostname, flags, | |
| 324 this); | |
| 325 job = new CertVerifierJob(worker); | |
| 326 inflight_.insert(std::make_pair(key, job)); | |
|
willchan no longer on Chromium
2010/12/13 09:30:53
Should this be moved after the conditional? So we
agl
2010/12/13 16:25:04
I think, originally, my code could reenter this ob
| |
| 327 if (!worker->Start()) { | |
| 328 inflight_.erase(key); | |
| 329 delete job; | |
| 330 delete worker; | |
| 331 *out_req = NULL; | |
| 332 return ERR_FAILED; // TODO(wtc): Log an error message. | |
|
willchan no longer on Chromium
2010/12/13 09:30:53
Should this be ERR_UNEXPECTED?
| |
| 333 } | |
| 334 } | |
| 335 | |
| 336 CertVerifierRequest* request = | |
| 337 new CertVerifierRequest(callback, verify_result); | |
| 338 job->AddRequest(request); | |
| 339 *out_req = request; | |
| 340 return ERR_IO_PENDING; | |
| 341 } | |
| 342 | |
| 343 void CertVerifier::CancelRequest(RequestHandle req) { | |
| 344 DCHECK(CalledOnValidThread()); | |
| 345 CertVerifierRequest* request = reinterpret_cast<CertVerifierRequest*>(req); | |
| 346 request->Cancel(); | |
| 347 } | |
| 348 | |
| 349 void CertVerifier::ClearCache() { | |
| 350 DCHECK(CalledOnValidThread()); | |
| 351 | |
| 352 cache_.clear(); | |
| 353 // Leaves inflight_ alone. | |
| 354 } | |
| 355 | |
| 356 int CertVerifier::GetCacheSize() const { | |
| 357 DCHECK(CalledOnValidThread()); | |
| 358 | |
| 359 return cache_.size(); | |
| 360 } | |
| 361 | |
| 362 // HandleResult is called by CertVerifierWorker on the origin message loop. | |
| 363 // It deletes CertVerifierJob. | |
| 364 void CertVerifier::HandleResult(X509Certificate* cert, | |
| 365 const std::string& hostname, | |
| 366 int flags, | |
| 367 int error, | |
| 368 const CertVerifyResult& verify_result) { | |
| 369 DCHECK(CalledOnValidThread()); | |
| 370 | |
| 371 const base::Time current_time(time_service_->Now()); | |
| 372 | |
| 373 CachedCertVerifyResult cached_result; | |
| 374 cached_result.error = error; | |
| 375 cached_result.result = verify_result; | |
| 376 uint32 ttl = kTTLSecs; | |
| 377 cached_result.expiry = current_time + base::TimeDelta::FromSeconds(ttl); | |
| 378 | |
| 379 const RequestParams key = {cert->fingerprint(), hostname, flags}; | |
| 380 | |
| 381 DCHECK_GE(kMaxCacheEntries, 1u); | |
| 382 DCHECK_LE(cache_.size(), kMaxCacheEntries); | |
| 383 if (cache_.size() == kMaxCacheEntries) { | |
| 384 // Need to remove an element of the cache. | |
| 385 std::map<RequestParams, CachedCertVerifyResult>::iterator i, cur; | |
| 386 for (i = cache_.begin(); i != cache_.end(); ) { | |
| 387 cur = i++; | |
| 388 if (cur->second.HasExpired(current_time)) | |
| 389 cache_.erase(cur); | |
| 390 } | |
| 391 } | |
| 392 if (cache_.size() == kMaxCacheEntries) { | |
| 393 // If we didn't clear out any expired entries, we just remove the first | |
| 394 // element. Crummy but simple. | |
| 395 cache_.erase(cache_.begin()); | |
| 396 } | |
| 397 | |
| 398 cache_.insert(std::make_pair(key, cached_result)); | |
| 399 | |
| 400 std::map<RequestParams, CertVerifierJob*>::iterator j; | |
| 401 j = inflight_.find(key); | |
| 402 if (j == inflight_.end()) { | |
| 403 NOTREACHED(); | |
| 404 return; | |
| 405 } | |
| 406 CertVerifierJob* job = j->second; | |
| 407 inflight_.erase(j); | |
| 408 | |
| 409 job->HandleResult(cached_result); | |
| 410 delete job; | |
| 411 } | |
| 412 | |
| 413 ///////////////////////////////////////////////////////////////////// | |
| 414 | |
| 415 SingleRequestCertVerifier::SingleRequestCertVerifier( | |
| 416 CertVerifier* cert_verifier) | |
| 417 : cert_verifier_(cert_verifier), | |
| 418 cur_request_(NULL), | |
| 419 cur_request_callback_(NULL), | |
| 420 ALLOW_THIS_IN_INITIALIZER_LIST( | |
| 421 callback_(this, &SingleRequestCertVerifier::OnVerifyCompletion)) { | |
| 422 DCHECK(cert_verifier_ != NULL); | |
| 423 } | |
| 424 | |
| 425 SingleRequestCertVerifier::~SingleRequestCertVerifier() { | |
| 426 if (cur_request_) { | |
| 427 cert_verifier_->CancelRequest(cur_request_); | |
| 428 cur_request_ = NULL; | |
| 429 } | |
| 430 } | |
| 431 | |
| 432 int SingleRequestCertVerifier::Verify(X509Certificate* cert, | |
| 433 const std::string& hostname, | |
| 434 int flags, | |
| 435 CertVerifyResult* verify_result, | |
| 436 CompletionCallback* callback) { | |
| 437 // Should not be already in use. | |
| 438 DCHECK(!cur_request_ && !cur_request_callback_); | |
| 136 | 439 |
| 137 // Do a synchronous verification. | 440 // Do a synchronous verification. |
| 138 if (!callback) { | 441 if (!callback) |
| 139 CertVerifyResult result; | 442 return cert->Verify(hostname, flags, verify_result); |
| 140 int rv = cert->Verify(hostname, flags, &result); | 443 |
| 141 *verify_result = result; | 444 CertVerifier::RequestHandle request = NULL; |
| 142 return rv; | 445 |
| 143 } | 446 // We need to be notified of completion before |callback| is called, so that |
| 144 | 447 // we can clear out |cur_request_*|. |
| 145 request_ = new Request(this, cert, hostname, flags, verify_result, callback); | 448 int rv = cert_verifier_->Verify( |
| 146 | 449 cert, hostname, flags, verify_result, &callback_, &request); |
| 147 // Dispatch to worker thread... | 450 |
| 148 if (!WorkerPool::PostTask(FROM_HERE, | 451 if (rv == ERR_IO_PENDING) { |
| 149 NewRunnableMethod(request_.get(), &Request::DoVerify), true)) { | 452 // Cleared in OnVerifyCompletion(). |
| 150 NOTREACHED(); | 453 cur_request_ = request; |
| 151 request_ = NULL; | 454 cur_request_callback_ = callback; |
| 152 return ERR_FAILED; | 455 } |
| 153 } | 456 |
| 154 | 457 return rv; |
| 155 return ERR_IO_PENDING; | 458 } |
| 459 | |
| 460 void SingleRequestCertVerifier::OnVerifyCompletion(int result) { | |
| 461 DCHECK(cur_request_ && cur_request_callback_); | |
| 462 | |
| 463 CompletionCallback* callback = cur_request_callback_; | |
| 464 | |
| 465 // Clear the outstanding request information. | |
| 466 cur_request_ = NULL; | |
| 467 cur_request_callback_ = NULL; | |
| 468 | |
| 469 // Call the user's original callback. | |
| 470 callback->Run(result); | |
| 156 } | 471 } |
| 157 | 472 |
| 158 } // namespace net | 473 } // namespace net |
| 474 | |
| 475 DISABLE_RUNNABLE_METHOD_REFCOUNT(net::CertVerifierWorker); | |
| 476 | |
| OLD | NEW |