| 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) {
|
|
|