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