summaryrefslogtreecommitdiffstats
path: root/net/ssl
diff options
context:
space:
mode:
authorjuanlang@google.com <juanlang@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2013-08-14 21:54:15 +0000
committerjuanlang@google.com <juanlang@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2013-08-14 21:54:15 +0000
commitef8bac0f8932da0134b406ce0d4fb8923abe9870 (patch)
treed6a6a4ed24698009fb4ce6aaec88e9e33481e867 /net/ssl
parent22c8b40c92670bc0fe632f900774ef443128d410 (diff)
downloadchromium_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.cc201
-rw-r--r--net/ssl/server_bound_cert_service.h48
-rw-r--r--net/ssl/server_bound_cert_service_unittest.cc289
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