summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbrandonsalmon@chromium.org <brandonsalmon@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-07-10 21:18:00 +0000
committerbrandonsalmon@chromium.org <brandonsalmon@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-07-10 21:18:00 +0000
commitf37f04f61b8bdaa6b3a43ff72a359281b741f11f (patch)
tree187c10fb478abb22d05e599daec3aac10d2e8ec1
parentbe22c8895d8aa4a0ee026a213508aebd8e17f4cb (diff)
downloadchromium_src-f37f04f61b8bdaa6b3a43ff72a359281b741f11f.zip
chromium_src-f37f04f61b8bdaa6b3a43ff72a359281b741f11f.tar.gz
chromium_src-f37f04f61b8bdaa6b3a43ff72a359281b741f11f.tar.bz2
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
-rw-r--r--net/http/http_cache.cc12
-rw-r--r--net/http/http_cache.h5
-rw-r--r--net/http/http_cache_transaction.cc170
-rw-r--r--net/http/http_cache_transaction.h5
-rw-r--r--tools/metrics/histograms/histograms.xml61
5 files changed, 252 insertions, 1 deletions
diff --git a/net/http/http_cache.cc b/net/http/http_cache.cc
index ef2e3fd..c45ab41 100644
--- a/net/http/http_cache.cc
+++ b/net/http/http_cache.cc
@@ -33,6 +33,7 @@
#include "net/base/net_errors.h"
#include "net/base/upload_data_stream.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/http_cache_transaction.h"
#include "net/http/http_network_layer.h"
@@ -45,6 +46,11 @@
namespace {
+bool UseCertCache() {
+ return base::FieldTrialList::FindFullName("CertCacheTrial") ==
+ "ExperimentGroup";
+}
+
// Adaptor to delete a file on a worker thread.
void DeletePath(base::FilePath path) {
base::DeleteFile(path, false);
@@ -346,6 +352,7 @@ HttpCache::~HttpCache() {
// 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.
+ cert_cache_.reset();
disk_cache_.reset();
PendingOpsMap::iterator pending_it = pending_ops_.begin();
@@ -1168,8 +1175,11 @@ void HttpCache::OnBackendCreated(int result, PendingOp* pending_op) {
// work items. The first call saves the backend and releases the factory,
// and the last call clears building_backend_.
backend_factory_.reset(); // Reclaim memory.
- if (result == OK)
+ if (result == OK) {
disk_cache_ = pending_op->backend.Pass();
+ if (UseCertCache())
+ cert_cache_.reset(new DiskBasedCertCache(disk_cache_.get()));
+ }
}
if (!pending_op->pending_queue.empty()) {
diff --git a/net/http/http_cache.h b/net/http/http_cache.h
index 752b98a..5bdda8a 100644
--- a/net/http/http_cache.h
+++ b/net/http/http_cache.h
@@ -44,6 +44,7 @@ class Entry;
namespace net {
class CertVerifier;
+class DiskBasedCertCache;
class HostResolver;
class HttpAuthHandlerFactory;
class HttpNetworkSession;
@@ -142,6 +143,8 @@ class NET_EXPORT HttpCache : public HttpTransactionFactory,
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
// 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
@@ -405,6 +408,8 @@ class NET_EXPORT HttpCache : public HttpTransactionFactory,
scoped_ptr<disk_cache::Backend> disk_cache_;
+ scoped_ptr<DiskBasedCertCache> cert_cache_;
+
// The set of active entries indexed by cache key.
ActiveEntriesMap active_entries_;
diff --git a/net/http/http_cache_transaction.cc b/net/http/http_cache_transaction.cc
index bf79111..aba7923 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",
+ 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
// 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);
diff --git a/net/http/http_cache_transaction.h b/net/http/http_cache_transaction.h
index fd1018b..8159edc 100644
--- a/net/http/http_cache_transaction.h
+++ b/net/http/http_cache_transaction.h
@@ -267,6 +267,11 @@ class HttpCache::Transaction : public HttpTransaction {
int DoCacheWriteData(int num_bytes);
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.
void SetRequest(const BoundNetLog& net_log, const HttpRequestInfo* request);
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index b460be5..2427345 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -3622,6 +3622,51 @@ Therefore, the affected-histogram name has to have at least one dot in it.
</summary>
</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">
<owner>rvargas@chromium.org</owner>
<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.
<int value="20" label="FutureCat (&gt;10.10), 8-bit (?)"/>
</enum>
+<enum name="CertificateChainPosition" type="int">
+ <int value="0" label="Root Certificate"/>
+</enum>
+
<enum name="ChannelLayout" type="int">
<int value="0" label="CHANNEL_LAYOUT_NONE"/>
<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.
<affected-histogram name="CertificateType2.NonBR"/>
</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=".">
<suffix name="Register" label="Register request"/>
<suffix name="UpdatePrinter" label="Update printer request"/>