OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/http/http_cache.h" | 5 #include "net/http/http_cache.h" |
6 | 6 |
7 #include <stdint.h> | 7 #include <stdint.h> |
8 | 8 |
9 #include <algorithm> | 9 #include <algorithm> |
10 #include <memory> | 10 #include <memory> |
11 #include <utility> | 11 #include <utility> |
12 #include <vector> | 12 #include <vector> |
13 | 13 |
14 #include "base/bind.h" | 14 #include "base/bind.h" |
15 #include "base/bind_helpers.h" | 15 #include "base/bind_helpers.h" |
| 16 #include "base/format_macros.h" |
16 #include "base/macros.h" | 17 #include "base/macros.h" |
17 #include "base/memory/ptr_util.h" | 18 #include "base/memory/ptr_util.h" |
18 #include "base/message_loop/message_loop.h" | 19 #include "base/message_loop/message_loop.h" |
19 #include "base/run_loop.h" | 20 #include "base/run_loop.h" |
20 #include "base/strings/string_util.h" | 21 #include "base/strings/string_util.h" |
21 #include "base/strings/stringprintf.h" | 22 #include "base/strings/stringprintf.h" |
22 #include "base/test/simple_test_clock.h" | 23 #include "base/test/simple_test_clock.h" |
23 #include "net/base/cache_type.h" | 24 #include "net/base/cache_type.h" |
24 #include "net/base/elements_upload_data_stream.h" | 25 #include "net/base/elements_upload_data_stream.h" |
25 #include "net/base/host_port_pair.h" | 26 #include "net/base/host_port_pair.h" |
(...skipping 270 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
296 base::Time(), | 297 base::Time(), |
297 "", | 298 "", |
298 LOAD_VALIDATE_CACHE, | 299 LOAD_VALIDATE_CACHE, |
299 "HTTP/1.1 200 OK", | 300 "HTTP/1.1 200 OK", |
300 "Cache-Control: max-age=10000\n", | 301 "Cache-Control: max-age=10000\n", |
301 base::Time(), | 302 base::Time(), |
302 "<html><body>Google Blah Blah</body></html>", | 303 "<html><body>Google Blah Blah</body></html>", |
303 TEST_MODE_SYNC_NET_START, | 304 TEST_MODE_SYNC_NET_START, |
304 &FastTransactionServer::FastNoStoreHandler, | 305 &FastTransactionServer::FastNoStoreHandler, |
305 nullptr, | 306 nullptr, |
| 307 nullptr, |
306 0, | 308 0, |
307 0, | 309 0, |
308 OK}; | 310 OK}; |
309 | 311 |
310 // This class provides a handler for kRangeGET_TransactionOK so that the range | 312 // This class provides a handler for kRangeGET_TransactionOK so that the range |
311 // request can be served on demand. | 313 // request can be served on demand. |
312 class RangeTransactionServer { | 314 class RangeTransactionServer { |
313 public: | 315 public: |
314 RangeTransactionServer() { | 316 RangeTransactionServer() { |
315 not_modified_ = false; | 317 not_modified_ = false; |
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
454 response_headers->replace(response_headers->find("Content-Length:"), | 456 response_headers->replace(response_headers->find("Content-Length:"), |
455 content_length.size(), content_length); | 457 content_length.size(), content_length); |
456 } | 458 } |
457 } else { | 459 } else { |
458 response_status->assign("HTTP/1.1 304 Not Modified"); | 460 response_status->assign("HTTP/1.1 304 Not Modified"); |
459 response_data->clear(); | 461 response_data->clear(); |
460 } | 462 } |
461 } | 463 } |
462 | 464 |
463 const MockTransaction kRangeGET_TransactionOK = { | 465 const MockTransaction kRangeGET_TransactionOK = { |
464 "http://www.google.com/range", | 466 "http://www.google.com/range", "GET", base::Time(), |
465 "GET", | 467 "Range: bytes = 40-49\r\n" EXTRA_HEADER, LOAD_NORMAL, |
466 base::Time(), | |
467 "Range: bytes = 40-49\r\n" EXTRA_HEADER, | |
468 LOAD_NORMAL, | |
469 "HTTP/1.1 206 Partial Content", | 468 "HTTP/1.1 206 Partial Content", |
470 "Last-Modified: Sat, 18 Apr 2007 01:10:43 GMT\n" | 469 "Last-Modified: Sat, 18 Apr 2007 01:10:43 GMT\n" |
471 "ETag: \"foo\"\n" | 470 "ETag: \"foo\"\n" |
472 "Accept-Ranges: bytes\n" | 471 "Accept-Ranges: bytes\n" |
473 "Content-Length: 10\n", | 472 "Content-Length: 10\n", |
474 base::Time(), | 473 base::Time(), "rg: 40-49 ", TEST_MODE_NORMAL, |
475 "rg: 40-49 ", | 474 &RangeTransactionServer::RangeHandler, nullptr, nullptr, 0, 0, OK}; |
476 TEST_MODE_NORMAL, | |
477 &RangeTransactionServer::RangeHandler, | |
478 nullptr, | |
479 0, | |
480 0, | |
481 OK}; | |
482 | 475 |
483 const char kFullRangeData[] = | 476 const char kFullRangeData[] = |
484 "rg: 00-09 rg: 10-19 rg: 20-29 rg: 30-39 " | 477 "rg: 00-09 rg: 10-19 rg: 20-29 rg: 30-39 " |
485 "rg: 40-49 rg: 50-59 rg: 60-69 rg: 70-79 "; | 478 "rg: 40-49 rg: 50-59 rg: 60-69 rg: 70-79 "; |
486 | 479 |
487 // Verifies the response headers (|response|) match a partial content | 480 // Verifies the response headers (|response|) match a partial content |
488 // response for the range starting at |start| and ending at |end|. | 481 // response for the range starting at |start| and ending at |end|. |
489 void Verify206Response(const std::string& response, int start, int end) { | 482 void Verify206Response(const std::string& response, int start, int end) { |
490 std::string raw_headers( | 483 std::string raw_headers( |
491 HttpUtil::AssembleRawHeaders(response.data(), response.size())); | 484 HttpUtil::AssembleRawHeaders(response.data(), response.size())); |
(...skipping 6557 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
7049 EXPECT_GT(callback.GetResult(rv), 0); | 7042 EXPECT_GT(callback.GetResult(rv), 0); |
7050 rv = trans->Read(buf.get(), 256, callback.callback()); | 7043 rv = trans->Read(buf.get(), 256, callback.callback()); |
7051 EXPECT_EQ(callback.GetResult(rv), 0); | 7044 EXPECT_EQ(callback.GetResult(rv), 0); |
7052 } | 7045 } |
7053 | 7046 |
7054 // Verify that the disk entry was updated. | 7047 // Verify that the disk entry was updated. |
7055 VerifyTruncatedFlag(&cache, kRangeGET_TransactionOK.url, false, 80); | 7048 VerifyTruncatedFlag(&cache, kRangeGET_TransactionOK.url, false, 80); |
7056 RemoveMockTransaction(&kRangeGET_TransactionOK); | 7049 RemoveMockTransaction(&kRangeGET_TransactionOK); |
7057 } | 7050 } |
7058 | 7051 |
| 7052 namespace { |
| 7053 |
| 7054 enum class TransactionPhase { |
| 7055 BEFORE_FIRST_READ, |
| 7056 AFTER_FIRST_READ, |
| 7057 AFTER_NETWORK_READ |
| 7058 }; |
| 7059 |
| 7060 using CacheInitializer = void (*)(MockHttpCache*); |
| 7061 using HugeCacheTestConfiguration = |
| 7062 std::pair<TransactionPhase, CacheInitializer>; |
| 7063 |
| 7064 class HttpCacheHugeResourceTest |
| 7065 : public ::testing::TestWithParam<HugeCacheTestConfiguration> { |
| 7066 public: |
| 7067 static std::list<HugeCacheTestConfiguration> GetTestModes(); |
| 7068 static std::list<HugeCacheTestConfiguration> kTestModes; |
| 7069 |
| 7070 // CacheInitializer callbacks. These are used to initialize the cache |
| 7071 // depending on the test run configuration. |
| 7072 |
| 7073 // Initializes a cache containing a truncated entry containing the first 20 |
| 7074 // bytes of the reponse body. |
| 7075 static void SetupTruncatedCacheEntry(MockHttpCache* cache); |
| 7076 |
| 7077 // Initializes a cache containing a sparse entry. The first 10 bytes are |
| 7078 // present in the cache. |
| 7079 static void SetupPrefixSparseCacheEntry(MockHttpCache* cache); |
| 7080 |
| 7081 // Initializes a cache containing a sparse entry. The 10 bytes at offset |
| 7082 // 99990 are present in the cache. |
| 7083 static void SetupInfixSparseCacheEntry(MockHttpCache* cache); |
| 7084 |
| 7085 protected: |
| 7086 static void LargeResourceTransactionHandler( |
| 7087 const net::HttpRequestInfo* request, |
| 7088 std::string* response_status, |
| 7089 std::string* response_headers, |
| 7090 std::string* response_data); |
| 7091 static int LargeBufferReader(int64_t content_length, |
| 7092 int64_t offset, |
| 7093 net::IOBuffer* buf, |
| 7094 int buf_len); |
| 7095 |
| 7096 static void SetFlagOnBeforeNetworkStart(bool* started, bool* /* defer */); |
| 7097 |
| 7098 // Size of resource to be tested. |
| 7099 static const int64_t kTotalSize = 5000LL * 1000 * 1000; |
| 7100 }; |
| 7101 |
| 7102 const int64_t HttpCacheHugeResourceTest::kTotalSize; |
| 7103 |
| 7104 // static |
| 7105 void HttpCacheHugeResourceTest::LargeResourceTransactionHandler( |
| 7106 const net::HttpRequestInfo* request, |
| 7107 std::string* response_status, |
| 7108 std::string* response_headers, |
| 7109 std::string* response_data) { |
| 7110 std::string if_range; |
| 7111 if (!request->extra_headers.GetHeader(net::HttpRequestHeaders::kIfRange, |
| 7112 &if_range)) { |
| 7113 // If there were no range headers in the request, we are going to just |
| 7114 // return the entire response body. |
| 7115 *response_status = "HTTP/1.1 200 Success"; |
| 7116 *response_headers = base::StringPrintf("Content-Length: %" PRId64 |
| 7117 "\n" |
| 7118 "ETag: \"foo\"\n" |
| 7119 "Accept-Ranges: bytes\n", |
| 7120 kTotalSize); |
| 7121 return; |
| 7122 } |
| 7123 |
| 7124 // From this point on, we should be processing a valid byte-range request. |
| 7125 EXPECT_EQ("\"foo\"", if_range); |
| 7126 |
| 7127 std::string range_header; |
| 7128 EXPECT_TRUE(request->extra_headers.GetHeader(net::HttpRequestHeaders::kRange, |
| 7129 &range_header)); |
| 7130 std::vector<net::HttpByteRange> ranges; |
| 7131 |
| 7132 EXPECT_TRUE(net::HttpUtil::ParseRangeHeader(range_header, &ranges)); |
| 7133 ASSERT_EQ(1u, ranges.size()); |
| 7134 |
| 7135 net::HttpByteRange range = ranges[0]; |
| 7136 EXPECT_TRUE(range.HasFirstBytePosition()); |
| 7137 int64_t last_byte_position = |
| 7138 range.HasLastBytePosition() ? range.last_byte_position() : kTotalSize - 1; |
| 7139 |
| 7140 *response_status = "HTTP/1.1 206 Partial"; |
| 7141 *response_headers = base::StringPrintf( |
| 7142 "Content-Range: bytes %" PRId64 "-%" PRId64 "/%" PRId64 |
| 7143 "\n" |
| 7144 "Content-Length: %" PRId64 "\n", |
| 7145 range.first_byte_position(), last_byte_position, kTotalSize, |
| 7146 last_byte_position - range.first_byte_position() + 1); |
| 7147 } |
| 7148 |
| 7149 // static |
| 7150 int HttpCacheHugeResourceTest::LargeBufferReader(int64_t content_length, |
| 7151 int64_t offset, |
| 7152 net::IOBuffer* buf, |
| 7153 int buf_len) { |
| 7154 // This test involves reading multiple gigabytes of data. To make it run in a |
| 7155 // reasonable amount of time, we are going to skip filling the buffer with |
| 7156 // data. Instead the test relies on verifying that the count of bytes expected |
| 7157 // at the end is correct. |
| 7158 EXPECT_LT(0, content_length); |
| 7159 EXPECT_LE(offset, content_length); |
| 7160 int num = std::min(static_cast<int64_t>(buf_len), content_length - offset); |
| 7161 return num; |
| 7162 } |
| 7163 |
| 7164 // static |
| 7165 void HttpCacheHugeResourceTest::SetFlagOnBeforeNetworkStart(bool* started, |
| 7166 bool* /* defer */) { |
| 7167 *started = true; |
| 7168 } |
| 7169 |
| 7170 // static |
| 7171 void HttpCacheHugeResourceTest::SetupTruncatedCacheEntry(MockHttpCache* cache) { |
| 7172 ScopedMockTransaction scoped_transaction(kRangeGET_TransactionOK); |
| 7173 std::string cached_headers = base::StringPrintf( |
| 7174 "HTTP/1.1 200 OK\n" |
| 7175 "Last-Modified: Sat, 18 Apr 2007 01:10:43 GMT\n" |
| 7176 "ETag: \"foo\"\n" |
| 7177 "Accept-Ranges: bytes\n" |
| 7178 "Content-Length: %" PRId64 "\n", |
| 7179 kTotalSize); |
| 7180 CreateTruncatedEntry(cached_headers, cache); |
| 7181 } |
| 7182 |
| 7183 // static |
| 7184 void HttpCacheHugeResourceTest::SetupPrefixSparseCacheEntry( |
| 7185 MockHttpCache* cache) { |
| 7186 MockTransaction transaction(kRangeGET_TransactionOK); |
| 7187 transaction.handler = nullptr; |
| 7188 transaction.request_headers = "Range: bytes = 0-9\r\n" EXTRA_HEADER; |
| 7189 transaction.response_headers = |
| 7190 "Last-Modified: Sat, 18 Apr 2007 01:10:43 GMT\n" |
| 7191 "ETag: \"foo\"\n" |
| 7192 "Accept-Ranges: bytes\n" |
| 7193 "Content-Range: bytes 0-9/5000000000\n" |
| 7194 "Content-Length: 10\n"; |
| 7195 AddMockTransaction(&transaction); |
| 7196 std::string headers; |
| 7197 RunTransactionTestWithResponse(cache->http_cache(), transaction, &headers); |
| 7198 RemoveMockTransaction(&transaction); |
| 7199 } |
| 7200 |
| 7201 // static |
| 7202 void HttpCacheHugeResourceTest::SetupInfixSparseCacheEntry( |
| 7203 MockHttpCache* cache) { |
| 7204 MockTransaction transaction(kRangeGET_TransactionOK); |
| 7205 transaction.handler = nullptr; |
| 7206 transaction.request_headers = "Range: bytes = 99990-99999\r\n" EXTRA_HEADER; |
| 7207 transaction.response_headers = |
| 7208 "Last-Modified: Sat, 18 Apr 2007 01:10:43 GMT\n" |
| 7209 "ETag: \"foo\"\n" |
| 7210 "Accept-Ranges: bytes\n" |
| 7211 "Content-Range: bytes 99990-99999/5000000000\n" |
| 7212 "Content-Length: 10\n"; |
| 7213 AddMockTransaction(&transaction); |
| 7214 std::string headers; |
| 7215 RunTransactionTestWithResponse(cache->http_cache(), transaction, &headers); |
| 7216 RemoveMockTransaction(&transaction); |
| 7217 } |
| 7218 |
| 7219 // static |
| 7220 std::list<HugeCacheTestConfiguration> |
| 7221 HttpCacheHugeResourceTest::GetTestModes() { |
| 7222 std::list<HugeCacheTestConfiguration> test_modes; |
| 7223 const TransactionPhase kTransactionPhases[] = { |
| 7224 TransactionPhase::BEFORE_FIRST_READ, TransactionPhase::AFTER_FIRST_READ, |
| 7225 TransactionPhase::AFTER_NETWORK_READ}; |
| 7226 const CacheInitializer kInitializers[] = {&SetupTruncatedCacheEntry, |
| 7227 &SetupPrefixSparseCacheEntry, |
| 7228 &SetupInfixSparseCacheEntry}; |
| 7229 |
| 7230 for (const auto phase : kTransactionPhases) |
| 7231 for (const auto initializer : kInitializers) |
| 7232 test_modes.push_back(std::make_pair(phase, initializer)); |
| 7233 |
| 7234 return test_modes; |
| 7235 } |
| 7236 |
| 7237 // static |
| 7238 std::list<HugeCacheTestConfiguration> HttpCacheHugeResourceTest::kTestModes = |
| 7239 HttpCacheHugeResourceTest::GetTestModes(); |
| 7240 |
| 7241 INSTANTIATE_TEST_CASE_P( |
| 7242 _, |
| 7243 HttpCacheHugeResourceTest, |
| 7244 ::testing::ValuesIn(HttpCacheHugeResourceTest::kTestModes)); |
| 7245 |
| 7246 } // namespace |
| 7247 |
| 7248 // Test what happens when StopCaching() is called while reading a huge resource |
| 7249 // fetched via GET. Various combinations of cache state and when StopCaching() |
| 7250 // is called is controlled by the parameter passed into the test via the |
| 7251 // INSTANTIATE_TEST_CASE_P invocation above. |
| 7252 TEST_P(HttpCacheHugeResourceTest, |
| 7253 StopCachingFollowedByReadForHugeTruncatedResource) { |
| 7254 // This test is going to be repeated for all combinations of TransactionPhase |
| 7255 // and CacheInitializers returned by GetTestModes(). |
| 7256 const TransactionPhase stop_caching_phase = GetParam().first; |
| 7257 const CacheInitializer cache_initializer = GetParam().second; |
| 7258 |
| 7259 MockHttpCache cache; |
| 7260 (*cache_initializer)(&cache); |
| 7261 |
| 7262 MockTransaction transaction(kSimpleGET_Transaction); |
| 7263 transaction.url = kRangeGET_TransactionOK.url; |
| 7264 transaction.handler = &LargeResourceTransactionHandler; |
| 7265 transaction.read_handler = &LargeBufferReader; |
| 7266 ScopedMockTransaction scoped_transaction(transaction); |
| 7267 |
| 7268 MockHttpRequest request(transaction); |
| 7269 net::TestCompletionCallback callback; |
| 7270 std::unique_ptr<net::HttpTransaction> http_transaction; |
| 7271 int rv = cache.http_cache()->CreateTransaction(net::DEFAULT_PRIORITY, |
| 7272 &http_transaction); |
| 7273 ASSERT_EQ(net::OK, rv); |
| 7274 ASSERT_TRUE(http_transaction.get()); |
| 7275 |
| 7276 bool network_transaction_started = false; |
| 7277 if (stop_caching_phase == TransactionPhase::AFTER_NETWORK_READ) { |
| 7278 http_transaction->SetBeforeNetworkStartCallback( |
| 7279 base::Bind(&SetFlagOnBeforeNetworkStart, &network_transaction_started)); |
| 7280 } |
| 7281 |
| 7282 rv = http_transaction->Start(&request, callback.callback(), |
| 7283 net::BoundNetLog()); |
| 7284 rv = callback.GetResult(rv); |
| 7285 ASSERT_EQ(net::OK, rv); |
| 7286 |
| 7287 if (stop_caching_phase == TransactionPhase::BEFORE_FIRST_READ) |
| 7288 http_transaction->StopCaching(); |
| 7289 |
| 7290 int64_t total_bytes_received = 0; |
| 7291 |
| 7292 EXPECT_EQ(kTotalSize, |
| 7293 http_transaction->GetResponseInfo()->headers->GetContentLength()); |
| 7294 do { |
| 7295 // This test simulates reading gigabytes of data. Buffer size is set to 10MB |
| 7296 // to reduce the number of reads and speed up the test. |
| 7297 const int kBufferSize = 1024 * 1024 * 10; |
| 7298 scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(kBufferSize)); |
| 7299 rv = http_transaction->Read(buf.get(), kBufferSize, callback.callback()); |
| 7300 rv = callback.GetResult(rv); |
| 7301 |
| 7302 if (stop_caching_phase == TransactionPhase::AFTER_FIRST_READ && |
| 7303 total_bytes_received == 0) { |
| 7304 http_transaction->StopCaching(); |
| 7305 } |
| 7306 |
| 7307 if (rv > 0) |
| 7308 total_bytes_received += rv; |
| 7309 |
| 7310 if (network_transaction_started && |
| 7311 stop_caching_phase == TransactionPhase::AFTER_NETWORK_READ) { |
| 7312 http_transaction->StopCaching(); |
| 7313 network_transaction_started = false; |
| 7314 } |
| 7315 } while (rv > 0); |
| 7316 |
| 7317 // The only verification we are going to do is that the received resource has |
| 7318 // the correct size. This is sufficient to verify that the state machine |
| 7319 // didn't terminate abruptly due to the StopCaching() call. |
| 7320 EXPECT_EQ(kTotalSize, total_bytes_received); |
| 7321 } |
| 7322 |
7059 // Tests that we detect truncated resources from the net when there is | 7323 // Tests that we detect truncated resources from the net when there is |
7060 // a Content-Length header. | 7324 // a Content-Length header. |
7061 TEST(HttpCache, TruncatedByContentLength) { | 7325 TEST(HttpCache, TruncatedByContentLength) { |
7062 MockHttpCache cache; | 7326 MockHttpCache cache; |
7063 TestCompletionCallback callback; | 7327 TestCompletionCallback callback; |
7064 | 7328 |
7065 MockTransaction transaction(kSimpleGET_Transaction); | 7329 MockTransaction transaction(kSimpleGET_Transaction); |
7066 AddMockTransaction(&transaction); | 7330 AddMockTransaction(&transaction); |
7067 transaction.response_headers = "Cache-Control: max-age=10000\n" | 7331 transaction.response_headers = "Cache-Control: max-age=10000\n" |
7068 "Content-Length: 100\n"; | 7332 "Content-Length: 100\n"; |
(...skipping 666 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
7735 EXPECT_EQ(1, cache.disk_cache()->open_count()); | 7999 EXPECT_EQ(1, cache.disk_cache()->open_count()); |
7736 EXPECT_EQ(1, cache.disk_cache()->create_count()); | 8000 EXPECT_EQ(1, cache.disk_cache()->create_count()); |
7737 EXPECT_TRUE(response_info.was_cached); | 8001 EXPECT_TRUE(response_info.was_cached); |
7738 | 8002 |
7739 // The new SSL state is reported. | 8003 // The new SSL state is reported. |
7740 EXPECT_EQ(status2, response_info.ssl_info.connection_status); | 8004 EXPECT_EQ(status2, response_info.ssl_info.connection_status); |
7741 EXPECT_TRUE(cert2->Equals(response_info.ssl_info.cert.get())); | 8005 EXPECT_TRUE(cert2->Equals(response_info.ssl_info.cert.get())); |
7742 } | 8006 } |
7743 | 8007 |
7744 } // namespace net | 8008 } // namespace net |
OLD | NEW |