Chromium Code Reviews| Index: net/http/http_cache_unittest.cc |
| diff --git a/net/http/http_cache_unittest.cc b/net/http/http_cache_unittest.cc |
| index 779af46f7c7f1d9ee2a0e6cc78d70e6fe41b2cf6..f1c550c8e502d04bfc42b4eb852959f7f3a48924 100644 |
| --- a/net/http/http_cache_unittest.cc |
| +++ b/net/http/http_cache_unittest.cc |
| @@ -5,9 +5,11 @@ |
| #include "net/http/http_cache.h" |
| #include <algorithm> |
| +#include <list> |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| +#include "base/format_macros.h" |
| #include "base/memory/scoped_vector.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/run_loop.h" |
| @@ -276,7 +278,6 @@ const MockTransaction kFastNoStoreGET_Transaction = { |
| &FastTransactionServer::FastNoStoreHandler, |
| nullptr, |
| 0, |
| - 0, |
| OK}; |
| // This class provides a handler for kRangeGET_TransactionOK so that the range |
| @@ -335,13 +336,23 @@ bool RangeTransactionServer::bad_200_ = false; |
| // AddHeadersFromString() (_not_ AddHeaderFromString()). |
| #define EXTRA_HEADER EXTRA_HEADER_LINE "\r\n" |
| -static const char kExtraHeaderKey[] = "Extra"; |
| +#define RETURN_DEFAULT_RANGE_HEADER "X-Return-Default-Range: 1\r\n" |
| + |
| +#define INDICATE_NO_RANGES_HEADER "X-Indicate-No-Ranges: 1\r\n" |
| + |
| +const char kExtraHeaderKey[] = "Extra"; |
| + |
| +const char kFullRangeData[] = |
| + "rg: 00-09 rg: 10-19 rg: 20-29 rg: 30-39 " |
| + "rg: 40-49 rg: 50-59 rg: 60-69 rg: 70-79 "; |
| // Static. |
| void RangeTransactionServer::RangeHandler(const HttpRequestInfo* request, |
| std::string* response_status, |
| std::string* response_headers, |
| std::string* response_data) { |
| + SCOPED_TRACE(testing::Message() << "Request headers: \n" |
| + << request->extra_headers.ToString()); |
| if (request->extra_headers.IsEmpty()) { |
| response_status->assign("HTTP/1.1 416 Requested Range Not Satisfiable"); |
| response_data->clear(); |
| @@ -375,8 +386,17 @@ void RangeTransactionServer::RangeHandler(const HttpRequestInfo* request, |
| ranges.size() != 1) { |
| // This is not a byte range request. We return 200. |
| response_status->assign("HTTP/1.1 200 OK"); |
| - response_headers->assign("Date: Wed, 28 Nov 2007 09:40:09 GMT"); |
| - response_data->assign("Not a range"); |
| + response_headers->assign("Date: Wed, 28 Nov 2007 09:40:09 GMT\n"); |
| + if (request->extra_headers.HasHeader("X-Indicate-No-Ranges")) { |
|
rvargas (doing something else)
2015/09/15 01:07:38
nit: maybe X-No-Ranges ?
rvargas (doing something else)
2015/09/15 01:07:39
Document new headers at line 311.
asanka
2015/09/17 21:59:41
Done. And documented the header at line 311.
|
| + response_data->assign("Not a range"); |
| + return; |
| + } |
| + response_headers->append( |
| + "Last-Modified: Sat, 18 Apr 2007 01:10:43 GMT\n" |
| + "ETag: \"foo\"\n" |
| + "Accept-Ranges: bytes\n" |
| + "Content-Length: 80\n"); |
| + response_data->assign(kFullRangeData); |
|
rvargas (doing something else)
2015/09/15 01:07:39
I'm worried that returning "correct" data by defau
|
| return; |
| } |
| @@ -393,6 +413,7 @@ void RangeTransactionServer::RangeHandler(const HttpRequestInfo* request, |
| response_data->clear(); |
| return; |
| } |
| + response_status->assign("HTTP/1.1 206 Partial"); |
| EXPECT_TRUE(byte_range.ComputeBounds(80)); |
| int start = static_cast<int>(byte_range.first_byte_position()); |
| @@ -449,13 +470,8 @@ const MockTransaction kRangeGET_TransactionOK = { |
| &RangeTransactionServer::RangeHandler, |
| nullptr, |
| 0, |
| - 0, |
| OK}; |
| -const char kFullRangeData[] = |
| - "rg: 00-09 rg: 10-19 rg: 20-29 rg: 30-39 " |
| - "rg: 40-49 rg: 50-59 rg: 60-69 rg: 70-79 "; |
| - |
| // Verifies the response headers (|response|) match a partial content |
| // response for the range starting at |start| and ending at |end|. |
| void Verify206Response(std::string response, int start, int end) { |
| @@ -2095,11 +2111,11 @@ TEST(HttpCache, GET_DontValidateCache_VaryMismatch) { |
| "Vary: Foo\n"; |
| AddMockTransaction(&transaction); |
| RunTransactionTest(cache.http_cache(), transaction); |
| - |
|
rvargas (doing something else)
2015/09/15 01:07:39
nit: keep this line.
asanka
2015/09/17 21:59:41
Done.
|
| // Read from the cache and don't revalidate the entry. |
| RevalidationServer server; |
| transaction.handler = server.Handler; |
| transaction.request_headers = "Foo: none\r\n"; |
| + |
| BoundTestNetLog log; |
| LoadTimingInfo load_timing_info; |
| RunTransactionTestAndGetTiming(cache.http_cache(), transaction, log.bound(), |
| @@ -3898,7 +3914,7 @@ TEST(HttpCache, GET_NoConditionalization) { |
| // Don't ask for a range. The cache will attempt to use the cached data but |
| // should discard it as it cannot be validated. A regular request should go |
| // to the server and a new entry should be created. |
| - transaction.request_headers = EXTRA_HEADER; |
| + transaction.request_headers = INDICATE_NO_RANGES_HEADER EXTRA_HEADER; |
| transaction.data = "Not a range"; |
| RunTransactionTestWithResponse(cache.http_cache(), transaction, &headers); |
| @@ -4366,7 +4382,8 @@ TEST(HttpCache, GET_206ReturnsSubrangeRange_CachedContent) { |
| // abort caching, restarting the request. |
| // The second network request should not be a byte range request so the server |
| // should return 200 + "Not a range" |
| - transaction.request_headers = "X-Return-Default-Range:\r\n" EXTRA_HEADER; |
| + transaction.request_headers = |
| + RETURN_DEFAULT_RANGE_HEADER INDICATE_NO_RANGES_HEADER EXTRA_HEADER; |
|
rvargas (doing something else)
2015/09/15 01:07:39
nit: do we really need the new #defines? I feel li
asanka
2015/09/17 21:59:41
Hmm. I see what you mean. Should I remove the othe
rvargas (doing something else)
2015/09/19 01:09:34
Do you mean EXTRA_HEADER? I think that was added t
|
| transaction.data = "Not a range"; |
| RunTransactionTestWithResponse(cache.http_cache(), transaction, &headers); |
| @@ -4758,7 +4775,7 @@ TEST(HttpCache, GET_Previous206_NewContent) { |
| // server will not modify the response so we'll get the default range... a |
| // real server will answer with 200. |
| MockTransaction transaction2(kRangeGET_TransactionOK); |
| - transaction2.request_headers = EXTRA_HEADER; |
| + transaction2.request_headers = INDICATE_NO_RANGES_HEADER EXTRA_HEADER; |
| transaction2.load_flags |= LOAD_VALIDATE_CACHE; |
| transaction2.data = "Not a range"; |
| RangeTransactionServer handler; |
| @@ -5368,7 +5385,8 @@ TEST(HttpCache, RangeGET_FastFlakyServer) { |
| MockHttpCache cache; |
| ScopedMockTransaction transaction(kRangeGET_TransactionOK); |
| - transaction.request_headers = "Range: bytes = 40-\r\n" EXTRA_HEADER; |
| + transaction.request_headers = |
| + "Range: bytes = 40-\r\n" INDICATE_NO_RANGES_HEADER EXTRA_HEADER; |
| transaction.test_mode = TEST_MODE_SYNC_NET_START; |
| transaction.load_flags |= LOAD_VALIDATE_CACHE; |
| @@ -5896,7 +5914,7 @@ TEST(HttpCache, GET_IncompleteResource2) { |
| // retry the request without using byte ranges. |
| std::string headers; |
| MockTransaction transaction(kRangeGET_TransactionOK); |
| - transaction.request_headers = EXTRA_HEADER; |
| + transaction.request_headers = INDICATE_NO_RANGES_HEADER EXTRA_HEADER; |
| transaction.data = "Not a range"; |
| RunTransactionTestWithResponse(cache.http_cache(), transaction, &headers); |
| @@ -6051,7 +6069,7 @@ TEST(HttpCache, GET_IncompleteResource4) { |
| // Now make a regular request. |
| std::string headers; |
| MockTransaction transaction(kRangeGET_TransactionOK); |
| - transaction.request_headers = EXTRA_HEADER; |
| + transaction.request_headers = INDICATE_NO_RANGES_HEADER EXTRA_HEADER; |
| transaction.data = "Not a range"; |
| RangeTransactionServer handler; |
| handler.set_bad_200(true); |
| @@ -6224,9 +6242,7 @@ TEST(HttpCache, CachedRedirect) { |
| ASSERT_EQ(OK, cache.CreateTransaction(&trans)); |
| int rv = trans->Start(&request, callback.callback(), BoundNetLog()); |
| - if (rv == ERR_IO_PENDING) |
| - rv = callback.WaitForResult(); |
| - ASSERT_EQ(OK, rv); |
| + ASSERT_EQ(OK, callback.GetResult(rv)); |
| const HttpResponseInfo* info = trans->GetResponseInfo(); |
| ASSERT_TRUE(info); |
| @@ -6746,45 +6762,8 @@ TEST(HttpCache, DoneReading) { |
| EXPECT_EQ(1, cache.disk_cache()->create_count()); |
| } |
| -// Tests that we stop caching when told. |
| -TEST(HttpCache, StopCachingDeletesEntry) { |
|
rvargas (doing something else)
2015/09/15 01:07:39
I'm curious about what fails with this test now. O
asanka
2015/09/17 21:59:41
It was testing whether StopCaching() deletes non-r
|
| - MockHttpCache cache; |
| - TestCompletionCallback callback; |
| - MockHttpRequest request(kSimpleGET_Transaction); |
| - |
| - { |
| - scoped_ptr<HttpTransaction> trans; |
| - ASSERT_EQ(OK, cache.CreateTransaction(&trans)); |
| - |
| - int rv = trans->Start(&request, callback.callback(), BoundNetLog()); |
| - EXPECT_EQ(OK, callback.GetResult(rv)); |
| - |
| - scoped_refptr<IOBuffer> buf(new IOBuffer(256)); |
| - rv = trans->Read(buf.get(), 10, callback.callback()); |
| - EXPECT_EQ(10, callback.GetResult(rv)); |
| - |
| - trans->StopCaching(); |
| - |
| - // We should be able to keep reading. |
| - rv = trans->Read(buf.get(), 256, callback.callback()); |
| - EXPECT_GT(callback.GetResult(rv), 0); |
| - rv = trans->Read(buf.get(), 256, callback.callback()); |
| - EXPECT_EQ(0, callback.GetResult(rv)); |
| - } |
| - |
| - // Make sure that the ActiveEntry is gone. |
| - base::MessageLoop::current()->RunUntilIdle(); |
| - |
| - // Verify that the entry is gone. |
| - RunTransactionTest(cache.http_cache(), kSimpleGET_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()); |
| -} |
| - |
| -// Tests that we stop caching when told, even if DoneReading is called |
| -// after StopCaching. |
| +// Tests that we stop caching when told, even if DoneReading is called after |
| +// StopCaching. |
| TEST(HttpCache, StopCachingThenDoneReadingDeletesEntry) { |
| MockHttpCache cache; |
| TestCompletionCallback callback; |
| @@ -6859,53 +6838,60 @@ TEST(HttpCache, StopCachingWithAuthDeletesEntry) { |
| EXPECT_EQ(2, cache.disk_cache()->create_count()); |
| } |
| -// Tests that when we are told to stop caching we don't throw away valid data. |
| -TEST(HttpCache, StopCachingSavesEntry) { |
| +// Even if the request is resumable, the cache is still going to throw away the |
| +// partial entry if StopCaching() is called. This is done on the assumption that |
| +// such an entry is unlikely to be consumed from the cache again. |
| +TEST(HttpCache, StopCachingDeletesNewEntry) { |
| MockHttpCache cache; |
| TestCompletionCallback callback; |
| MockHttpRequest request(kSimpleGET_Transaction); |
| - { |
| - scoped_ptr<HttpTransaction> trans; |
| - ASSERT_EQ(OK, cache.CreateTransaction(&trans)); |
| + scoped_ptr<HttpTransaction> transaction; |
| + ASSERT_EQ(OK, cache.CreateTransaction(&transaction)); |
| - // Force a response that can be resumed. |
| - MockTransaction mock_transaction(kSimpleGET_Transaction); |
| - AddMockTransaction(&mock_transaction); |
| - mock_transaction.response_headers = "Cache-Control: max-age=10000\n" |
| - "Content-Length: 42\n" |
| - "Etag: \"foo\"\n"; |
| + MockTransaction mock_transaction(kSimpleGET_Transaction); |
| + AddMockTransaction(&mock_transaction); |
| + mock_transaction.response_headers = |
| + "Cache-Control: max-age=10000\n" |
| + "Content-Length: 42\n" |
| + "Accept-Ranges: bytes\n" |
| + "Etag: \"foo\"\n"; |
| + int rv = transaction->Start(&request, callback.callback(), BoundNetLog()); |
| + ASSERT_EQ(OK, callback.GetResult(rv)); |
| - int rv = trans->Start(&request, callback.callback(), BoundNetLog()); |
| - EXPECT_EQ(OK, callback.GetResult(rv)); |
| + const size_t kBufferSize = 256; |
| + scoped_refptr<IOBuffer> buf(new IOBuffer(kBufferSize)); |
| + rv = transaction->Read(buf.get(), 10, callback.callback()); |
| + EXPECT_EQ(callback.GetResult(rv), 10); |
| - scoped_refptr<IOBuffer> buf(new IOBuffer(256)); |
| - rv = trans->Read(buf.get(), 10, callback.callback()); |
| - EXPECT_EQ(callback.GetResult(rv), 10); |
| + transaction->StopCaching(); |
| - trans->StopCaching(); |
| + // Should still be able to read the remainder of the resource. Data should |
| + // still be coming from the same network request, which as asserted by the |
| + // network_layer()->transaction_count() test below. |
| + rv = transaction->Read(buf.get(), kBufferSize, callback.callback()); |
| + EXPECT_EQ(32, callback.GetResult(rv)); |
| + rv = transaction->Read(buf.get(), kBufferSize, callback.callback()); |
| + EXPECT_EQ(0, callback.GetResult(rv)); |
| - // We should be able to keep reading. |
| - rv = trans->Read(buf.get(), 256, callback.callback()); |
| - EXPECT_GT(callback.GetResult(rv), 0); |
| - rv = trans->Read(buf.get(), 256, callback.callback()); |
| - EXPECT_EQ(callback.GetResult(rv), 0); |
| + RemoveMockTransaction(&mock_transaction); |
| - RemoveMockTransaction(&mock_transaction); |
| - } |
| + // At this point, the cache entry should be gone. It should've been doomed and |
| + // released during the previous Read() cycles. |
| + disk_cache::Entry* cache_entry = nullptr; |
| + ASSERT_FALSE( |
| + cache.OpenBackendEntry(kSimpleGET_Transaction.url, &cache_entry)); |
| + EXPECT_EQ(1, cache.disk_cache()->create_count()); |
| + EXPECT_EQ(0, cache.disk_cache()->open_count()); |
| - // Verify that the entry is marked as incomplete. |
| - disk_cache::Entry* entry; |
| - ASSERT_TRUE(cache.OpenBackendEntry(kSimpleGET_Transaction.url, &entry)); |
| - HttpResponseInfo response; |
| - bool truncated = false; |
| - EXPECT_TRUE(MockHttpCache::ReadResponseInfo(entry, &response, &truncated)); |
| - EXPECT_TRUE(truncated); |
| - entry->Close(); |
| + // StopCaching shouldn't cause any additional network requests. |
| + EXPECT_EQ(1, cache.network_layer()->transaction_count()); |
| } |
| -// Tests that we handle truncated enries when StopCaching is called. |
| -TEST(HttpCache, StopCachingTruncatedEntry) { |
| +// If the request was being fulfilled via a truncated cache entry, and the |
| +// client calls StopCaching(), the transaction should remove the truncated cache |
| +// entry. |
| +TEST(HttpCache, StopCachingRemovesPreviousTruncatedEntry) { |
| MockHttpCache cache; |
| TestCompletionCallback callback; |
| MockHttpRequest request(kRangeGET_TransactionOK); |
| @@ -6919,44 +6905,960 @@ TEST(HttpCache, StopCachingTruncatedEntry) { |
| "Accept-Ranges: bytes\n" |
| "Content-Length: 80\n"); |
| CreateTruncatedEntry(raw_headers, &cache); |
| + cache.ResetCounts(); |
| - { |
| - // Now make a regular request. |
| - scoped_ptr<HttpTransaction> trans; |
| - ASSERT_EQ(OK, cache.CreateTransaction(&trans)); |
| + // Now make a regular request. |
| + scoped_ptr<HttpTransaction> transaction; |
| + ASSERT_EQ(OK, cache.CreateTransaction(&transaction)); |
| - int rv = trans->Start(&request, callback.callback(), BoundNetLog()); |
| - EXPECT_EQ(OK, callback.GetResult(rv)); |
| + int rv = transaction->Start(&request, callback.callback(), BoundNetLog()); |
| + ASSERT_EQ(OK, callback.GetResult(rv)); |
| + // Start() should send a single byte range request to validate the truncated |
| + // cache entry. |
| + EXPECT_EQ(1, cache.network_layer()->transaction_count()); |
| - scoped_refptr<IOBuffer> buf(new IOBuffer(256)); |
| - rv = trans->Read(buf.get(), 10, callback.callback()); |
| - EXPECT_EQ(callback.GetResult(rv), 10); |
| + const size_t kBufferSize = 256; |
| + scoped_refptr<IOBuffer> buf(new IOBuffer(kBufferSize)); |
| - // This is actually going to do nothing. |
| - trans->StopCaching(); |
| + // The size of the resource is 80 bytes. The first 20 should be available in |
| + // the truncated cache entry. Let's read the first 10 bytes which should all |
| + // come from the cache. |
| + rv = transaction->Read(buf.get(), 10, callback.callback()); |
| + EXPECT_EQ(callback.GetResult(rv), 10); |
| - // We should be able to keep reading. |
| - rv = trans->Read(buf.get(), 256, callback.callback()); |
| - EXPECT_GT(callback.GetResult(rv), 0); |
| - rv = trans->Read(buf.get(), 256, callback.callback()); |
| - EXPECT_GT(callback.GetResult(rv), 0); |
| - rv = trans->Read(buf.get(), 256, callback.callback()); |
| - EXPECT_EQ(callback.GetResult(rv), 0); |
| - } |
| + transaction->StopCaching(); |
| - // Verify that the disk entry was updated. |
| - disk_cache::Entry* entry; |
| - ASSERT_TRUE(cache.OpenBackendEntry(kRangeGET_TransactionOK.url, &entry)); |
| - EXPECT_EQ(80, entry->GetDataSize(1)); |
| - bool truncated = true; |
| - HttpResponseInfo response; |
| - EXPECT_TRUE(MockHttpCache::ReadResponseInfo(entry, &response, &truncated)); |
| - EXPECT_FALSE(truncated); |
| - entry->Close(); |
| + // We should be able to keep reading. The previous StopCaching() call switches |
| + // the transaction over to a network read. Hence the next read should slurp in |
| + // the remainder of the resource from the network. |
| + rv = transaction->Read(buf.get(), kBufferSize, callback.callback()); |
| + EXPECT_EQ(callback.GetResult(rv), 70); |
| + rv = transaction->Read(buf.get(), kBufferSize, callback.callback()); |
| + EXPECT_EQ(callback.GetResult(rv), 0); |
| + |
| + // The cache entry should be gone now. |
| + disk_cache::Entry* entry = nullptr; |
| + ASSERT_FALSE(cache.OpenBackendEntry(kRangeGET_TransactionOK.url, &entry)); |
| + |
| + // Transaction opens existing entry, but doesn't create any new entries. |
| + EXPECT_EQ(0, cache.disk_cache()->create_count()); |
| + EXPECT_EQ(1, cache.disk_cache()->open_count()); |
| + // Two network requests: |
| + // - 1 request for validation. |
| + // - 1 request for the range 10-. |
| + EXPECT_EQ(2, cache.network_layer()->transaction_count()); |
| RemoveMockTransaction(&kRangeGET_TransactionOK); |
| } |
| +TEST(HttpCache, StopCachingKeepsSparseEntry) { |
| + MockHttpCache cache; |
| + TestCompletionCallback callback; |
| + |
| + // Create a sparse entry which contains 10 bytes at offset 20 out of an 80 |
| + // byte resource. |
| + MockTransaction mock_transaction(kRangeGET_TransactionOK); |
| + mock_transaction.handler = nullptr; |
| + mock_transaction.request_headers = "Range: bytes = 20-29\r\n" EXTRA_HEADER; |
| + mock_transaction.response_headers = |
| + "Last-Modified: Sat, 18 Apr 2007 01:10:43 GMT\n" |
| + "ETag: \"foo\"\n" |
| + "Accept-Ranges: bytes\n" |
| + "Content-Range: bytes 20-29/80\n" |
| + "Content-Length: 10\n"; |
| + mock_transaction.data = "rg: 20-29 "; |
| + AddMockTransaction(&mock_transaction); |
| + std::string headers; |
| + RunTransactionTestWithResponse(cache.http_cache(), mock_transaction, |
| + &headers); |
| + |
| + // Verify our assumptions. There should be sparse entry here. |
| + disk_cache::Entry* cache_entry; |
| + ASSERT_TRUE(cache.OpenBackendEntry(mock_transaction.url, &cache_entry)); |
| + EXPECT_TRUE(cache_entry->CouldBeSparse()); |
| + cache_entry->Close(); |
| + RemoveMockTransaction(&mock_transaction); |
| + |
| + cache.ResetCounts(); |
| + |
| + // Prepare a request to read the entire contents of the resource. This should |
| + // usually hope between network -> cache -> network. But not today. |
| + mock_transaction.request_headers = EXTRA_HEADER; |
| + mock_transaction.handler = kRangeGET_TransactionOK.handler; |
| + mock_transaction.response_headers = |
| + "Last-Modified: Sat, 18 Apr 2007 01:10:43 GMT\n" |
| + "ETag: \"foo\"\n" |
| + "Accept-Ranges: bytes\n" |
| + "Content-Length: 80\n"; |
| + AddMockTransaction(&mock_transaction); |
| + MockHttpRequest request(mock_transaction); |
| + |
| + scoped_ptr<HttpTransaction> transaction; |
| + ASSERT_EQ(OK, cache.CreateTransaction(&transaction)); |
| + int rv = transaction->Start(&request, callback.callback(), BoundNetLog()); |
| + ASSERT_EQ(OK, callback.GetResult(rv)); |
| + |
| + const size_t kBufferSize = 256; |
| + scoped_refptr<IOBuffer> buffer(new IOBuffer(kBufferSize)); |
| + |
| + rv = transaction->Read(buffer.get(), 10, callback.callback()); |
| + EXPECT_EQ(10, callback.GetResult(rv)); |
| + |
| + // This should've caused a network read of 10 bytes. |
| + EXPECT_EQ(1, cache.network_layer()->transaction_count()); |
| + |
| + // StopCaching() should cause the transaction to switch entirely to the |
| + // network for the remainder of the response. Since the existing network |
| + // transaction doesn't cover the entirety of the resource, the cache creates a |
| + // new network transaction. |
| + transaction->StopCaching(); |
| + |
| + // Since it's coming from a single network request, we can slurp the remainder |
| + // of the resource in one request. |
| + rv = transaction->Read(buffer.get(), kBufferSize, callback.callback()); |
| + EXPECT_EQ(70, callback.GetResult(rv)); |
| + |
| + // And we hit EOF. |
| + rv = transaction->Read(buffer.get(), kBufferSize, callback.callback()); |
| + EXPECT_EQ(0, callback.GetResult(rv)); |
| + |
| + // Another network transaction is issued for the remainder of the resource |
| + // following the StopCaching call. |
| + EXPECT_EQ(2, cache.network_layer()->transaction_count()); |
| + |
| + // And the sparse entry is still here. |
| + ASSERT_TRUE(cache.OpenBackendEntry(mock_transaction.url, &cache_entry)); |
| + cache_entry->Close(); |
| + RemoveMockTransaction(&mock_transaction); |
| +} |
| + |
| +// If StopCaching() is called while serving the entire request out of cache, the |
| +// transaction is expected to ignore the StopCaching() call and continue serving |
| +// the remainder of the resource out of the cache. |
| +TEST(HttpCache, StopCachingKeepsFullEntry) { |
| + MockHttpCache cache; |
| + TestCompletionCallback callback; |
| + |
| + MockTransaction mock_transaction(kRangeGET_TransactionOK); |
| + mock_transaction.handler = nullptr; |
| + mock_transaction.request_headers = EXTRA_HEADER; |
| + mock_transaction.status = "HTTP/1.1 200 OK"; |
| + mock_transaction.response_headers = |
| + "Last-Modified: Sat, 18 Apr 2007 01:10:43 GMT\n" |
| + "ETag: \"foo\"\n" |
| + "Content-Length: 80\n"; |
| + mock_transaction.data = kFullRangeData; |
| + AddMockTransaction(&mock_transaction); |
| + std::string headers; |
| + RunTransactionTestWithResponse(cache.http_cache(), mock_transaction, |
| + &headers); |
| + |
| + cache.ResetCounts(); |
| + |
| + mock_transaction.request_headers = EXTRA_HEADER; |
| + mock_transaction.handler = kRangeGET_TransactionOK.handler; |
| + mock_transaction.response_headers = |
| + "Last-Modified: Sat, 18 Apr 2007 01:10:43 GMT\n" |
| + "ETag: \"foo\"\n" |
| + "Accept-Ranges: bytes\n" |
| + "Content-Length: 80\n"; |
| + AddMockTransaction(&mock_transaction); |
| + MockHttpRequest request(mock_transaction); |
| + |
| + scoped_ptr<HttpTransaction> transaction; |
| + ASSERT_EQ(OK, cache.CreateTransaction(&transaction)); |
| + int rv = transaction->Start(&request, callback.callback(), BoundNetLog()); |
| + ASSERT_EQ(OK, callback.GetResult(rv)); |
| + |
| + const size_t kBufferSize = 256; |
| + scoped_refptr<IOBuffer> buffer(new IOBuffer(kBufferSize)); |
| + |
| + rv = transaction->Read(buffer.get(), 10, callback.callback()); |
| + EXPECT_EQ(10, callback.GetResult(rv)); |
| + |
| + // This should've caused a cache read of 10 bytes. No new network transactions |
| + // are expected. |
| + EXPECT_EQ(0, cache.network_layer()->transaction_count()); |
| + |
| + // StopCaching() doesn't do anything because the entire resource is cached |
| + // already. We proceed with the cache read. |
| + transaction->StopCaching(); |
| + |
| + // Since it's all coming from a single network request, we can slurp the |
| + // remainder of the resource in one request. |
| + rv = transaction->Read(buffer.get(), kBufferSize, callback.callback()); |
| + EXPECT_EQ(70, callback.GetResult(rv)); |
| + |
| + // And we hit EOF. |
| + rv = transaction->Read(buffer.get(), kBufferSize, callback.callback()); |
| + EXPECT_EQ(0, callback.GetResult(rv)); |
| + |
| + // We should've only seen one network transactions at this point. |
| + EXPECT_EQ(0, cache.network_layer()->transaction_count()); |
| + |
| + // And the entry is still here. |
| + EXPECT_EQ(1, cache.disk_cache()->open_count()); |
| + EXPECT_EQ(0, cache.disk_cache()->create_count()); |
| + disk_cache::Entry* cache_entry; |
| + ASSERT_TRUE(cache.OpenBackendEntry(mock_transaction.url, &cache_entry)); |
| + cache_entry->Close(); |
| + |
| + RemoveMockTransaction(&mock_transaction); |
| +} |
| + |
| +namespace { |
| + |
| +const MockTransaction kResumableGET_Transaction = { |
| + kRangeGET_TransactionOK.url, "GET", base::Time(), EXTRA_HEADER, 0, |
|
rvargas (doing something else)
2015/09/15 01:07:39
nit: can we use one line per member here?
asanka
2015/09/17 21:59:41
git cl format.
|
| + "HTTP/1.1 200 OK", |
| + "Last-Modified: Sat, 18 Apr 2007 01:10:43 GMT\n" |
| + "Date: Sat, 18 Apr 2015 01:10:43 GMT\n" |
| + "ETag: \"foo\"\n" |
| + "Accept-Ranges: bytes\n" |
| + "Content-Length: 80\n", |
| + base::Time(), kFullRangeData, TEST_MODE_NORMAL, |
| + &RangeTransactionServer::RangeHandler}; |
|
rvargas (doing something else)
2015/09/15 01:07:39
declare all members.
|
| + |
| +} // namespace |
| + |
| +TEST(HttpCache, StopCachingReleasesCacheLockForNewCacheEntry) { |
| + MockHttpCache cache; |
| + TestCompletionCallback callback; |
| + ScopedMockTransaction mock_transaction(kResumableGET_Transaction); |
| + MockHttpRequest mock_request(mock_transaction); |
| + |
| + scoped_ptr<HttpTransaction> first_transaction; |
| + ASSERT_EQ(OK, cache.CreateTransaction(&first_transaction)); |
| + |
| + int rv = first_transaction->Start(&mock_request, callback.callback(), |
| + BoundNetLog()); |
| + ASSERT_EQ(OK, callback.GetResult(rv)); |
| + |
| + const size_t kBufferSize = 1024; |
| + scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(kBufferSize)); |
| + rv = first_transaction->Read(buf.get(), 10, callback.callback()); |
| + EXPECT_EQ(10, callback.GetResult(rv)); |
| + |
| + first_transaction->StopCaching(); |
| + |
| + rv = first_transaction->Read(buf.get(), 10, callback.callback()); |
| + EXPECT_EQ(10, callback.GetResult(rv)); |
| + |
| + // Since the cache entry is no longer locked by |first_transaction|, we can |
| + // start another transaction without blocking. The test should time out if |
| + // this is not the case (i.e. the Start() call has to wait indefinitely[*] |
| + // for the previous transaction to release the lock). |
| + // |
| + // [*]: The wait isn't really indefinite since the Start() call times out |
| + // (currently after 20 seconds) and proceeds without the cache entry. Failure |
| + // to release the cahe entry is accounted for by the previous |
| + // disk_cache()->GetEntryCount() expectation. |
| + scoped_ptr<HttpTransaction> second_transaction; |
| + ASSERT_EQ(OK, cache.CreateTransaction(&second_transaction)); |
| + rv = second_transaction->Start(&mock_request, callback.callback(), |
| + BoundNetLog()); |
| + ASSERT_EQ(OK, callback.GetResult(rv)); |
| + |
| + // The first cache entry was gone by the time second_transaction started. |
| + // Hence each transaction creates a cache entry. |
| + EXPECT_EQ(2, cache.disk_cache()->create_count()); |
| + EXPECT_EQ(0, cache.disk_cache()->open_count()); |
| + |
| + // The cache entry from second_transaction is still around. |
| + disk_cache::Entry* cache_entry = nullptr; |
| + ASSERT_TRUE(cache.OpenBackendEntry(mock_transaction.url, &cache_entry)); |
| + cache_entry->Close(); |
| + |
| + // One network request for each cache transaction. |
| + EXPECT_EQ(2, cache.network_layer()->transaction_count()); |
| +} |
| + |
| +TEST(HttpCache, StopCachingReleasesCacheLockForTruncatedEntry) { |
| + MockHttpCache cache; |
| + TestCompletionCallback callback; |
| + ScopedMockTransaction mock_transaction(kResumableGET_Transaction); |
| + MockHttpRequest mock_request(kResumableGET_Transaction); |
| + std::string raw_headers( |
| + "HTTP/1.1 200 OK\n" |
| + "Last-Modified: Sat, 18 Apr 2007 01:10:43 GMT\n" |
| + "Date: Sat, 18 Apr 2015 01:10:43 GMT\n" |
| + "ETag: \"foo\"\n" |
| + "Accept-Ranges: bytes\n" |
| + "Content-Length: 80\n"); |
| + CreateTruncatedEntry(raw_headers, &cache); |
| + cache.ResetCounts(); |
| + |
| + scoped_ptr<HttpTransaction> first_transaction; |
| + int rv = cache.CreateTransaction(&first_transaction); |
| + ASSERT_EQ(OK, rv); |
| + ASSERT_TRUE(first_transaction.get()); |
| + |
| + rv = first_transaction->Start(&mock_request, callback.callback(), |
| + BoundNetLog()); |
| + ASSERT_EQ(OK, callback.GetResult(rv)); |
| + |
| + first_transaction->StopCaching(); |
| + |
| + const int kBufferSize = 1024; |
| + scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(kBufferSize)); |
| + rv = first_transaction->Read(buf.get(), 10, callback.callback()); |
| + EXPECT_EQ(10, callback.GetResult(rv)); |
| + |
| + // Now that the cache lock is released, another transaction can begin. The |
| + // Start() call should complete without delay. The test should timeout if this |
| + // is not the case (i.e. the Start() call has to wait indefinitely[*] for the |
| + // previous transaction to release the lock). |
| + // |
| + // [*]: The wait isn't really indefinite since the Start() call times out |
| + // (currently after 20 seconds) and proceeds without the cache entry. This |
| + // case is accounted for by the network_layer()->transaction_count() |
| + // expectation below. |
| + scoped_ptr<HttpTransaction> second_transaction; |
| + rv = cache.CreateTransaction(&second_transaction); |
| + ASSERT_EQ(OK, rv); |
| + ASSERT_TRUE(second_transaction); |
| + rv = second_transaction->Start(&mock_request, callback.callback(), |
| + BoundNetLog()); |
| + ASSERT_EQ(OK, callback.GetResult(rv)); |
| + |
| + // first_transaction: 1 open cache entry + 2 network fetches. |
| + // second_transaction: 1 create cache entry + 1 network fetch. |
| + EXPECT_EQ(1, cache.disk_cache()->create_count()); |
| + EXPECT_EQ(1, cache.disk_cache()->open_count()); |
| + EXPECT_EQ(3, cache.network_layer()->transaction_count()); |
| +} |
| + |
| +TEST(HttpCache, StopCachingReleasesCacheLockForSparseEntry) { |
| + MockHttpCache cache; |
| + TestCompletionCallback callback; |
| + |
| + // Create a sparse entry which contains 10 bytes at offset 20 out of an 80 |
| + // byte resource. |
| + MockTransaction mock_transaction(kRangeGET_TransactionOK); |
| + mock_transaction.handler = nullptr; |
| + mock_transaction.request_headers = "Range: bytes = 20-29\r\n" EXTRA_HEADER; |
| + mock_transaction.response_headers = |
| + "Last-Modified: Sat, 18 Apr 2007 01:10:43 GMT\n" |
| + "ETag: \"foo\"\n" |
| + "Accept-Ranges: bytes\n" |
| + "Content-Range: bytes 20-29/80\n" |
| + "Content-Length: 10\n"; |
| + mock_transaction.data = "rg: 20-29 "; |
| + AddMockTransaction(&mock_transaction); |
| + std::string headers; |
| + RunTransactionTestWithResponse(cache.http_cache(), mock_transaction, |
| + &headers); |
| + |
| + // Verify our assumptions. There should be sparse entry here. |
| + disk_cache::Entry* cache_entry; |
| + ASSERT_TRUE(cache.OpenBackendEntry(mock_transaction.url, &cache_entry)); |
| + EXPECT_TRUE(cache_entry->CouldBeSparse()); |
| + cache_entry->Close(); |
| + RemoveMockTransaction(&mock_transaction); |
| + |
| + cache.ResetCounts(); |
| + |
| + // Prepare a request to read the entire contents of the resource. This should |
| + // usually hope between network -> cache -> network. But not today. |
| + mock_transaction.request_headers = EXTRA_HEADER; |
| + mock_transaction.handler = kRangeGET_TransactionOK.handler; |
| + mock_transaction.response_headers = |
| + "Last-Modified: Sat, 18 Apr 2007 01:10:43 GMT\n" |
| + "ETag: \"foo\"\n" |
| + "Accept-Ranges: bytes\n" |
| + "Content-Length: 10\n"; |
| + AddMockTransaction(&mock_transaction); |
| + MockHttpRequest request(mock_transaction); |
| + |
| + scoped_ptr<HttpTransaction> first_transaction; |
| + ASSERT_EQ(OK, cache.CreateTransaction(&first_transaction)); |
| + int rv = |
| + first_transaction->Start(&request, callback.callback(), BoundNetLog()); |
| + ASSERT_EQ(OK, callback.GetResult(rv)); |
| + |
| + const size_t kBufferSize = 256; |
| + scoped_refptr<IOBuffer> buffer(new IOBuffer(kBufferSize)); |
| + |
| + rv = first_transaction->Read(buffer.get(), 10, callback.callback()); |
| + EXPECT_EQ(10, callback.GetResult(rv)); |
| + |
| + // Create another cache transaction. This one is blocked on the first |
| + // transaction's cache lock. |
| + scoped_ptr<HttpTransaction> second_transaction; |
| + TestCompletionCallback second_callback; |
| + ASSERT_EQ(OK, cache.CreateTransaction(&second_transaction)); |
| + rv = second_transaction->Start(&request, second_callback.callback(), |
| + BoundNetLog()); |
| + ASSERT_EQ(ERR_IO_PENDING, rv); |
| + |
| + // StopCaching() should cause the transaction to switch entirely to the |
| + // network for the remainder of the response. Since the existing network |
| + // transaction doesn't cover the entirety of the resource, the cache creates a |
| + // new network transaction. |
| + first_transaction->StopCaching(); |
| + |
| + // Since it's coming from a single network request, we can slurp the remaining |
| + // 70 bytes of the resource in one request. Let's leave a little bit on the |
| + // pipe so that the stream doesn't hit EOF. |
| + rv = first_transaction->Read(buffer.get(), 60, callback.callback()); |
| + EXPECT_EQ(60, callback.GetResult(rv)); |
| + |
| + // The second transaction should become unblocked at this point. |
| + EXPECT_EQ(OK, second_callback.GetResult(ERR_IO_PENDING)); |
| + |
| + // And it can read the entire resource too, but needs to alternate between |
| + // cache and network.. |
| + // 0-9 from cache (due to the first Read() on first_transaction). |
| + EXPECT_EQ(10, second_callback.GetResult(second_transaction->Read( |
| + buffer.get(), kBufferSize, second_callback.callback()))); |
| + |
| + // 10-19 From network: |
| + EXPECT_EQ(10, second_callback.GetResult(second_transaction->Read( |
| + buffer.get(), kBufferSize, second_callback.callback()))); |
| + |
| + // 20-29 From cache (due to test setup): |
| + EXPECT_EQ(10, second_callback.GetResult(second_transaction->Read( |
| + buffer.get(), kBufferSize, second_callback.callback()))); |
| + |
| + // 30-79 From network again: |
| + EXPECT_EQ(50, second_callback.GetResult(second_transaction->Read( |
| + buffer.get(), kBufferSize, second_callback.callback()))); |
| + EXPECT_EQ(0, second_callback.GetResult(second_transaction->Read( |
| + buffer.get(), kBufferSize, second_callback.callback()))); |
| + |
| + // Now drain first_transaction as well. |
| + EXPECT_EQ(10, callback.GetResult(first_transaction->Read( |
| + buffer.get(), kBufferSize, callback.callback()))); |
| + EXPECT_EQ(0, callback.GetResult(first_transaction->Read( |
| + buffer.get(), kBufferSize, callback.callback()))); |
| + |
| + // first_transaction: |
| + // 1 open cache entry + 2 network fetches (0-19 and 10-79). |
| + // second_transaction: |
| + // 1 open cache entry + 2 network fetches (10-19 and 30-79). |
| + EXPECT_EQ(0, cache.disk_cache()->create_count()); |
| + EXPECT_EQ(4, cache.network_layer()->transaction_count()); |
| + |
| + // Even though there are two HttpCache::OpenEntry() calls, there's only one |
| + // DiskCache::OpenEntry call because HttpCache already has an active entry. |
| + EXPECT_EQ(1, cache.disk_cache()->open_count()); |
| + |
| + // And the sparse entry is still here. |
| + ASSERT_TRUE(cache.OpenBackendEntry(mock_transaction.url, &cache_entry)); |
| + cache_entry->Close(); |
| + RemoveMockTransaction(&mock_transaction); |
| +} |
| + |
| +TEST(HttpCache, StopCachingBeforeStartNoCacheEntry) { |
| + MockHttpCache cache; |
| + TestCompletionCallback callback; |
| + MockTransaction transaction(kSimpleGET_Transaction); |
| + cache.ResetCounts(); |
| + |
| + AddMockTransaction(&transaction); |
| + scoped_ptr<HttpTransaction> http_transaction; |
| + |
| + int rv = cache.CreateTransaction(&http_transaction); |
| + ASSERT_EQ(OK, rv); |
| + |
| + MockHttpRequest request(transaction); |
| + http_transaction->StopCaching(); |
| + rv = http_transaction->Start(&request, callback.callback(), BoundNetLog()); |
| + ASSERT_EQ(OK, callback.GetResult(rv)); |
| + |
| + ReadAndVerifyTransaction(http_transaction.get(), transaction); |
| + RemoveMockTransaction(&transaction); |
| + |
| + // If StopCaching() is called before Start(), then the cache should not try to |
| + // create an entry. |
| + EXPECT_EQ(1, cache.network_layer()->transaction_count()); |
| + EXPECT_EQ(0, cache.disk_cache()->open_count()); |
| + EXPECT_EQ(0, cache.disk_cache()->create_count()); |
| +} |
| + |
| +TEST(HttpCache, StopCachingBeforeStartOnlyFromCache) { |
| + MockHttpCache cache; |
| + TestCompletionCallback callback; |
| + MockTransaction transaction(kSimpleGET_Transaction); |
| + |
| + AddMockTransaction(&transaction); |
| + RunTransactionTest(cache.http_cache(), transaction); |
| + RemoveMockTransaction(&transaction); |
| + cache.ResetCounts(); |
| + |
| + transaction.load_flags |= LOAD_ONLY_FROM_CACHE; |
| + AddMockTransaction(&transaction); |
| + |
| + scoped_ptr<HttpTransaction> http_transaction; |
| + |
| + int rv = cache.CreateTransaction(&http_transaction); |
| + ASSERT_EQ(OK, rv); |
| + |
| + MockHttpRequest request(transaction); |
| + http_transaction->StopCaching(); |
| + rv = http_transaction->Start(&request, callback.callback(), BoundNetLog()); |
| + ASSERT_EQ(OK, callback.GetResult(rv)); |
| + |
| + ReadAndVerifyTransaction(http_transaction.get(), transaction); |
| + RemoveMockTransaction(&transaction); |
| + |
| + EXPECT_EQ(0, cache.network_layer()->transaction_count()); |
| + EXPECT_EQ(1, cache.disk_cache()->open_count()); |
| + EXPECT_EQ(0, cache.disk_cache()->create_count()); |
| +} |
| + |
| +TEST(HttpCache, StopCachingBeforeStartOnlyNonexistentCacheEntry) { |
| + MockHttpCache cache; |
| + TestCompletionCallback callback; |
| + MockTransaction transaction(kSimpleGET_Transaction); |
| + cache.ResetCounts(); |
| + |
| + transaction.load_flags |= LOAD_ONLY_FROM_CACHE; |
| + AddMockTransaction(&transaction); |
| + |
| + scoped_ptr<HttpTransaction> http_transaction; |
| + |
| + int rv = cache.CreateTransaction(&http_transaction); |
| + ASSERT_EQ(OK, rv); |
| + |
| + MockHttpRequest request(transaction); |
| + http_transaction->StopCaching(); |
| + rv = http_transaction->Start(&request, callback.callback(), BoundNetLog()); |
| + ASSERT_EQ(ERR_CACHE_MISS, callback.GetResult(rv)); |
| + RemoveMockTransaction(&transaction); |
| + |
| + EXPECT_EQ(0, cache.network_layer()->transaction_count()); |
| + EXPECT_EQ(0, cache.disk_cache()->open_count()); |
| + EXPECT_EQ(0, cache.disk_cache()->create_count()); |
| +} |
| + |
| +TEST(HttpCache, StopCachingBeforeStartPreferringNonexistentCacheEntry) { |
| + MockHttpCache cache; |
| + TestCompletionCallback callback; |
| + MockTransaction transaction(kSimpleGET_Transaction); |
| + cache.ResetCounts(); |
| + |
| + transaction.load_flags |= LOAD_PREFERRING_CACHE; |
| + AddMockTransaction(&transaction); |
| + |
| + scoped_ptr<HttpTransaction> http_transaction; |
| + |
| + int rv = cache.CreateTransaction(&http_transaction); |
| + ASSERT_EQ(OK, rv); |
| + |
| + MockHttpRequest request(transaction); |
| + http_transaction->StopCaching(); |
| + rv = http_transaction->Start(&request, callback.callback(), BoundNetLog()); |
| + ASSERT_EQ(OK, callback.GetResult(rv)); |
| + |
| + ReadAndVerifyTransaction(http_transaction.get(), transaction); |
| + RemoveMockTransaction(&transaction); |
| + |
| + EXPECT_EQ(1, cache.network_layer()->transaction_count()); |
| + EXPECT_EQ(0, cache.disk_cache()->open_count()); |
| + EXPECT_EQ(0, cache.disk_cache()->create_count()); |
| +} |
| + |
| +TEST(HttpCache, StopCachingBeforeStartPreferringCacheEntry) { |
| + MockHttpCache cache; |
| + TestCompletionCallback callback; |
| + MockTransaction transaction(kSimpleGET_Transaction); |
| + |
| + AddMockTransaction(&transaction); |
| + RunTransactionTest(cache.http_cache(), transaction); |
| + RemoveMockTransaction(&transaction); |
| + cache.ResetCounts(); |
| + |
| + transaction.load_flags |= LOAD_PREFERRING_CACHE; |
| + AddMockTransaction(&transaction); |
| + |
| + scoped_ptr<HttpTransaction> http_transaction; |
| + |
| + int rv = cache.CreateTransaction(&http_transaction); |
| + ASSERT_EQ(OK, rv); |
| + |
| + MockHttpRequest request(transaction); |
| + http_transaction->StopCaching(); |
| + rv = http_transaction->Start(&request, callback.callback(), BoundNetLog()); |
| + ASSERT_EQ(OK, callback.GetResult(rv)); |
| + |
| + ReadAndVerifyTransaction(http_transaction.get(), transaction); |
| + RemoveMockTransaction(&transaction); |
| + |
| + EXPECT_EQ(0, cache.network_layer()->transaction_count()); |
| + EXPECT_EQ(1, cache.disk_cache()->open_count()); |
| + EXPECT_EQ(0, cache.disk_cache()->create_count()); |
| +} |
| + |
| +TEST(HttpCache, StopCachingBeforeStartPreferringTruncatedCacheEntry) { |
| + MockHttpCache cache; |
| + TestCompletionCallback callback; |
| + MockTransaction transaction(kRangeGET_TransactionOK); |
| + transaction.load_flags |= LOAD_PREFERRING_CACHE; |
| + transaction.request_headers = EXTRA_HEADER; |
| + transaction.data = kFullRangeData; |
| + AddMockTransaction(&transaction); |
| + |
| + std::string raw_headers( |
| + "HTTP/1.1 200 OK\n" |
| + "Last-Modified: Sat, 18 Apr 2007 01:10:43 GMT\n" |
| + "ETag: \"foo\"\n" |
| + "Accept-Ranges: bytes\n" |
| + "Content-Length: 80\n"); |
| + CreateTruncatedEntry(raw_headers, &cache); |
| + cache.ResetCounts(); |
| + |
| + scoped_ptr<HttpTransaction> http_transaction; |
| + |
| + int rv = cache.CreateTransaction(&http_transaction); |
| + ASSERT_EQ(OK, rv); |
| + |
| + MockHttpRequest request(transaction); |
| + http_transaction->StopCaching(); |
| + rv = http_transaction->Start(&request, callback.callback(), BoundNetLog()); |
| + ASSERT_EQ(OK, callback.GetResult(rv)); |
| + |
| + ReadAndVerifyTransaction(http_transaction.get(), transaction); |
| + RemoveMockTransaction(&transaction); |
| + |
| + // 1 validation request + 1 request for entire range = 2 network reqeusts. |
| + EXPECT_EQ(2, cache.network_layer()->transaction_count()); |
| + EXPECT_EQ(1, cache.disk_cache()->open_count()); |
| + EXPECT_EQ(0, cache.disk_cache()->create_count()); |
| +} |
| + |
| +TEST(HttpCache, |
| + StopCachingBeforeStartForExternallyConditionalizedTruncatedEntry) { |
| + MockHttpCache cache; |
| + TestCompletionCallback callback; |
| + RangeTransactionServer range_transaction_server; |
| + range_transaction_server.set_not_modified(true); |
| + MockTransaction transaction(kRangeGET_TransactionOK); |
| + transaction.request_headers = "If-None-Match: \"foo\"\r\n" EXTRA_HEADER, |
| + transaction.data = ""; |
| + transaction.load_flags |= LOAD_PREFERRING_CACHE; |
| + AddMockTransaction(&transaction); |
| + |
| + std::string raw_headers( |
| + "HTTP/1.1 200 OK\n" |
| + "Last-Modified: Sat, 18 Apr 2007 01:10:43 GMT\n" |
| + "ETag: \"foo\"\n" |
| + "Accept-Ranges: bytes\n" |
| + "Content-Length: 80\n"); |
| + CreateTruncatedEntry(raw_headers, &cache); |
| + cache.ResetCounts(); |
| + |
| + scoped_ptr<HttpTransaction> http_transaction; |
| + |
| + int rv = cache.CreateTransaction(&http_transaction); |
| + ASSERT_EQ(OK, rv); |
| + |
| + MockHttpRequest request(transaction); |
| + http_transaction->StopCaching(); |
| + rv = http_transaction->Start(&request, callback.callback(), BoundNetLog()); |
| + ASSERT_EQ(OK, callback.GetResult(rv)); |
| + |
| + ReadAndVerifyTransaction(http_transaction.get(), transaction); |
| + RemoveMockTransaction(&transaction); |
| + |
| + EXPECT_EQ(1, cache.network_layer()->transaction_count()); |
| + EXPECT_EQ(1, cache.disk_cache()->open_count()); |
| + EXPECT_EQ(0, cache.disk_cache()->create_count()); |
| +} |
| + |
| +TEST(HttpCache, StopCachingAfterStartForLoadPreferringCacheWithInvalidation) { |
| + MockHttpCache cache; |
| + TestCompletionCallback callback; |
| + MockTransaction transaction(kSimpleGET_Transaction); |
| + |
| + AddMockTransaction(&transaction); |
| + RunTransactionTest(cache.http_cache(), transaction); |
| + RemoveMockTransaction(&transaction); |
| + cache.ResetCounts(); |
| + |
| + transaction.load_flags |= LOAD_PREFERRING_CACHE; |
| + AddMockTransaction(&transaction); |
| + |
| + scoped_ptr<HttpTransaction> http_transaction; |
| + |
| + int rv = cache.CreateTransaction(&http_transaction); |
| + ASSERT_EQ(OK, rv); |
| + |
| + MockHttpRequest request(transaction); |
| + rv = http_transaction->Start(&request, callback.callback(), BoundNetLog()); |
| + ASSERT_EQ(OK, callback.GetResult(rv)); |
| + |
| + http_transaction->StopCaching(); |
| + |
| + ReadAndVerifyTransaction(http_transaction.get(), transaction); |
| + RemoveMockTransaction(&transaction); |
| + |
| + EXPECT_EQ(0, cache.network_layer()->transaction_count()); |
| + EXPECT_EQ(1, cache.disk_cache()->open_count()); |
| + EXPECT_EQ(0, cache.disk_cache()->create_count()); |
| +} |
| + |
| +namespace { |
| + |
| +enum class TransactionPhase { |
| + BEFORE_FIRST_READ, |
| + AFTER_FIRST_READ, |
| + AFTER_NETWORK_READ |
| +}; |
| + |
| +using CacheInitializer = void (*)(MockHttpCache*); |
| +using HugeCacheTestConfiguration = |
| + std::pair<TransactionPhase, CacheInitializer>; |
| + |
| +class HttpCacheHugeResourceTest |
| + : public ::testing::TestWithParam<HugeCacheTestConfiguration> { |
| + public: |
| + static std::list<HugeCacheTestConfiguration> GetTestModes(); |
| + static std::list<HugeCacheTestConfiguration> kTestModes; |
| + |
| + // CacheInitializer callbacks. These are used to initialize the cache |
| + // depending on the test run configuration. |
| + |
| + // Initializes a cache containing a truncated entry containing the first 20 |
| + // bytes of the reponse body. |
| + static void SetupTruncatedCacheEntry(MockHttpCache* cache); |
| + |
| + // Initializes a cache containing a sparse entry. The first 10 bytes are |
| + // present in the cache. |
| + static void SetupPrefixSparseCacheEntry(MockHttpCache* cache); |
| + |
| + // Initializes a cache containing a sparse entry. The 10 bytes at offset |
| + // 99999990 are present in the cache. |
| + static void SetupInfixSparseCacheEntry(MockHttpCache* cache); |
| + |
| + protected: |
| + static void ExpectByteRangeTransactionHandler( |
| + const net::HttpRequestInfo* request, |
| + std::string* response_status, |
| + std::string* response_headers, |
| + std::string* response_data); |
| + static int LargeBufferReader(int64 content_length, |
| + int64 offset, |
| + net::IOBuffer* buf, |
| + int buf_len); |
| + |
| + static void SetFlagOnBeforeNetworkStart(bool* started, bool* /* defer */); |
| + |
| + // Size of resource to be tested. |
| + static const int64 kTotalSize = |
| + 5000000000LL; // Five beeeeeelllliooonn bytes! |
| +}; |
| + |
| +const int64 HttpCacheHugeResourceTest::kTotalSize; |
| + |
| +// static |
| +void HttpCacheHugeResourceTest::ExpectByteRangeTransactionHandler( |
| + const net::HttpRequestInfo* request, |
| + std::string* response_status, |
| + std::string* response_headers, |
| + std::string* response_data) { |
| + std::string if_range; |
| + EXPECT_TRUE(request->extra_headers.GetHeader( |
| + net::HttpRequestHeaders::kIfRange, &if_range)); |
| + EXPECT_EQ("\"foo\"", if_range); |
| + |
| + std::string range_header; |
| + EXPECT_TRUE(request->extra_headers.GetHeader(net::HttpRequestHeaders::kRange, |
| + &range_header)); |
| + std::vector<net::HttpByteRange> ranges; |
| + |
| + EXPECT_TRUE(net::HttpUtil::ParseRangeHeader(range_header, &ranges)); |
| + ASSERT_EQ(1u, ranges.size()); |
| + |
| + net::HttpByteRange range = ranges[0]; |
| + EXPECT_TRUE(range.HasFirstBytePosition()); |
| + int64 last_byte_position = |
| + range.HasLastBytePosition() ? range.last_byte_position() : kTotalSize - 1; |
| + |
| + response_status->assign("HTTP/1.1 206 Partial"); |
| + response_headers->assign(base::StringPrintf( |
| + "Content-Range: bytes %" PRId64 "-%" PRId64 "/%" PRId64 |
| + "\n" |
| + "Content-Length: %" PRId64 "\n", |
| + range.first_byte_position(), last_byte_position, kTotalSize, |
| + last_byte_position - range.first_byte_position() + 1)); |
| +} |
| + |
| +// static |
| +int HttpCacheHugeResourceTest::LargeBufferReader(int64 content_length, |
| + int64 offset, |
| + net::IOBuffer* buf, |
| + int buf_len) { |
| + // This test involves reading multiple gigabytes of data. To make it run in a |
| + // reasonable amount of time, we are going to skip filling the buffer with |
| + // data. Instead the test relies on verifying that the count of bytes expected |
| + // at the end is correct. |
| + EXPECT_LT(0, content_length); |
| + EXPECT_LE(offset, content_length); |
| + int num = std::min(static_cast<int64>(buf_len), content_length - offset); |
| + return num; |
| +} |
| + |
| +// static |
| +void HttpCacheHugeResourceTest::SetFlagOnBeforeNetworkStart(bool* started, |
| + bool* /* defer */) { |
| + *started = true; |
| +} |
| + |
| +// static |
| +void HttpCacheHugeResourceTest::SetupTruncatedCacheEntry(MockHttpCache* cache) { |
| + ScopedMockTransaction scoped_transaction(kRangeGET_TransactionOK); |
| + std::string cached_headers = base::StringPrintf( |
| + "HTTP/1.1 200 OK\n" |
| + "Last-Modified: Sat, 18 Apr 2007 01:10:43 GMT\n" |
| + "ETag: \"foo\"\n" |
| + "Accept-Ranges: bytes\n" |
| + "Content-Length: %" PRId64 "\n", |
| + kTotalSize); |
| + CreateTruncatedEntry(cached_headers, cache); |
| +} |
| + |
| +// static |
| +void HttpCacheHugeResourceTest::SetupPrefixSparseCacheEntry( |
| + MockHttpCache* cache) { |
| + MockTransaction transaction(kRangeGET_TransactionOK); |
| + transaction.handler = nullptr; |
| + transaction.request_headers = "Range: bytes = 0-9\r\n" EXTRA_HEADER; |
| + transaction.response_headers = |
| + "Last-Modified: Sat, 18 Apr 2007 01:10:43 GMT\n" |
| + "ETag: \"foo\"\n" |
| + "Accept-Ranges: bytes\n" |
| + "Content-Range: bytes 0-9/5000000000\n" |
| + "Content-Length: 10\n"; |
| + AddMockTransaction(&transaction); |
| + std::string headers; |
| + RunTransactionTestWithResponse(cache->http_cache(), transaction, &headers); |
| + RemoveMockTransaction(&transaction); |
| +} |
| + |
| +// static |
| +void HttpCacheHugeResourceTest::SetupInfixSparseCacheEntry( |
| + MockHttpCache* cache) { |
| + MockTransaction transaction(kRangeGET_TransactionOK); |
| + transaction.handler = nullptr; |
| + transaction.request_headers = |
| + "Range: bytes = 99999990-99999999\r\n" EXTRA_HEADER; |
| + transaction.response_headers = |
| + "Last-Modified: Sat, 18 Apr 2007 01:10:43 GMT\n" |
| + "ETag: \"foo\"\n" |
| + "Accept-Ranges: bytes\n" |
| + "Content-Range: bytes 99999990-99999999/5000000000\n" |
| + "Content-Length: 10\n"; |
| + AddMockTransaction(&transaction); |
| + std::string headers; |
| + RunTransactionTestWithResponse(cache->http_cache(), transaction, &headers); |
| + RemoveMockTransaction(&transaction); |
| +} |
| + |
| +// static |
| +std::list<HugeCacheTestConfiguration> |
| +HttpCacheHugeResourceTest::GetTestModes() { |
| + std::list<HugeCacheTestConfiguration> test_modes; |
| + const TransactionPhase kTransactionPhases[] = { |
| + TransactionPhase::BEFORE_FIRST_READ, TransactionPhase::AFTER_FIRST_READ, |
| + TransactionPhase::AFTER_NETWORK_READ}; |
| + const CacheInitializer kInitializers[] = {&SetupTruncatedCacheEntry, |
| + &SetupPrefixSparseCacheEntry, |
| + &SetupInfixSparseCacheEntry}; |
| + |
| + for (const auto phase : kTransactionPhases) |
| + for (const auto initializer : kInitializers) |
| + test_modes.push_back(std::make_pair(phase, initializer)); |
| + |
| + return test_modes; |
| +} |
| + |
| +// static |
| +std::list<HugeCacheTestConfiguration> HttpCacheHugeResourceTest::kTestModes = |
| + HttpCacheHugeResourceTest::GetTestModes(); |
| + |
| +INSTANTIATE_TEST_CASE_P( |
| + _, |
| + HttpCacheHugeResourceTest, |
| + ::testing::ValuesIn(HttpCacheHugeResourceTest::kTestModes)); |
| + |
| +} // namespace |
| + |
| +// Test what happens when StopCaching() is called while reading a huge resource |
| +// fetched via GET. Various combinations of cache state and when StopCaching() |
| +// is called is controlled by the parameter passed into the test via the |
| +// INSTANTIATE_TEST_CASE_P invocation above. |
| +TEST_P(HttpCacheHugeResourceTest, |
| + StopCachingFollowedByReadForHugeTruncatedResource) { |
| + // This test is going to be repeated for all combinations of TransactionPhase |
| + // and CacheInitializers returned by GetTestModes(). |
| + const TransactionPhase stop_caching_phase = GetParam().first; |
| + const CacheInitializer cache_initializer = GetParam().second; |
| + |
| + MockHttpCache cache; |
| + (*cache_initializer)(&cache); |
| + |
| + MockTransaction transaction(kSimpleGET_Transaction); |
| + transaction.url = kRangeGET_TransactionOK.url; |
| + transaction.handler = &ExpectByteRangeTransactionHandler; |
| + transaction.read_handler = &LargeBufferReader; |
| + ScopedMockTransaction scoped_transaction(transaction); |
| + |
| + MockHttpRequest request(transaction); |
| + net::TestCompletionCallback callback; |
| + scoped_ptr<net::HttpTransaction> http_transaction; |
| + int rv = cache.http_cache()->CreateTransaction(net::DEFAULT_PRIORITY, |
| + &http_transaction); |
| + ASSERT_EQ(net::OK, rv); |
| + ASSERT_TRUE(http_transaction.get()); |
| + |
| + bool network_transaction_started = false; |
| + if (stop_caching_phase == TransactionPhase::AFTER_NETWORK_READ) { |
| + http_transaction->SetBeforeNetworkStartCallback( |
| + base::Bind(&SetFlagOnBeforeNetworkStart, &network_transaction_started)); |
| + } |
| + |
| + rv = http_transaction->Start(&request, callback.callback(), |
| + net::BoundNetLog()); |
| + rv = callback.GetResult(rv); |
| + ASSERT_EQ(net::OK, rv); |
| + |
| + if (stop_caching_phase == TransactionPhase::BEFORE_FIRST_READ) |
| + http_transaction->StopCaching(); |
| + |
| + int64 total_bytes_received = 0; |
| + |
| + EXPECT_EQ(kTotalSize, |
| + http_transaction->GetResponseInfo()->headers->GetContentLength()); |
| + do { |
| + // This test simulates reading Gigabytes of data. Buffer size is set to 1MB |
| + // to reduce the number of reads and speedup the test. |
| + const int kBufferSize = 1024 * 1024; |
| + scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(kBufferSize)); |
| + rv = http_transaction->Read(buf.get(), kBufferSize, callback.callback()); |
| + rv = callback.GetResult(rv); |
| + |
| + if (stop_caching_phase == TransactionPhase::AFTER_FIRST_READ && |
| + total_bytes_received == 0) { |
| + http_transaction->StopCaching(); |
| + } |
| + |
| + if (rv > 0) |
| + total_bytes_received += rv; |
| + |
| + if (network_transaction_started && |
| + stop_caching_phase == TransactionPhase::AFTER_NETWORK_READ) { |
| + http_transaction->StopCaching(); |
| + network_transaction_started = false; |
| + } |
| + } while (rv > 0); |
| + |
| + // The only verification we are going to do is that the received resource has |
| + // the correct size. This is sufficient to verify that the state machine |
| + // didn't terminate abruptly due to the StopCaching() call. |
| + EXPECT_EQ(kTotalSize, total_bytes_received); |
| +} |
| + |
| // Tests that we detect truncated resources from the net when there is |
| // a Content-Length header. |
| TEST(HttpCache, TruncatedByContentLength) { |