Commit 95d3767a authored by Bill Budge's avatar Bill Budge Committed by Commit Bot

[code cache] De-duplicate large double-keyed cache entries

- Changes very large code entry writing to create an indirection. The
  first entry is double-keyed just like other size entries. However, it
  contains response time, size, and a hashed checksum of the data. The
  checksum is used as a second key for another entry that contains a
  no header and the actual code.
- Changes very large code entry fetches to do a double-keyed read as
  before. If there is a checksum in the header, that is used as a
  second key to read the actual code.
- Every origin that loads a given resource for the first time will
  get a cache miss. To get a cache hit, the origin must first generate
  code and write it to the cache. The code is checksummed using SHA-256
  to make it very unlikely for a malicious origin to store its code to
  the same key as another origin. Origins that generate identical code
  share the same code entry.

Design Document:
https://docs.google.com/document/d/1SCH15oCFJW55jsTJZ0T7XAQIZVryI_IJOIENyL_q4n4/edit

Bug:chromium:936107

Change-Id: If99d2dada102382d51f97286fb3e5d9d2faf3aa6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1846102
Commit-Queue: Bill Budge <bbudge@chromium.org>
Reviewed-by: default avatarMythri Alle <mythria@chromium.org>
Reviewed-by: default avatarChris Palmer <palmer@chromium.org>
Reviewed-by: default avatarKinuko Yasuda <kinuko@chromium.org>
Reviewed-by: default avatarMaksim Orlovich <morlovich@chromium.org>
Cr-Commit-Position: refs/heads/master@{#707389}
parent 804eeab2
...@@ -6,7 +6,9 @@ ...@@ -6,7 +6,9 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/memory/ptr_util.h" #include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h" #include "base/metrics/histogram_macros.h"
#include "base/strings/string_number_conversions.h"
#include "content/public/common/url_constants.h" #include "content/public/common/url_constants.h"
#include "crypto/sha2.h"
#include "net/base/completion_once_callback.h" #include "net/base/completion_once_callback.h"
#include "net/base/url_util.h" #include "net/base/url_util.h"
#include "url/gurl.h" #include "url/gurl.h"
...@@ -67,20 +69,51 @@ std::string GetCacheKey(const GURL& resource_url, const GURL& origin_lock) { ...@@ -67,20 +69,51 @@ std::string GetCacheKey(const GURL& resource_url, const GURL& origin_lock) {
return key; return key;
} }
constexpr int kResponseTimeSizeInBytes = sizeof(int64_t); constexpr size_t kResponseTimeSizeInBytes = sizeof(int64_t);
constexpr int kDataSizeInBytes = sizeof(uint32_t); constexpr size_t kDataSizeInBytes = sizeof(uint32_t);
constexpr int kHeaderSizeInBytes = kResponseTimeSizeInBytes + kDataSizeInBytes; constexpr size_t kHeaderSizeInBytes =
kResponseTimeSizeInBytes + kDataSizeInBytes;
// The SHA-256 checksum is used as the key for the de-duplicated code data. We
// must convert the checksum to a string key in a way that is guaranteed not to
// match a key generated by |GetCacheKey|. A simple way to do this is to convert
// it to a hex number string, which is twice as long as the checksum.
constexpr size_t kSHAKeySizeInBytes = 2 * crypto::kSHA256Length;
// This is the threshold for storing the header and cached code in stream 0, // This is the threshold for storing the header and cached code in stream 0,
// which is read into memory on opening an entry. JavaScript code caching stores // which is read into memory on opening an entry. JavaScript code caching stores
// time stamps with no data, or timestamps with just a tag, and we observe many // time stamps with no data, or timestamps with just a tag, and we observe many
// 8 and 16 byte reads and writes. Make the threshold larger to speed up many // 8 and 16 byte reads and writes. Make the threshold larger to speed up small
// code entries too. // code entries too.
constexpr int kSmallDataLimit = 4096; constexpr size_t kSmallDataLimit = 4096;
// This is the maximum size for code that will be stored under the key generated
// by |GetCacheKey|. Each origin will get its own copy of the generated code for
// a given resource. Code that is larger than this limit will be stored under a
// key derived from the code checksum, and each origin using a given resource
// gets its own small entry under the key generated by |GetCacheKey| that holds
// the hash, enabling a two stage lookup.
constexpr size_t kLargeDataLimit = 64 * 1024;
// Checks that the header data in the small buffer is valid. We may read cache
// entries that were written by a previous version of Chrome which use obsolete
// formats. These reads should fail and be doomed as soon as possible.
bool IsValidHeader(scoped_refptr<net::IOBufferWithSize> small_buffer) {
size_t buffer_size = small_buffer->size();
if (buffer_size < kHeaderSizeInBytes)
return false;
uint32_t data_size;
memcpy(&data_size, small_buffer->data() + kResponseTimeSizeInBytes,
kDataSizeInBytes);
if (data_size <= kSmallDataLimit)
return buffer_size == kHeaderSizeInBytes + data_size;
if (data_size <= kLargeDataLimit)
return buffer_size == kHeaderSizeInBytes;
return buffer_size == kHeaderSizeInBytes + kSHAKeySizeInBytes;
}
void WriteSmallDataHeader(scoped_refptr<net::IOBufferWithSize> buffer, void WriteCommonDataHeader(scoped_refptr<net::IOBufferWithSize> buffer,
const base::Time& response_time, const base::Time& response_time,
uint32_t data_size) { uint32_t data_size) {
DCHECK_LE(kHeaderSizeInBytes, buffer->size()); DCHECK_LE(static_cast<int>(kHeaderSizeInBytes), buffer->size());
int64_t serialized_time = int64_t serialized_time =
response_time.ToDeltaSinceWindowsEpoch().InMicroseconds(); response_time.ToDeltaSinceWindowsEpoch().InMicroseconds();
memcpy(buffer->data(), &serialized_time, kResponseTimeSizeInBytes); memcpy(buffer->data(), &serialized_time, kResponseTimeSizeInBytes);
...@@ -89,15 +122,16 @@ void WriteSmallDataHeader(scoped_refptr<net::IOBufferWithSize> buffer, ...@@ -89,15 +122,16 @@ void WriteSmallDataHeader(scoped_refptr<net::IOBufferWithSize> buffer,
kDataSizeInBytes); kDataSizeInBytes);
} }
void ReadSmallDataHeader(scoped_refptr<net::IOBufferWithSize> buffer, void ReadCommonDataHeader(scoped_refptr<net::IOBufferWithSize> buffer,
base::Time* response_time, base::Time* response_time,
uint32_t* data_size) { uint32_t* data_size) {
DCHECK_LE(kHeaderSizeInBytes, buffer->size()); DCHECK_LE(static_cast<int>(kHeaderSizeInBytes), buffer->size());
int64_t raw_response_time = *(reinterpret_cast<int64_t*>(buffer->data())); int64_t raw_response_time;
memcpy(&raw_response_time, buffer->data(), kResponseTimeSizeInBytes);
*response_time = base::Time::FromDeltaSinceWindowsEpoch( *response_time = base::Time::FromDeltaSinceWindowsEpoch(
base::TimeDelta::FromMicroseconds(raw_response_time)); base::TimeDelta::FromMicroseconds(raw_response_time));
*data_size = memcpy(data_size, buffer->data() + kResponseTimeSizeInBytes,
*(reinterpret_cast<uint32_t*>(buffer->data() + kResponseTimeSizeInBytes)); kDataSizeInBytes);
} }
static_assert(mojo_base::BigBuffer::kMaxInlineBytes <= static_assert(mojo_base::BigBuffer::kMaxInlineBytes <=
...@@ -171,7 +205,7 @@ class GeneratedCodeCache::PendingOperation { ...@@ -171,7 +205,7 @@ class GeneratedCodeCache::PendingOperation {
key_(key), key_(key),
small_buffer_(small_buffer), small_buffer_(small_buffer),
large_buffer_(large_buffer) { large_buffer_(large_buffer) {
DCHECK_EQ(Operation::kWrite, op_); DCHECK(Operation::kWrite == op_ || Operation::kWriteWithSHAKey == op_);
} }
PendingOperation(Operation op, PendingOperation(Operation op,
...@@ -181,6 +215,21 @@ class GeneratedCodeCache::PendingOperation { ...@@ -181,6 +215,21 @@ class GeneratedCodeCache::PendingOperation {
DCHECK_EQ(Operation::kFetch, op_); DCHECK_EQ(Operation::kFetch, op_);
} }
PendingOperation(Operation op,
const std::string& key,
const base::Time& response_time,
scoped_refptr<net::IOBufferWithSize> small_buffer,
scoped_refptr<BigIOBuffer> large_buffer,
ReadDataCallback read_callback)
: op_(op),
key_(key),
response_time_(response_time),
small_buffer_(small_buffer),
large_buffer_(large_buffer),
read_callback_(std::move(read_callback)) {
DCHECK_EQ(Operation::kFetchWithSHAKey, op_);
}
PendingOperation(Operation op, const std::string& key) : op_(op), key_(key) { PendingOperation(Operation op, const std::string& key) : op_(op), key_(key) {
DCHECK_EQ(Operation::kDelete, op_); DCHECK_EQ(Operation::kDelete, op_);
} }
...@@ -212,12 +261,19 @@ class GeneratedCodeCache::PendingOperation { ...@@ -212,12 +261,19 @@ class GeneratedCodeCache::PendingOperation {
large_buffer_ = large_buffer; large_buffer_ = large_buffer;
} }
// This returns the site-specific response time for merged code entries.
const base::Time& response_time() const {
DCHECK_EQ(Operation::kFetchWithSHAKey, op_);
return response_time_;
}
// These are called by write and fetch operations to track buffer completions // These are called by write and fetch operations to track buffer completions
// and signal when the operation has finished, and whether it was successful. // and signal when the operation has finished, and whether it was successful.
bool succeeded() const { return succeeded_; } bool succeeded() const { return succeeded_; }
bool AddBufferCompletion(bool succeeded) { bool AddBufferCompletion(bool succeeded) {
DCHECK(op_ == Operation::kWrite || op_ == Operation::kFetch); DCHECK(op_ == Operation::kWrite || op_ == Operation::kWriteWithSHAKey ||
op_ == Operation::kFetch || op_ == Operation::kFetchWithSHAKey);
if (!succeeded) if (!succeeded)
succeeded_ = false; succeeded_ = false;
DCHECK_GT(2, completions_); DCHECK_GT(2, completions_);
...@@ -228,6 +284,7 @@ class GeneratedCodeCache::PendingOperation { ...@@ -228,6 +284,7 @@ class GeneratedCodeCache::PendingOperation {
private: private:
const Operation op_; const Operation op_;
const std::string key_; const std::string key_;
const base::Time response_time_;
scoped_refptr<net::IOBufferWithSize> small_buffer_; scoped_refptr<net::IOBufferWithSize> small_buffer_;
scoped_refptr<BigIOBuffer> large_buffer_; scoped_refptr<BigIOBuffer> large_buffer_;
ReadDataCallback read_callback_; ReadDataCallback read_callback_;
...@@ -275,37 +332,67 @@ void GeneratedCodeCache::WriteEntry(const GURL& url, ...@@ -275,37 +332,67 @@ void GeneratedCodeCache::WriteEntry(const GURL& url,
return; return;
} }
// If data is small, combine the header and data into a single write. // Reject buffers that are large enough to cause overflow problems.
if (data.size() >= std::numeric_limits<int32_t>::max())
return;
scoped_refptr<net::IOBufferWithSize> small_buffer; scoped_refptr<net::IOBufferWithSize> small_buffer;
scoped_refptr<BigIOBuffer> large_buffer; scoped_refptr<BigIOBuffer> large_buffer;
uint32_t data_size = static_cast<uint32_t>(data.size()); uint32_t data_size = static_cast<uint32_t>(data.size());
// We have three different cache entry layouts, depending on data size.
if (data_size <= kSmallDataLimit) { if (data_size <= kSmallDataLimit) {
// 1. Small
// [stream0] response time, size, data
// [stream1] <empty>
small_buffer = base::MakeRefCounted<net::IOBufferWithSize>( small_buffer = base::MakeRefCounted<net::IOBufferWithSize>(
kHeaderSizeInBytes + data.size()); kHeaderSizeInBytes + data.size());
// Copy |data| into the small buffer. // Copy |data| into the small buffer.
memcpy(small_buffer->data() + kHeaderSizeInBytes, data.data(), data.size()); memcpy(small_buffer->data() + kHeaderSizeInBytes, data.data(), data.size());
// We write 0 bytes and truncate stream 1 to clear any stale data. // Write 0 bytes and truncate stream 1 to clear any stale data.
large_buffer = base::MakeRefCounted<BigIOBuffer>(mojo_base::BigBuffer()); large_buffer = base::MakeRefCounted<BigIOBuffer>(mojo_base::BigBuffer());
} else { } else if (data_size <= kLargeDataLimit) {
// 2. Large
// [stream0] response time, size
// [stream1] data
small_buffer = small_buffer =
base::MakeRefCounted<net::IOBufferWithSize>(kHeaderSizeInBytes); base::MakeRefCounted<net::IOBufferWithSize>(kHeaderSizeInBytes);
large_buffer = base::MakeRefCounted<BigIOBuffer>(std::move(data)); large_buffer = base::MakeRefCounted<BigIOBuffer>(std::move(data));
} else {
// 3. Very Large
// [stream0] response time, size, checksum
// [stream1] <empty>
// [stream0 (checksum key entry)] <empty>
// [stream1 (checksum key entry)] data
uint8_t result[crypto::kSHA256Length];
crypto::SHA256HashString(
base::StringPiece(reinterpret_cast<char*>(data.data()), data.size()),
result, base::size(result));
std::string checksum_key = base::HexEncode(result, base::size(result));
small_buffer = base::MakeRefCounted<net::IOBufferWithSize>(
kHeaderSizeInBytes + kSHAKeySizeInBytes);
// Copy |checksum_key| into the small buffer.
DCHECK_EQ(kSHAKeySizeInBytes, checksum_key.length());
memcpy(small_buffer->data() + kHeaderSizeInBytes, checksum_key.data(),
kSHAKeySizeInBytes);
// Write 0 bytes and truncate stream 1 to clear any stale data.
large_buffer = base::MakeRefCounted<BigIOBuffer>(mojo_base::BigBuffer());
// Issue another write operation for the code, with the checksum as the key
// and nothing in the header.
auto small_buffer2 = base::MakeRefCounted<net::IOBufferWithSize>(0);
auto large_buffer2 = base::MakeRefCounted<BigIOBuffer>(std::move(data));
auto op2 = std::make_unique<PendingOperation>(Operation::kWriteWithSHAKey,
checksum_key, small_buffer2,
large_buffer2);
EnqueueOperation(std::move(op2));
} }
WriteSmallDataHeader(small_buffer, response_time, data_size); WriteCommonDataHeader(small_buffer, response_time, data_size);
// Create the write operation. // Create the write operation.
std::string key = GetCacheKey(url, origin_lock); std::string key = GetCacheKey(url, origin_lock);
auto op = std::make_unique<PendingOperation>(Operation::kWrite, key, auto op = std::make_unique<PendingOperation>(Operation::kWrite, key,
small_buffer, large_buffer); small_buffer, large_buffer);
EnqueueOperation(std::move(op));
if (backend_state_ != kInitialized) {
// Insert it into the list of pending operations while the backend is
// still being opened.
pending_ops_.emplace(std::move(op));
return;
}
EnqueueOperationAndIssueIfNext(std::move(op));
} }
void GeneratedCodeCache::FetchEntry(const GURL& url, void GeneratedCodeCache::FetchEntry(const GURL& url,
...@@ -321,14 +408,7 @@ void GeneratedCodeCache::FetchEntry(const GURL& url, ...@@ -321,14 +408,7 @@ void GeneratedCodeCache::FetchEntry(const GURL& url,
std::string key = GetCacheKey(url, origin_lock); std::string key = GetCacheKey(url, origin_lock);
auto op = std::make_unique<PendingOperation>(Operation::kFetch, key, auto op = std::make_unique<PendingOperation>(Operation::kFetch, key,
std::move(read_data_callback)); std::move(read_data_callback));
if (backend_state_ != kInitialized) { EnqueueOperation(std::move(op));
// Insert it into the list of pending operations while the backend is
// still being opened.
pending_ops_.emplace(std::move(op));
return;
}
EnqueueOperationAndIssueIfNext(std::move(op));
} }
void GeneratedCodeCache::DeleteEntry(const GURL& url, const GURL& origin_lock) { void GeneratedCodeCache::DeleteEntry(const GURL& url, const GURL& origin_lock) {
...@@ -340,15 +420,7 @@ void GeneratedCodeCache::DeleteEntry(const GURL& url, const GURL& origin_lock) { ...@@ -340,15 +420,7 @@ void GeneratedCodeCache::DeleteEntry(const GURL& url, const GURL& origin_lock) {
std::string key = GetCacheKey(url, origin_lock); std::string key = GetCacheKey(url, origin_lock);
auto op = std::make_unique<PendingOperation>(Operation::kDelete, key); auto op = std::make_unique<PendingOperation>(Operation::kDelete, key);
EnqueueOperation(std::move(op));
if (backend_state_ != kInitialized) {
// Insert it into the list of pending operations while the backend is
// still being opened.
pending_ops_.emplace(std::move(op));
return;
}
EnqueueOperationAndIssueIfNext(std::move(op));
} }
void GeneratedCodeCache::CreateBackend() { void GeneratedCodeCache::CreateBackend() {
...@@ -386,6 +458,18 @@ void GeneratedCodeCache::DidCreateBackend( ...@@ -386,6 +458,18 @@ void GeneratedCodeCache::DidCreateBackend(
IssuePendingOperations(); IssuePendingOperations();
} }
void GeneratedCodeCache::EnqueueOperation(
std::unique_ptr<PendingOperation> op) {
if (backend_state_ != kInitialized) {
// Insert it into the list of pending operations while the backend is
// still being opened.
pending_ops_.emplace(std::move(op));
return;
}
EnqueueOperationAndIssueIfNext(std::move(op));
}
void GeneratedCodeCache::IssuePendingOperations() { void GeneratedCodeCache::IssuePendingOperations() {
// Issue any operations that were received while creating the backend. // Issue any operations that were received while creating the backend.
while (!pending_ops_.empty()) { while (!pending_ops_.empty()) {
...@@ -407,9 +491,11 @@ void GeneratedCodeCache::IssuePendingOperations() { ...@@ -407,9 +491,11 @@ void GeneratedCodeCache::IssuePendingOperations() {
void GeneratedCodeCache::IssueOperation(PendingOperation* op) { void GeneratedCodeCache::IssueOperation(PendingOperation* op) {
switch (op->operation()) { switch (op->operation()) {
case kFetch: case kFetch:
case kFetchWithSHAKey:
FetchEntryImpl(op); FetchEntryImpl(op);
break; break;
case kWrite: case kWrite:
case kWriteWithSHAKey:
WriteEntryImpl(op); WriteEntryImpl(op);
break; break;
case kDelete: case kDelete:
...@@ -422,7 +508,8 @@ void GeneratedCodeCache::IssueOperation(PendingOperation* op) { ...@@ -422,7 +508,8 @@ void GeneratedCodeCache::IssueOperation(PendingOperation* op) {
} }
void GeneratedCodeCache::WriteEntryImpl(PendingOperation* op) { void GeneratedCodeCache::WriteEntryImpl(PendingOperation* op) {
DCHECK_EQ(Operation::kWrite, op->operation()); DCHECK(Operation::kWrite == op->operation() ||
Operation::kWriteWithSHAKey == op->operation());
if (backend_state_ != kInitialized) { if (backend_state_ != kInitialized) {
// Silently fail the request. // Silently fail the request.
CloseOperationAndIssueNext(op); CloseOperationAndIssueNext(op);
...@@ -442,7 +529,8 @@ void GeneratedCodeCache::WriteEntryImpl(PendingOperation* op) { ...@@ -442,7 +529,8 @@ void GeneratedCodeCache::WriteEntryImpl(PendingOperation* op) {
void GeneratedCodeCache::OpenCompleteForWrite( void GeneratedCodeCache::OpenCompleteForWrite(
PendingOperation* op, PendingOperation* op,
disk_cache::EntryResult entry_result) { disk_cache::EntryResult entry_result) {
DCHECK_EQ(Operation::kWrite, op->operation()); DCHECK(Operation::kWrite == op->operation() ||
Operation::kWriteWithSHAKey == op->operation());
if (entry_result.net_error() != net::OK) { if (entry_result.net_error() != net::OK) {
CollectStatistics(CacheEntryStatus::kError); CollectStatistics(CacheEntryStatus::kError);
CloseOperationAndIssueNext(op); CloseOperationAndIssueNext(op);
...@@ -459,6 +547,20 @@ void GeneratedCodeCache::OpenCompleteForWrite( ...@@ -459,6 +547,20 @@ void GeneratedCodeCache::OpenCompleteForWrite(
// There should be a valid entry if the open was successful. // There should be a valid entry if the open was successful.
DCHECK(entry); DCHECK(entry);
// For merged entries, don't write if the entry already exists.
if (op->operation() == Operation::kWriteWithSHAKey) {
int small_size = entry->GetDataSize(kSmallDataStream);
int large_size = entry->GetDataSize(kLargeDataStream);
if (small_size == 0 && large_size == op->large_buffer()->size()) {
// Skip overwriting with identical data.
CloseOperationAndIssueNext(op);
return;
}
// Otherwise, there shouldn't be any data for this entry yet.
DCHECK_EQ(0, small_size);
DCHECK_EQ(0, large_size);
}
// Write the small data first, truncating. // Write the small data first, truncating.
auto small_buffer = op->small_buffer(); auto small_buffer = op->small_buffer();
int result = entry->WriteData( int result = entry->WriteData(
...@@ -486,7 +588,8 @@ void GeneratedCodeCache::OpenCompleteForWrite( ...@@ -486,7 +588,8 @@ void GeneratedCodeCache::OpenCompleteForWrite(
void GeneratedCodeCache::WriteSmallBufferComplete(PendingOperation* op, void GeneratedCodeCache::WriteSmallBufferComplete(PendingOperation* op,
int rv) { int rv) {
DCHECK_EQ(Operation::kWrite, op->operation()); DCHECK(Operation::kWrite == op->operation() ||
Operation::kWriteWithSHAKey == op->operation());
if (op->AddBufferCompletion(rv == op->small_buffer()->size())) { if (op->AddBufferCompletion(rv == op->small_buffer()->size())) {
WriteComplete(op); WriteComplete(op);
} }
...@@ -494,14 +597,16 @@ void GeneratedCodeCache::WriteSmallBufferComplete(PendingOperation* op, ...@@ -494,14 +597,16 @@ void GeneratedCodeCache::WriteSmallBufferComplete(PendingOperation* op,
void GeneratedCodeCache::WriteLargeBufferComplete(PendingOperation* op, void GeneratedCodeCache::WriteLargeBufferComplete(PendingOperation* op,
int rv) { int rv) {
DCHECK_EQ(Operation::kWrite, op->operation()); DCHECK(Operation::kWrite == op->operation() ||
Operation::kWriteWithSHAKey == op->operation());
if (op->AddBufferCompletion(rv == op->large_buffer()->size())) { if (op->AddBufferCompletion(rv == op->large_buffer()->size())) {
WriteComplete(op); WriteComplete(op);
} }
} }
void GeneratedCodeCache::WriteComplete(PendingOperation* op) { void GeneratedCodeCache::WriteComplete(PendingOperation* op) {
DCHECK_EQ(Operation::kWrite, op->operation()); DCHECK(Operation::kWrite == op->operation() ||
Operation::kWriteWithSHAKey == op->operation());
if (!op->succeeded()) { if (!op->succeeded()) {
// The write failed; record the failure and doom the entry here. // The write failed; record the failure and doom the entry here.
CollectStatistics(CacheEntryStatus::kWriteFailed); CollectStatistics(CacheEntryStatus::kWriteFailed);
...@@ -511,7 +616,8 @@ void GeneratedCodeCache::WriteComplete(PendingOperation* op) { ...@@ -511,7 +616,8 @@ void GeneratedCodeCache::WriteComplete(PendingOperation* op) {
} }
void GeneratedCodeCache::FetchEntryImpl(PendingOperation* op) { void GeneratedCodeCache::FetchEntryImpl(PendingOperation* op) {
DCHECK_EQ(Operation::kFetch, op->operation()); DCHECK(Operation::kFetch == op->operation() ||
Operation::kFetchWithSHAKey == op->operation());
if (backend_state_ != kInitialized) { if (backend_state_ != kInitialized) {
op->TakeReadCallback().Run(base::Time(), mojo_base::BigBuffer()); op->TakeReadCallback().Run(base::Time(), mojo_base::BigBuffer());
CloseOperationAndIssueNext(op); CloseOperationAndIssueNext(op);
...@@ -531,7 +637,8 @@ void GeneratedCodeCache::FetchEntryImpl(PendingOperation* op) { ...@@ -531,7 +637,8 @@ void GeneratedCodeCache::FetchEntryImpl(PendingOperation* op) {
void GeneratedCodeCache::OpenCompleteForRead( void GeneratedCodeCache::OpenCompleteForRead(
PendingOperation* op, PendingOperation* op,
disk_cache::EntryResult entry_result) { disk_cache::EntryResult entry_result) {
DCHECK_EQ(Operation::kFetch, op->operation()); DCHECK(Operation::kFetch == op->operation() ||
Operation::kFetchWithSHAKey == op->operation());
if (entry_result.net_error() != net::OK) { if (entry_result.net_error() != net::OK) {
CollectStatistics(CacheEntryStatus::kMiss); CollectStatistics(CacheEntryStatus::kMiss);
op->TakeReadCallback().Run(base::Time(), mojo_base::BigBuffer()); op->TakeReadCallback().Run(base::Time(), mojo_base::BigBuffer());
...@@ -544,13 +651,20 @@ void GeneratedCodeCache::OpenCompleteForRead( ...@@ -544,13 +651,20 @@ void GeneratedCodeCache::OpenCompleteForRead(
DCHECK(entry); DCHECK(entry);
int small_size = entry->GetDataSize(kSmallDataStream); int small_size = entry->GetDataSize(kSmallDataStream);
scoped_refptr<net::IOBufferWithSize> small_buffer =
base::MakeRefCounted<net::IOBufferWithSize>(small_size);
op->set_small_buffer(small_buffer);
int large_size = entry->GetDataSize(kLargeDataStream); int large_size = entry->GetDataSize(kLargeDataStream);
scoped_refptr<BigIOBuffer> large_buffer = scoped_refptr<net::IOBufferWithSize> small_buffer;
base::MakeRefCounted<BigIOBuffer>(large_size); scoped_refptr<BigIOBuffer> large_buffer;
op->set_large_buffer(large_buffer); if (op->operation() == Operation::kFetch) {
small_buffer = base::MakeRefCounted<net::IOBufferWithSize>(small_size);
op->set_small_buffer(small_buffer);
large_buffer = base::MakeRefCounted<BigIOBuffer>(large_size);
op->set_large_buffer(large_buffer);
} else {
small_buffer = op->small_buffer();
large_buffer = op->large_buffer();
DCHECK_EQ(small_size, small_buffer->size());
DCHECK_EQ(large_size, large_buffer->size());
}
// Read the small data first. // Read the small data first.
int result = entry->ReadData( int result = entry->ReadData(
...@@ -577,8 +691,11 @@ void GeneratedCodeCache::OpenCompleteForRead( ...@@ -577,8 +691,11 @@ void GeneratedCodeCache::OpenCompleteForRead(
} }
void GeneratedCodeCache::ReadSmallBufferComplete(PendingOperation* op, int rv) { void GeneratedCodeCache::ReadSmallBufferComplete(PendingOperation* op, int rv) {
DCHECK_EQ(Operation::kFetch, op->operation()); DCHECK(Operation::kFetch == op->operation() ||
bool succeeded = rv == op->small_buffer()->size() && rv >= kHeaderSizeInBytes; Operation::kFetchWithSHAKey == op->operation());
bool no_header = op->operation() == Operation::kFetchWithSHAKey;
bool succeeded = (rv == op->small_buffer()->size() &&
(no_header || IsValidHeader(op->small_buffer())));
CollectStatistics(succeeded ? CacheEntryStatus::kHit CollectStatistics(succeeded ? CacheEntryStatus::kHit
: CacheEntryStatus::kMiss); : CacheEntryStatus::kMiss);
...@@ -591,30 +708,52 @@ void GeneratedCodeCache::ReadSmallBufferComplete(PendingOperation* op, int rv) { ...@@ -591,30 +708,52 @@ void GeneratedCodeCache::ReadSmallBufferComplete(PendingOperation* op, int rv) {
} }
void GeneratedCodeCache::ReadLargeBufferComplete(PendingOperation* op, int rv) { void GeneratedCodeCache::ReadLargeBufferComplete(PendingOperation* op, int rv) {
DCHECK_EQ(Operation::kFetch, op->operation()); DCHECK(Operation::kFetch == op->operation() ||
Operation::kFetchWithSHAKey == op->operation());
if (op->AddBufferCompletion(rv == op->large_buffer()->size())) if (op->AddBufferCompletion(rv == op->large_buffer()->size()))
ReadComplete(op); ReadComplete(op);
} }
void GeneratedCodeCache::ReadComplete(PendingOperation* op) { void GeneratedCodeCache::ReadComplete(PendingOperation* op) {
DCHECK_EQ(Operation::kFetch, op->operation()); DCHECK(Operation::kFetch == op->operation() ||
Operation::kFetchWithSHAKey == op->operation());
if (!op->succeeded()) { if (!op->succeeded()) {
op->TakeReadCallback().Run(base::Time(), mojo_base::BigBuffer()); op->TakeReadCallback().Run(base::Time(), mojo_base::BigBuffer());
// Doom this entry since it is inaccessible. // Doom this entry since it is inaccessible.
DoomEntry(op); DoomEntry(op);
} else { } else {
base::Time response_time; if (op->operation() != Operation::kFetchWithSHAKey) {
uint32_t data_size = 0; base::Time response_time;
ReadSmallDataHeader(op->small_buffer(), &response_time, &data_size); uint32_t data_size = 0;
if (data_size <= kSmallDataLimit) { ReadCommonDataHeader(op->small_buffer(), &response_time, &data_size);
// Small data, copy the data from the small buffer. if (data_size <= kSmallDataLimit) {
DCHECK_EQ(0, op->large_buffer()->size()); // Small data. Copy the data from the small buffer.
mojo_base::BigBuffer data(data_size); DCHECK_EQ(0, op->large_buffer()->size());
memcpy(data.data(), op->small_buffer()->data() + kHeaderSizeInBytes, mojo_base::BigBuffer data(data_size);
data_size); memcpy(data.data(), op->small_buffer()->data() + kHeaderSizeInBytes,
op->TakeReadCallback().Run(response_time, std::move(data)); data_size);
op->TakeReadCallback().Run(response_time, std::move(data));
} else if (data_size <= kLargeDataLimit) {
// Large data below the merging threshold. Return the large buffer.
op->TakeReadCallback().Run(response_time,
op->large_buffer()->TakeBuffer());
} else {
// Very large data. Create the second fetch using the checksum as key.
DCHECK_EQ(static_cast<int>(kHeaderSizeInBytes + kSHAKeySizeInBytes),
op->small_buffer()->size());
std::string checksum_key(
op->small_buffer()->data() + kHeaderSizeInBytes,
kSHAKeySizeInBytes);
auto small_buffer = base::MakeRefCounted<net::IOBufferWithSize>(0);
auto large_buffer = base::MakeRefCounted<BigIOBuffer>(data_size);
auto op2 = std::make_unique<PendingOperation>(
Operation::kFetchWithSHAKey, checksum_key, response_time,
small_buffer, large_buffer, op->TakeReadCallback());
EnqueueOperation(std::move(op2));
}
} else { } else {
op->TakeReadCallback().Run(response_time, // Large merged code data with no header. |op| holds the response time.
op->TakeReadCallback().Run(op->response_time(),
op->large_buffer()->TakeBuffer()); op->large_buffer()->TakeBuffer());
} }
} }
...@@ -622,7 +761,7 @@ void GeneratedCodeCache::ReadComplete(PendingOperation* op) { ...@@ -622,7 +761,7 @@ void GeneratedCodeCache::ReadComplete(PendingOperation* op) {
} }
void GeneratedCodeCache::DeleteEntryImpl(PendingOperation* op) { void GeneratedCodeCache::DeleteEntryImpl(PendingOperation* op) {
DCHECK(op->operation() == Operation::kDelete); DCHECK_EQ(Operation::kDelete, op->operation());
DoomEntry(op); DoomEntry(op);
CloseOperationAndIssueNext(op); CloseOperationAndIssueNext(op);
} }
......
...@@ -119,7 +119,14 @@ class CONTENT_EXPORT GeneratedCodeCache { ...@@ -119,7 +119,14 @@ class CONTENT_EXPORT GeneratedCodeCache {
enum BackendState { kInitializing, kInitialized, kFailed }; enum BackendState { kInitializing, kInitialized, kFailed };
// The operation requested. // The operation requested.
enum Operation { kFetch, kWrite, kDelete, kGetBackend }; enum Operation {
kFetch,
kFetchWithSHAKey,
kWrite,
kWriteWithSHAKey,
kDelete,
kGetBackend
};
// Data streams corresponding to each entry. // Data streams corresponding to each entry.
enum { kSmallDataStream = 0, kLargeDataStream = 1 }; enum { kSmallDataStream = 0, kLargeDataStream = 1 };
...@@ -130,6 +137,9 @@ class CONTENT_EXPORT GeneratedCodeCache { ...@@ -130,6 +137,9 @@ class CONTENT_EXPORT GeneratedCodeCache {
scoped_refptr<base::RefCountedData<ScopedBackendPtr>> backend_ptr, scoped_refptr<base::RefCountedData<ScopedBackendPtr>> backend_ptr,
int rv); int rv);
// Adds operation to the appropriate queue.
void EnqueueOperation(std::unique_ptr<PendingOperation> op);
// Issues ops that were received while the backend was being initialized. // Issues ops that were received while the backend was being initialized.
void IssuePendingOperations(); void IssuePendingOperations();
void IssueOperation(PendingOperation* op); void IssueOperation(PendingOperation* op);
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/files/scoped_temp_dir.h" #include "base/files/scoped_temp_dir.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/task_environment.h" #include "base/test/task_environment.h"
#include "content/public/test/browser_task_environment.h" #include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_utils.h" #include "content/public/test/test_utils.h"
...@@ -17,8 +18,15 @@ namespace content { ...@@ -17,8 +18,15 @@ namespace content {
class GeneratedCodeCacheTest : public testing::Test { class GeneratedCodeCacheTest : public testing::Test {
public: public:
static const int kLargeSizeInBytes = 8192; // This should be larger than |kSmallDataLimit| in generated_code_cache.cc.
static const int kMaxSizeInBytes = 1024 * 1024; static const size_t kLargeSizeInBytes = 8192;
// This should be larger than |kLargeDataLimit| in generated_code_cache.cc.
// Additionally, this shouldn't exceed 1/8 of the maximum cache size below,
// |kMaxSizeInBytes|.
static const size_t kVeryLargeSizeInBytes = 128 * 1024;
static const size_t kMaxSizeInBytes = 1024 * 1024;
static_assert(kMaxSizeInBytes / kVeryLargeSizeInBytes > 0UL,
"Cache will be too small to hold a very large item");
static constexpr char kInitialUrl[] = "http://example.com/script.js"; static constexpr char kInitialUrl[] = "http://example.com/script.js";
static constexpr char kInitialOrigin[] = "http://example.com"; static constexpr char kInitialOrigin[] = "http://example.com";
static constexpr char kInitialData[] = "InitialData"; static constexpr char kInitialData[] = "InitialData";
...@@ -121,7 +129,7 @@ class GeneratedCodeCacheTest : public testing::Test { ...@@ -121,7 +129,7 @@ class GeneratedCodeCacheTest : public testing::Test {
constexpr char GeneratedCodeCacheTest::kInitialUrl[]; constexpr char GeneratedCodeCacheTest::kInitialUrl[];
constexpr char GeneratedCodeCacheTest::kInitialOrigin[]; constexpr char GeneratedCodeCacheTest::kInitialOrigin[];
constexpr char GeneratedCodeCacheTest::kInitialData[]; constexpr char GeneratedCodeCacheTest::kInitialData[];
const int GeneratedCodeCacheTest::kMaxSizeInBytes; const size_t GeneratedCodeCacheTest::kMaxSizeInBytes;
TEST_F(GeneratedCodeCacheTest, CheckResponseTime) { TEST_F(GeneratedCodeCacheTest, CheckResponseTime) {
GURL url(kInitialUrl); GURL url(kInitialUrl);
...@@ -131,7 +139,6 @@ TEST_F(GeneratedCodeCacheTest, CheckResponseTime) { ...@@ -131,7 +139,6 @@ TEST_F(GeneratedCodeCacheTest, CheckResponseTime) {
std::string data = "SerializedCodeForScript"; std::string data = "SerializedCodeForScript";
base::Time response_time = base::Time::Now(); base::Time response_time = base::Time::Now();
WriteToCache(url, origin_lock, data, response_time); WriteToCache(url, origin_lock, data, response_time);
task_environment_.RunUntilIdle();
FetchFromCache(url, origin_lock); FetchFromCache(url, origin_lock);
task_environment_.RunUntilIdle(); task_environment_.RunUntilIdle();
...@@ -160,7 +167,6 @@ TEST_F(GeneratedCodeCacheTest, WriteEntry) { ...@@ -160,7 +167,6 @@ TEST_F(GeneratedCodeCacheTest, WriteEntry) {
std::string data = "SerializedCodeForScript"; std::string data = "SerializedCodeForScript";
base::Time response_time = base::Time::Now(); base::Time response_time = base::Time::Now();
WriteToCache(new_url, origin_lock, data, response_time); WriteToCache(new_url, origin_lock, data, response_time);
task_environment_.RunUntilIdle();
FetchFromCache(new_url, origin_lock); FetchFromCache(new_url, origin_lock);
task_environment_.RunUntilIdle(); task_environment_.RunUntilIdle();
...@@ -177,7 +183,22 @@ TEST_F(GeneratedCodeCacheTest, WriteLargeEntry) { ...@@ -177,7 +183,22 @@ TEST_F(GeneratedCodeCacheTest, WriteLargeEntry) {
std::string large_data(kLargeSizeInBytes, 'x'); std::string large_data(kLargeSizeInBytes, 'x');
base::Time response_time = base::Time::Now(); base::Time response_time = base::Time::Now();
WriteToCache(new_url, origin_lock, large_data, response_time); WriteToCache(new_url, origin_lock, large_data, response_time);
FetchFromCache(new_url, origin_lock);
task_environment_.RunUntilIdle(); task_environment_.RunUntilIdle();
ASSERT_TRUE(received_);
EXPECT_EQ(large_data, received_data_);
EXPECT_EQ(response_time, received_response_time_);
}
TEST_F(GeneratedCodeCacheTest, WriteVeryLargeEntry) {
GURL new_url("http://example1.com/script.js");
GURL origin_lock = GURL(kInitialOrigin);
InitializeCache(GeneratedCodeCache::CodeCacheType::kJavaScript);
std::string large_data(kVeryLargeSizeInBytes, 'x');
base::Time response_time = base::Time::Now();
WriteToCache(new_url, origin_lock, large_data, response_time);
FetchFromCache(new_url, origin_lock); FetchFromCache(new_url, origin_lock);
task_environment_.RunUntilIdle(); task_environment_.RunUntilIdle();
...@@ -206,7 +227,6 @@ TEST_F(GeneratedCodeCacheTest, WriteEntryWithEmptyData) { ...@@ -206,7 +227,6 @@ TEST_F(GeneratedCodeCacheTest, WriteEntryWithEmptyData) {
InitializeCache(GeneratedCodeCache::CodeCacheType::kJavaScript); InitializeCache(GeneratedCodeCache::CodeCacheType::kJavaScript);
base::Time response_time = base::Time::Now(); base::Time response_time = base::Time::Now();
WriteToCache(url, origin_lock, std::string(), response_time); WriteToCache(url, origin_lock, std::string(), response_time);
task_environment_.RunUntilIdle();
FetchFromCache(url, origin_lock); FetchFromCache(url, origin_lock);
task_environment_.RunUntilIdle(); task_environment_.RunUntilIdle();
...@@ -223,7 +243,6 @@ TEST_F(GeneratedCodeCacheTest, WriteEntryFailure) { ...@@ -223,7 +243,6 @@ TEST_F(GeneratedCodeCacheTest, WriteEntryFailure) {
base::Time response_time = base::Time::Now(); base::Time response_time = base::Time::Now();
std::string too_big_data(kMaxSizeInBytes * 8, 0); std::string too_big_data(kMaxSizeInBytes * 8, 0);
WriteToCache(url, origin_lock, too_big_data, response_time); WriteToCache(url, origin_lock, too_big_data, response_time);
task_environment_.RunUntilIdle();
FetchFromCache(url, origin_lock); FetchFromCache(url, origin_lock);
task_environment_.RunUntilIdle(); task_environment_.RunUntilIdle();
...@@ -245,7 +264,6 @@ TEST_F(GeneratedCodeCacheTest, WriteEntryFailureOutOfOrder) { ...@@ -245,7 +264,6 @@ TEST_F(GeneratedCodeCacheTest, WriteEntryFailureOutOfOrder) {
base::Time response_time = base::Time::Now(); base::Time response_time = base::Time::Now();
std::string too_big_data(kMaxSizeInBytes * 8, 0); std::string too_big_data(kMaxSizeInBytes * 8, 0);
WriteToCache(url, origin_lock, too_big_data, response_time); WriteToCache(url, origin_lock, too_big_data, response_time);
task_environment_.RunUntilIdle();
FetchFromCache(url, origin_lock); FetchFromCache(url, origin_lock);
task_environment_.RunUntilIdle(); task_environment_.RunUntilIdle();
...@@ -275,7 +293,6 @@ TEST_F(GeneratedCodeCacheTest, WriteEntryPendingOp) { ...@@ -275,7 +293,6 @@ TEST_F(GeneratedCodeCacheTest, WriteEntryPendingOp) {
std::string data = "SerializedCodeForScript"; std::string data = "SerializedCodeForScript";
base::Time response_time = base::Time::Now(); base::Time response_time = base::Time::Now();
WriteToCache(new_url, origin_lock, data, response_time); WriteToCache(new_url, origin_lock, data, response_time);
task_environment_.RunUntilIdle();
FetchFromCache(new_url, origin_lock); FetchFromCache(new_url, origin_lock);
task_environment_.RunUntilIdle(); task_environment_.RunUntilIdle();
...@@ -292,7 +309,22 @@ TEST_F(GeneratedCodeCacheTest, WriteLargeEntryPendingOp) { ...@@ -292,7 +309,22 @@ TEST_F(GeneratedCodeCacheTest, WriteLargeEntryPendingOp) {
std::string large_data(kLargeSizeInBytes, 'x'); std::string large_data(kLargeSizeInBytes, 'x');
base::Time response_time = base::Time::Now(); base::Time response_time = base::Time::Now();
WriteToCache(new_url, origin_lock, large_data, response_time); WriteToCache(new_url, origin_lock, large_data, response_time);
FetchFromCache(new_url, origin_lock);
task_environment_.RunUntilIdle(); task_environment_.RunUntilIdle();
ASSERT_TRUE(received_);
EXPECT_EQ(large_data, received_data_);
EXPECT_EQ(response_time, received_response_time_);
}
TEST_F(GeneratedCodeCacheTest, WriteVeryLargeEntryPendingOp) {
GURL new_url("http://example1.com/script1.js");
GURL origin_lock = GURL(kInitialOrigin);
InitializeCache(GeneratedCodeCache::CodeCacheType::kJavaScript);
std::string large_data(kVeryLargeSizeInBytes, 'x');
base::Time response_time = base::Time::Now();
WriteToCache(new_url, origin_lock, large_data, response_time);
FetchFromCache(new_url, origin_lock); FetchFromCache(new_url, origin_lock);
task_environment_.RunUntilIdle(); task_environment_.RunUntilIdle();
...@@ -322,7 +354,6 @@ TEST_F(GeneratedCodeCacheTest, UpdateDataOfExistingEntry) { ...@@ -322,7 +354,6 @@ TEST_F(GeneratedCodeCacheTest, UpdateDataOfExistingEntry) {
std::string new_data = "SerializedCodeForScriptOverwrite"; std::string new_data = "SerializedCodeForScriptOverwrite";
base::Time response_time = base::Time::Now(); base::Time response_time = base::Time::Now();
WriteToCache(url, origin_lock, new_data, response_time); WriteToCache(url, origin_lock, new_data, response_time);
task_environment_.RunUntilIdle();
FetchFromCache(url, origin_lock); FetchFromCache(url, origin_lock);
task_environment_.RunUntilIdle(); task_environment_.RunUntilIdle();
...@@ -339,7 +370,6 @@ TEST_F(GeneratedCodeCacheTest, UpdateDataOfSmallExistingEntry) { ...@@ -339,7 +370,6 @@ TEST_F(GeneratedCodeCacheTest, UpdateDataOfSmallExistingEntry) {
std::string new_data(kLargeSizeInBytes, 'x'); std::string new_data(kLargeSizeInBytes, 'x');
base::Time response_time = base::Time::Now(); base::Time response_time = base::Time::Now();
WriteToCache(url, origin_lock, new_data, response_time); WriteToCache(url, origin_lock, new_data, response_time);
task_environment_.RunUntilIdle();
FetchFromCache(url, origin_lock); FetchFromCache(url, origin_lock);
task_environment_.RunUntilIdle(); task_environment_.RunUntilIdle();
...@@ -359,7 +389,25 @@ TEST_F(GeneratedCodeCacheTest, UpdateDataOfLargeExistingEntry) { ...@@ -359,7 +389,25 @@ TEST_F(GeneratedCodeCacheTest, UpdateDataOfLargeExistingEntry) {
std::string new_data = large_data + "Overwrite"; std::string new_data = large_data + "Overwrite";
response_time = base::Time::Now(); response_time = base::Time::Now();
WriteToCache(url, origin_lock, new_data, response_time); WriteToCache(url, origin_lock, new_data, response_time);
FetchFromCache(url, origin_lock);
task_environment_.RunUntilIdle(); task_environment_.RunUntilIdle();
ASSERT_TRUE(received_);
EXPECT_EQ(new_data, received_data_);
EXPECT_EQ(response_time, received_response_time_);
}
TEST_F(GeneratedCodeCacheTest, UpdateDataOfVeryLargeExistingEntry) {
GURL url(kInitialUrl);
GURL origin_lock = GURL(kInitialOrigin);
InitializeCache(GeneratedCodeCache::CodeCacheType::kJavaScript);
std::string large_data(kVeryLargeSizeInBytes, 'x');
base::Time response_time = base::Time::Now();
WriteToCache(url, origin_lock, large_data, response_time);
std::string new_data = large_data + "Overwrite";
response_time = base::Time::Now();
WriteToCache(url, origin_lock, new_data, response_time);
FetchFromCache(url, origin_lock); FetchFromCache(url, origin_lock);
task_environment_.RunUntilIdle(); task_environment_.RunUntilIdle();
...@@ -379,7 +427,25 @@ TEST_F(GeneratedCodeCacheTest, TruncateDataOfLargeExistingEntry) { ...@@ -379,7 +427,25 @@ TEST_F(GeneratedCodeCacheTest, TruncateDataOfLargeExistingEntry) {
std::string new_data = "SerializedCodeForScriptOverwrite"; std::string new_data = "SerializedCodeForScriptOverwrite";
response_time = base::Time::Now(); response_time = base::Time::Now();
WriteToCache(url, origin_lock, new_data, response_time); WriteToCache(url, origin_lock, new_data, response_time);
FetchFromCache(url, origin_lock);
task_environment_.RunUntilIdle(); task_environment_.RunUntilIdle();
ASSERT_TRUE(received_);
EXPECT_EQ(new_data, received_data_);
EXPECT_EQ(response_time, received_response_time_);
}
TEST_F(GeneratedCodeCacheTest, TruncateDataOfVeryLargeExistingEntry) {
GURL url(kInitialUrl);
GURL origin_lock = GURL(kInitialOrigin);
InitializeCache(GeneratedCodeCache::CodeCacheType::kJavaScript);
std::string large_data(kVeryLargeSizeInBytes, 'x');
base::Time response_time = base::Time::Now();
WriteToCache(url, origin_lock, large_data, response_time);
std::string new_data = "SerializedCodeForScriptOverwrite";
response_time = base::Time::Now();
WriteToCache(url, origin_lock, new_data, response_time);
FetchFromCache(url, origin_lock); FetchFromCache(url, origin_lock);
task_environment_.RunUntilIdle(); task_environment_.RunUntilIdle();
...@@ -409,7 +475,6 @@ TEST_F(GeneratedCodeCacheTest, FetchEntriesFromSameOrigin) { ...@@ -409,7 +475,6 @@ TEST_F(GeneratedCodeCacheTest, FetchEntriesFromSameOrigin) {
std::string data_second_resource = "SerializedCodeForSecondResource"; std::string data_second_resource = "SerializedCodeForSecondResource";
WriteToCache(second_url, origin_lock, data_second_resource, base::Time()); WriteToCache(second_url, origin_lock, data_second_resource, base::Time());
task_environment_.RunUntilIdle();
FetchFromCache(url, origin_lock); FetchFromCache(url, origin_lock);
task_environment_.RunUntilIdle(); task_environment_.RunUntilIdle();
...@@ -433,7 +498,6 @@ TEST_F(GeneratedCodeCacheTest, FetchSucceedsFromDifferentOrigins) { ...@@ -433,7 +498,6 @@ TEST_F(GeneratedCodeCacheTest, FetchSucceedsFromDifferentOrigins) {
std::string data_origin1 = "SerializedCodeForSecondOrigin"; std::string data_origin1 = "SerializedCodeForSecondOrigin";
WriteToCache(url, origin_lock1, data_origin1, base::Time()); WriteToCache(url, origin_lock1, data_origin1, base::Time());
task_environment_.RunUntilIdle();
FetchFromCache(url, origin_lock); FetchFromCache(url, origin_lock);
task_environment_.RunUntilIdle(); task_environment_.RunUntilIdle();
...@@ -446,6 +510,74 @@ TEST_F(GeneratedCodeCacheTest, FetchSucceedsFromDifferentOrigins) { ...@@ -446,6 +510,74 @@ TEST_F(GeneratedCodeCacheTest, FetchSucceedsFromDifferentOrigins) {
EXPECT_EQ(data_origin1, received_data_); EXPECT_EQ(data_origin1, received_data_);
} }
TEST_F(GeneratedCodeCacheTest, VeryLargeEntriesAreMerged) {
GURL url("http://example.com/script.js");
InitializeCache(GeneratedCodeCache::CodeCacheType::kJavaScript);
// Write more copies of the same resource than the cache can hold unless they
// are merged by content.
for (size_t i = 0; i < 2 * kMaxSizeInBytes / kVeryLargeSizeInBytes; ++i) {
GURL origin_lock = GURL(std::string("http://example") +
base::NumberToString(i) + std::string(".com"));
std::string large_data(kVeryLargeSizeInBytes, 'x');
WriteToCache(url, origin_lock, large_data, base::Time());
}
for (size_t i = 0; i < 2 * kMaxSizeInBytes / kVeryLargeSizeInBytes; ++i) {
GURL origin_lock = GURL(std::string("http://example") +
base::NumberToString(i) + std::string(".com"));
std::string large_data(kVeryLargeSizeInBytes, 'x');
FetchFromCache(url, origin_lock);
task_environment_.RunUntilIdle();
ASSERT_TRUE(received_);
EXPECT_EQ(large_data, received_data_);
received_ = false;
received_data_ = std::string();
}
}
TEST_F(GeneratedCodeCacheTest, StressVeryLargeEntries) {
GURL url("http://example.com/script.js");
InitializeCache(GeneratedCodeCache::CodeCacheType::kJavaScript);
// Fill the cache with very large data keyed by the SHA-256 checksum.
char data1 = 0;
for (size_t i = 0; i < kMaxSizeInBytes / kVeryLargeSizeInBytes - 1;
++i, ++data1) {
GURL origin_lock = GURL(std::string("http://example") +
base::NumberToString(i) + std::string(".com"));
std::string large_data(kVeryLargeSizeInBytes, data1);
WriteToCache(url, origin_lock, large_data, base::Time());
}
// Fill the cache with new data. The old entries should be purged to make
// room for the new ones.
char data2 = -128;
for (size_t i = 0; i < kMaxSizeInBytes / kVeryLargeSizeInBytes - 1;
++i, ++data2) {
GURL origin_lock = GURL(std::string("http://example") +
base::NumberToString(i) + std::string(".com"));
std::string large_data(kVeryLargeSizeInBytes, data2);
WriteToCache(url, origin_lock, large_data, base::Time());
}
data2 = -128;
for (size_t i = 0; i < kMaxSizeInBytes / kVeryLargeSizeInBytes - 1;
++i, ++data2) {
GURL origin_lock = GURL(std::string("http://example") +
base::NumberToString(i) + std::string(".com"));
FetchFromCache(url, origin_lock);
task_environment_.RunUntilIdle();
// We can't depend too strongly on the disk cache storage heuristic. Verify
// that if we received data, it's what we wrote.
if (!received_null_) {
std::string large_data(kVeryLargeSizeInBytes, data2);
EXPECT_EQ(large_data, received_data_);
received_ = false;
received_data_ = std::string();
}
}
}
TEST_F(GeneratedCodeCacheTest, FetchSucceedsEmptyOriginLock) { TEST_F(GeneratedCodeCacheTest, FetchSucceedsEmptyOriginLock) {
GURL url("http://example.com/script.js"); GURL url("http://example.com/script.js");
GURL origin_lock = GURL(""); GURL origin_lock = GURL("");
...@@ -453,7 +585,6 @@ TEST_F(GeneratedCodeCacheTest, FetchSucceedsEmptyOriginLock) { ...@@ -453,7 +585,6 @@ TEST_F(GeneratedCodeCacheTest, FetchSucceedsEmptyOriginLock) {
InitializeCache(GeneratedCodeCache::CodeCacheType::kJavaScript); InitializeCache(GeneratedCodeCache::CodeCacheType::kJavaScript);
std::string data = "SerializedCodeForEmptyOrigin"; std::string data = "SerializedCodeForEmptyOrigin";
WriteToCache(url, origin_lock, data, base::Time()); WriteToCache(url, origin_lock, data, base::Time());
task_environment_.RunUntilIdle();
FetchFromCache(url, origin_lock); FetchFromCache(url, origin_lock);
task_environment_.RunUntilIdle(); task_environment_.RunUntilIdle();
...@@ -469,11 +600,9 @@ TEST_F(GeneratedCodeCacheTest, FetchEmptyOriginVsValidOriginLocks) { ...@@ -469,11 +600,9 @@ TEST_F(GeneratedCodeCacheTest, FetchEmptyOriginVsValidOriginLocks) {
InitializeCache(GeneratedCodeCache::CodeCacheType::kJavaScript); InitializeCache(GeneratedCodeCache::CodeCacheType::kJavaScript);
std::string empty_origin_data = "SerializedCodeForEmptyOrigin"; std::string empty_origin_data = "SerializedCodeForEmptyOrigin";
WriteToCache(url, empty_origin_lock, empty_origin_data, base::Time()); WriteToCache(url, empty_origin_lock, empty_origin_data, base::Time());
task_environment_.RunUntilIdle();
std::string valid_origin_data = "SerializedCodeForValidOrigin"; std::string valid_origin_data = "SerializedCodeForValidOrigin";
WriteToCache(url, origin_lock, valid_origin_data, base::Time()); WriteToCache(url, origin_lock, valid_origin_data, base::Time());
task_environment_.RunUntilIdle();
FetchFromCache(url, empty_origin_lock); FetchFromCache(url, empty_origin_lock);
task_environment_.RunUntilIdle(); task_environment_.RunUntilIdle();
......
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