diff options
author | juanlang@google.com <juanlang@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-14 21:54:15 +0000 |
---|---|---|
committer | juanlang@google.com <juanlang@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-14 21:54:15 +0000 |
commit | ef8bac0f8932da0134b406ce0d4fb8923abe9870 (patch) | |
tree | d6a6a4ed24698009fb4ce6aaec88e9e33481e867 /net/ssl | |
parent | 22c8b40c92670bc0fe632f900774ef443128d410 (diff) | |
download | chromium_src-ef8bac0f8932da0134b406ce0d4fb8923abe9870.zip chromium_src-ef8bac0f8932da0134b406ce0d4fb8923abe9870.tar.gz chromium_src-ef8bac0f8932da0134b406ce0d4fb8923abe9870.tar.bz2 |
Add a method to get, but not create, the domain bound cert for a given domain.
BUG=259097
Review URL: https://chromiumcodereview.appspot.com/22387005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@217660 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/ssl')
-rw-r--r-- | net/ssl/server_bound_cert_service.cc | 201 | ||||
-rw-r--r-- | net/ssl/server_bound_cert_service.h | 48 | ||||
-rw-r--r-- | net/ssl/server_bound_cert_service_unittest.cc | 289 |
3 files changed, 433 insertions, 105 deletions
diff --git a/net/ssl/server_bound_cert_service.cc b/net/ssl/server_bound_cert_service.cc index 4bc82ed..2bbcbc7 100644 --- a/net/ssl/server_bound_cert_service.cc +++ b/net/ssl/server_bound_cert_service.cc @@ -43,7 +43,8 @@ const int kValidityPeriodInDays = 365; const int kSystemTimeValidityBufferInDays = 90; // Used by the GetDomainBoundCertResult histogram to record the final -// outcome of each GetDomainBoundCert call. Do not re-use values. +// outcome of each GetDomainBoundCert or GetOrCreateDomainBoundCert call. +// Do not re-use values. enum GetCertResult { // Synchronously found and returned an existing domain bound cert. SYNC_SUCCESS = 0, @@ -57,7 +58,8 @@ enum GetCertResult { ASYNC_FAILURE_CREATE_CERT = 4, ASYNC_FAILURE_EXPORT_KEY = 5, ASYNC_FAILURE_UNKNOWN = 6, - // GetDomainBoundCert was called with invalid arguments. + // GetDomainBoundCert or GetOrCreateDomainBoundCert was called with + // invalid arguments. INVALID_ARGUMENT = 7, // We don't support any of the cert types the server requested. UNSUPPORTED_TYPE = 8, @@ -277,14 +279,18 @@ class ServerBoundCertServiceWorker { // origin message loop. class ServerBoundCertServiceJob { public: - ServerBoundCertServiceJob() { } + ServerBoundCertServiceJob(bool create_if_missing) + : create_if_missing_(create_if_missing) { + } ~ServerBoundCertServiceJob() { if (!requests_.empty()) DeleteAllCanceled(); } - void AddRequest(ServerBoundCertServiceRequest* request) { + void AddRequest(ServerBoundCertServiceRequest* request, + bool create_if_missing = false) { + create_if_missing_ |= create_if_missing; requests_.push_back(request); } @@ -294,6 +300,8 @@ class ServerBoundCertServiceJob { PostAll(error, private_key, cert); } + bool CreateIfMissing() const { return create_if_missing_; } + private: void PostAll(int error, const std::string& private_key, @@ -320,6 +328,7 @@ class ServerBoundCertServiceJob { } std::vector<ServerBoundCertServiceRequest*> requests_; + bool create_if_missing_; }; // static @@ -388,7 +397,7 @@ std::string ServerBoundCertService::GetDomainForHost(const std::string& host) { return domain; } -int ServerBoundCertService::GetDomainBoundCert( +int ServerBoundCertService::GetOrCreateDomainBoundCert( const std::string& host, std::string* private_key, std::string* cert, @@ -411,49 +420,15 @@ int ServerBoundCertService::GetDomainBoundCert( requests_++; - // See if an identical request is currently in flight. - ServerBoundCertServiceJob* job = NULL; - std::map<std::string, ServerBoundCertServiceJob*>::const_iterator j; - j = inflight_.find(domain); - if (j != inflight_.end()) { - // An identical request is in flight already. We'll just attach our - // callback. - job = j->second; - inflight_joins_++; - - ServerBoundCertServiceRequest* request = new ServerBoundCertServiceRequest( - request_start, - base::Bind(&RequestHandle::OnRequestComplete, - base::Unretained(out_req)), - private_key, cert); - job->AddRequest(request); - out_req->RequestStarted(this, request, callback); + // See if a request for the same domain is currently in flight. + bool create_if_missing = true; + if (JoinToInFlightRequest(request_start, domain, private_key, cert, + create_if_missing, callback, out_req)) { return ERR_IO_PENDING; } - // Check if a domain bound cert of an acceptable type already exists for this - // domain. Note that |expiration_time| is ignored, and expired certs are - // considered valid. - base::Time expiration_time; - int err = server_bound_cert_store_->GetServerBoundCert( - domain, - &expiration_time /* ignored */, - private_key, - cert, - base::Bind(&ServerBoundCertService::GotServerBoundCert, - weak_ptr_factory_.GetWeakPtr())); - - if (err == OK) { - // Sync lookup found a valid cert. - DVLOG(1) << "Cert store had valid cert for " << domain; - cert_store_hits_++; - RecordGetDomainBoundCertResult(SYNC_SUCCESS); - base::TimeDelta request_time = base::TimeTicks::Now() - request_start; - UMA_HISTOGRAM_TIMES("DomainBoundCerts.GetCertTimeSync", request_time); - RecordGetCertTime(request_time); - return OK; - } - + int err = LookupDomainBoundCert(request_start, domain, private_key, cert, + create_if_missing, callback, out_req); if (err == ERR_FILE_NOT_FOUND) { // Sync lookup did not find a valid cert. Start generating a new one. workers_created_++; @@ -467,19 +442,17 @@ int ServerBoundCertService::GetDomainBoundCert( RecordGetDomainBoundCertResult(WORKER_FAILURE); return ERR_INSUFFICIENT_RESOURCES; } - } - - if (err == ERR_IO_PENDING || err == ERR_FILE_NOT_FOUND) { - // We are either waiting for async DB lookup, or waiting for cert - // generation. Create a job & request to track it. - job = new ServerBoundCertServiceJob(); + // We are waiting for cert generation. Create a job & request to track it. + ServerBoundCertServiceJob* job = + new ServerBoundCertServiceJob(create_if_missing); inflight_[domain] = job; ServerBoundCertServiceRequest* request = new ServerBoundCertServiceRequest( request_start, base::Bind(&RequestHandle::OnRequestComplete, base::Unretained(out_req)), - private_key, cert); + private_key, + cert); job->AddRequest(request); out_req->RequestStarted(this, request, callback); return ERR_IO_PENDING; @@ -488,6 +461,41 @@ int ServerBoundCertService::GetDomainBoundCert( return err; } +int ServerBoundCertService::GetDomainBoundCert( + const std::string& host, + std::string* private_key, + std::string* cert, + const CompletionCallback& callback, + RequestHandle* out_req) { + DVLOG(1) << __FUNCTION__ << " " << host; + DCHECK(CalledOnValidThread()); + base::TimeTicks request_start = base::TimeTicks::Now(); + + if (callback.is_null() || !private_key || !cert || host.empty()) { + RecordGetDomainBoundCertResult(INVALID_ARGUMENT); + return ERR_INVALID_ARGUMENT; + } + + std::string domain = GetDomainForHost(host); + if (domain.empty()) { + RecordGetDomainBoundCertResult(INVALID_ARGUMENT); + return ERR_INVALID_ARGUMENT; + } + + requests_++; + + // See if a request for the same domain currently in flight. + bool create_if_missing = false; + if (JoinToInFlightRequest(request_start, domain, private_key, cert, + create_if_missing, callback, out_req)) { + return ERR_IO_PENDING; + } + + int err = LookupDomainBoundCert(request_start, domain, private_key, cert, + create_if_missing, callback, out_req); + return err; +} + void ServerBoundCertService::GotServerBoundCert( int err, const std::string& server_identifier, @@ -511,7 +519,13 @@ void ServerBoundCertService::GotServerBoundCert( HandleResult(OK, server_identifier, key, cert); return; } - // Async lookup did not find a valid cert. Start generating a new one. + // Async lookup did not find a valid cert. If no request asked to create one, + // return the error directly. + if (!j->second->CreateIfMissing()) { + HandleResult(err, server_identifier, key, cert); + return; + } + // At least one request asked to create a cert => start generating a new one. workers_created_++; ServerBoundCertServiceWorker* worker = new ServerBoundCertServiceWorker( server_identifier, @@ -524,7 +538,6 @@ void ServerBoundCertService::GotServerBoundCert( server_identifier, std::string(), std::string()); - return; } } @@ -579,6 +592,86 @@ void ServerBoundCertService::HandleResult( delete job; } +bool ServerBoundCertService::JoinToInFlightRequest( + const base::TimeTicks& request_start, + const std::string& domain, + std::string* private_key, + std::string* cert, + bool create_if_missing, + const CompletionCallback& callback, + RequestHandle* out_req) { + ServerBoundCertServiceJob* job = NULL; + std::map<std::string, ServerBoundCertServiceJob*>::const_iterator j = + inflight_.find(domain); + if (j != inflight_.end()) { + // A request for the same domain is in flight already. We'll attach our + // callback, but we'll also mark it as requiring a cert if one's mising. + job = j->second; + inflight_joins_++; + + ServerBoundCertServiceRequest* request = new ServerBoundCertServiceRequest( + request_start, + base::Bind(&RequestHandle::OnRequestComplete, + base::Unretained(out_req)), + private_key, + cert); + job->AddRequest(request, create_if_missing); + out_req->RequestStarted(this, request, callback); + return true; + } + return false; +} + +int ServerBoundCertService::LookupDomainBoundCert( + const base::TimeTicks& request_start, + const std::string& domain, + std::string* private_key, + std::string* cert, + bool create_if_missing, + const CompletionCallback& callback, + RequestHandle* out_req) { + // Check if a domain bound cert already exists for this domain. Note that + // |expiration_time| is ignored, and expired certs are considered valid. + base::Time expiration_time; + int err = server_bound_cert_store_->GetServerBoundCert( + domain, + &expiration_time /* ignored */, + private_key, + cert, + base::Bind(&ServerBoundCertService::GotServerBoundCert, + weak_ptr_factory_.GetWeakPtr())); + + if (err == OK) { + // Sync lookup found a valid cert. + DVLOG(1) << "Cert store had valid cert for " << domain; + cert_store_hits_++; + RecordGetDomainBoundCertResult(SYNC_SUCCESS); + base::TimeDelta request_time = base::TimeTicks::Now() - request_start; + UMA_HISTOGRAM_TIMES("DomainBoundCerts.GetCertTimeSync", request_time); + RecordGetCertTime(request_time); + return OK; + } + + if (err == ERR_IO_PENDING) { + // We are waiting for async DB lookup. Create a job & request to track it. + ServerBoundCertServiceJob* job = + new ServerBoundCertServiceJob(create_if_missing); + inflight_[domain] = job; + + ServerBoundCertServiceRequest* request = new ServerBoundCertServiceRequest( + request_start, + base::Bind(&RequestHandle::OnRequestComplete, + base::Unretained(out_req)), + private_key, + cert); + job->AddRequest(request); + out_req->RequestStarted(this, request, callback); + return ERR_IO_PENDING; + } + + return err; +} + int ServerBoundCertService::cert_count() { return server_bound_cert_store_->GetCertCount(); } diff --git a/net/ssl/server_bound_cert_service.h b/net/ssl/server_bound_cert_service.h index d931ec8..0dc7f4a 100644 --- a/net/ssl/server_bound_cert_service.h +++ b/net/ssl/server_bound_cert_service.h @@ -106,6 +106,31 @@ class NET_EXPORT ServerBoundCertService // |*out_req| will be initialized with a handle to the async request. This // RequestHandle object must be cancelled or destroyed before the // ServerBoundCertService is destroyed. + int GetOrCreateDomainBoundCert( + const std::string& host, + std::string* private_key, + std::string* cert, + const CompletionCallback& callback, + RequestHandle* out_req); + + // Fetches the domain bound cert for the specified host if one exists. + // Returns OK if successful, ERR_FILE_NOT_FOUND if none exists, or an error + // code upon failure. + // + // On successful completion, |private_key| stores a DER-encoded + // PrivateKeyInfo struct, and |cert| stores a DER-encoded certificate. + // The PrivateKeyInfo is always an ECDSA private key. + // + // |callback| must not be null. ERR_IO_PENDING is returned if the operation + // could not be completed immediately, in which case the result code will + // be passed to the callback when available. If an in-flight + // GetDomainBoundCert is pending, and a new GetOrCreateDomainBoundCert + // request arrives for the same domain, the GetDomainBoundCert request will + // not complete until a new cert is created. + // + // |*out_req| will be initialized with a handle to the async request. This + // RequestHandle object must be cancelled or destroyed before the + // ServerBoundCertService is destroyed. int GetDomainBoundCert( const std::string& host, std::string* private_key, @@ -143,6 +168,29 @@ class NET_EXPORT ServerBoundCertService const std::string& private_key, const std::string& cert); + // Searches for an in-flight request for the same domain. If found, + // attaches to the request and returns true. Returns false if no in-flight + // request is found. + bool JoinToInFlightRequest(const base::TimeTicks& request_start, + const std::string& domain, + std::string* private_key, + std::string* cert, + bool create_if_missing, + const CompletionCallback& callback, + RequestHandle* out_req); + + // Looks for the domain bound cert for |domain| in this service's store. + // Returns OK if it can be found synchronously, ERR_IO_PENDING if the + // result cannot be obtained synchronously, or a network error code on + // failure (including failure to find a domain-bound cert of |domain|). + int LookupDomainBoundCert(const base::TimeTicks& request_start, + const std::string& domain, + std::string* private_key, + std::string* cert, + bool create_if_missing, + const CompletionCallback& callback, + RequestHandle* out_req); + scoped_ptr<ServerBoundCertStore> server_bound_cert_store_; scoped_refptr<base::TaskRunner> task_runner_; diff --git a/net/ssl/server_bound_cert_service_unittest.cc b/net/ssl/server_bound_cert_service_unittest.cc index d7b8553..b8ca1fa 100644 --- a/net/ssl/server_bound_cert_service_unittest.cc +++ b/net/ssl/server_bound_cert_service_unittest.cc @@ -136,6 +136,24 @@ TEST_F(ServerBoundCertServiceTest, GetDomainForHost) { // See http://crbug.com/91512 - implement OpenSSL version of CreateSelfSigned. #if !defined(USE_OPENSSL) +TEST_F(ServerBoundCertServiceTest, GetCacheMiss) { + std::string host("encrypted.google.com"); + + int error; + TestCompletionCallback callback; + ServerBoundCertService::RequestHandle request_handle; + + // Synchronous completion, because the store is initialized. + std::string private_key, der_cert; + EXPECT_EQ(0, service_->cert_count()); + error = service_->GetDomainBoundCert( + host, &private_key, &der_cert, callback.callback(), &request_handle); + EXPECT_EQ(ERR_FILE_NOT_FOUND, error); + EXPECT_FALSE(request_handle.is_active()); + EXPECT_EQ(0, service_->cert_count()); + EXPECT_TRUE(der_cert.empty()); +} + TEST_F(ServerBoundCertServiceTest, CacheHit) { std::string host("encrypted.google.com"); @@ -146,7 +164,7 @@ TEST_F(ServerBoundCertServiceTest, CacheHit) { // Asynchronous completion. std::string private_key_info1, der_cert1; EXPECT_EQ(0, service_->cert_count()); - error = service_->GetDomainBoundCert( + error = service_->GetOrCreateDomainBoundCert( host, &private_key_info1, &der_cert1, callback.callback(), &request_handle); EXPECT_EQ(ERR_IO_PENDING, error); @@ -160,7 +178,7 @@ TEST_F(ServerBoundCertServiceTest, CacheHit) { // Synchronous completion. std::string private_key_info2, der_cert2; - error = service_->GetDomainBoundCert( + error = service_->GetOrCreateDomainBoundCert( host, &private_key_info2, &der_cert2, callback.callback(), &request_handle); EXPECT_FALSE(request_handle.is_active()); @@ -169,8 +187,19 @@ TEST_F(ServerBoundCertServiceTest, CacheHit) { EXPECT_EQ(private_key_info1, private_key_info2); EXPECT_EQ(der_cert1, der_cert2); - EXPECT_EQ(2u, service_->requests()); - EXPECT_EQ(1u, service_->cert_store_hits()); + // Synchronous get. + std::string private_key_info3, der_cert3; + error = service_->GetDomainBoundCert( + host, &private_key_info3, &der_cert3, callback.callback(), + &request_handle); + EXPECT_FALSE(request_handle.is_active()); + EXPECT_EQ(OK, error); + EXPECT_EQ(1, service_->cert_count()); + EXPECT_EQ(der_cert1, der_cert3); + EXPECT_EQ(private_key_info1, private_key_info3); + + EXPECT_EQ(3u, service_->requests()); + EXPECT_EQ(2u, service_->cert_store_hits()); EXPECT_EQ(0u, service_->inflight_joins()); } @@ -182,7 +211,7 @@ TEST_F(ServerBoundCertServiceTest, StoreCerts) { std::string host1("encrypted.google.com"); std::string private_key_info1, der_cert1; EXPECT_EQ(0, service_->cert_count()); - error = service_->GetDomainBoundCert( + error = service_->GetOrCreateDomainBoundCert( host1, &private_key_info1, &der_cert1, callback.callback(), &request_handle); EXPECT_EQ(ERR_IO_PENDING, error); @@ -193,7 +222,7 @@ TEST_F(ServerBoundCertServiceTest, StoreCerts) { std::string host2("www.verisign.com"); std::string private_key_info2, der_cert2; - error = service_->GetDomainBoundCert( + error = service_->GetOrCreateDomainBoundCert( host2, &private_key_info2, &der_cert2, callback.callback(), &request_handle); EXPECT_EQ(ERR_IO_PENDING, error); @@ -204,7 +233,7 @@ TEST_F(ServerBoundCertServiceTest, StoreCerts) { std::string host3("www.twitter.com"); std::string private_key_info3, der_cert3; - error = service_->GetDomainBoundCert( + error = service_->GetOrCreateDomainBoundCert( host3, &private_key_info3, &der_cert3, callback.callback(), &request_handle); EXPECT_EQ(ERR_IO_PENDING, error); @@ -234,13 +263,13 @@ TEST_F(ServerBoundCertServiceTest, InflightJoin) { TestCompletionCallback callback2; ServerBoundCertService::RequestHandle request_handle2; - error = service_->GetDomainBoundCert( + error = service_->GetOrCreateDomainBoundCert( host, &private_key_info1, &der_cert1, callback1.callback(), &request_handle1); EXPECT_EQ(ERR_IO_PENDING, error); EXPECT_TRUE(request_handle1.is_active()); // Should join with the original request. - error = service_->GetDomainBoundCert( + error = service_->GetOrCreateDomainBoundCert( host, &private_key_info2, &der_cert2, callback2.callback(), &request_handle2); EXPECT_EQ(ERR_IO_PENDING, error); @@ -254,6 +283,45 @@ TEST_F(ServerBoundCertServiceTest, InflightJoin) { EXPECT_EQ(2u, service_->requests()); EXPECT_EQ(0u, service_->cert_store_hits()); EXPECT_EQ(1u, service_->inflight_joins()); + EXPECT_EQ(1u, service_->workers_created()); +} + +// Tests an inflight join of a Get request to a GetOrCreate request. +TEST_F(ServerBoundCertServiceTest, InflightJoinGetOrCreateAndGet) { + std::string host("encrypted.google.com"); + int error; + + std::string private_key_info1, der_cert1; + TestCompletionCallback callback1; + ServerBoundCertService::RequestHandle request_handle1; + + std::string private_key_info2; + std::string der_cert2; + TestCompletionCallback callback2; + ServerBoundCertService::RequestHandle request_handle2; + + error = service_->GetOrCreateDomainBoundCert( + host, &private_key_info1, &der_cert1, + callback1.callback(), &request_handle1); + EXPECT_EQ(ERR_IO_PENDING, error); + EXPECT_TRUE(request_handle1.is_active()); + // Should join with the original request. + error = service_->GetDomainBoundCert( + host, &private_key_info2, &der_cert2, callback2.callback(), + &request_handle2); + EXPECT_EQ(ERR_IO_PENDING, error); + EXPECT_TRUE(request_handle2.is_active()); + + error = callback1.WaitForResult(); + EXPECT_EQ(OK, error); + error = callback2.WaitForResult(); + EXPECT_EQ(OK, error); + EXPECT_EQ(der_cert1, der_cert2); + + EXPECT_EQ(2u, service_->requests()); + EXPECT_EQ(0u, service_->cert_store_hits()); + EXPECT_EQ(1u, service_->inflight_joins()); + EXPECT_EQ(1u, service_->workers_created()); } TEST_F(ServerBoundCertServiceTest, ExtractValuesFromBytesEC) { @@ -263,7 +331,7 @@ TEST_F(ServerBoundCertServiceTest, ExtractValuesFromBytesEC) { TestCompletionCallback callback; ServerBoundCertService::RequestHandle request_handle; - error = service_->GetDomainBoundCert( + error = service_->GetOrCreateDomainBoundCert( host, &private_key_info, &der_cert, callback.callback(), &request_handle); EXPECT_EQ(ERR_IO_PENDING, error); @@ -297,11 +365,11 @@ TEST_F(ServerBoundCertServiceTest, CancelRequest) { int error; ServerBoundCertService::RequestHandle request_handle; - error = service_->GetDomainBoundCert(host, - &private_key_info, - &der_cert, - base::Bind(&FailTest), - &request_handle); + error = service_->GetOrCreateDomainBoundCert(host, + &private_key_info, + &der_cert, + base::Bind(&FailTest), + &request_handle); EXPECT_EQ(ERR_IO_PENDING, error); EXPECT_TRUE(request_handle.is_active()); request_handle.Cancel(); @@ -326,11 +394,11 @@ TEST_F(ServerBoundCertServiceTest, CancelRequestByHandleDestruction) { { ServerBoundCertService::RequestHandle request_handle; - error = service_->GetDomainBoundCert(host, - &private_key_info, - &der_cert, - base::Bind(&FailTest), - &request_handle); + error = service_->GetOrCreateDomainBoundCert(host, + &private_key_info, + &der_cert, + base::Bind(&FailTest), + &request_handle); EXPECT_EQ(ERR_IO_PENDING, error); EXPECT_TRUE(request_handle.is_active()); } @@ -352,11 +420,11 @@ TEST_F(ServerBoundCertServiceTest, DestructionWithPendingRequest) { int error; ServerBoundCertService::RequestHandle request_handle; - error = service_->GetDomainBoundCert(host, - &private_key_info, - &der_cert, - base::Bind(&FailTest), - &request_handle); + error = service_->GetOrCreateDomainBoundCert(host, + &private_key_info, + &der_cert, + base::Bind(&FailTest), + &request_handle); EXPECT_EQ(ERR_IO_PENDING, error); EXPECT_TRUE(request_handle.is_active()); @@ -391,11 +459,11 @@ TEST_F(ServerBoundCertServiceTest, RequestAfterPoolShutdown) { int error; ServerBoundCertService::RequestHandle request_handle; - error = service_->GetDomainBoundCert(host, - &private_key_info, - &der_cert, - base::Bind(&FailTest), - &request_handle); + error = service_->GetOrCreateDomainBoundCert(host, + &private_key_info, + &der_cert, + base::Bind(&FailTest), + &request_handle); // If we got here without crashing or a valgrind error, it worked. ASSERT_EQ(ERR_INSUFFICIENT_RESOURCES, error); EXPECT_FALSE(request_handle.is_active()); @@ -420,27 +488,27 @@ TEST_F(ServerBoundCertServiceTest, SimultaneousCreation) { TestCompletionCallback callback3; ServerBoundCertService::RequestHandle request_handle3; - error = service_->GetDomainBoundCert(host1, - &private_key_info1, - &der_cert1, - callback1.callback(), - &request_handle1); + error = service_->GetOrCreateDomainBoundCert(host1, + &private_key_info1, + &der_cert1, + callback1.callback(), + &request_handle1); EXPECT_EQ(ERR_IO_PENDING, error); EXPECT_TRUE(request_handle1.is_active()); - error = service_->GetDomainBoundCert(host2, - &private_key_info2, - &der_cert2, - callback2.callback(), - &request_handle2); + error = service_->GetOrCreateDomainBoundCert(host2, + &private_key_info2, + &der_cert2, + callback2.callback(), + &request_handle2); EXPECT_EQ(ERR_IO_PENDING, error); EXPECT_TRUE(request_handle2.is_active()); - error = service_->GetDomainBoundCert(host3, - &private_key_info3, - &der_cert3, - callback3.callback(), - &request_handle3); + error = service_->GetOrCreateDomainBoundCert(host3, + &private_key_info3, + &der_cert3, + callback3.callback(), + &request_handle3); EXPECT_EQ(ERR_IO_PENDING, error); EXPECT_TRUE(request_handle3.is_active()); @@ -492,7 +560,7 @@ TEST_F(ServerBoundCertServiceTest, Expiration) { // Cert is valid - synchronous completion. std::string private_key_info1, der_cert1; - error = service_->GetDomainBoundCert( + error = service_->GetOrCreateDomainBoundCert( "good", &private_key_info1, &der_cert1, callback.callback(), &request_handle); EXPECT_EQ(OK, error); @@ -503,7 +571,7 @@ TEST_F(ServerBoundCertServiceTest, Expiration) { // Expired cert is valid as well - synchronous completion. std::string private_key_info2, der_cert2; - error = service_->GetDomainBoundCert( + error = service_->GetOrCreateDomainBoundCert( "expired", &private_key_info2, &der_cert2, callback.callback(), &request_handle); EXPECT_EQ(OK, error); @@ -513,7 +581,7 @@ TEST_F(ServerBoundCertServiceTest, Expiration) { EXPECT_STREQ("d", der_cert2.c_str()); } -TEST_F(ServerBoundCertServiceTest, AsyncStoreGetNoCertsInStore) { +TEST_F(ServerBoundCertServiceTest, AsyncStoreGetOrCreateNoCertsInStore) { MockServerBoundCertStoreWithAsyncGet* mock_store = new MockServerBoundCertStoreWithAsyncGet(); service_ = scoped_ptr<ServerBoundCertService>( @@ -528,7 +596,7 @@ TEST_F(ServerBoundCertServiceTest, AsyncStoreGetNoCertsInStore) { // Asynchronous completion with no certs in the store. std::string private_key_info, der_cert; EXPECT_EQ(0, service_->cert_count()); - error = service_->GetDomainBoundCert( + error = service_->GetOrCreateDomainBoundCert( host, &private_key_info, &der_cert, callback.callback(), &request_handle); EXPECT_EQ(ERR_IO_PENDING, error); EXPECT_TRUE(request_handle.is_active()); @@ -544,7 +612,38 @@ TEST_F(ServerBoundCertServiceTest, AsyncStoreGetNoCertsInStore) { EXPECT_FALSE(request_handle.is_active()); } -TEST_F(ServerBoundCertServiceTest, AsyncStoreGetOneCertInStore) { +TEST_F(ServerBoundCertServiceTest, AsyncStoreGetNoCertsInStore) { + MockServerBoundCertStoreWithAsyncGet* mock_store = + new MockServerBoundCertStoreWithAsyncGet(); + service_ = scoped_ptr<ServerBoundCertService>( + new ServerBoundCertService(mock_store, sequenced_worker_pool_)); + + std::string host("encrypted.google.com"); + + int error; + TestCompletionCallback callback; + ServerBoundCertService::RequestHandle request_handle; + + // Asynchronous completion with no certs in the store. + std::string private_key, der_cert; + EXPECT_EQ(0, service_->cert_count()); + error = service_->GetDomainBoundCert( + host, &private_key, &der_cert, callback.callback(), &request_handle); + EXPECT_EQ(ERR_IO_PENDING, error); + EXPECT_TRUE(request_handle.is_active()); + + mock_store->CallGetServerBoundCertCallbackWithResult( + ERR_FILE_NOT_FOUND, base::Time(), std::string(), std::string()); + + error = callback.WaitForResult(); + EXPECT_EQ(ERR_FILE_NOT_FOUND, error); + EXPECT_EQ(0, service_->cert_count()); + EXPECT_EQ(0u, service_->workers_created()); + EXPECT_TRUE(der_cert.empty()); + EXPECT_FALSE(request_handle.is_active()); +} + +TEST_F(ServerBoundCertServiceTest, AsyncStoreGetOrCreateOneCertInStore) { MockServerBoundCertStoreWithAsyncGet* mock_store = new MockServerBoundCertStoreWithAsyncGet(); service_ = scoped_ptr<ServerBoundCertService>( @@ -559,7 +658,7 @@ TEST_F(ServerBoundCertServiceTest, AsyncStoreGetOneCertInStore) { // Asynchronous completion with a cert in the store. std::string private_key_info, der_cert; EXPECT_EQ(0, service_->cert_count()); - error = service_->GetDomainBoundCert( + error = service_->GetOrCreateDomainBoundCert( host, &private_key_info, &der_cert, callback.callback(), &request_handle); EXPECT_EQ(ERR_IO_PENDING, error); EXPECT_TRUE(request_handle.is_active()); @@ -580,6 +679,94 @@ TEST_F(ServerBoundCertServiceTest, AsyncStoreGetOneCertInStore) { EXPECT_FALSE(request_handle.is_active()); } +TEST_F(ServerBoundCertServiceTest, AsyncStoreGetOneCertInStore) { + MockServerBoundCertStoreWithAsyncGet* mock_store = + new MockServerBoundCertStoreWithAsyncGet(); + service_ = scoped_ptr<ServerBoundCertService>( + new ServerBoundCertService(mock_store, sequenced_worker_pool_)); + + std::string host("encrypted.google.com"); + + int error; + TestCompletionCallback callback; + ServerBoundCertService::RequestHandle request_handle; + + // Asynchronous completion with a cert in the store. + std::string private_key, der_cert; + EXPECT_EQ(0, service_->cert_count()); + error = service_->GetDomainBoundCert( + host, &private_key, &der_cert, callback.callback(), &request_handle); + EXPECT_EQ(ERR_IO_PENDING, error); + EXPECT_TRUE(request_handle.is_active()); + + mock_store->CallGetServerBoundCertCallbackWithResult( + OK, base::Time(), "ab", "cd"); + + error = callback.WaitForResult(); + EXPECT_EQ(OK, error); + EXPECT_EQ(1, service_->cert_count()); + EXPECT_EQ(1u, service_->requests()); + EXPECT_EQ(1u, service_->cert_store_hits()); + // Because the cert was found in the store, no new workers should have been + // created. + EXPECT_EQ(0u, service_->workers_created()); + EXPECT_STREQ("cd", der_cert.c_str()); + EXPECT_FALSE(request_handle.is_active()); +} + +TEST_F(ServerBoundCertServiceTest, AsyncStoreGetThenCreateNoCertsInStore) { + MockServerBoundCertStoreWithAsyncGet* mock_store = + new MockServerBoundCertStoreWithAsyncGet(); + service_ = scoped_ptr<ServerBoundCertService>( + new ServerBoundCertService(mock_store, sequenced_worker_pool_)); + + std::string host("encrypted.google.com"); + + int error; + + // Asynchronous get with no certs in the store. + TestCompletionCallback callback1; + ServerBoundCertService::RequestHandle request_handle1; + std::string private_key1, der_cert1; + EXPECT_EQ(0, service_->cert_count()); + error = service_->GetDomainBoundCert( + host, &private_key1, &der_cert1, callback1.callback(), &request_handle1); + EXPECT_EQ(ERR_IO_PENDING, error); + EXPECT_TRUE(request_handle1.is_active()); + + // Asynchronous get/create with no certs in the store. + TestCompletionCallback callback2; + ServerBoundCertService::RequestHandle request_handle2; + std::string private_key2, der_cert2; + EXPECT_EQ(0, service_->cert_count()); + error = service_->GetOrCreateDomainBoundCert( + host, &private_key2, &der_cert2, callback2.callback(), &request_handle2); + EXPECT_EQ(ERR_IO_PENDING, error); + EXPECT_TRUE(request_handle2.is_active()); + + mock_store->CallGetServerBoundCertCallbackWithResult( + ERR_FILE_NOT_FOUND, base::Time(), std::string(), std::string()); + + // Even though the first request didn't ask to create a cert, it gets joined + // by the second, which does, so both succeed. + error = callback1.WaitForResult(); + EXPECT_EQ(OK, error); + error = callback2.WaitForResult(); + EXPECT_EQ(OK, error); + + // One cert is created, one request is joined. + EXPECT_EQ(2U, service_->requests()); + EXPECT_EQ(1, service_->cert_count()); + EXPECT_EQ(1u, service_->workers_created()); + EXPECT_EQ(1u, service_->inflight_joins()); + EXPECT_FALSE(der_cert1.empty()); + EXPECT_EQ(der_cert1, der_cert2); + EXPECT_FALSE(private_key1.empty()); + EXPECT_EQ(private_key1, private_key2); + EXPECT_FALSE(request_handle1.is_active()); + EXPECT_FALSE(request_handle2.is_active()); +} + #endif // !defined(USE_OPENSSL) } // namespace |