Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(53)

Unified Diff: net/http/http_cache_unittest.cc

Issue 2721933002: HttpCache::Transaction layer allowing parallel validation (Closed)
Patch Set: nit addressed Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « net/http/http_cache_transaction.cc ('k') | net/http/http_transaction.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: net/http/http_cache_unittest.cc
diff --git a/net/http/http_cache_unittest.cc b/net/http/http_cache_unittest.cc
index 13182e43f640f9b903d42de3ea9fb53c4c9481f6..62269b7952c8b89bc3d66dc773b42c22f62311fb 100644
--- a/net/http/http_cache_unittest.cc
+++ b/net/http/http_cache_unittest.cc
@@ -147,6 +147,10 @@ void TestLoadTimingCachedResponse(const LoadTimingInfo& load_timing_info) {
EXPECT_TRUE(load_timing_info.receive_headers_end.is_null());
}
+void DeferNetworkStart(bool* defer) {
+ *defer = true;
+}
+
class DeleteCacheCompletionCallback : public TestCompletionCallbackBase {
public:
explicit DeleteCacheCompletionCallback(MockHttpCache* cache)
@@ -182,6 +186,17 @@ void ReadAndVerifyTransaction(HttpTransaction* trans,
EXPECT_EQ(expected, content);
}
+void ReadRemainingAndVerifyTransaction(HttpTransaction* trans,
+ std::string& already_read,
+ const MockTransaction& trans_info) {
+ std::string content;
+ int rv = ReadTransaction(trans, &content);
+ EXPECT_THAT(rv, IsOk());
+
+ std::string expected(trans_info.data);
+ EXPECT_EQ(expected, already_read + content);
+}
+
void RunTransactionTestBase(HttpCache* cache,
const MockTransaction& trans_info,
const MockHttpRequest& request,
@@ -1176,180 +1191,1036 @@ TEST(HttpCache, SimpleGET_LoadBypassCache_Implicit) {
MockTransaction transaction(kSimpleGET_Transaction);
transaction.request_headers = "pragma: no-cache\r\n";
- RunTransactionTest(cache.http_cache(), transaction);
+ RunTransactionTest(cache.http_cache(), transaction);
+
+ EXPECT_EQ(2, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(2, cache.disk_cache()->create_count());
+}
+
+TEST(HttpCache, SimpleGET_LoadBypassCache_Implicit2) {
+ MockHttpCache cache;
+
+ // write to the cache
+ RunTransactionTest(cache.http_cache(), kSimpleGET_Transaction);
+
+ // force this transaction to write to the cache again
+ MockTransaction transaction(kSimpleGET_Transaction);
+ transaction.request_headers = "cache-control: no-cache\r\n";
+
+ RunTransactionTest(cache.http_cache(), transaction);
+
+ EXPECT_EQ(2, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(2, cache.disk_cache()->create_count());
+}
+
+TEST(HttpCache, SimpleGET_LoadValidateCache) {
+ MockHttpCache cache;
+
+ // Write to the cache.
+ RunTransactionTest(cache.http_cache(), kSimpleGET_Transaction);
+
+ // Read from the cache.
+ RunTransactionTest(cache.http_cache(), kSimpleGET_Transaction);
+
+ // Force this transaction to validate the cache.
+ MockTransaction transaction(kSimpleGET_Transaction);
+ transaction.load_flags |= LOAD_VALIDATE_CACHE;
+
+ HttpResponseInfo response_info;
+ BoundTestNetLog log;
+ LoadTimingInfo load_timing_info;
+ RunTransactionTestWithResponseInfoAndGetTiming(
+ cache.http_cache(), transaction, &response_info, log.bound(),
+ &load_timing_info);
+
+ EXPECT_EQ(2, cache.network_layer()->transaction_count());
+ EXPECT_EQ(1, cache.disk_cache()->open_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+ EXPECT_TRUE(response_info.network_accessed);
+ TestLoadTimingNetworkRequest(load_timing_info);
+}
+
+TEST(HttpCache, SimpleGET_LoadValidateCache_Implicit) {
+ MockHttpCache cache;
+
+ // write to the cache
+ RunTransactionTest(cache.http_cache(), kSimpleGET_Transaction);
+
+ // read from the cache
+ RunTransactionTest(cache.http_cache(), kSimpleGET_Transaction);
+
+ // force this transaction to validate the cache
+ MockTransaction transaction(kSimpleGET_Transaction);
+ transaction.request_headers = "cache-control: max-age=0\r\n";
+
+ RunTransactionTest(cache.http_cache(), transaction);
+
+ EXPECT_EQ(2, cache.network_layer()->transaction_count());
+ EXPECT_EQ(1, cache.disk_cache()->open_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+}
+
+// Tests that |unused_since_prefetch| is updated accordingly (e.g. it is set to
+// true after a prefetch and set back to false when the prefetch is used).
+TEST(HttpCache, SimpleGET_UnusedSincePrefetch) {
+ MockHttpCache cache;
+ HttpResponseInfo response_info;
+
+ // A normal load does not have |unused_since_prefetch| set.
+ RunTransactionTestWithResponseInfoAndGetTiming(
+ cache.http_cache(), kSimpleGET_Transaction, &response_info,
+ BoundTestNetLog().bound(), nullptr);
+ EXPECT_FALSE(response_info.unused_since_prefetch);
+ EXPECT_FALSE(response_info.was_cached);
+
+ // The prefetch itself does not have |unused_since_prefetch| set.
+ MockTransaction prefetch_transaction(kSimpleGET_Transaction);
+ prefetch_transaction.load_flags |= LOAD_PREFETCH;
+ RunTransactionTestWithResponseInfoAndGetTiming(
+ cache.http_cache(), prefetch_transaction, &response_info,
+ BoundTestNetLog().bound(), nullptr);
+ EXPECT_FALSE(response_info.unused_since_prefetch);
+ EXPECT_TRUE(response_info.was_cached);
+
+ // A duplicated prefetch has |unused_since_prefetch| set.
+ RunTransactionTestWithResponseInfoAndGetTiming(
+ cache.http_cache(), prefetch_transaction, &response_info,
+ BoundTestNetLog().bound(), nullptr);
+ EXPECT_TRUE(response_info.unused_since_prefetch);
+ EXPECT_TRUE(response_info.was_cached);
+
+ // |unused_since_prefetch| is still true after two prefetches in a row.
+ RunTransactionTestWithResponseInfoAndGetTiming(
+ cache.http_cache(), kSimpleGET_Transaction, &response_info,
+ BoundTestNetLog().bound(), nullptr);
+ EXPECT_TRUE(response_info.unused_since_prefetch);
+ EXPECT_TRUE(response_info.was_cached);
+
+ // The resource has now been used, back to normal behavior.
+ RunTransactionTestWithResponseInfoAndGetTiming(
+ cache.http_cache(), kSimpleGET_Transaction, &response_info,
+ BoundTestNetLog().bound(), nullptr);
+ EXPECT_FALSE(response_info.unused_since_prefetch);
+ EXPECT_TRUE(response_info.was_cached);
+}
+
+static void PreserveRequestHeaders_Handler(const HttpRequestInfo* request,
+ std::string* response_status,
+ std::string* response_headers,
+ std::string* response_data) {
+ EXPECT_TRUE(request->extra_headers.HasHeader(kExtraHeaderKey));
+}
+
+// Tests that we don't remove extra headers for simple requests.
+TEST(HttpCache, SimpleGET_PreserveRequestHeaders) {
+ MockHttpCache cache;
+
+ MockTransaction transaction(kSimpleGET_Transaction);
+ transaction.handler = PreserveRequestHeaders_Handler;
+ transaction.request_headers = EXTRA_HEADER;
+ transaction.response_headers = "Cache-Control: max-age=0\n";
+ AddMockTransaction(&transaction);
+
+ // Write, then revalidate the entry.
+ RunTransactionTest(cache.http_cache(), transaction);
+ RunTransactionTest(cache.http_cache(), transaction);
+
+ EXPECT_EQ(2, cache.network_layer()->transaction_count());
+ EXPECT_EQ(1, cache.disk_cache()->open_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+ RemoveMockTransaction(&transaction);
+}
+
+// Tests that we don't remove extra headers for conditionalized requests.
+TEST(HttpCache, ConditionalizedGET_PreserveRequestHeaders) {
+ MockHttpCache cache;
+
+ // Write to the cache.
+ RunTransactionTest(cache.http_cache(), kETagGET_Transaction);
+
+ MockTransaction transaction(kETagGET_Transaction);
+ transaction.handler = PreserveRequestHeaders_Handler;
+ transaction.request_headers = "If-None-Match: \"foopy\"\r\n" EXTRA_HEADER;
+ AddMockTransaction(&transaction);
+
+ RunTransactionTest(cache.http_cache(), transaction);
+
+ EXPECT_EQ(2, cache.network_layer()->transaction_count());
+ EXPECT_EQ(1, cache.disk_cache()->open_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+ RemoveMockTransaction(&transaction);
+}
+
+TEST(HttpCache, SimpleGET_ManyReaders) {
+ MockHttpCache cache;
+
+ MockHttpRequest request(kSimpleGET_Transaction);
+
+ std::vector<std::unique_ptr<Context>> context_list;
+ const int kNumTransactions = 5;
+
+ for (int i = 0; i < kNumTransactions; ++i) {
+ context_list.push_back(base::MakeUnique<Context>());
+ auto& c = context_list[i];
+
+ c->result = cache.CreateTransaction(&c->trans);
+ ASSERT_THAT(c->result, IsOk());
+ EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
+
+ c->result =
+ c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
+ }
+
+ // All requests are waiting for the active entry.
+ for (auto& context : context_list) {
+ EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, context->trans->GetLoadState());
+ }
+
+ // Allow all requests to move from the Create queue to the active entry.
+ base::RunLoop().RunUntilIdle();
+
+ // The first request should be a writer at this point, and the subsequent
+ // requests should have passed the validation phase and waiting for the
+ // response to be written to the cache before they can read.
+ EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
+ EXPECT_EQ(kNumTransactions - 1,
+ cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
+
+ EXPECT_EQ(1, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+ // All requests depend on the writer, and the writer is between Start and
+ // Read, i.e. idle.
+ for (auto& context : context_list) {
+ EXPECT_EQ(LOAD_STATE_IDLE, context->trans->GetLoadState());
+ }
+
+ for (int i = 0; i < kNumTransactions; ++i) {
+ auto& c = context_list[i];
+ if (c->result == ERR_IO_PENDING)
+ c->result = c->callback.WaitForResult();
+
+ if (i > 0) {
+ EXPECT_FALSE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
+ EXPECT_EQ(kNumTransactions - i,
+ cache.GetCountReaders(kSimpleGET_Transaction.url));
+ }
+
+ ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
+ }
+
+ // We should not have had to re-open the disk entry
+
+ EXPECT_EQ(1, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+}
+
+// Tests that we can have parallel validation on range requests.
+TEST(HttpCache, RangeGET_ParallelValidationNoMatch) {
+ MockHttpCache cache;
+
+ ScopedMockTransaction transaction(kRangeGET_TransactionOK);
+ MockHttpRequest request(transaction);
+
+ std::vector<std::unique_ptr<Context>> context_list;
+ const int kNumTransactions = 5;
+
+ for (int i = 0; i < kNumTransactions; ++i) {
+ context_list.push_back(base::MakeUnique<Context>());
+ auto& c = context_list[i];
+
+ c->result = cache.CreateTransaction(&c->trans);
+ ASSERT_THAT(c->result, IsOk());
+ EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
+
+ c->result =
+ c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
+ }
+
+ // All requests are waiting for the active entry.
+ for (auto& context : context_list) {
+ EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, context->trans->GetLoadState());
+ }
+
+ // Allow all requests to move from the Create queue to the active entry.
+ base::RunLoop().RunUntilIdle();
+
+ // The first entry should have been doomed. Since the 1st transaction has not
+ // started writing to the cache, MockDiskEntry::CouldBeSparse() returns false
+ // leading to restarting the dooming the entry and restarting the second
+ // transaction.
+ EXPECT_TRUE(cache.IsWriterPresent(kRangeGET_TransactionOK.url));
+ EXPECT_EQ(0, cache.GetCountDoneHeadersQueue(kRangeGET_TransactionOK.url));
+
+ EXPECT_EQ(5, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(5, cache.disk_cache()->create_count());
+
+ for (auto& context : context_list) {
+ EXPECT_EQ(LOAD_STATE_IDLE, context->trans->GetLoadState());
+ }
+
+ for (int i = 0; i < kNumTransactions; ++i) {
+ auto& c = context_list[i];
+ if (c->result == ERR_IO_PENDING)
+ c->result = c->callback.WaitForResult();
+
+ ReadAndVerifyTransaction(c->trans.get(), kRangeGET_TransactionOK);
+ }
+
+ EXPECT_EQ(5, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(5, cache.disk_cache()->create_count());
+}
+
+// Tests parallel validation on range requests with non-overlapping ranges.
+TEST(HttpCache, RangeGET_ParallelValidationDifferentRanges) {
+ MockHttpCache cache;
+
+ ScopedMockTransaction transaction(kRangeGET_TransactionOK);
+
+ std::vector<std::unique_ptr<Context>> context_list;
+ const int kNumTransactions = 2;
+
+ for (int i = 0; i < kNumTransactions; ++i) {
+ context_list.push_back(base::MakeUnique<Context>());
+ }
+
+ // Let 1st transaction complete headers phase for ranges 40-49.
+ std::string first_read;
+ {
+ MockHttpRequest request(transaction);
+ auto& c = context_list[0];
+ c->result = cache.CreateTransaction(&c->trans);
+ ASSERT_THAT(c->result, IsOk());
+ EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
+
+ c->result =
+ c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
+ base::RunLoop().RunUntilIdle();
+
+ // Start writing to the cache so that MockDiskEntry::CouldBeSparse() returns
+ // true.
+ const int kBufferSize = 5;
+ scoped_refptr<IOBuffer> buffer(new IOBuffer(kBufferSize));
+ ReleaseBufferCompletionCallback cb(buffer.get());
+ c->result = c->trans->Read(buffer.get(), kBufferSize, cb.callback());
+ EXPECT_EQ(kBufferSize, cb.GetResult(c->result));
+
+ std::string data_read(buffer->data(), kBufferSize);
+ first_read = data_read;
+
+ EXPECT_EQ(LOAD_STATE_READING_RESPONSE, c->trans->GetLoadState());
+ }
+
+ // 2nd transaction requests ranges 30-39.
+ {
+ transaction.request_headers = "Range: bytes = 30-39\r\n" EXTRA_HEADER;
+ MockHttpRequest request(transaction);
+ auto& c = context_list[1];
+ c->result = cache.CreateTransaction(&c->trans);
+ ASSERT_THAT(c->result, IsOk());
+ EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
+
+ c->result =
+ c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
+ }
+
+ EXPECT_TRUE(cache.IsWriterPresent(kRangeGET_TransactionOK.url));
+ EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(kRangeGET_TransactionOK.url));
+
+ EXPECT_EQ(2, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+ for (int i = 0; i < kNumTransactions; ++i) {
+ auto& c = context_list[i];
+ if (c->result == ERR_IO_PENDING)
+ c->result = c->callback.WaitForResult();
+
+ if (i == 0) {
+ ReadRemainingAndVerifyTransaction(c->trans.get(), first_read,
+ transaction);
+ continue;
+ }
+
+ transaction.data = "rg: 30-39 ";
+ ReadAndVerifyTransaction(c->trans.get(), transaction);
+ }
+
+ EXPECT_EQ(2, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+ // Fetch from the cache to check that ranges 30-49 have been successfully
+ // cached.
+ {
+ MockTransaction transaction(kRangeGET_TransactionOK);
+ transaction.request_headers = "Range: bytes = 30-49\r\n" EXTRA_HEADER;
+ transaction.data = "rg: 30-39 rg: 40-49 ";
+ std::string headers;
+ RunTransactionTestWithResponse(cache.http_cache(), transaction, &headers);
+ Verify206Response(headers, 30, 49);
+ }
+
+ EXPECT_EQ(2, cache.network_layer()->transaction_count());
+ EXPECT_EQ(1, cache.disk_cache()->open_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+}
+
+// Tests parallel validation on range requests with overlapping ranges.
+TEST(HttpCache, RangeGET_ParallelValidationOverlappingRanges) {
+ MockHttpCache cache;
+
+ ScopedMockTransaction transaction(kRangeGET_TransactionOK);
+
+ std::vector<std::unique_ptr<Context>> context_list;
+ const int kNumTransactions = 2;
+
+ for (int i = 0; i < kNumTransactions; ++i) {
+ context_list.push_back(base::MakeUnique<Context>());
+ }
+
+ // Let 1st transaction complete headers phase for ranges 40-49.
+ std::string first_read;
+ {
+ MockHttpRequest request(transaction);
+ auto& c = context_list[0];
+ c->result = cache.CreateTransaction(&c->trans);
+ ASSERT_THAT(c->result, IsOk());
+ EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
+
+ c->result =
+ c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
+ base::RunLoop().RunUntilIdle();
+
+ // Start writing to the cache so that MockDiskEntry::CouldBeSparse() returns
+ // true.
+ const int kBufferSize = 5;
+ scoped_refptr<IOBuffer> buffer(new IOBuffer(kBufferSize));
+ ReleaseBufferCompletionCallback cb(buffer.get());
+ c->result = c->trans->Read(buffer.get(), kBufferSize, cb.callback());
+ EXPECT_EQ(kBufferSize, cb.GetResult(c->result));
+
+ std::string data_read(buffer->data(), kBufferSize);
+ first_read = data_read;
+
+ EXPECT_EQ(LOAD_STATE_READING_RESPONSE, c->trans->GetLoadState());
+ }
+
+ // 2nd transaction requests ranges 30-49.
+ {
+ transaction.request_headers = "Range: bytes = 30-49\r\n" EXTRA_HEADER;
+ MockHttpRequest request(transaction);
+ auto& c = context_list[1];
+ c->result = cache.CreateTransaction(&c->trans);
+ ASSERT_THAT(c->result, IsOk());
+ EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
+
+ c->result =
+ c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
+ }
+
+ EXPECT_TRUE(cache.IsWriterPresent(kRangeGET_TransactionOK.url));
+ EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(kRangeGET_TransactionOK.url));
+
+ // Should have created another transaction for the uncached range.
+ EXPECT_EQ(2, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+ for (int i = 0; i < kNumTransactions; ++i) {
+ auto& c = context_list[i];
+ if (c->result == ERR_IO_PENDING)
+ c->result = c->callback.WaitForResult();
+
+ if (i == 0) {
+ ReadRemainingAndVerifyTransaction(c->trans.get(), first_read,
+ transaction);
+ continue;
+ }
+
+ transaction.data = "rg: 30-39 rg: 40-49 ";
+ ReadAndVerifyTransaction(c->trans.get(), transaction);
+ }
+
+ EXPECT_EQ(2, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+ // Fetch from the cache to check that ranges 30-49 have been successfully
+ // cached.
+ {
+ MockTransaction transaction(kRangeGET_TransactionOK);
+ transaction.request_headers = "Range: bytes = 30-49\r\n" EXTRA_HEADER;
+ transaction.data = "rg: 30-39 rg: 40-49 ";
+ std::string headers;
+ RunTransactionTestWithResponse(cache.http_cache(), transaction, &headers);
+ Verify206Response(headers, 30, 49);
+ }
+
+ EXPECT_EQ(2, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+}
+
+// Parallel validation results in 200.
+TEST(HttpCache, SimpleGET_ParallelValidationNoMatch) {
+ MockHttpCache cache;
+ MockHttpRequest request(kSimpleGET_Transaction);
+ request.load_flags |= LOAD_VALIDATE_CACHE;
+ std::vector<std::unique_ptr<Context>> context_list;
+ const int kNumTransactions = 5;
+ for (int i = 0; i < kNumTransactions; ++i) {
+ context_list.push_back(base::MakeUnique<Context>());
+ auto& c = context_list[i];
+ c->result = cache.CreateTransaction(&c->trans);
+ ASSERT_THAT(c->result, IsOk());
+ EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
+ c->result =
+ c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
+ }
+
+ // All requests are waiting for the active entry.
+ for (auto& context : context_list) {
+ EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, context->trans->GetLoadState());
+ }
+
+ // Allow all requests to move from the Create queue to the active entry.
+ base::RunLoop().RunUntilIdle();
+
+ // The first request should be a writer at this point, and the subsequent
+ // requests should have passed the validation phase and created their own
+ // entries since none of them matched the headers of the earlier one.
+ EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
+
+ // Note that there are only 3 entries created and not 5 since every other
+ // transaction would have gone to the network.
+ EXPECT_EQ(5, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(3, cache.disk_cache()->create_count());
+
+ // All requests depend on the writer, and the writer is between Start and
+ // Read, i.e. idle.
+ for (auto& context : context_list) {
+ EXPECT_EQ(LOAD_STATE_IDLE, context->trans->GetLoadState());
+ }
+
+ for (auto& context : context_list) {
+ if (context->result == ERR_IO_PENDING)
+ context->result = context->callback.WaitForResult();
+ ReadAndVerifyTransaction(context->trans.get(), kSimpleGET_Transaction);
+ }
+
+ EXPECT_EQ(5, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(3, cache.disk_cache()->create_count());
+}
+
+// Tests that a GET followed by a DELETE results in DELETE immediately starting
+// the headers phase and the entry is doomed.
+TEST(HttpCache, SimpleGET_ParallelValidationDelete) {
+ MockHttpCache cache;
+
+ MockHttpRequest request(kSimpleGET_Transaction);
+ request.load_flags |= LOAD_VALIDATE_CACHE;
+
+ MockHttpRequest delete_request(kSimpleGET_Transaction);
+ delete_request.method = "DELETE";
+
+ std::vector<std::unique_ptr<Context>> context_list;
+ const int kNumTransactions = 2;
+
+ for (int i = 0; i < kNumTransactions; ++i) {
+ context_list.push_back(base::MakeUnique<Context>());
+ auto& c = context_list[i];
+
+ MockHttpRequest* this_request = &request;
+ if (i == 1)
+ this_request = &delete_request;
+
+ c->result = cache.CreateTransaction(&c->trans);
+ ASSERT_THAT(c->result, IsOk());
+ EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
+
+ c->result = c->trans->Start(this_request, c->callback.callback(),
+ NetLogWithSource());
+ }
+
+ // All requests are waiting for the active entry.
+ for (auto& context : context_list) {
+ EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, context->trans->GetLoadState());
+ }
+
+ // Allow all requests to move from the Create queue to the active entry.
+ base::RunLoop().RunUntilIdle();
+
+ // The first request should be a writer at this point, and the subsequent
+ // request should have passed the validation phase and doomed the existing
+ // entry.
+ EXPECT_TRUE(
+ cache.disk_cache()->IsDiskEntryDoomed(kSimpleGET_Transaction.url));
+
+ EXPECT_EQ(2, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+ // All requests depend on the writer, and the writer is between Start and
+ // Read, i.e. idle.
+ for (auto& context : context_list) {
+ EXPECT_EQ(LOAD_STATE_IDLE, context->trans->GetLoadState());
+ }
+
+ for (auto& context : context_list) {
+ if (context->result == ERR_IO_PENDING)
+ context->result = context->callback.WaitForResult();
+ ReadAndVerifyTransaction(context->trans.get(), kSimpleGET_Transaction);
+ }
+
+ EXPECT_EQ(2, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+}
+
+// Tests that a transaction which is in validated queue can be destroyed without
+// any impact to other transactions.
+TEST(HttpCache, SimpleGET_ParallelValidationCancelValidated) {
+ MockHttpCache cache;
+
+ MockHttpRequest request(kSimpleGET_Transaction);
+
+ std::vector<std::unique_ptr<Context>> context_list;
+ const int kNumTransactions = 2;
+
+ for (int i = 0; i < kNumTransactions; ++i) {
+ context_list.push_back(base::MakeUnique<Context>());
+ auto& c = context_list[i];
+
+ c->result = cache.CreateTransaction(&c->trans);
+ ASSERT_THAT(c->result, IsOk());
+
+ c->result =
+ c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
+ }
+
+ // Allow all requests to move from the Create queue to the active entry.
+ base::RunLoop().RunUntilIdle();
+
+ // The first request should be a writer at this point, and the subsequent
+ // requests should have completed validation.
+
+ EXPECT_EQ(1, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+ EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
+ EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
+
+ context_list[1].reset();
+
+ EXPECT_EQ(0, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
+
+ // Complete the rest of the transactions.
+ for (auto& context : context_list) {
+ if (!context)
+ continue;
+ ReadAndVerifyTransaction(context->trans.get(), kSimpleGET_Transaction);
+ }
+
+ EXPECT_EQ(1, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+}
+
+// Tests that a transaction which is in readers can be destroyed without
+// any impact to other transactions.
+TEST(HttpCache, SimpleGET_ParallelValidationCancelReader) {
+ MockHttpCache cache;
+
+ MockHttpRequest request(kSimpleGET_Transaction);
+
+ MockTransaction transaction(kSimpleGET_Transaction);
+ transaction.load_flags |= LOAD_VALIDATE_CACHE;
+ MockHttpRequest validate_request(transaction);
+
+ int kNumTransactions = 4;
+ std::vector<std::unique_ptr<Context>> context_list;
+
+ for (int i = 0; i < kNumTransactions; ++i) {
+ context_list.push_back(base::MakeUnique<Context>());
+ auto& c = context_list[i];
+
+ c->result = cache.CreateTransaction(&c->trans);
+ ASSERT_THAT(c->result, IsOk());
+
+ MockHttpRequest* this_request = &request;
+ if (i == 3) {
+ this_request = &validate_request;
+ c->trans->SetBeforeNetworkStartCallback(base::Bind(&DeferNetworkStart));
+ }
+
+ c->result = c->trans->Start(this_request, c->callback.callback(),
+ NetLogWithSource());
+ }
+
+ // Allow all requests to move from the Create queue to the active entry.
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(2, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+ EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
+ EXPECT_EQ(2, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
+ EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
+
+ // Complete the response body.
+ auto& c = context_list[0];
+ ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
+
+ // Rest of the transactions should move to readers.
+ EXPECT_FALSE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
+ EXPECT_EQ(2, cache.GetCountReaders(kSimpleGET_Transaction.url));
+ EXPECT_EQ(0, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
+ EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
+
+ // Add 2 new transactions.
+ kNumTransactions = 6;
+
+ for (int i = 4; i < kNumTransactions; ++i) {
+ context_list.push_back(base::MakeUnique<Context>());
+ auto& c = context_list[i];
+
+ c->result = cache.CreateTransaction(&c->trans);
+ ASSERT_THAT(c->result, IsOk());
+
+ c->result =
+ c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
+ }
+
+ EXPECT_EQ(2, cache.GetCountAddToEntryQueue(kSimpleGET_Transaction.url));
+
+ // Delete a reader.
+ context_list[1].reset();
+
+ // Deleting the reader did not impact any other transaction.
+ EXPECT_EQ(1, cache.GetCountReaders(kSimpleGET_Transaction.url));
+ EXPECT_EQ(2, cache.GetCountAddToEntryQueue(kSimpleGET_Transaction.url));
+ EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
+
+ // Resume network start for headers_transaction. It will doom the entry as it
+ // will be a 200 and will go to network for the response body.
+ auto& context = context_list[3];
+ context->trans->ResumeNetworkStart();
+
+ // The pending transactions will be added to a new entry.
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
+ EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
+
+ // Complete the rest of the transactions.
+ for (int i = 2; i < kNumTransactions; ++i) {
+ auto& c = context_list[i];
+ ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
+ }
+
+ EXPECT_EQ(3, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(2, cache.disk_cache()->create_count());
+}
+
+// Tests that a transaction is in validated queue and writer is destroyed
+// leading to restarting the validated transaction.
+TEST(HttpCache, SimpleGET_ParallelValidationCancelWriter) {
+ MockHttpCache cache;
+
+ MockHttpRequest request(kSimpleGET_Transaction);
+
+ MockTransaction transaction(kSimpleGET_Transaction);
+ transaction.load_flags |= LOAD_VALIDATE_CACHE;
+ MockHttpRequest validate_request(transaction);
+
+ const int kNumTransactions = 3;
+ std::vector<std::unique_ptr<Context>> context_list;
+
+ for (int i = 0; i < kNumTransactions; ++i) {
+ context_list.push_back(base::MakeUnique<Context>());
+ auto& c = context_list[i];
+
+ c->result = cache.CreateTransaction(&c->trans);
+ ASSERT_THAT(c->result, IsOk());
+
+ MockHttpRequest* this_request = &request;
+ if (i == 2) {
+ this_request = &validate_request;
+ c->trans->SetBeforeNetworkStartCallback(base::Bind(&DeferNetworkStart));
+ }
+
+ c->result = c->trans->Start(this_request, c->callback.callback(),
+ NetLogWithSource());
+ }
+
+ // Allow all requests to move from the Create queue to the active entry.
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(2, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+ EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
+ EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
+ EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
+
+ // Deleting the writer at this point will lead to destroying the entry and
+ // restarting the remaining transactions which will then create a new entry.
+ context_list[0].reset();
+
+ // Resume network start for headers_transaction. It should be restarted due to
+ // writer cancellation.
+ auto& c = context_list[2];
+ c->trans->ResumeNetworkStart();
+
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
+ EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
+
+ // Resume network start for the transaction the second time.
+ c->trans->ResumeNetworkStart();
+ base::RunLoop().RunUntilIdle();
+
+ // Headers transaction would have doomed the new entry created.
+ EXPECT_TRUE(
+ cache.disk_cache()->IsDiskEntryDoomed(kSimpleGET_Transaction.url));
+
+ // Complete the rest of the transactions.
+ for (auto& context : context_list) {
+ if (!context)
+ continue;
+ ReadAndVerifyTransaction(context->trans.get(), kSimpleGET_Transaction);
+ }
+
+ EXPECT_EQ(4, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(2, cache.disk_cache()->create_count());
+}
+
+// Tests when a writer is destroyed mid-read and entry is marked as truncated,
+// it should lead to restarting the dependent transactions.
+TEST(HttpCache, SimpleGET_ParallelValidationCancelWriterTruncateEntry) {
+ MockHttpCache cache;
+
+ ScopedMockTransaction transaction(kSimpleGET_Transaction);
+ transaction.response_headers =
+ "Last-Modified: Wed, 28 Nov 2007 00:40:09 GMT\n"
+ "Content-Length: 22\n"
+ "Etag: \"foopy\"\n";
+ MockHttpRequest request(transaction);
+
+ const int kNumTransactions = 3;
+ std::vector<std::unique_ptr<Context>> context_list;
+
+ for (int i = 0; i < kNumTransactions; ++i) {
+ context_list.push_back(base::MakeUnique<Context>());
+ auto& c = context_list[i];
+
+ c->result = cache.CreateTransaction(&c->trans);
+ ASSERT_THAT(c->result, IsOk());
+
+ c->result =
+ c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
+ }
+
+ // Allow all requests to move from the Create queue to the active entry.
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(1, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+ EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
+ EXPECT_EQ(2, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
+
+ // Let first transaction read some bytes, so that entry can be marked as
+ // truncated.
+ {
+ auto& c = context_list[0];
+ const int kBufferSize = 5;
+ scoped_refptr<IOBuffer> buffer(new IOBuffer(kBufferSize));
+ ReleaseBufferCompletionCallback cb(buffer.get());
+ c->result = c->trans->Read(buffer.get(), kBufferSize, cb.callback());
+ EXPECT_EQ(kBufferSize, cb.GetResult(c->result));
+ }
+
+ // Deleting the writer at this point will lead to restarting the remaining
+ // transactions which will then be able to read the truncated status of the
+ // entry.
+ context_list[0].reset();
+
+ base::RunLoop().RunUntilIdle();
EXPECT_EQ(2, cache.network_layer()->transaction_count());
EXPECT_EQ(0, cache.disk_cache()->open_count());
- EXPECT_EQ(2, cache.disk_cache()->create_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+ // Complete the rest of the transactions.
+ for (auto& context : context_list) {
+ if (!context)
+ continue;
+ ReadAndVerifyTransaction(context->trans.get(), kSimpleGET_Transaction);
+ }
}
-TEST(HttpCache, SimpleGET_LoadBypassCache_Implicit2) {
+// Tests that when StopCaching is invoked on a writer, dependent transactions
+// are restarted.
+TEST(HttpCache, SimpleGET_ParallelValidationStopCaching) {
MockHttpCache cache;
- // write to the cache
- RunTransactionTest(cache.http_cache(), kSimpleGET_Transaction);
+ MockHttpRequest request(kSimpleGET_Transaction);
- // force this transaction to write to the cache again
MockTransaction transaction(kSimpleGET_Transaction);
- transaction.request_headers = "cache-control: no-cache\r\n";
-
- RunTransactionTest(cache.http_cache(), transaction);
+ transaction.load_flags |= LOAD_VALIDATE_CACHE;
+ MockHttpRequest validate_request(transaction);
- EXPECT_EQ(2, cache.network_layer()->transaction_count());
- EXPECT_EQ(0, cache.disk_cache()->open_count());
- EXPECT_EQ(2, cache.disk_cache()->create_count());
-}
+ const int kNumTransactions = 3;
+ std::vector<std::unique_ptr<Context>> context_list;
-TEST(HttpCache, SimpleGET_LoadValidateCache) {
- MockHttpCache cache;
+ for (int i = 0; i < kNumTransactions; ++i) {
+ context_list.push_back(base::MakeUnique<Context>());
+ auto& c = context_list[i];
- // Write to the cache.
- RunTransactionTest(cache.http_cache(), kSimpleGET_Transaction);
+ c->result = cache.CreateTransaction(&c->trans);
+ ASSERT_THAT(c->result, IsOk());
- // Read from the cache.
- RunTransactionTest(cache.http_cache(), kSimpleGET_Transaction);
+ MockHttpRequest* this_request = &request;
+ if (i == 2) {
+ this_request = &validate_request;
+ c->trans->SetBeforeNetworkStartCallback(base::Bind(&DeferNetworkStart));
+ }
- // Force this transaction to validate the cache.
- MockTransaction transaction(kSimpleGET_Transaction);
- transaction.load_flags |= LOAD_VALIDATE_CACHE;
+ c->result = c->trans->Start(this_request, c->callback.callback(),
+ NetLogWithSource());
+ }
- HttpResponseInfo response_info;
- BoundTestNetLog log;
- LoadTimingInfo load_timing_info;
- RunTransactionTestWithResponseInfoAndGetTiming(
- cache.http_cache(), transaction, &response_info, log.bound(),
- &load_timing_info);
+ // Allow all requests to move from the Create queue to the active entry.
+ base::RunLoop().RunUntilIdle();
EXPECT_EQ(2, cache.network_layer()->transaction_count());
- EXPECT_EQ(1, cache.disk_cache()->open_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
EXPECT_EQ(1, cache.disk_cache()->create_count());
- EXPECT_TRUE(response_info.network_accessed);
- TestLoadTimingNetworkRequest(load_timing_info);
-}
-TEST(HttpCache, SimpleGET_LoadValidateCache_Implicit) {
- MockHttpCache cache;
+ EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
+ EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
+ EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
- // write to the cache
- RunTransactionTest(cache.http_cache(), kSimpleGET_Transaction);
+ // Invoking StopCaching on the writer will lead to dooming the entry and
+ // restarting the remaining transactions which will then create a new entry.
+ context_list[0]->trans->StopCaching();
- // read from the cache
- RunTransactionTest(cache.http_cache(), kSimpleGET_Transaction);
+ // Resume network start for headers_transaction. It should be restarted due to
+ // writer cancellation.
+ auto& c = context_list[2];
+ c->trans->ResumeNetworkStart();
- // force this transaction to validate the cache
- MockTransaction transaction(kSimpleGET_Transaction);
- transaction.request_headers = "cache-control: max-age=0\r\n";
+ base::RunLoop().RunUntilIdle();
- RunTransactionTest(cache.http_cache(), transaction);
+ EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
+ EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
- EXPECT_EQ(2, cache.network_layer()->transaction_count());
- EXPECT_EQ(1, cache.disk_cache()->open_count());
- EXPECT_EQ(1, cache.disk_cache()->create_count());
+ // Resume network start for the transaction the second time.
+ c->trans->ResumeNetworkStart();
+ base::RunLoop().RunUntilIdle();
+
+ // Headers transaction would have doomed the new entry created.
+ EXPECT_TRUE(
+ cache.disk_cache()->IsDiskEntryDoomed(kSimpleGET_Transaction.url));
+
+ // Complete the rest of the transactions.
+ for (auto& context : context_list) {
+ if (!context)
+ continue;
+ ReadAndVerifyTransaction(context->trans.get(), kSimpleGET_Transaction);
+ }
+
+ EXPECT_EQ(4, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(2, cache.disk_cache()->create_count());
}
-// Tests that |unused_since_prefetch| is updated accordingly (e.g. it is set to
-// true after a prefetch and set back to false when the prefetch is used).
-TEST(HttpCache, SimpleGET_UnusedSincePrefetch) {
+// Tests that a transaction is currently in headers phase and is destroyed
+// leading to destroying the entry.
+TEST(HttpCache, SimpleGET_ParallelValidationCancelHeaders) {
MockHttpCache cache;
- HttpResponseInfo response_info;
-
- // A normal load does not have |unused_since_prefetch| set.
- RunTransactionTestWithResponseInfoAndGetTiming(
- cache.http_cache(), kSimpleGET_Transaction, &response_info,
- BoundTestNetLog().bound(), nullptr);
- EXPECT_FALSE(response_info.unused_since_prefetch);
- EXPECT_FALSE(response_info.was_cached);
- // The prefetch itself does not have |unused_since_prefetch| set.
- MockTransaction prefetch_transaction(kSimpleGET_Transaction);
- prefetch_transaction.load_flags |= LOAD_PREFETCH;
- RunTransactionTestWithResponseInfoAndGetTiming(
- cache.http_cache(), prefetch_transaction, &response_info,
- BoundTestNetLog().bound(), nullptr);
- EXPECT_FALSE(response_info.unused_since_prefetch);
- EXPECT_TRUE(response_info.was_cached);
+ MockHttpRequest request(kSimpleGET_Transaction);
- // A duplicated prefetch has |unused_since_prefetch| set.
- RunTransactionTestWithResponseInfoAndGetTiming(
- cache.http_cache(), prefetch_transaction, &response_info,
- BoundTestNetLog().bound(), nullptr);
- EXPECT_TRUE(response_info.unused_since_prefetch);
- EXPECT_TRUE(response_info.was_cached);
+ const int kNumTransactions = 2;
+ std::vector<std::unique_ptr<Context>> context_list;
- // |unused_since_prefetch| is still true after two prefetches in a row.
- RunTransactionTestWithResponseInfoAndGetTiming(
- cache.http_cache(), kSimpleGET_Transaction, &response_info,
- BoundTestNetLog().bound(), nullptr);
- EXPECT_TRUE(response_info.unused_since_prefetch);
- EXPECT_TRUE(response_info.was_cached);
+ for (int i = 0; i < kNumTransactions; ++i) {
+ context_list.push_back(base::MakeUnique<Context>());
+ auto& c = context_list[i];
- // The resource has now been used, back to normal behavior.
- RunTransactionTestWithResponseInfoAndGetTiming(
- cache.http_cache(), kSimpleGET_Transaction, &response_info,
- BoundTestNetLog().bound(), nullptr);
- EXPECT_FALSE(response_info.unused_since_prefetch);
- EXPECT_TRUE(response_info.was_cached);
-}
+ c->result = cache.CreateTransaction(&c->trans);
+ ASSERT_THAT(c->result, IsOk());
-static void PreserveRequestHeaders_Handler(const HttpRequestInfo* request,
- std::string* response_status,
- std::string* response_headers,
- std::string* response_data) {
- EXPECT_TRUE(request->extra_headers.HasHeader(kExtraHeaderKey));
-}
+ if (i == 0)
+ c->trans->SetBeforeNetworkStartCallback(base::Bind(&DeferNetworkStart));
-// Tests that we don't remove extra headers for simple requests.
-TEST(HttpCache, SimpleGET_PreserveRequestHeaders) {
- MockHttpCache cache;
+ c->result =
+ c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
+ }
- MockTransaction transaction(kSimpleGET_Transaction);
- transaction.handler = PreserveRequestHeaders_Handler;
- transaction.request_headers = EXTRA_HEADER;
- transaction.response_headers = "Cache-Control: max-age=0\n";
- AddMockTransaction(&transaction);
+ base::RunLoop().RunUntilIdle();
- // Write, then revalidate the entry.
- RunTransactionTest(cache.http_cache(), transaction);
- RunTransactionTest(cache.http_cache(), transaction);
+ EXPECT_TRUE(cache.IsHeadersTransactionPresent(kSimpleGET_Transaction.url));
+ EXPECT_EQ(1, cache.GetCountAddToEntryQueue(kSimpleGET_Transaction.url));
- EXPECT_EQ(2, cache.network_layer()->transaction_count());
- EXPECT_EQ(1, cache.disk_cache()->open_count());
+ EXPECT_EQ(1, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
EXPECT_EQ(1, cache.disk_cache()->create_count());
- RemoveMockTransaction(&transaction);
-}
-
-// Tests that we don't remove extra headers for conditionalized requests.
-TEST(HttpCache, ConditionalizedGET_PreserveRequestHeaders) {
- MockHttpCache cache;
- // Write to the cache.
- RunTransactionTest(cache.http_cache(), kETagGET_Transaction);
+ // Delete the headers transaction.
+ context_list[0].reset();
- MockTransaction transaction(kETagGET_Transaction);
- transaction.handler = PreserveRequestHeaders_Handler;
- transaction.request_headers = "If-None-Match: \"foopy\"\r\n"
- EXTRA_HEADER;
- AddMockTransaction(&transaction);
+ base::RunLoop().RunUntilIdle();
- RunTransactionTest(cache.http_cache(), transaction);
+ // Complete the rest of the transactions.
+ for (auto& context : context_list) {
+ if (!context)
+ continue;
+ ReadAndVerifyTransaction(context->trans.get(), kSimpleGET_Transaction);
+ }
EXPECT_EQ(2, cache.network_layer()->transaction_count());
- EXPECT_EQ(1, cache.disk_cache()->open_count());
- EXPECT_EQ(1, cache.disk_cache()->create_count());
- RemoveMockTransaction(&transaction);
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(2, cache.disk_cache()->create_count());
}
-TEST(HttpCache, SimpleGET_ManyReaders) {
+// Similar to the above test, except here cache write fails and the
+// validated transactions should be restarted.
+TEST(HttpCache, SimpleGET_ParallelValidationFailWrite) {
MockHttpCache cache;
MockHttpRequest request(kSimpleGET_Transaction);
- std::vector<std::unique_ptr<Context>> context_list;
const int kNumTransactions = 5;
+ std::vector<std::unique_ptr<Context>> context_list;
for (int i = 0; i < kNumTransactions; ++i) {
context_list.push_back(base::MakeUnique<Context>());
- Context* c = context_list[i].get();
+ auto& c = context_list[i];
c->result = cache.CreateTransaction(&c->trans);
ASSERT_THAT(c->result, IsOk());
@@ -1360,40 +2231,57 @@ TEST(HttpCache, SimpleGET_ManyReaders) {
}
// All requests are waiting for the active entry.
- for (int i = 0; i < kNumTransactions; ++i) {
- Context* c = context_list[i].get();
- EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, c->trans->GetLoadState());
+ for (auto& context : context_list) {
+ EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, context->trans->GetLoadState());
}
// Allow all requests to move from the Create queue to the active entry.
base::RunLoop().RunUntilIdle();
// The first request should be a writer at this point, and the subsequent
- // requests should be pending.
+ // requests should have passed the validation phase and waiting for the
+ // response to be written to the cache before they can read.
+ EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
+ EXPECT_EQ(4, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
+
+ // All requests depend on the writer, and the writer is between Start and
+ // Read, i.e. idle.
+ for (auto& context : context_list) {
+ EXPECT_EQ(LOAD_STATE_IDLE, context->trans->GetLoadState());
+ }
+
+ // The first request should be a writer at this point, and the subsequent
+ // requests should have passed the validation phase and waiting for the
+ // response to be written to the cache before they can read.
EXPECT_EQ(1, cache.network_layer()->transaction_count());
EXPECT_EQ(0, cache.disk_cache()->open_count());
EXPECT_EQ(1, cache.disk_cache()->create_count());
- // All requests depend on the writer, and the writer is between Start and
- // Read, i.e. idle.
- for (int i = 0; i < kNumTransactions; ++i) {
- Context* c = context_list[i].get();
- EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
- }
+ // Fail the request.
+ cache.disk_cache()->set_soft_failures(true);
+ // We have to open the entry again to propagate the failure flag.
+ disk_cache::Entry* en;
+ cache.OpenBackendEntry(kSimpleGET_Transaction.url, &en);
+ en->Close();
for (int i = 0; i < kNumTransactions; ++i) {
- Context* c = context_list[i].get();
+ auto& c = context_list[i];
if (c->result == ERR_IO_PENDING)
c->result = c->callback.WaitForResult();
+ if (i == 1) {
+ // The earlier entry must be destroyed and its disk entry doomed.
+ EXPECT_TRUE(
+ cache.disk_cache()->IsDiskEntryDoomed(kSimpleGET_Transaction.url));
+ }
ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
}
- // We should not have had to re-open the disk entry
-
- EXPECT_EQ(1, cache.network_layer()->transaction_count());
- EXPECT_EQ(0, cache.disk_cache()->open_count());
- EXPECT_EQ(1, cache.disk_cache()->create_count());
+ // Since validated transactions were restarted and new entry read/write
+ // operations would also fail, all requests would have gone to the network.
+ EXPECT_EQ(5, cache.network_layer()->transaction_count());
+ EXPECT_EQ(1, cache.disk_cache()->open_count());
+ EXPECT_EQ(5, cache.disk_cache()->create_count());
}
// This is a test for http://code.google.com/p/chromium/issues/detail?id=4769.
@@ -1440,11 +2328,12 @@ TEST(HttpCache, SimpleGET_RacingReaders) {
c->result = c->callback.WaitForResult();
ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
- // Now we have 2 active readers and two queued transactions.
-
+ // Now all transactions should be waiting for read to be invoked. Two readers
+ // are because of the load flags and remaining two transactions were converted
+ // to readers after skipping validation. Note that the remaining two went on
+ // to process the headers in parallel with readers present on the entry.
EXPECT_EQ(LOAD_STATE_IDLE, context_list[2]->trans->GetLoadState());
- EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE,
- context_list[3]->trans->GetLoadState());
+ EXPECT_EQ(LOAD_STATE_IDLE, context_list[3]->trans->GetLoadState());
c = context_list[1].get();
ASSERT_THAT(c->result, IsError(ERR_IO_PENDING));
@@ -1504,12 +2393,16 @@ TEST(HttpCache, SimpleGET_DoomWithPending) {
NetLogWithSource());
}
+ base::RunLoop().RunUntilIdle();
+
// The first request should be a writer at this point, and the two subsequent
// requests should be pending. The last request doomed the first entry.
EXPECT_EQ(2, cache.network_layer()->transaction_count());
- // Cancel the first queued transaction.
+ // Cancel the second transaction. Note that this and the 3rd transactions
+ // would have completed their headers phase and would be waiting in the
+ // done_headers_queue when the 2nd transaction is cancelled.
context_list[1].reset();
for (int i = 0; i < kNumTransactions; ++i) {
@@ -1551,11 +2444,12 @@ TEST(HttpCache, FastNoStoreGET_DoneWithPending) {
base::RunLoop().RunUntilIdle();
// The first request should be a writer at this point, and the subsequent
- // requests should be pending.
+ // requests should have completed validation. Since the validation does not
+ // result in a match, a new entry would be created.
- EXPECT_EQ(1, cache.network_layer()->transaction_count());
+ EXPECT_EQ(3, cache.network_layer()->transaction_count());
EXPECT_EQ(0, cache.disk_cache()->open_count());
- EXPECT_EQ(1, cache.disk_cache()->create_count());
+ EXPECT_EQ(2, cache.disk_cache()->create_count());
// Now, make sure that the second request asks for the entry not to be stored.
request_handler.set_no_store(true);
@@ -1609,6 +2503,9 @@ TEST(HttpCache, SimpleGET_ManyWriters_CancelFirst) {
if (c->result == ERR_IO_PENDING)
c->result = c->callback.WaitForResult();
// Destroy only the first transaction.
+ // This should lead to all transactions to restart, even those that have
+ // validated themselves and were waiting for the writer transaction to
+ // complete writing to the cache.
if (i == 0) {
context_list[i].reset();
}
@@ -2497,6 +3394,8 @@ TEST(HttpCache, ETagGET_ConditionalRequest_304_NoStore) {
// be returned.
// (4) loads |kUrl| from cache only -- expects |cached_response_2| to be
// returned.
+// The entry will be created once and will be opened for the 3 subsequent
+// requests.
static void ConditionalizedRequestUpdatesCacheHelper(
const Response& net_response_1,
const Response& net_response_2,
@@ -6069,13 +6968,14 @@ TEST(HttpCache, GET_IncompleteResource_Cancel) {
rv = c->trans->Read(buf.get(), buf->size(), c->callback.callback());
EXPECT_EQ(5, c->callback.GetResult(rv));
- // Cancel the requests.
+ // Since |pending| is currently validating the already written headers
+ // it will be restarted as well.
c.reset();
pending.reset();
EXPECT_EQ(1, cache.network_layer()->transaction_count());
EXPECT_EQ(1, cache.disk_cache()->open_count());
- EXPECT_EQ(2, cache.disk_cache()->create_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
base::RunLoop().RunUntilIdle();
RemoveMockTransaction(&transaction);
@@ -6800,46 +7700,6 @@ TEST(HttpCache, WriteMetadata_Fail) {
EXPECT_EQ(1, cache.disk_cache()->create_count());
}
-// Tests that if a metadata writer transaction hits cache lock timeout, it will
-// error out.
-TEST(HttpCache, WriteMetadata_CacheLockTimeout) {
- MockHttpCache cache;
-
- // Write to the cache
- HttpResponseInfo response;
- RunTransactionTestWithResponseInfo(cache.http_cache(), kSimpleGET_Transaction,
- &response);
- EXPECT_FALSE(response.metadata.get());
-
- MockHttpRequest request(kSimpleGET_Transaction);
- Context c1;
- ASSERT_THAT(cache.CreateTransaction(&c1.trans), IsOk());
- ASSERT_EQ(ERR_IO_PENDING, c1.trans->Start(&request, c1.callback.callback(),
- NetLogWithSource()));
-
- cache.SimulateCacheLockTimeout();
-
- // Write meta data to the same entry.
- scoped_refptr<IOBufferWithSize> buf(new IOBufferWithSize(50));
- memset(buf->data(), 0, buf->size());
- base::strlcpy(buf->data(), "Hi there", buf->size());
- cache.http_cache()->WriteMetadata(GURL(kSimpleGET_Transaction.url),
- DEFAULT_PRIORITY, response.response_time,
- buf.get(), buf->size());
-
- // Release the buffer before the operation takes place.
- buf = NULL;
-
- // Makes sure we finish pending operations.
- base::RunLoop().RunUntilIdle();
-
- RunTransactionTestWithResponseInfo(cache.http_cache(), kSimpleGET_Transaction,
- &response);
-
- // The writer transaction should fail due to cache lock timeout.
- ASSERT_FALSE(response.metadata.get());
-}
-
// Tests that we ignore VARY checks when writing metadata since the request
// headers for the WriteMetadata transaction are made up.
TEST(HttpCache, WriteMetadata_IgnoreVary) {
@@ -7167,10 +8027,6 @@ TEST(HttpCache, StopCachingWithAuthDeletesEntry) {
EXPECT_THAT(callback.GetResult(rv), IsOk());
trans->StopCaching();
-
- scoped_refptr<IOBuffer> buf(new IOBuffer(256));
- rv = trans->Read(buf.get(), 10, callback.callback());
- EXPECT_EQ(callback.GetResult(rv), 10);
}
RemoveMockTransaction(&mock_transaction);
@@ -7219,7 +8075,9 @@ TEST(HttpCache, StopCachingSavesEntry) {
}
// Verify that the entry is marked as incomplete.
- VerifyTruncatedFlag(&cache, kSimpleGET_Transaction.url, true, 0);
+ // VerifyTruncatedFlag(&cache, kSimpleGET_Transaction.url, true, 0);
+ // Verify that the entry is doomed.
+ cache.disk_cache()->IsDiskEntryDoomed(kSimpleGET_Transaction.url);
}
// Tests that we handle truncated enries when StopCaching is called.
« no previous file with comments | « net/http/http_cache_transaction.cc ('k') | net/http/http_transaction.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698