Skip to content

Commit

Permalink
Adding DiskBasedCertCache to HttpCache (+UMA).
Browse files Browse the repository at this point in the history
Review URL: https://codereview.chromium.org/356953003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@282429 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
brandonsalmon@chromium.org committed Jul 10, 2014
1 parent be22c88 commit f37f04f
Show file tree
Hide file tree
Showing 5 changed files with 252 additions and 1 deletion.
12 changes: 11 additions & 1 deletion net/http/http_cache.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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);
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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()) {
Expand Down
5 changes: 5 additions & 0 deletions net/http/http_cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class Entry;
namespace net {

class CertVerifier;
class DiskBasedCertCache;
class HostResolver;
class HttpAuthHandlerFactory;
class HttpNetworkSession;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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_;

Expand Down
170 changes: 170 additions & 0 deletions net/http/http_cache_transaction.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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.
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
5 changes: 5 additions & 0 deletions net/http/http_cache_transaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
61 changes: 61 additions & 0 deletions tools/metrics/histograms/histograms.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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"/>
Expand Down Expand Up @@ -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"/>
Expand Down

0 comments on commit f37f04f

Please sign in to comment.