Chromium Code Reviews| Index: net/http/http_cache_transaction.cc |
| diff --git a/net/http/http_cache_transaction.cc b/net/http/http_cache_transaction.cc |
| index bf791118be88b704966c7d3a886d5fcc4c924f63..aba792330bf4dcf92e78deeb88ff44ff88fc663f 100644 |
| --- a/net/http/http_cache_transaction.cc |
| +++ b/net/http/http_cache_transaction.cc |
| @@ -32,6 +32,7 @@ |
| #include "net/base/upload_data_stream.h" |
| #include "net/cert/cert_status_flags.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_request_info.h" |
| #include "net/http/http_response_headers.h" |
| @@ -47,6 +48,111 @@ using base::TimeTicks; |
| 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", |
|
wtc
2014/07/10 18:42:21
Should this histogram name also use the "CertIo" p
brandonsalmon
2014/07/10 19:00:13
It was my intention that "Chain" would become a di
|
| + 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); |
|
wtc
2014/07/10 18:42:21
IMPORTANT: should we return here? Otherwise the re
brandonsalmon
2014/07/10 19:00:13
I did this because it didn't make sense to me to r
|
| + |
| + 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); |
|
wtc
2014/07/10 18:42:21
Nit: use curly braces because the bodies of the if
|
| +} |
| + |
| +// |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 |
| // a "non-error response" is one with a 2xx (Successful) or 3xx |
| // (Redirection) status code. |
| @@ -1495,6 +1601,10 @@ int HttpCache::Transaction::DoCacheReadResponseComplete(int result) { |
| 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. |
| int current_size = entry_->disk_entry->GetDataSize(kResponseContentIndex); |
| if (response_.headers->GetContentLength() == current_size) |
| @@ -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, |
| const HttpRequestInfo* request) { |
| net_log_ = net_log; |
| @@ -2327,6 +2493,10 @@ int HttpCache::Transaction::WriteResponseInfoToEntry(bool truncated) { |
| 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 |
| // headers; when in record mode, record everything. |
| bool skip_transient_headers = (cache_->mode() != RECORD); |