| Index: net/http/http_cache_unittest.cc
|
| diff --git a/net/http/http_cache_unittest.cc b/net/http/http_cache_unittest.cc
|
| index 2b16cb77cf667f17015b7ff9ae7234e77c2215ea..a3d83f4853399779b1dc214e239bc9823fbe3087 100644
|
| --- a/net/http/http_cache_unittest.cc
|
| +++ b/net/http/http_cache_unittest.cc
|
| @@ -13,6 +13,7 @@
|
|
|
| #include "base/bind.h"
|
| #include "base/bind_helpers.h"
|
| +#include "base/format_macros.h"
|
| #include "base/macros.h"
|
| #include "base/memory/ptr_util.h"
|
| #include "base/message_loop/message_loop.h"
|
| @@ -303,6 +304,7 @@ const MockTransaction kFastNoStoreGET_Transaction = {
|
| TEST_MODE_SYNC_NET_START,
|
| &FastTransactionServer::FastNoStoreHandler,
|
| nullptr,
|
| + nullptr,
|
| 0,
|
| 0,
|
| OK};
|
| @@ -461,24 +463,15 @@ void RangeTransactionServer::RangeHandler(const HttpRequestInfo* request,
|
| }
|
|
|
| const MockTransaction kRangeGET_TransactionOK = {
|
| - "http://www.google.com/range",
|
| - "GET",
|
| - base::Time(),
|
| - "Range: bytes = 40-49\r\n" EXTRA_HEADER,
|
| - LOAD_NORMAL,
|
| + "http://www.google.com/range", "GET", base::Time(),
|
| + "Range: bytes = 40-49\r\n" EXTRA_HEADER, LOAD_NORMAL,
|
| "HTTP/1.1 206 Partial Content",
|
| "Last-Modified: Sat, 18 Apr 2007 01:10:43 GMT\n"
|
| "ETag: \"foo\"\n"
|
| "Accept-Ranges: bytes\n"
|
| "Content-Length: 10\n",
|
| - base::Time(),
|
| - "rg: 40-49 ",
|
| - TEST_MODE_NORMAL,
|
| - &RangeTransactionServer::RangeHandler,
|
| - nullptr,
|
| - 0,
|
| - 0,
|
| - OK};
|
| + base::Time(), "rg: 40-49 ", TEST_MODE_NORMAL,
|
| + &RangeTransactionServer::RangeHandler, nullptr, nullptr, 0, 0, OK};
|
|
|
| const char kFullRangeData[] =
|
| "rg: 00-09 rg: 10-19 rg: 20-29 rg: 30-39 "
|
| @@ -7056,6 +7049,277 @@ TEST(HttpCache, StopCachingTruncatedEntry) {
|
| RemoveMockTransaction(&kRangeGET_TransactionOK);
|
| }
|
|
|
| +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
|
| + // 99990 are present in the cache.
|
| + static void SetupInfixSparseCacheEntry(MockHttpCache* cache);
|
| +
|
| + protected:
|
| + static void LargeResourceTransactionHandler(
|
| + const net::HttpRequestInfo* request,
|
| + std::string* response_status,
|
| + std::string* response_headers,
|
| + std::string* response_data);
|
| + static int LargeBufferReader(int64_t content_length,
|
| + int64_t offset,
|
| + net::IOBuffer* buf,
|
| + int buf_len);
|
| +
|
| + static void SetFlagOnBeforeNetworkStart(bool* started, bool* /* defer */);
|
| +
|
| + // Size of resource to be tested.
|
| + static const int64_t kTotalSize = 5000LL * 1000 * 1000;
|
| +};
|
| +
|
| +const int64_t HttpCacheHugeResourceTest::kTotalSize;
|
| +
|
| +// static
|
| +void HttpCacheHugeResourceTest::LargeResourceTransactionHandler(
|
| + const net::HttpRequestInfo* request,
|
| + std::string* response_status,
|
| + std::string* response_headers,
|
| + std::string* response_data) {
|
| + std::string if_range;
|
| + if (!request->extra_headers.GetHeader(net::HttpRequestHeaders::kIfRange,
|
| + &if_range)) {
|
| + // If there were no range headers in the request, we are going to just
|
| + // return the entire response body.
|
| + *response_status = "HTTP/1.1 200 Success";
|
| + *response_headers = base::StringPrintf("Content-Length: %" PRId64
|
| + "\n"
|
| + "ETag: \"foo\"\n"
|
| + "Accept-Ranges: bytes\n",
|
| + kTotalSize);
|
| + return;
|
| + }
|
| +
|
| + // From this point on, we should be processing a valid byte-range request.
|
| + 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_t last_byte_position =
|
| + range.HasLastBytePosition() ? range.last_byte_position() : kTotalSize - 1;
|
| +
|
| + *response_status = "HTTP/1.1 206 Partial";
|
| + *response_headers = 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_t content_length,
|
| + int64_t 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_t>(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 = 99990-99999\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 99990-99999/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 = &LargeResourceTransactionHandler;
|
| + transaction.read_handler = &LargeBufferReader;
|
| + ScopedMockTransaction scoped_transaction(transaction);
|
| +
|
| + MockHttpRequest request(transaction);
|
| + net::TestCompletionCallback callback;
|
| + std::unique_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_t 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 10MB
|
| + // to reduce the number of reads and speed up the test.
|
| + const int kBufferSize = 1024 * 1024 * 10;
|
| + 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) {
|
|
|