Index: net/http/http_cache_unittest.cc |
diff --git a/net/http/http_cache_unittest.cc b/net/http/http_cache_unittest.cc |
index 27222a905449abe15cd263149bb25c373a576a89..288b1c7dab731e000ddeacd9a53dca87dfea9a5d 100644 |
--- a/net/http/http_cache_unittest.cc |
+++ b/net/http/http_cache_unittest.cc |
@@ -1375,7 +1375,8 @@ TEST(HttpCache, SimpleGET_ManyReaders) { |
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_EQ(1, cache.network_layer()->transaction_count()); |
EXPECT_EQ(0, cache.disk_cache()->open_count()); |
@@ -1407,6 +1408,175 @@ TEST(HttpCache, SimpleGET_ManyReaders) { |
} |
} |
+// Tests parallel validation similar to the above test, except here validation |
+// request is sent out to the network and results in 304. |
+TEST(HttpCache, SimpleGET_ParallelValidation) { |
+ MockHttpCache cache; |
+ |
+ std::vector<MockHttpRequest> request; |
+ ScopedMockTransaction transaction(kTypicalGET_Transaction); |
+ transaction.response_headers = |
+ "Etag: \"foopy\"\n" |
+ "Cache-Control: max-age=0\n"; |
+ MockHttpRequest request0(transaction); |
+ request.push_back(request0); |
+ |
+ ScopedMockTransaction transaction1(kTypicalGET_Transaction); |
+ transaction1.status = "HTTP/1.1 304 Not Modified"; |
+ transaction1.load_flags |= LOAD_VALIDATE_CACHE; |
+ transaction1.response_headers = |
+ "Etag: \"foopy\"\n" |
+ "Cache-Control: max-age=3600\n"; |
+ MockHttpRequest request1(transaction1); |
+ request.push_back(request1); |
+ |
+ std::vector<Context*> context_list; |
+ const int kNumTransactions = 2; |
+ |
+ for (int i = 0; i < kNumTransactions; ++i) { |
+ context_list.push_back(new Context()); |
+ Context* 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[i], c->callback.callback(), |
+ NetLogWithSource()); |
+ } |
+ |
+ // All requests are waiting for the active entry. |
+ for (int i = 0; i < kNumTransactions; ++i) { |
+ Context* c = context_list[i]; |
+ EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, c->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_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 (int i = 0; i < kNumTransactions; ++i) { |
+ Context* c = context_list[i]; |
+ EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState()); |
+ } |
+ |
+ for (int i = 0; i < kNumTransactions; ++i) { |
+ Context* c = context_list[i]; |
+ if (c->result == ERR_IO_PENDING) |
+ c->result = c->callback.WaitForResult(); |
+ ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction); |
+ } |
+ |
+ // We should not have had to re-open the disk entry |
+ |
+ 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) { |
+ Context* c = context_list[i]; |
+ delete c; |
+ } |
+} |
+ |
+// Tests parallel validation similar to the above test, except here cache write |
+// fails and the validated transactions should be restarted. |
+TEST(HttpCache, SimpleGET_ParallelValidationFailWrite) { |
+ MockHttpCache cache; |
+ |
+ std::vector<MockHttpRequest> request; |
+ ScopedMockTransaction transaction(kTypicalGET_Transaction); |
+ transaction.response_headers = |
+ "Etag: \"foopy\"\n" |
+ "Cache-Control: max-age=0\n"; |
+ MockHttpRequest request0(transaction); |
+ request.push_back(request0); |
+ |
+ ScopedMockTransaction transaction1(kTypicalGET_Transaction); |
+ transaction1.status = "HTTP/1.1 304 Not Modified"; |
+ transaction1.load_flags |= LOAD_VALIDATE_CACHE; |
+ transaction1.response_headers = |
+ "Etag: \"foopy\"\n" |
+ "Cache-Control: max-age=3600\n"; |
+ MockHttpRequest request1(transaction1); |
+ request.push_back(request1); |
+ |
+ std::vector<Context*> context_list; |
+ const int kNumTransactions = 2; |
+ |
+ for (int i = 0; i < kNumTransactions; ++i) { |
+ context_list.push_back(new Context()); |
+ Context* 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[i], c->callback.callback(), |
+ NetLogWithSource()); |
+ } |
+ |
+ // All requests are waiting for the active entry. |
+ for (int i = 0; i < kNumTransactions; ++i) { |
+ Context* c = context_list[i]; |
+ EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, c->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_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 (int i = 0; i < kNumTransactions; ++i) { |
+ Context* c = context_list[i]; |
+ 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(kTypicalGET_Transaction.url, &en); |
+ en->Close(); |
+ |
+ for (int i = 0; i < kNumTransactions; ++i) { |
+ Context* c = context_list[i]; |
+ if (c->result == ERR_IO_PENDING) |
+ c->result = c->callback.WaitForResult(); |
+ ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction); |
+ } |
+ |
+ // We should not have had to re-open the disk entry |
+ |
+ // Since validated transaction was restarted, there is an additional network |
+ // transaction created. |
+ EXPECT_EQ(3, cache.network_layer()->transaction_count()); |
+ EXPECT_EQ(1, cache.disk_cache()->open_count()); |
+ EXPECT_EQ(2, cache.disk_cache()->create_count()); |
+ |
+ for (int i = 0; i < kNumTransactions; ++i) { |
+ Context* c = context_list[i]; |
+ delete c; |
+ } |
+} |
+ |
// This is a test for http://code.google.com/p/chromium/issues/detail?id=4769. |
// If cancelling a request is racing with another request for the same resource |
// finishing, we have to make sure that we remove both transactions from the |
@@ -1451,11 +1621,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 presnt 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]; |
ASSERT_THAT(c->result, IsError(ERR_IO_PENDING)); |
@@ -1520,12 +1691,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) { |
@@ -1567,11 +1742,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); |
@@ -1625,6 +1801,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) { |
delete c; |
context_list[i] = NULL; |
@@ -1649,6 +1828,137 @@ TEST(HttpCache, SimpleGET_ManyWriters_CancelFirst) { |
} |
} |
+// Tests that a transaction which is in validated queue can be destroyed without |
+// any impact to other transactions. |
+TEST(HttpCache, SimpleGET_ManyWriters_CancelValidated) { |
+ MockHttpCache cache; |
+ |
+ MockHttpRequest request(kSimpleGET_Transaction); |
+ |
+ std::vector<Context*> context_list; |
+ const int kNumTransactions = 2; |
+ |
+ for (int i = 0; i < kNumTransactions; ++i) { |
+ context_list.push_back(new Context()); |
+ Context* 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 be pending. |
+ |
+ EXPECT_EQ(1, 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) { |
+ Context* c = context_list[i]; |
+ if (i == 0 && c->result == ERR_IO_PENDING) |
+ c->result = c->callback.WaitForResult(); |
+ // Destroy one of the transactions waiting after the validated stage. |
+ if (i == 1) { |
+ delete c; |
+ context_list[i] = NULL; |
+ } |
+ } |
+ |
+ // Complete the rest of the transactions. |
+ for (int i = 0; i < kNumTransactions; ++i) { |
+ if (i == 1) |
+ continue; |
+ Context* c = context_list[i]; |
+ ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction); |
+ } |
+ |
+ // We should 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()); |
+ |
+ for (int i = 0; i < kNumTransactions; ++i) { |
+ if (i == 1) |
+ continue; |
+ Context* c = context_list[i]; |
+ delete c; |
+ } |
+} |
+ |
+// Tests that a transaction which is currently validating can be destroyed |
+// without any impact to other transactions. |
+TEST(HttpCache, SimpleGET_ManyWriters_CancelValidating) { |
+ MockHttpCache cache; |
+ |
+ MockHttpRequest request(kSimpleGET_Transaction); |
+ |
+ std::vector<Context*> context_list; |
+ const int kNumTransactions = 2; |
+ |
+ for (int i = 0; i < kNumTransactions; ++i) { |
+ context_list.push_back(new Context()); |
+ Context* 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()); |
+ } |
+ |
+ // Delete the currently validating transaction. |
+ Context* c = context_list[1]; |
+ EXPECT_EQ(ERR_IO_PENDING, c->result); |
+ delete c; |
+ context_list[1] = nullptr; |
+ |
+ // 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. |
+ |
+ EXPECT_EQ(1, 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) { |
+ if (i == 1) |
+ continue; |
+ Context* c = context_list[i]; |
+ if (c->result == ERR_IO_PENDING) |
+ c->result = c->callback.WaitForResult(); |
+ } |
+ |
+ // Complete the rest of the transactions. |
+ for (int i = 0; i < kNumTransactions; ++i) { |
+ if (i == 1) |
+ continue; |
+ Context* c = context_list[i]; |
+ ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction); |
+ } |
+ |
+ // We should 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()); |
+ |
+ for (int i = 0; i < kNumTransactions; ++i) { |
+ if (i == 1) |
+ continue; |
+ Context* c = context_list[i]; |
+ delete c; |
+ } |
+} |
+ |
// Tests that we can cancel requests that are queued waiting to open the disk |
// cache entry. |
TEST(HttpCache, SimpleGET_ManyWriters_CancelCreate) { |
@@ -2540,6 +2850,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, |
@@ -6115,12 +6427,14 @@ TEST(HttpCache, GET_IncompleteResource_Cancel) { |
EXPECT_EQ(5, c->callback.GetResult(rv)); |
// Cancel the requests. |
+ // Since |pending| is currently vaidating the already written headers |
+ // it will be restarted as well. |
delete c; |
delete pending; |
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); |
@@ -6845,46 +7159,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) { |
@@ -7212,10 +7486,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); |