|  | // Copyright 2014 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "net/ssl/default_channel_id_store.h" | 
|  |  | 
|  | #include "base/bind.h" | 
|  | #include "base/message_loop/message_loop.h" | 
|  | #include "base/metrics/histogram.h" | 
|  | #include "net/base/net_errors.h" | 
|  |  | 
|  | namespace net { | 
|  |  | 
|  | // -------------------------------------------------------------------------- | 
|  | // Task | 
|  | class DefaultChannelIDStore::Task { | 
|  | public: | 
|  | virtual ~Task(); | 
|  |  | 
|  | // Runs the task and invokes the client callback on the thread that | 
|  | // originally constructed the task. | 
|  | virtual void Run(DefaultChannelIDStore* store) = 0; | 
|  |  | 
|  | protected: | 
|  | void InvokeCallback(base::Closure callback) const; | 
|  | }; | 
|  |  | 
|  | DefaultChannelIDStore::Task::~Task() { | 
|  | } | 
|  |  | 
|  | void DefaultChannelIDStore::Task::InvokeCallback( | 
|  | base::Closure callback) const { | 
|  | if (!callback.is_null()) | 
|  | callback.Run(); | 
|  | } | 
|  |  | 
|  | // -------------------------------------------------------------------------- | 
|  | // GetChannelIDTask | 
|  | class DefaultChannelIDStore::GetChannelIDTask | 
|  | : public DefaultChannelIDStore::Task { | 
|  | public: | 
|  | GetChannelIDTask(const std::string& server_identifier, | 
|  | const GetChannelIDCallback& callback); | 
|  | ~GetChannelIDTask() override; | 
|  | void Run(DefaultChannelIDStore* store) override; | 
|  |  | 
|  | private: | 
|  | std::string server_identifier_; | 
|  | GetChannelIDCallback callback_; | 
|  | }; | 
|  |  | 
|  | DefaultChannelIDStore::GetChannelIDTask::GetChannelIDTask( | 
|  | const std::string& server_identifier, | 
|  | const GetChannelIDCallback& callback) | 
|  | : server_identifier_(server_identifier), | 
|  | callback_(callback) { | 
|  | } | 
|  |  | 
|  | DefaultChannelIDStore::GetChannelIDTask::~GetChannelIDTask() { | 
|  | } | 
|  |  | 
|  | void DefaultChannelIDStore::GetChannelIDTask::Run( | 
|  | DefaultChannelIDStore* store) { | 
|  | base::Time expiration_time; | 
|  | std::string private_key_result; | 
|  | std::string cert_result; | 
|  | int err = store->GetChannelID( | 
|  | server_identifier_, &expiration_time, &private_key_result, | 
|  | &cert_result, GetChannelIDCallback()); | 
|  | DCHECK(err != ERR_IO_PENDING); | 
|  |  | 
|  | InvokeCallback(base::Bind(callback_, err, server_identifier_, | 
|  | expiration_time, private_key_result, cert_result)); | 
|  | } | 
|  |  | 
|  | // -------------------------------------------------------------------------- | 
|  | // SetChannelIDTask | 
|  | class DefaultChannelIDStore::SetChannelIDTask | 
|  | : public DefaultChannelIDStore::Task { | 
|  | public: | 
|  | SetChannelIDTask(const std::string& server_identifier, | 
|  | base::Time creation_time, | 
|  | base::Time expiration_time, | 
|  | const std::string& private_key, | 
|  | const std::string& cert); | 
|  | ~SetChannelIDTask() override; | 
|  | void Run(DefaultChannelIDStore* store) override; | 
|  |  | 
|  | private: | 
|  | std::string server_identifier_; | 
|  | base::Time creation_time_; | 
|  | base::Time expiration_time_; | 
|  | std::string private_key_; | 
|  | std::string cert_; | 
|  | }; | 
|  |  | 
|  | DefaultChannelIDStore::SetChannelIDTask::SetChannelIDTask( | 
|  | const std::string& server_identifier, | 
|  | base::Time creation_time, | 
|  | base::Time expiration_time, | 
|  | const std::string& private_key, | 
|  | const std::string& cert) | 
|  | : server_identifier_(server_identifier), | 
|  | creation_time_(creation_time), | 
|  | expiration_time_(expiration_time), | 
|  | private_key_(private_key), | 
|  | cert_(cert) { | 
|  | } | 
|  |  | 
|  | DefaultChannelIDStore::SetChannelIDTask::~SetChannelIDTask() { | 
|  | } | 
|  |  | 
|  | void DefaultChannelIDStore::SetChannelIDTask::Run( | 
|  | DefaultChannelIDStore* store) { | 
|  | store->SyncSetChannelID(server_identifier_, creation_time_, | 
|  | expiration_time_, private_key_, cert_); | 
|  | } | 
|  |  | 
|  | // -------------------------------------------------------------------------- | 
|  | // DeleteChannelIDTask | 
|  | class DefaultChannelIDStore::DeleteChannelIDTask | 
|  | : public DefaultChannelIDStore::Task { | 
|  | public: | 
|  | DeleteChannelIDTask(const std::string& server_identifier, | 
|  | const base::Closure& callback); | 
|  | ~DeleteChannelIDTask() override; | 
|  | void Run(DefaultChannelIDStore* store) override; | 
|  |  | 
|  | private: | 
|  | std::string server_identifier_; | 
|  | base::Closure callback_; | 
|  | }; | 
|  |  | 
|  | DefaultChannelIDStore::DeleteChannelIDTask:: | 
|  | DeleteChannelIDTask( | 
|  | const std::string& server_identifier, | 
|  | const base::Closure& callback) | 
|  | : server_identifier_(server_identifier), | 
|  | callback_(callback) { | 
|  | } | 
|  |  | 
|  | DefaultChannelIDStore::DeleteChannelIDTask:: | 
|  | ~DeleteChannelIDTask() { | 
|  | } | 
|  |  | 
|  | void DefaultChannelIDStore::DeleteChannelIDTask::Run( | 
|  | DefaultChannelIDStore* store) { | 
|  | store->SyncDeleteChannelID(server_identifier_); | 
|  |  | 
|  | InvokeCallback(callback_); | 
|  | } | 
|  |  | 
|  | // -------------------------------------------------------------------------- | 
|  | // DeleteAllCreatedBetweenTask | 
|  | class DefaultChannelIDStore::DeleteAllCreatedBetweenTask | 
|  | : public DefaultChannelIDStore::Task { | 
|  | public: | 
|  | DeleteAllCreatedBetweenTask(base::Time delete_begin, | 
|  | base::Time delete_end, | 
|  | const base::Closure& callback); | 
|  | ~DeleteAllCreatedBetweenTask() override; | 
|  | void Run(DefaultChannelIDStore* store) override; | 
|  |  | 
|  | private: | 
|  | base::Time delete_begin_; | 
|  | base::Time delete_end_; | 
|  | base::Closure callback_; | 
|  | }; | 
|  |  | 
|  | DefaultChannelIDStore::DeleteAllCreatedBetweenTask:: | 
|  | DeleteAllCreatedBetweenTask( | 
|  | base::Time delete_begin, | 
|  | base::Time delete_end, | 
|  | const base::Closure& callback) | 
|  | : delete_begin_(delete_begin), | 
|  | delete_end_(delete_end), | 
|  | callback_(callback) { | 
|  | } | 
|  |  | 
|  | DefaultChannelIDStore::DeleteAllCreatedBetweenTask:: | 
|  | ~DeleteAllCreatedBetweenTask() { | 
|  | } | 
|  |  | 
|  | void DefaultChannelIDStore::DeleteAllCreatedBetweenTask::Run( | 
|  | DefaultChannelIDStore* store) { | 
|  | store->SyncDeleteAllCreatedBetween(delete_begin_, delete_end_); | 
|  |  | 
|  | InvokeCallback(callback_); | 
|  | } | 
|  |  | 
|  | // -------------------------------------------------------------------------- | 
|  | // GetAllChannelIDsTask | 
|  | class DefaultChannelIDStore::GetAllChannelIDsTask | 
|  | : public DefaultChannelIDStore::Task { | 
|  | public: | 
|  | explicit GetAllChannelIDsTask(const GetChannelIDListCallback& callback); | 
|  | ~GetAllChannelIDsTask() override; | 
|  | void Run(DefaultChannelIDStore* store) override; | 
|  |  | 
|  | private: | 
|  | std::string server_identifier_; | 
|  | GetChannelIDListCallback callback_; | 
|  | }; | 
|  |  | 
|  | DefaultChannelIDStore::GetAllChannelIDsTask:: | 
|  | GetAllChannelIDsTask(const GetChannelIDListCallback& callback) | 
|  | : callback_(callback) { | 
|  | } | 
|  |  | 
|  | DefaultChannelIDStore::GetAllChannelIDsTask:: | 
|  | ~GetAllChannelIDsTask() { | 
|  | } | 
|  |  | 
|  | void DefaultChannelIDStore::GetAllChannelIDsTask::Run( | 
|  | DefaultChannelIDStore* store) { | 
|  | ChannelIDList cert_list; | 
|  | store->SyncGetAllChannelIDs(&cert_list); | 
|  |  | 
|  | InvokeCallback(base::Bind(callback_, cert_list)); | 
|  | } | 
|  |  | 
|  | // -------------------------------------------------------------------------- | 
|  | // DefaultChannelIDStore | 
|  |  | 
|  | DefaultChannelIDStore::DefaultChannelIDStore( | 
|  | PersistentStore* store) | 
|  | : initialized_(false), | 
|  | loaded_(false), | 
|  | store_(store), | 
|  | weak_ptr_factory_(this) {} | 
|  |  | 
|  | int DefaultChannelIDStore::GetChannelID( | 
|  | const std::string& server_identifier, | 
|  | base::Time* expiration_time, | 
|  | std::string* private_key_result, | 
|  | std::string* cert_result, | 
|  | const GetChannelIDCallback& callback) { | 
|  | DCHECK(CalledOnValidThread()); | 
|  | InitIfNecessary(); | 
|  |  | 
|  | if (!loaded_) { | 
|  | EnqueueTask(scoped_ptr<Task>( | 
|  | new GetChannelIDTask(server_identifier, callback))); | 
|  | return ERR_IO_PENDING; | 
|  | } | 
|  |  | 
|  | ChannelIDMap::iterator it = channel_ids_.find(server_identifier); | 
|  |  | 
|  | if (it == channel_ids_.end()) | 
|  | return ERR_FILE_NOT_FOUND; | 
|  |  | 
|  | ChannelID* channel_id = it->second; | 
|  | *expiration_time = channel_id->expiration_time(); | 
|  | *private_key_result = channel_id->private_key(); | 
|  | *cert_result = channel_id->cert(); | 
|  |  | 
|  | return OK; | 
|  | } | 
|  |  | 
|  | void DefaultChannelIDStore::SetChannelID( | 
|  | const std::string& server_identifier, | 
|  | base::Time creation_time, | 
|  | base::Time expiration_time, | 
|  | const std::string& private_key, | 
|  | const std::string& cert) { | 
|  | RunOrEnqueueTask(scoped_ptr<Task>(new SetChannelIDTask( | 
|  | server_identifier, creation_time, expiration_time, private_key, | 
|  | cert))); | 
|  | } | 
|  |  | 
|  | void DefaultChannelIDStore::DeleteChannelID( | 
|  | const std::string& server_identifier, | 
|  | const base::Closure& callback) { | 
|  | RunOrEnqueueTask(scoped_ptr<Task>( | 
|  | new DeleteChannelIDTask(server_identifier, callback))); | 
|  | } | 
|  |  | 
|  | void DefaultChannelIDStore::DeleteAllCreatedBetween( | 
|  | base::Time delete_begin, | 
|  | base::Time delete_end, | 
|  | const base::Closure& callback) { | 
|  | RunOrEnqueueTask(scoped_ptr<Task>( | 
|  | new DeleteAllCreatedBetweenTask(delete_begin, delete_end, callback))); | 
|  | } | 
|  |  | 
|  | void DefaultChannelIDStore::DeleteAll( | 
|  | const base::Closure& callback) { | 
|  | DeleteAllCreatedBetween(base::Time(), base::Time(), callback); | 
|  | } | 
|  |  | 
|  | void DefaultChannelIDStore::GetAllChannelIDs( | 
|  | const GetChannelIDListCallback& callback) { | 
|  | RunOrEnqueueTask(scoped_ptr<Task>(new GetAllChannelIDsTask(callback))); | 
|  | } | 
|  |  | 
|  | int DefaultChannelIDStore::GetChannelIDCount() { | 
|  | DCHECK(CalledOnValidThread()); | 
|  |  | 
|  | return channel_ids_.size(); | 
|  | } | 
|  |  | 
|  | void DefaultChannelIDStore::SetForceKeepSessionState() { | 
|  | DCHECK(CalledOnValidThread()); | 
|  | InitIfNecessary(); | 
|  |  | 
|  | if (store_.get()) | 
|  | store_->SetForceKeepSessionState(); | 
|  | } | 
|  |  | 
|  | DefaultChannelIDStore::~DefaultChannelIDStore() { | 
|  | DeleteAllInMemory(); | 
|  | } | 
|  |  | 
|  | void DefaultChannelIDStore::DeleteAllInMemory() { | 
|  | DCHECK(CalledOnValidThread()); | 
|  |  | 
|  | for (ChannelIDMap::iterator it = channel_ids_.begin(); | 
|  | it != channel_ids_.end(); ++it) { | 
|  | delete it->second; | 
|  | } | 
|  | channel_ids_.clear(); | 
|  | } | 
|  |  | 
|  | void DefaultChannelIDStore::InitStore() { | 
|  | DCHECK(CalledOnValidThread()); | 
|  | DCHECK(store_.get()) << "Store must exist to initialize"; | 
|  | DCHECK(!loaded_); | 
|  |  | 
|  | store_->Load(base::Bind(&DefaultChannelIDStore::OnLoaded, | 
|  | weak_ptr_factory_.GetWeakPtr())); | 
|  | } | 
|  |  | 
|  | void DefaultChannelIDStore::OnLoaded( | 
|  | scoped_ptr<ScopedVector<ChannelID> > channel_ids) { | 
|  | DCHECK(CalledOnValidThread()); | 
|  |  | 
|  | for (std::vector<ChannelID*>::const_iterator it = channel_ids->begin(); | 
|  | it != channel_ids->end(); ++it) { | 
|  | DCHECK(channel_ids_.find((*it)->server_identifier()) == | 
|  | channel_ids_.end()); | 
|  | channel_ids_[(*it)->server_identifier()] = *it; | 
|  | } | 
|  | channel_ids->weak_clear(); | 
|  |  | 
|  | loaded_ = true; | 
|  |  | 
|  | base::TimeDelta wait_time; | 
|  | if (!waiting_tasks_.empty()) | 
|  | wait_time = base::TimeTicks::Now() - waiting_tasks_start_time_; | 
|  | DVLOG(1) << "Task delay " << wait_time.InMilliseconds(); | 
|  | UMA_HISTOGRAM_CUSTOM_TIMES("DomainBoundCerts.TaskMaxWaitTime", | 
|  | wait_time, | 
|  | base::TimeDelta::FromMilliseconds(1), | 
|  | base::TimeDelta::FromMinutes(1), | 
|  | 50); | 
|  | UMA_HISTOGRAM_COUNTS_100("DomainBoundCerts.TaskWaitCount", | 
|  | waiting_tasks_.size()); | 
|  |  | 
|  |  | 
|  | for (ScopedVector<Task>::iterator i = waiting_tasks_.begin(); | 
|  | i != waiting_tasks_.end(); ++i) | 
|  | (*i)->Run(this); | 
|  | waiting_tasks_.clear(); | 
|  | } | 
|  |  | 
|  | void DefaultChannelIDStore::SyncSetChannelID( | 
|  | const std::string& server_identifier, | 
|  | base::Time creation_time, | 
|  | base::Time expiration_time, | 
|  | const std::string& private_key, | 
|  | const std::string& cert) { | 
|  | DCHECK(CalledOnValidThread()); | 
|  | DCHECK(loaded_); | 
|  |  | 
|  | InternalDeleteChannelID(server_identifier); | 
|  | InternalInsertChannelID( | 
|  | server_identifier, | 
|  | new ChannelID( | 
|  | server_identifier, creation_time, expiration_time, private_key, | 
|  | cert)); | 
|  | } | 
|  |  | 
|  | void DefaultChannelIDStore::SyncDeleteChannelID( | 
|  | const std::string& server_identifier) { | 
|  | DCHECK(CalledOnValidThread()); | 
|  | DCHECK(loaded_); | 
|  | InternalDeleteChannelID(server_identifier); | 
|  | } | 
|  |  | 
|  | void DefaultChannelIDStore::SyncDeleteAllCreatedBetween( | 
|  | base::Time delete_begin, | 
|  | base::Time delete_end) { | 
|  | DCHECK(CalledOnValidThread()); | 
|  | DCHECK(loaded_); | 
|  | for (ChannelIDMap::iterator it = channel_ids_.begin(); | 
|  | it != channel_ids_.end();) { | 
|  | ChannelIDMap::iterator cur = it; | 
|  | ++it; | 
|  | ChannelID* channel_id = cur->second; | 
|  | if ((delete_begin.is_null() || | 
|  | channel_id->creation_time() >= delete_begin) && | 
|  | (delete_end.is_null() || channel_id->creation_time() < delete_end)) { | 
|  | if (store_.get()) | 
|  | store_->DeleteChannelID(*channel_id); | 
|  | delete channel_id; | 
|  | channel_ids_.erase(cur); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void DefaultChannelIDStore::SyncGetAllChannelIDs( | 
|  | ChannelIDList* channel_id_list) { | 
|  | DCHECK(CalledOnValidThread()); | 
|  | DCHECK(loaded_); | 
|  | for (ChannelIDMap::iterator it = channel_ids_.begin(); | 
|  | it != channel_ids_.end(); ++it) | 
|  | channel_id_list->push_back(*it->second); | 
|  | } | 
|  |  | 
|  | void DefaultChannelIDStore::EnqueueTask(scoped_ptr<Task> task) { | 
|  | DCHECK(CalledOnValidThread()); | 
|  | DCHECK(!loaded_); | 
|  | if (waiting_tasks_.empty()) | 
|  | waiting_tasks_start_time_ = base::TimeTicks::Now(); | 
|  | waiting_tasks_.push_back(task.release()); | 
|  | } | 
|  |  | 
|  | void DefaultChannelIDStore::RunOrEnqueueTask(scoped_ptr<Task> task) { | 
|  | DCHECK(CalledOnValidThread()); | 
|  | InitIfNecessary(); | 
|  |  | 
|  | if (!loaded_) { | 
|  | EnqueueTask(task.Pass()); | 
|  | return; | 
|  | } | 
|  |  | 
|  | task->Run(this); | 
|  | } | 
|  |  | 
|  | void DefaultChannelIDStore::InternalDeleteChannelID( | 
|  | const std::string& server_identifier) { | 
|  | DCHECK(CalledOnValidThread()); | 
|  | DCHECK(loaded_); | 
|  |  | 
|  | ChannelIDMap::iterator it = channel_ids_.find(server_identifier); | 
|  | if (it == channel_ids_.end()) | 
|  | return;  // There is nothing to delete. | 
|  |  | 
|  | ChannelID* channel_id = it->second; | 
|  | if (store_.get()) | 
|  | store_->DeleteChannelID(*channel_id); | 
|  | channel_ids_.erase(it); | 
|  | delete channel_id; | 
|  | } | 
|  |  | 
|  | void DefaultChannelIDStore::InternalInsertChannelID( | 
|  | const std::string& server_identifier, | 
|  | ChannelID* channel_id) { | 
|  | DCHECK(CalledOnValidThread()); | 
|  | DCHECK(loaded_); | 
|  |  | 
|  | if (store_.get()) | 
|  | store_->AddChannelID(*channel_id); | 
|  | channel_ids_[server_identifier] = channel_id; | 
|  | } | 
|  |  | 
|  | DefaultChannelIDStore::PersistentStore::PersistentStore() {} | 
|  |  | 
|  | DefaultChannelIDStore::PersistentStore::~PersistentStore() {} | 
|  |  | 
|  | }  // namespace net |