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. |