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

Unified Diff: net/http/http_cache_unittest.cc

Issue 1230113012: [net] Better StopCaching() handling for HttpCache::Transaction. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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) {

Powered by Google App Engine
This is Rietveld 408576698