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

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: Release cache lock early and request open range if it's the final range. Created 5 years, 5 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 8d62ee0b4d59c7d2d03d6b36a555c1a580c1883a..486e1ccc65dab07d503b55d66b0977d64a9845e1 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,8 +6764,9 @@ TEST(HttpCache, DoneReading) {
EXPECT_EQ(1, cache.disk_cache()->create_count());
}
-// Tests that we stop caching when told.
-TEST(HttpCache, StopCachingDeletesEntry) {
+// Tests that calling StopCaching() causes the cache entry to be deleted if the
+// request cannot be resumed.
+TEST(HttpCache, StopCachingDeletesEntryIfRequestIsNotResumable) {
MockHttpCache cache;
TestCompletionCallback callback;
MockHttpRequest request(kSimpleGET_Transaction);
@@ -6775,7 +6794,8 @@ TEST(HttpCache, StopCachingDeletesEntry) {
// Make sure that the ActiveEntry is gone.
base::MessageLoop::current()->RunUntilIdle();
- // Verify that the entry is gone.
+ // Verify that the entry is gone. The request is not resumeable, so the cache
+ // entry should be deleted if only part of the response was cached.
RunTransactionTest(cache.http_cache(), kSimpleGET_Transaction);
EXPECT_EQ(2, cache.network_layer()->transaction_count());
@@ -6860,7 +6880,9 @@ TEST(HttpCache, StopCachingWithAuthDeletesEntry) {
}
// Tests that when we are told to stop caching we don't throw away valid data.
-TEST(HttpCache, StopCachingSavesEntry) {
+// If the request is resumable (i.e. has strong validators), then the cache
+// should hold on to the partial response.
+TEST(HttpCache, StopCachingSavesEntryIfRequestIsResumable) {
MockHttpCache cache;
TestCompletionCallback callback;
MockHttpRequest request(kSimpleGET_Transaction);
@@ -6905,7 +6927,7 @@ TEST(HttpCache, StopCachingSavesEntry) {
}
// Tests that we handle truncated enries when StopCaching is called.
-TEST(HttpCache, StopCachingTruncatedEntry) {
+TEST(HttpCache, StopCachingPreservesAlreadyTruncatedEntry) {
MockHttpCache cache;
TestCompletionCallback callback;
MockHttpRequest request(kRangeGET_TransactionOK);
@@ -6932,7 +6954,8 @@ TEST(HttpCache, StopCachingTruncatedEntry) {
rv = trans->Read(buf.get(), 10, callback.callback());
EXPECT_EQ(callback.GetResult(rv), 10);
- // This is actually going to do nothing.
+ // This prevents any further writes to the cache entry, but doesn't discard
+ // the existing entry.
trans->StopCaching();
// We should be able to keep reading.
@@ -6944,19 +6967,464 @@ TEST(HttpCache, StopCachingTruncatedEntry) {
EXPECT_EQ(callback.GetResult(rv), 0);
}
- // Verify that the disk entry was updated.
+ // Verify that the disk entry was not updated.
disk_cache::Entry* entry;
ASSERT_TRUE(cache.OpenBackendEntry(kRangeGET_TransactionOK.url, &entry));
- EXPECT_EQ(80, entry->GetDataSize(1));
- bool truncated = true;
+ EXPECT_EQ(20, entry->GetDataSize(1));
+ bool truncated = false;
HttpResponseInfo response;
EXPECT_TRUE(MockHttpCache::ReadResponseInfo(entry, &response, &truncated));
- EXPECT_FALSE(truncated);
+ EXPECT_TRUE(truncated);
entry->Close();
RemoveMockTransaction(&kRangeGET_TransactionOK);
}
+namespace {
+
+enum TransactionPhases {
+ TRANSACTION_PHASE_BEFORE_FIRST_READ,
+ TRANSACTION_PHASE_ON_BEFORE_NETWORK_START,
+ TRANSACTION_PHASE_AFTER_FIRST_READ,
+ TRANSACTION_PHASE_AFTER_NETWORK_READ
+};
+
+using CacheInitializer = void (*)(MockHttpCache*);
+using HugeCacheTestConfiguration =
+ std::pair<TransactionPhases, CacheInitializer>;
+
+class HttpCacheHugeResourceTest
+ : public ::testing::TestWithParam<HugeCacheTestConfiguration> {
+ public:
+ static std::list<HugeCacheTestConfiguration> GetTestModes();
+ static std::list<HugeCacheTestConfiguration> kTestModes;
+
+ 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 */);
+
+ // 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);
+
+ // 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 TransactionPhases kTransactionPhases[] = {
+ TRANSACTION_PHASE_BEFORE_FIRST_READ,
+ TRANSACTION_PHASE_ON_BEFORE_NETWORK_START,
+ TRANSACTION_PHASE_AFTER_FIRST_READ, TRANSACTION_PHASE_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 TransactionPhases
+ // and CacheInitializers returned by GetTestModes().
+ TransactionPhases stop_caching_phase = GetParam().first;
+ 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());
+
+ if (stop_caching_phase == TRANSACTION_PHASE_ON_BEFORE_NETWORK_START)
+ http_transaction->SetBeforeNetworkStartCallback(
+ base::Bind(&StopCachingOnBeforeNetworkStart, http_transaction.get()));
+
+ bool network_transaction_started = false;
+ if (stop_caching_phase == TRANSACTION_PHASE_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 == TRANSACTION_PHASE_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 == TRANSACTION_PHASE_AFTER_FIRST_READ &&
+ total_bytes_received == 0)
+ http_transaction->StopCaching();
+
+ if (rv > 0)
+ total_bytes_received += rv;
+
+ if (network_transaction_started &&
+ stop_caching_phase == TRANSACTION_PHASE_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);
+}
+
+TEST(HttpCache, StopCachingReleasesCacheLockForNonResumableRequest) {
+ MockHttpCache cache;
+ TestCompletionCallback callback;
+ ScopedMockTransaction mock_transaction(kSimpleGET_Transaction);
+ MockHttpRequest mock_request(mock_transaction);
+ const int kContentLength = std::string(mock_transaction.data).size();
+
+ scoped_ptr<HttpTransaction> transaction;
+ int rv = cache.CreateTransaction(&transaction);
+ ASSERT_EQ(OK, rv);
+ ASSERT_TRUE(transaction.get());
+
+ rv = transaction->Start(&mock_request, callback.callback(), BoundNetLog());
+ ASSERT_EQ(OK, callback.GetResult(rv));
+
+ transaction->StopCaching();
+
+ const int kBufferSize = 1024;
+ scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(kBufferSize));
+ rv = transaction->Read(buf.get(), 10, callback.callback());
+ EXPECT_EQ(10, callback.GetResult(rv));
+
+ // The cache entry should be gone by now because the request can't be resumed.
+ // |transaction| is still active and hasn't reached EOF. The test fails if the
+ // previous Read() or Start() didn't result in the cache entry being released
+ // and destroyed.
+ disk_cache::Entry* entry = nullptr;
+ EXPECT_FALSE(cache.OpenBackendEntry(mock_transaction.url, &entry));
+ EXPECT_EQ(0, cache.disk_cache()->GetEntryCount());
+
+ // The transaction should still be able to continue reading till the end of
+ // the resource is reached.
+ rv = transaction->Read(buf.get(), kBufferSize, callback.callback());
+
+ // The final read should return the remainder of the resource excluding the 10
+ // bytes already read.
+ EXPECT_EQ(kContentLength - 10, callback.GetResult(rv));
+
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(1, cache.network_layer()->transaction_count());
+}
+
+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, StopCachingReleasesCacheLockForResumableNotCachedRequest) {
+ MockHttpCache cache;
+ TestCompletionCallback callback;
+ ScopedMockTransaction mock_transaction(kResumableGET_Transaction);
+ MockHttpRequest mock_request(mock_transaction);
+
+ scoped_ptr<HttpTransaction> transaction;
+ int rv = cache.CreateTransaction(&transaction);
+ ASSERT_EQ(OK, rv);
+ ASSERT_TRUE(transaction.get());
+
+ rv = transaction->Start(&mock_request, callback.callback(), BoundNetLog());
+ ASSERT_EQ(OK, callback.GetResult(rv));
+
+ const int kBufferSize = 1024;
+ scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(kBufferSize));
+ rv = transaction->Read(buf.get(), 10, callback.callback());
+ EXPECT_EQ(10, callback.GetResult(rv));
+
+ transaction->StopCaching();
+
+ rv = transaction->Read(buf.get(), 10, callback.callback());
+ EXPECT_EQ(10, callback.GetResult(rv));
+
+ // Similar to the above test, but the cache entry still exists at this point
+ // because the request is resumable. The expectation is that the cache entry
+ // has been released at this point due the earlier Start()/Read() calls. Since
+ // the cache is released, we can start another transaction. The Start() call
+ // should succeed without any delay. The test should timeout if this is not
+ // the case (i.e. the Start() call has to wait indefinitely[*] for the
+ // previuos 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));
+
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+ EXPECT_EQ(1, cache.disk_cache()->open_count());
+ EXPECT_EQ(1, cache.disk_cache()->GetEntryCount());
+
+ // Initial network request + validation request for the second transaction.
+ EXPECT_EQ(2, cache.network_layer()->transaction_count());
+}
+
+TEST(HttpCache, StopCachingReleasesCacheLockForResumableTruncatedEntry) {
+ 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);
+
+ scoped_ptr<HttpTransaction> transaction;
+ int rv = cache.CreateTransaction(&transaction);
+ ASSERT_EQ(OK, rv);
+ ASSERT_TRUE(transaction.get());
+
+ rv = transaction->Start(&mock_request, callback.callback(), BoundNetLog());
+ ASSERT_EQ(OK, callback.GetResult(rv));
+
+ transaction->StopCaching();
+
+ const int kBufferSize = 1024;
+ scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(kBufferSize));
+ rv = transaction->Read(buf.get(), 20, callback.callback());
+ EXPECT_EQ(20, callback.GetResult(rv));
+
+ // This request hits the network and causes the cache lock to be released. We
+ // don't expect the cache lock to be released until the final network range
+ // request is issued and begins streaming the body.
+ rv = 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));
+
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+ EXPECT_EQ(2, cache.disk_cache()->open_count());
+ EXPECT_EQ(3, cache.network_layer()->transaction_count());
+ EXPECT_EQ(1, cache.disk_cache()->GetEntryCount());
+}
+
// 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