Adding DiskBasedCertCache to HttpCache (+UMA).

Review URL: https://codereview.chromium.org/356953003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@282429 0039d316-1c4b-4281-b951-d872f2087c98
parent be22c889
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include "net/base/net_errors.h" #include "net/base/net_errors.h"
#include "net/base/upload_data_stream.h" #include "net/base/upload_data_stream.h"
#include "net/disk_cache/disk_cache.h" #include "net/disk_cache/disk_cache.h"
#include "net/http/disk_based_cert_cache.h"
#include "net/http/disk_cache_based_quic_server_info.h" #include "net/http/disk_cache_based_quic_server_info.h"
#include "net/http/http_cache_transaction.h" #include "net/http/http_cache_transaction.h"
#include "net/http/http_network_layer.h" #include "net/http/http_network_layer.h"
...@@ -45,6 +46,11 @@ ...@@ -45,6 +46,11 @@
namespace { namespace {
bool UseCertCache() {
return base::FieldTrialList::FindFullName("CertCacheTrial") ==
"ExperimentGroup";
}
// Adaptor to delete a file on a worker thread. // Adaptor to delete a file on a worker thread.
void DeletePath(base::FilePath path) { void DeletePath(base::FilePath path) {
base::DeleteFile(path, false); base::DeleteFile(path, false);
...@@ -346,6 +352,7 @@ HttpCache::~HttpCache() { ...@@ -346,6 +352,7 @@ HttpCache::~HttpCache() {
// Before deleting pending_ops_, we have to make sure that the disk cache is // Before deleting pending_ops_, we have to make sure that the disk cache is
// done with said operations, or it will attempt to use deleted data. // done with said operations, or it will attempt to use deleted data.
cert_cache_.reset();
disk_cache_.reset(); disk_cache_.reset();
PendingOpsMap::iterator pending_it = pending_ops_.begin(); PendingOpsMap::iterator pending_it = pending_ops_.begin();
...@@ -1168,8 +1175,11 @@ void HttpCache::OnBackendCreated(int result, PendingOp* pending_op) { ...@@ -1168,8 +1175,11 @@ void HttpCache::OnBackendCreated(int result, PendingOp* pending_op) {
// work items. The first call saves the backend and releases the factory, // work items. The first call saves the backend and releases the factory,
// and the last call clears building_backend_. // and the last call clears building_backend_.
backend_factory_.reset(); // Reclaim memory. backend_factory_.reset(); // Reclaim memory.
if (result == OK) if (result == OK) {
disk_cache_ = pending_op->backend.Pass(); disk_cache_ = pending_op->backend.Pass();
if (UseCertCache())
cert_cache_.reset(new DiskBasedCertCache(disk_cache_.get()));
}
} }
if (!pending_op->pending_queue.empty()) { if (!pending_op->pending_queue.empty()) {
......
...@@ -44,6 +44,7 @@ class Entry; ...@@ -44,6 +44,7 @@ class Entry;
namespace net { namespace net {
class CertVerifier; class CertVerifier;
class DiskBasedCertCache;
class HostResolver; class HostResolver;
class HttpAuthHandlerFactory; class HttpAuthHandlerFactory;
class HttpNetworkSession; class HttpNetworkSession;
...@@ -142,6 +143,8 @@ class NET_EXPORT HttpCache : public HttpTransactionFactory, ...@@ -142,6 +143,8 @@ class NET_EXPORT HttpCache : public HttpTransactionFactory,
HttpTransactionFactory* network_layer() { return network_layer_.get(); } HttpTransactionFactory* network_layer() { return network_layer_.get(); }
DiskBasedCertCache* cert_cache() const { return cert_cache_.get(); }
// Retrieves the cache backend for this HttpCache instance. If the backend // Retrieves the cache backend for this HttpCache instance. If the backend
// is not initialized yet, this method will initialize it. The return value is // is not initialized yet, this method will initialize it. The return value is
// a network error code, and it could be ERR_IO_PENDING, in which case the // a network error code, and it could be ERR_IO_PENDING, in which case the
...@@ -405,6 +408,8 @@ class NET_EXPORT HttpCache : public HttpTransactionFactory, ...@@ -405,6 +408,8 @@ class NET_EXPORT HttpCache : public HttpTransactionFactory,
scoped_ptr<disk_cache::Backend> disk_cache_; scoped_ptr<disk_cache::Backend> disk_cache_;
scoped_ptr<DiskBasedCertCache> cert_cache_;
// The set of active entries indexed by cache key. // The set of active entries indexed by cache key.
ActiveEntriesMap active_entries_; ActiveEntriesMap active_entries_;
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include "net/base/upload_data_stream.h" #include "net/base/upload_data_stream.h"
#include "net/cert/cert_status_flags.h" #include "net/cert/cert_status_flags.h"
#include "net/disk_cache/disk_cache.h" #include "net/disk_cache/disk_cache.h"
#include "net/http/disk_based_cert_cache.h"
#include "net/http/http_network_session.h" #include "net/http/http_network_session.h"
#include "net/http/http_request_info.h" #include "net/http/http_request_info.h"
#include "net/http/http_response_headers.h" #include "net/http/http_response_headers.h"
...@@ -47,6 +48,111 @@ using base::TimeTicks; ...@@ -47,6 +48,111 @@ using base::TimeTicks;
namespace { namespace {
// Stores data relevant to the statistics of writing and reading entire
// certificate chains using DiskBasedCertCache. |num_pending_ops| is the number
// of certificates in the chain that have pending operations in the
// DiskBasedCertCache. |start_time| is the time that the read and write
// commands began being issued to the DiskBasedCertCache.
// TODO(brandonsalmon): Remove this when it is no longer necessary to
// collect data.
class SharedChainData : public base::RefCounted<SharedChainData> {
public:
SharedChainData(int num_ops, TimeTicks start)
: num_pending_ops(num_ops), start_time(start) {}
int num_pending_ops;
TimeTicks start_time;
private:
friend class base::RefCounted<SharedChainData>;
~SharedChainData() {}
DISALLOW_COPY_AND_ASSIGN(SharedChainData);
};
// Used to obtain a cache entry key for an OSCertHandle.
// TODO(brandonsalmon): Remove this when cache keys are stored
// and no longer have to be recomputed to retrieve the OSCertHandle
// from the disk.
std::string GetCacheKeyForCert(net::X509Certificate::OSCertHandle cert_handle) {
net::SHA1HashValue fingerprint =
net::X509Certificate::CalculateFingerprint(cert_handle);
return "cert:" +
base::HexEncode(fingerprint.data, arraysize(fingerprint.data));
}
// |dist_from_root| indicates the position of the read certificate in the
// certificate chain, 0 indicating it is the root. |is_leaf| indicates
// whether or not the read certificate was the leaf of the chain.
// |shared_chain_data| contains data shared by each certificate in
// the chain.
void OnCertReadIOComplete(
int dist_from_root,
bool is_leaf,
const scoped_refptr<SharedChainData>& shared_chain_data,
net::X509Certificate::OSCertHandle cert_handle) {
// If |num_pending_ops| is one, this was the last pending read operation
// for this chain of certificates. The total time used to read the chain
// can be calculated by subtracting the starting time from Now().
shared_chain_data->num_pending_ops--;
if (!shared_chain_data->num_pending_ops) {
const TimeDelta read_chain_wait =
TimeTicks::Now() - shared_chain_data->start_time;
UMA_HISTOGRAM_CUSTOM_TIMES("DiskBasedCertCache.ChainReadTime",
read_chain_wait,
base::TimeDelta::FromMilliseconds(1),
base::TimeDelta::FromMinutes(10),
50);
}
bool success = (cert_handle != NULL);
if (is_leaf)
UMA_HISTOGRAM_BOOLEAN("DiskBasedCertCache.CertIoReadSuccessLeaf", success);
if (success)
UMA_HISTOGRAM_CUSTOM_COUNTS(
"DiskBasedCertCache.CertIoReadSuccess", dist_from_root, 0, 10, 7);
else
UMA_HISTOGRAM_CUSTOM_COUNTS(
"DiskBasedCertCache.CertIoReadFailure", dist_from_root, 0, 10, 7);
}
// |dist_from_root| indicates the position of the written certificate in the
// certificate chain, 0 indicating it is the root. |is_leaf| indicates
// whether or not the written certificate was the leaf of the chain.
// |shared_chain_data| contains data shared by each certificate in
// the chain.
void OnCertWriteIOComplete(
int dist_from_root,
bool is_leaf,
const scoped_refptr<SharedChainData>& shared_chain_data,
const std::string& key) {
// If |num_pending_ops| is one, this was the last pending write operation
// for this chain of certificates. The total time used to write the chain
// can be calculated by subtracting the starting time from Now().
shared_chain_data->num_pending_ops--;
if (!shared_chain_data->num_pending_ops) {
const TimeDelta write_chain_wait =
TimeTicks::Now() - shared_chain_data->start_time;
UMA_HISTOGRAM_CUSTOM_TIMES("DiskBasedCertCache.ChainWriteTime",
write_chain_wait,
base::TimeDelta::FromMilliseconds(1),
base::TimeDelta::FromMinutes(10),
50);
}
bool success = !key.empty();
if (is_leaf)
UMA_HISTOGRAM_BOOLEAN("DiskBasedCertCache.CertIoWriteSuccessLeaf", success);
if (success)
UMA_HISTOGRAM_CUSTOM_COUNTS(
"DiskBasedCertCache.CertIoWriteSuccess", dist_from_root, 0, 10, 7);
else
UMA_HISTOGRAM_CUSTOM_COUNTS(
"DiskBasedCertCache.CertIoWriteFailure", dist_from_root, 0, 10, 7);
}
// From http://tools.ietf.org/html/draft-ietf-httpbis-p6-cache-21#section-6 // From http://tools.ietf.org/html/draft-ietf-httpbis-p6-cache-21#section-6
// a "non-error response" is one with a 2xx (Successful) or 3xx // a "non-error response" is one with a 2xx (Successful) or 3xx
// (Redirection) status code. // (Redirection) status code.
...@@ -1495,6 +1601,10 @@ int HttpCache::Transaction::DoCacheReadResponseComplete(int result) { ...@@ -1495,6 +1601,10 @@ int HttpCache::Transaction::DoCacheReadResponseComplete(int result) {
return OnCacheReadError(result, true); return OnCacheReadError(result, true);
} }
// cert_cache() will be null if the CertCacheTrial field trial is disabled.
if (cache_->cert_cache() && response_.ssl_info.is_valid())
ReadCertChain();
// Some resources may have slipped in as truncated when they're not. // Some resources may have slipped in as truncated when they're not.
int current_size = entry_->disk_entry->GetDataSize(kResponseContentIndex); int current_size = entry_->disk_entry->GetDataSize(kResponseContentIndex);
if (response_.headers->GetContentLength() == current_size) if (response_.headers->GetContentLength() == current_size)
...@@ -1707,6 +1817,62 @@ int HttpCache::Transaction::DoCacheWriteDataComplete(int result) { ...@@ -1707,6 +1817,62 @@ int HttpCache::Transaction::DoCacheWriteDataComplete(int result) {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void HttpCache::Transaction::ReadCertChain() {
std::string key =
GetCacheKeyForCert(response_.ssl_info.cert->os_cert_handle());
const X509Certificate::OSCertHandles& intermediates =
response_.ssl_info.cert->GetIntermediateCertificates();
int dist_from_root = intermediates.size();
scoped_refptr<SharedChainData> shared_chain_data(
new SharedChainData(intermediates.size() + 1, TimeTicks::Now()));
cache_->cert_cache()->Get(key,
base::Bind(&OnCertReadIOComplete,
dist_from_root,
true /* is leaf */,
shared_chain_data));
for (X509Certificate::OSCertHandles::const_iterator it =
intermediates.begin();
it != intermediates.end();
++it) {
--dist_from_root;
key = GetCacheKeyForCert(*it);
cache_->cert_cache()->Get(key,
base::Bind(&OnCertReadIOComplete,
dist_from_root,
false /* is not leaf */,
shared_chain_data));
}
DCHECK_EQ(0, dist_from_root);
}
void HttpCache::Transaction::WriteCertChain() {
const X509Certificate::OSCertHandles& intermediates =
response_.ssl_info.cert->GetIntermediateCertificates();
int dist_from_root = intermediates.size();
scoped_refptr<SharedChainData> shared_chain_data(
new SharedChainData(intermediates.size() + 1, TimeTicks::Now()));
cache_->cert_cache()->Set(response_.ssl_info.cert->os_cert_handle(),
base::Bind(&OnCertWriteIOComplete,
dist_from_root,
true /* is leaf */,
shared_chain_data));
for (X509Certificate::OSCertHandles::const_iterator it =
intermediates.begin();
it != intermediates.end();
++it) {
--dist_from_root;
cache_->cert_cache()->Set(*it,
base::Bind(&OnCertWriteIOComplete,
dist_from_root,
false /* is not leaf */,
shared_chain_data));
}
DCHECK_EQ(0, dist_from_root);
}
void HttpCache::Transaction::SetRequest(const BoundNetLog& net_log, void HttpCache::Transaction::SetRequest(const BoundNetLog& net_log,
const HttpRequestInfo* request) { const HttpRequestInfo* request) {
net_log_ = net_log; net_log_ = net_log;
...@@ -2327,6 +2493,10 @@ int HttpCache::Transaction::WriteResponseInfoToEntry(bool truncated) { ...@@ -2327,6 +2493,10 @@ int HttpCache::Transaction::WriteResponseInfoToEntry(bool truncated) {
return OK; return OK;
} }
// cert_cache() will be null if the CertCacheTrial field trial is disabled.
if (cache_->cert_cache() && response_.ssl_info.is_valid())
WriteCertChain();
// When writing headers, we normally only write the non-transient // When writing headers, we normally only write the non-transient
// headers; when in record mode, record everything. // headers; when in record mode, record everything.
bool skip_transient_headers = (cache_->mode() != RECORD); bool skip_transient_headers = (cache_->mode() != RECORD);
......
...@@ -267,6 +267,11 @@ class HttpCache::Transaction : public HttpTransaction { ...@@ -267,6 +267,11 @@ class HttpCache::Transaction : public HttpTransaction {
int DoCacheWriteData(int num_bytes); int DoCacheWriteData(int num_bytes);
int DoCacheWriteDataComplete(int result); int DoCacheWriteDataComplete(int result);
// These functions are involved in a field trial testing storing certificates
// in seperate entries from the HttpResponseInfo.
void ReadCertChain();
void WriteCertChain();
// Sets request_ and fields derived from it. // Sets request_ and fields derived from it.
void SetRequest(const BoundNetLog& net_log, const HttpRequestInfo* request); void SetRequest(const BoundNetLog& net_log, const HttpRequestInfo* request);
......
...@@ -3622,6 +3622,51 @@ Therefore, the affected-histogram name has to have at least one dot in it. ...@@ -3622,6 +3622,51 @@ Therefore, the affected-histogram name has to have at least one dot in it.
</summary> </summary>
</histogram> </histogram>
<histogram name="DiskBasedCertCache.CertIo" enum="CertificateChainPosition">
<owner>brandonsalmon@chromium.org</owner>
<summary>
Records information about DiskBasedCertCache operations with respect to
certificate chain positions. Zero indicates that a certificate is root, one
indicates that it is the first intermediate certificate, etc.
</summary>
</histogram>
<histogram name="DiskBasedCertCache.CertIoReadSuccessLeaf"
enum="BooleanSuccess">
<owner>brandonsalmon@chromium.org</owner>
<summary>
Whether or not the leaf certificate of a certificate chain was successfuly
read from the disk cache.
</summary>
</histogram>
<histogram name="DiskBasedCertCache.CertIoWriteSuccessLeaf"
enum="BooleanSuccess">
<owner>brandonsalmon@chromium.org</owner>
<summary>
Whether or not the leaf certificate of a certificate chain was successfully
written to the disk cache.
</summary>
</histogram>
<histogram name="DiskBasedCertCache.ChainReadTime" units="milliseconds">
<owner>brandonsalmon@chromium.org</owner>
<summary>
Measures the wall clock time spent reading a certificate chain. The starting
time is when the read command is issued, and the ending time is when all of
the certificates in the chain have been read into memory.
</summary>
</histogram>
<histogram name="DiskBasedCertCache.ChainWriteTime" units="milliseconds">
<owner>brandonsalmon@chromium.org</owner>
<summary>
Measures the wall clock time spent writing a certificate chain to disk. The
starting time is when the write command is issued, and the ending time is
when all the certificates in the chain have been written to disk.
</summary>
</histogram>
<histogram name="DiskCache.0.FilesAge" units="hours"> <histogram name="DiskCache.0.FilesAge" units="hours">
<owner>rvargas@chromium.org</owner> <owner>rvargas@chromium.org</owner>
<summary>The age of the cache's files (wall time).</summary> <summary>The age of the cache's files (wall time).</summary>
...@@ -35789,6 +35834,10 @@ Therefore, the affected-histogram name has to have at least one dot in it. ...@@ -35789,6 +35834,10 @@ Therefore, the affected-histogram name has to have at least one dot in it.
<int value="20" label="FutureCat (&gt;10.10), 8-bit (?)"/> <int value="20" label="FutureCat (&gt;10.10), 8-bit (?)"/>
</enum> </enum>
<enum name="CertificateChainPosition" type="int">
<int value="0" label="Root Certificate"/>
</enum>
<enum name="ChannelLayout" type="int"> <enum name="ChannelLayout" type="int">
<int value="0" label="CHANNEL_LAYOUT_NONE"/> <int value="0" label="CHANNEL_LAYOUT_NONE"/>
<int value="1" label="CHANNEL_LAYOUT_UNSUPPORTED"/> <int value="1" label="CHANNEL_LAYOUT_UNSUPPORTED"/>
...@@ -48172,6 +48221,18 @@ Therefore, the affected-histogram name has to have at least one dot in it. ...@@ -48172,6 +48221,18 @@ Therefore, the affected-histogram name has to have at least one dot in it.
<affected-histogram name="CertificateType2.NonBR"/> <affected-histogram name="CertificateType2.NonBR"/>
</histogram_suffixes> </histogram_suffixes>
<histogram_suffixes name="CertIo" separator="">
<suffix name="ReadSuccess"
label="success rate of reading a certificate from the disk cache"/>
<suffix name="ReadFailure"
label="failure rate of reading a certificate from the disk cache"/>
<suffix name="WriteSuccess"
label="success rate of writing a certificate to the disk cache"/>
<suffix name="WriteFailure"
label="failure rate of writing a certificate to the disk cache"/>
<affected-histogram name="DiskBasedCertCache.CertIo"/>
</histogram_suffixes>
<histogram_suffixes name="CloudPrintRequests" separator="."> <histogram_suffixes name="CloudPrintRequests" separator=".">
<suffix name="Register" label="Register request"/> <suffix name="Register" label="Register request"/>
<suffix name="UpdatePrinter" label="Update printer request"/> <suffix name="UpdatePrinter" label="Update printer request"/>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment