Index: net/http/http_cache_unittest.cc |
diff --git a/net/http/http_cache_unittest.cc b/net/http/http_cache_unittest.cc |
index 8d62ee0b4d59c7d2d03d6b36a555c1a580c1883a..d1e97fc5026f95c78414183fd3fe2d26e3c58ace 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")) { |
+ 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); |
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); |
- |
// 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; |
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); |
@@ -6746,45 +6764,8 @@ TEST(HttpCache, DoneReading) { |
EXPECT_EQ(1, cache.disk_cache()->create_count()); |
} |
-// Tests that we stop caching when told. |
-TEST(HttpCache, StopCachingDeletesEntry) { |
- 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 +6840,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 there 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 +6907,725 @@ TEST(HttpCache, StopCachingTruncatedEntry) { |
"Accept-Ranges: bytes\n" |
"Content-Length: 80\n"); |
CreateTruncatedEntry(raw_headers, &cache); |
+ cache.disk_cache()->ResetCounts(); |
+ cache.network_layer()->ResetTransactionCount(); |
- { |
- // 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.network_layer()->ResetTransactionCount(); |
+ cache.disk_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.network_layer()->ResetTransactionCount(); |
+ cache.disk_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, |
+ "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}; |
+ |
+} // 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.disk_cache()->ResetCounts(); |
+ cache.network_layer()->ResetTransactionCount(); |
+ |
+ 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.network_layer()->ResetTransactionCount(); |
+ cache.disk_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); |
+} |
+ |
+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 */); |
+ static void StopCachingOnBeforeNetworkStart(HttpTransaction* transaction, |
+ 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::StopCachingOnBeforeNetworkStart( |
+ HttpTransaction* transaction, |
+ bool* /* defer */) { |
+ transaction->StopCaching(); |
+} |
+ |
+// 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) { |