diff options
author | rdsmith@chromium.org <rdsmith@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-23 16:17:27 +0000 |
---|---|---|
committer | rdsmith@chromium.org <rdsmith@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-23 16:17:27 +0000 |
commit | 2ef5d00e499d22100618d48f302dbba25fc26633 (patch) | |
tree | 00b70dad92ba6d98914fc5c5044b6bde7242902c | |
parent | a63e8d26092dc5dba81f25d396026a296c036999 (diff) | |
download | chromium_src-2ef5d00e499d22100618d48f302dbba25fc26633.zip chromium_src-2ef5d00e499d22100618d48f302dbba25fc26633.tar.gz chromium_src-2ef5d00e499d22100618d48f302dbba25fc26633.tar.bz2 |
Cache failover to LOAD_PREFERRING_CACHE if network response suggests offline.
(Controlled by new load flag.)
BUG=2204
R=rvargas@chromium.org
Review URL: https://chromiumcodereview.appspot.com/12310075
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@190020 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/test/data/webui/net_internals/log_view_painter.js | 32 | ||||
-rw-r--r-- | net/base/load_flags_list.h | 51 | ||||
-rw-r--r-- | net/http/http_cache_transaction.cc | 70 | ||||
-rw-r--r-- | net/http/http_cache_transaction.h | 4 | ||||
-rw-r--r-- | net/http/http_cache_unittest.cc | 112 | ||||
-rw-r--r-- | net/http/http_response_info.cc | 3 | ||||
-rw-r--r-- | net/http/http_response_info.h | 5 | ||||
-rw-r--r-- | net/http/http_transaction_unittest.cc | 23 | ||||
-rw-r--r-- | net/http/http_transaction_unittest.h | 3 | ||||
-rw-r--r-- | net/url_request/url_request_job_unittest.cc | 3 |
10 files changed, 241 insertions, 65 deletions
diff --git a/chrome/test/data/webui/net_internals/log_view_painter.js b/chrome/test/data/webui/net_internals/log_view_painter.js index fc8e7b6..33493af 100644 --- a/chrome/test/data/webui/net_internals/log_view_painter.js +++ b/chrome/test/data/webui/net_internals/log_view_painter.js @@ -189,7 +189,7 @@ function painterTestURLRequest() { }, { 'params': { - 'load_flags': 68223104, + 'load_flags': 136446208, 'method': 'GET', 'priority': 4, 'url': 'http://www.google.com/' @@ -213,7 +213,7 @@ function painterTestURLRequest() { }, { 'params': { - 'load_flags': 68223104, + 'load_flags': 136446208, 'method': 'GET', 'priority': 4, 'url': 'http://www.google.com/' @@ -720,14 +720,14 @@ function painterTestURLRequest() { testCase.expectedText = 't=1338864633224 [st= 0] +REQUEST_ALIVE [dt=789]\n' + 't=1338864633238 [st= 14] URL_REQUEST_START_JOB [dt=8]\n' + -' --> load_flags = 68223104 ' + +' --> load_flags = 136446208 ' + '(ENABLE_LOAD_TIMING | MAIN_FRAME | MAYBE_USER_GESTURE ' + '| VERIFY_EV_CERT)\n' + ' --> method = "GET"\n' + ' --> priority = 4\n' + ' --> url = "http://www.google.com/"\n' + 't=1338864633248 [st= 24] +URL_REQUEST_START_JOB [dt=279]\n' + -' --> load_flags = 68223104 ' + +' --> load_flags = 136446208 ' + '(ENABLE_LOAD_TIMING | MAIN_FRAME | MAYBE_USER_GESTURE ' + '| VERIFY_EV_CERT)\n' + ' --> method = "GET"\n' + @@ -806,7 +806,7 @@ function painterTestURLRequestIncomplete() { }, { 'params': { - 'load_flags': 128, + 'load_flags': 256, 'method': 'GET', 'priority': 4, 'url': 'http://www.google.com/' @@ -833,7 +833,7 @@ function painterTestURLRequestIncomplete() { testCase.expectedText = 't=1338864633224 [st= 0] +REQUEST_ALIVE [dt=?]\n' + 't=1338864633356 [st=132] URL_REQUEST_START_JOB [dt=60]\n' + -' --> load_flags = 128 (ENABLE_LOAD_TIMING)\n' + +' --> load_flags = 256 (ENABLE_LOAD_TIMING)\n' + ' --> method = "GET"\n' + ' --> priority = 4\n' + ' --> url = "http://www.google.com/"'; @@ -851,7 +851,7 @@ function painterTestURLRequestIncompleteFromLoadedLog() { testCase.expectedText = 't=1338864633224 [st= 0] +REQUEST_ALIVE [dt=789+]\n' + 't=1338864633356 [st=132] URL_REQUEST_START_JOB [dt=60]\n' + -' --> load_flags = 128 (ENABLE_LOAD_TIMING)\n' + +' --> load_flags = 256 (ENABLE_LOAD_TIMING)\n' + ' --> method = "GET"\n' + ' --> priority = 4\n' + ' --> url = "http://www.google.com/"\n' + @@ -879,7 +879,7 @@ function painterTestNetError() { }, { 'params': { - 'load_flags': 68223104, + 'load_flags': 136446208, 'method': 'GET', 'priority': 4, 'url': 'http://www.doesnotexistdomain.com/' @@ -903,7 +903,7 @@ function painterTestNetError() { }, { 'params': { - 'load_flags': 68223104, + 'load_flags': 136446208, 'method': 'GET', 'priority': 4, 'url': 'http://www.doesnotexistdomain.com/' @@ -1038,14 +1038,16 @@ function painterTestNetError() { testCase.expectedText = 't=1338864773894 [st= 0] +REQUEST_ALIVE [dt=475]\n' + 't=1338864773901 [st= 7] URL_REQUEST_START_JOB [dt=5]\n' + -' --> load_flags = 68223104 (ENABLE_LOAD_TIMING | ' + - 'MAIN_FRAME | MAYBE_USER_GESTURE | VERIFY_EV_CERT)\n' + +' --> load_flags = 136446208 (' + + 'ENABLE_LOAD_TIMING | MAIN_FRAME | MAYBE_USER_GESTURE ' + + '| VERIFY_EV_CERT)\n' + ' --> method = "GET"\n' + ' --> priority = 4\n' + ' --> url = "http://www.doesnotexistdomain.com/"\n' + 't=1338864773906 [st= 12] +URL_REQUEST_START_JOB [dt=245]\n' + -' --> load_flags = 68223104 (ENABLE_LOAD_TIMING | ' + - 'MAIN_FRAME | MAYBE_USER_GESTURE | VERIFY_EV_CERT)\n' + +' --> load_flags = 136446208 (' + + 'ENABLE_LOAD_TIMING | MAIN_FRAME | MAYBE_USER_GESTURE ' + + '| VERIFY_EV_CERT)\n' + ' --> method = "GET"\n' + ' --> priority = 4\n' + ' --> url = "http://www.doesnotexistdomain.com/"\n' + @@ -1715,7 +1717,7 @@ function painterTestInProgressURLRequest() { testCase.logEntries = [ { 'params': { - 'load_flags': 68223104, + 'load_flags': 136446208, 'load_state': LoadState.READING_RESPONSE, 'method': 'GET', 'url': 'http://www.MagicPonyShopper.com' @@ -1759,7 +1761,7 @@ function painterTestInProgressURLRequest() { testCase.expectedText = 't=1338864773994 [st= 0] +REQUEST_ALIVE [dt=375]\n' + -' --> load_flags = 68223104 ' + +' --> load_flags = 136446208 ' + '(ENABLE_LOAD_TIMING | MAIN_FRAME | MAYBE_USER_GESTURE ' + '| VERIFY_EV_CERT)\n' + ' --> load_state = ' + LoadState.READING_RESPONSE + diff --git a/net/base/load_flags_list.h b/net/base/load_flags_list.h index 635ce79..968c122 100644 --- a/net/base/load_flags_list.h +++ b/net/base/load_flags_list.h @@ -25,92 +25,97 @@ LOAD_FLAG(PREFERRING_CACHE, 1 << 2) // resource from the cache (or some equivalent local store). LOAD_FLAG(ONLY_FROM_CACHE, 1 << 3) +// Indicate that if the request fails at the network level in a way that +// indicates the source is unreachable, the request should fail over +// to as if LOAD_PREFERRING_CACHE had been set. +LOAD_FLAG(FROM_CACHE_IF_OFFLINE, 1 << 4) + // This is a navigation that will not use the cache at all. It does not // impact the HTTP request headers. -LOAD_FLAG(DISABLE_CACHE, 1 << 4) +LOAD_FLAG(DISABLE_CACHE, 1 << 5) // This is a navigation that will not be intercepted by any registered // URLRequest::Interceptors. -LOAD_FLAG(DISABLE_INTERCEPT, 1 << 5) +LOAD_FLAG(DISABLE_INTERCEPT, 1 << 6) // If present, upload progress messages should be provided to initiator. -LOAD_FLAG(ENABLE_UPLOAD_PROGRESS, 1 << 6) +LOAD_FLAG(ENABLE_UPLOAD_PROGRESS, 1 << 7) // If present, collect load timing for the request. -LOAD_FLAG(ENABLE_LOAD_TIMING, 1 << 7) +LOAD_FLAG(ENABLE_LOAD_TIMING, 1 << 8) // If present, ignores certificate mismatches with the domain name. // (The default behavior is to trigger an OnSSLCertificateError callback.) -LOAD_FLAG(IGNORE_CERT_COMMON_NAME_INVALID, 1 << 8) +LOAD_FLAG(IGNORE_CERT_COMMON_NAME_INVALID, 1 << 9) // If present, ignores certificate expiration dates // (The default behavior is to trigger an OnSSLCertificateError callback). -LOAD_FLAG(IGNORE_CERT_DATE_INVALID, 1 << 9) +LOAD_FLAG(IGNORE_CERT_DATE_INVALID, 1 << 10) // If present, trusts all certificate authorities // (The default behavior is to trigger an OnSSLCertificateError callback). -LOAD_FLAG(IGNORE_CERT_AUTHORITY_INVALID, 1 << 10) +LOAD_FLAG(IGNORE_CERT_AUTHORITY_INVALID, 1 << 11) // If present, causes certificate revocation checks to be skipped on secure // connections. -LOAD_FLAG(DISABLE_CERT_REVOCATION_CHECKING, 1 << 11) +LOAD_FLAG(DISABLE_CERT_REVOCATION_CHECKING, 1 << 12) // If present, ignores wrong key usage of the certificate // (The default behavior is to trigger an OnSSLCertificateError callback). -LOAD_FLAG(IGNORE_CERT_WRONG_USAGE, 1 << 12) +LOAD_FLAG(IGNORE_CERT_WRONG_USAGE, 1 << 13) // This load will not make any changes to cookies, including storing new // cookies or updating existing ones. -LOAD_FLAG(DO_NOT_SAVE_COOKIES, 1 << 13) +LOAD_FLAG(DO_NOT_SAVE_COOKIES, 1 << 14) // Do not resolve proxies. This override is used when downloading PAC files // to avoid having a circular dependency. -LOAD_FLAG(BYPASS_PROXY, 1 << 14) +LOAD_FLAG(BYPASS_PROXY, 1 << 15) // Indicate this request is for a download, as opposed to viewing. -LOAD_FLAG(IS_DOWNLOAD, 1 << 15) +LOAD_FLAG(IS_DOWNLOAD, 1 << 16) // Requires EV certificate verification. -LOAD_FLAG(VERIFY_EV_CERT, 1 << 16) +LOAD_FLAG(VERIFY_EV_CERT, 1 << 17) // This load will not send any cookies. -LOAD_FLAG(DO_NOT_SEND_COOKIES, 1 << 17) +LOAD_FLAG(DO_NOT_SEND_COOKIES, 1 << 18) // This load will not send authentication data (user name/password) // to the server (as opposed to the proxy). -LOAD_FLAG(DO_NOT_SEND_AUTH_DATA, 1 << 18) +LOAD_FLAG(DO_NOT_SEND_AUTH_DATA, 1 << 19) // This should only be used for testing (set by HttpNetworkTransaction). -LOAD_FLAG(IGNORE_ALL_CERT_ERRORS, 1 << 19) +LOAD_FLAG(IGNORE_ALL_CERT_ERRORS, 1 << 20) // Indicate that this is a top level frame, so that we don't assume it is a // subresource and speculatively pre-connect or pre-resolve when a referring // page is loaded. -LOAD_FLAG(MAIN_FRAME, 1 << 20) +LOAD_FLAG(MAIN_FRAME, 1 << 21) // Indicate that this is a sub frame, and hence it might have subresources that // should be speculatively resolved, or even speculatively preconnected. -LOAD_FLAG(SUB_FRAME, 1 << 21) +LOAD_FLAG(SUB_FRAME, 1 << 22) // If present, intercept actual request/response headers from network stack // and report them to renderer. This includes cookies, so the flag is only // respected if renderer has CanReadRawCookies capability in the security // policy. -LOAD_FLAG(REPORT_RAW_HEADERS, 1 << 22) +LOAD_FLAG(REPORT_RAW_HEADERS, 1 << 23) // Indicates that this load was motivated by the rel=prefetch feature, // and is (in theory) not intended for the current frame. -LOAD_FLAG(PREFETCH, 1 << 23) +LOAD_FLAG(PREFETCH, 1 << 24) // Indicates that this is a load that ignores limits and should complete // immediately. -LOAD_FLAG(IGNORE_LIMITS, 1 << 24) +LOAD_FLAG(IGNORE_LIMITS, 1 << 25) // Suppress login prompts for this request. Cached credentials or // default credentials may still be used for authentication. -LOAD_FLAG(DO_NOT_PROMPT_FOR_LOGIN, 1 << 25) +LOAD_FLAG(DO_NOT_PROMPT_FOR_LOGIN, 1 << 26) // Indicates that the operation is somewhat likely to be due to an // explicit user action. This can be used as a hint to treat the // request with higher priority. -LOAD_FLAG(MAYBE_USER_GESTURE, 1 << 26) +LOAD_FLAG(MAYBE_USER_GESTURE, 1 << 27) diff --git a/net/http/http_cache_transaction.cc b/net/http/http_cache_transaction.cc index c45a8a0..491f2f3 100644 --- a/net/http/http_cache_transaction.cc +++ b/net/http/http_cache_transaction.cc @@ -53,6 +53,15 @@ bool NonErrorResponse(int status_code) { return status_code_range == 2 || status_code_range == 3; } +// Error codes that will be considered indicative of a page being offline/ +// unreachable for LOAD_FROM_CACHE_IF_OFFLINE. +bool IsOfflineError(int error) { + return (error == net::ERR_NAME_NOT_RESOLVED || + error == net::ERR_INTERNET_DISCONNECTED || + error == net::ERR_ADDRESS_UNREACHABLE || + error == net::ERR_CONNECTION_TIMED_OUT); +} + } // namespace namespace net { @@ -142,6 +151,7 @@ HttpCache::Transaction::Transaction( cache_pending_(false), done_reading_(false), vary_mismatch_(false), + couldnt_conditionalize_request_(false), io_buf_len_(0), read_offset_(0), effective_load_flags_(0), @@ -812,6 +822,21 @@ int HttpCache::Transaction::DoSendRequestComplete(int result) { if (!cache_) return ERR_UNEXPECTED; + // If requested, and we have a readable cache entry, and we have + // an error indicating that we're offline as opposed to in contact + // with a bad server, read from cache anyway. + if ((effective_load_flags_ & LOAD_FROM_CACHE_IF_OFFLINE) && + IsOfflineError(result) && mode_ == READ_WRITE && entry_ && !partial_) { + UpdateTransactionPattern(PATTERN_NOT_COVERED); + response_.server_data_unavailable = true; + return SetupEntryForRead(); + } + + // If we tried to conditionalize the request and failed, we know + // we won't be reading from the cache after this point. + if (couldnt_conditionalize_request_) + mode_ = WRITE; + if (result == OK) { next_state_ = STATE_SUCCESSFUL_SEND_REQUEST; return OK; @@ -1765,33 +1790,21 @@ int HttpCache::Transaction::BeginCacheValidation() { if (skip_validation) { UpdateTransactionPattern(PATTERN_ENTRY_USED); - if (partial_.get()) { - if (truncated_ || is_sparse_ || !invalid_range_) { - // We are going to return the saved response headers to the caller, so - // we may need to adjust them first. - next_state_ = STATE_PARTIAL_HEADERS_RECEIVED; - return OK; - } else { - partial_.reset(); - } - } - cache_->ConvertWriterToReader(entry_); - mode_ = READ; - - if (entry_->disk_entry->GetDataSize(kMetadataIndex)) - next_state_ = STATE_CACHE_READ_METADATA; + return SetupEntryForRead(); } else { // Make the network request conditional, to see if we may reuse our cached // response. If we cannot do so, then we just resort to a normal fetch. - // Our mode remains READ_WRITE for a conditional request. We'll switch to - // either READ or WRITE mode once we hear back from the server. + // Our mode remains READ_WRITE for a conditional request. Even if the + // conditionalization fails, we don't switch to WRITE mode until we + // know we won't be falling back to using the cache entry in the + // LOAD_FROM_CACHE_IF_OFFLINE case. if (!ConditionalizeRequest()) { + couldnt_conditionalize_request_ = true; UpdateTransactionPattern(PATTERN_ENTRY_CANT_CONDITIONALIZE); if (partial_.get()) return DoRestartPartialRequest(); DCHECK_NE(206, response_.headers->response_code()); - mode_ = WRITE; } next_state_ = STATE_SEND_REQUEST; } @@ -2147,6 +2160,27 @@ void HttpCache::Transaction::FailRangeRequest() { partial_->FixResponseHeaders(response_.headers, false); } +int HttpCache::Transaction::SetupEntryForRead() { + network_trans_.reset(); + if (partial_.get()) { + if (truncated_ || is_sparse_ || !invalid_range_) { + // We are going to return the saved response headers to the caller, so + // we may need to adjust them first. + next_state_ = STATE_PARTIAL_HEADERS_RECEIVED; + return OK; + } else { + partial_.reset(); + } + } + cache_->ConvertWriterToReader(entry_); + mode_ = READ; + + if (entry_->disk_entry->GetDataSize(kMetadataIndex)) + next_state_ = STATE_CACHE_READ_METADATA; + return OK; +} + + int HttpCache::Transaction::ReadFromNetwork(IOBuffer* data, int data_len) { read_buf_ = data; io_buf_len_ = data_len; diff --git a/net/http/http_cache_transaction.h b/net/http/http_cache_transaction.h index 65ca970..e5365c4 100644 --- a/net/http/http_cache_transaction.h +++ b/net/http/http_cache_transaction.h @@ -313,6 +313,9 @@ class HttpCache::Transaction : public HttpTransaction { // satisfiable). void FailRangeRequest(); + // Setups the transaction for reading from the cache entry. + int SetupEntryForRead(); + // Reads data from the network. int ReadFromNetwork(IOBuffer* data, int data_len); @@ -413,6 +416,7 @@ class HttpCache::Transaction : public HttpTransaction { bool cache_pending_; // We are waiting for the HttpCache. bool done_reading_; bool vary_mismatch_; // The request doesn't match the stored vary data. + bool couldnt_conditionalize_request_; scoped_refptr<IOBuffer> read_buf_; int io_buf_len_; int read_offset_; diff --git a/net/http/http_cache_unittest.cc b/net/http/http_cache_unittest.cc index a97ba8a..ed4524f 100644 --- a/net/http/http_cache_unittest.cc +++ b/net/http/http_cache_unittest.cc @@ -156,7 +156,10 @@ void RunTransactionTestWithRequestAndLogAndDelegate( rv = trans->Start(&request, callback.callback(), net_log); if (rv == net::ERR_IO_PENDING) rv = callback.WaitForResult(); - ASSERT_EQ(net::OK, rv); + ASSERT_EQ(trans_info.return_code, rv); + + if (net::OK != rv) + return; const net::HttpResponseInfo* response = trans->GetResponseInfo(); ASSERT_TRUE(response); @@ -250,7 +253,8 @@ const MockTransaction kFastNoStoreGET_Transaction = { "<html><body>Google Blah Blah</body></html>", TEST_MODE_SYNC_NET_START, &FastTransactionServer::FastNoStoreHandler, - 0 + 0, + net::OK }; // This class provides a handler for kRangeGET_TransactionOK so that the range @@ -391,7 +395,8 @@ const MockTransaction kRangeGET_TransactionOK = { "rg: 40-49 ", TEST_MODE_NORMAL, &RangeTransactionServer::RangeHandler, - 0 + 0, + net::OK }; // Verifies the response headers (|response|) match a partial content @@ -847,6 +852,107 @@ TEST(HttpCache, SimpleGET_LoadPreferringCache_VaryMismatch) { RemoveMockTransaction(&transaction); } +// Tests that LOAD_FROM_CACHE_IF_OFFLINE returns proper response on +// network success +TEST(HttpCache, SimpleGET_CacheOverride_Network) { + MockHttpCache cache; + + // Prime cache. + MockTransaction transaction(kSimpleGET_Transaction); + transaction.load_flags |= net::LOAD_FROM_CACHE_IF_OFFLINE; + transaction.response_headers = "Cache-Control: no-cache\n"; + + AddMockTransaction(&transaction); + RunTransactionTest(cache.http_cache(), transaction); + EXPECT_EQ(1, cache.network_layer()->transaction_count()); + EXPECT_EQ(1, cache.disk_cache()->create_count()); + RemoveMockTransaction(&transaction); + + // Re-run transaction; make sure the result came from the network, + // not the cache. + transaction.data = "Changed data."; + AddMockTransaction(&transaction); + net::HttpResponseInfo response_info; + RunTransactionTestWithResponseInfo(cache.http_cache(), transaction, + &response_info); + + EXPECT_EQ(2, cache.network_layer()->transaction_count()); + EXPECT_FALSE(response_info.server_data_unavailable); + + RemoveMockTransaction(&transaction); +} + +// Tests that LOAD_FROM_CACHE_IF_OFFLINE returns proper response on +// offline failure +TEST(HttpCache, SimpleGET_CacheOverride_Offline) { + MockHttpCache cache; + + // Prime cache. + MockTransaction transaction(kSimpleGET_Transaction); + transaction.load_flags |= net::LOAD_FROM_CACHE_IF_OFFLINE; + transaction.response_headers = "Cache-Control: no-cache\n"; + + AddMockTransaction(&transaction); + RunTransactionTest(cache.http_cache(), transaction); + EXPECT_EQ(1, cache.network_layer()->transaction_count()); + EXPECT_EQ(1, cache.disk_cache()->create_count()); + RemoveMockTransaction(&transaction); + + // Network failure with offline error; should return cache entry above + + // flag signalling stale data. + transaction.return_code = net::ERR_NAME_NOT_RESOLVED; + AddMockTransaction(&transaction); + + MockHttpRequest request(transaction); + net::TestCompletionCallback callback; + scoped_ptr<net::HttpTransaction> trans; + int rv = cache.http_cache()->CreateTransaction( + net::DEFAULT_PRIORITY, &trans, NULL); + EXPECT_EQ(net::OK, rv); + ASSERT_TRUE(trans.get()); + rv = trans->Start(&request, callback.callback(), net::BoundNetLog()); + EXPECT_EQ(net::OK, callback.GetResult(rv)); + + const net::HttpResponseInfo* response_info = trans->GetResponseInfo(); + ASSERT_TRUE(response_info); + EXPECT_TRUE(response_info->server_data_unavailable); + EXPECT_TRUE(response_info->was_cached); + ReadAndVerifyTransaction(trans.get(), transaction); + EXPECT_EQ(2, cache.network_layer()->transaction_count()); + + RemoveMockTransaction(&transaction); +} + +// Tests that LOAD_FROM_CACHE_IF_OFFLINE returns proper response on +// non-offline failure failure +TEST(HttpCache, SimpleGET_CacheOverride_NonOffline) { + MockHttpCache cache; + + // Prime cache. + MockTransaction transaction(kSimpleGET_Transaction); + transaction.load_flags |= net::LOAD_FROM_CACHE_IF_OFFLINE; + transaction.response_headers = "Cache-Control: no-cache\n"; + + AddMockTransaction(&transaction); + RunTransactionTest(cache.http_cache(), transaction); + EXPECT_EQ(1, cache.network_layer()->transaction_count()); + EXPECT_EQ(1, cache.disk_cache()->create_count()); + RemoveMockTransaction(&transaction); + + // Network failure with non-offline error; should fail with that error. + transaction.return_code = net::ERR_PROXY_CONNECTION_FAILED; + AddMockTransaction(&transaction); + + net::HttpResponseInfo response_info2; + RunTransactionTestWithResponseInfo(cache.http_cache(), transaction, + &response_info2); + + EXPECT_EQ(2, cache.network_layer()->transaction_count()); + EXPECT_FALSE(response_info2.server_data_unavailable); + + RemoveMockTransaction(&transaction); +} + TEST(HttpCache, SimpleGET_LoadBypassCache) { MockHttpCache cache; diff --git a/net/http/http_response_info.cc b/net/http/http_response_info.cc index 53c2121..58ea3d9 100644 --- a/net/http/http_response_info.cc +++ b/net/http/http_response_info.cc @@ -90,6 +90,7 @@ enum { HttpResponseInfo::HttpResponseInfo() : was_cached(false), + server_data_unavailable(false), was_fetched_via_spdy(false), was_npn_negotiated(false), was_fetched_via_proxy(false), @@ -98,6 +99,7 @@ HttpResponseInfo::HttpResponseInfo() HttpResponseInfo::HttpResponseInfo(const HttpResponseInfo& rhs) : was_cached(rhs.was_cached), + server_data_unavailable(rhs.server_data_unavailable), was_fetched_via_spdy(rhs.was_fetched_via_spdy), was_npn_negotiated(rhs.was_npn_negotiated), was_fetched_via_proxy(rhs.was_fetched_via_proxy), @@ -119,6 +121,7 @@ HttpResponseInfo::~HttpResponseInfo() { HttpResponseInfo& HttpResponseInfo::operator=(const HttpResponseInfo& rhs) { was_cached = rhs.was_cached; + server_data_unavailable = rhs.server_data_unavailable; was_fetched_via_spdy = rhs.was_fetched_via_spdy; was_npn_negotiated = rhs.was_npn_negotiated; was_fetched_via_proxy = rhs.was_fetched_via_proxy; diff --git a/net/http/http_response_info.h b/net/http/http_response_info.h index c4ba2bd..c82dd07 100644 --- a/net/http/http_response_info.h +++ b/net/http/http_response_info.h @@ -59,6 +59,11 @@ class NET_EXPORT HttpResponseInfo { // when reloading previously visited pages (without going over the network). bool was_cached; + // True if the request was fetched from cache rather than the network + // because of a LOAD_FROM_CACHE_IF_OFFLINE flag when the system + // was unable to contact the server. + bool server_data_unavailable; + // True if the request was fetched over a SPDY channel. bool was_fetched_via_spdy; diff --git a/net/http/http_transaction_unittest.cc b/net/http/http_transaction_unittest.cc index 3d96628..8e19e29 100644 --- a/net/http/http_transaction_unittest.cc +++ b/net/http/http_transaction_unittest.cc @@ -38,7 +38,8 @@ const MockTransaction kSimpleGET_Transaction = { "<html><body>Google Blah Blah</body></html>", TEST_MODE_NORMAL, NULL, - 0 + 0, + net::OK }; const MockTransaction kSimplePOST_Transaction = { @@ -53,7 +54,8 @@ const MockTransaction kSimplePOST_Transaction = { "<html><body>Google Blah Blah</body></html>", TEST_MODE_NORMAL, NULL, - 0 + 0, + net::OK }; const MockTransaction kTypicalGET_Transaction = { @@ -69,7 +71,8 @@ const MockTransaction kTypicalGET_Transaction = { "<html><body>Google Blah Blah</body></html>", TEST_MODE_NORMAL, NULL, - 0 + 0, + net::OK }; const MockTransaction kETagGET_Transaction = { @@ -85,7 +88,8 @@ const MockTransaction kETagGET_Transaction = { "<html><body>Google Blah Blah</body></html>", TEST_MODE_NORMAL, NULL, - 0 + 0, + net::OK }; const MockTransaction kRangeGET_Transaction = { @@ -100,7 +104,8 @@ const MockTransaction kRangeGET_Transaction = { "<html><body>Google Blah Blah</body></html>", TEST_MODE_NORMAL, NULL, - 0 + 0, + net::OK }; static const MockTransaction* const kBuiltinMockTransactions[] = { @@ -234,6 +239,14 @@ int MockNetworkTransaction::Start(const net::HttpRequestInfo* request, if (!t) return net::ERR_FAILED; + // Return immediately if we're returning in error. + if (net::OK != t->return_code) { + if (test_mode_ & TEST_MODE_SYNC_NET_START) + return t->return_code; + CallbackLater(callback, t->return_code); + return net::ERR_IO_PENDING; + } + std::string resp_status = t->status; std::string resp_headers = t->response_headers; std::string resp_data = t->data; diff --git a/net/http/http_transaction_unittest.h b/net/http/http_transaction_unittest.h index 29d558d8..6e31097 100644 --- a/net/http/http_transaction_unittest.h +++ b/net/http/http_transaction_unittest.h @@ -64,6 +64,9 @@ struct MockTransaction { int test_mode; MockTransactionHandler handler; net::CertStatus cert_status; + // Value returned by MockNetworkTransaction::Start (potentially + // asynchronously if |!(test_mode & TEST_MODE_SYNC_NET_START)|.) + net::Error return_code; }; extern const MockTransaction kSimpleGET_Transaction; diff --git a/net/url_request/url_request_job_unittest.cc b/net/url_request/url_request_job_unittest.cc index 983188e..a925ee1 100644 --- a/net/url_request/url_request_job_unittest.cc +++ b/net/url_request/url_request_job_unittest.cc @@ -33,7 +33,8 @@ const MockTransaction kGZip_Transaction = { "", TEST_MODE_NORMAL, &GZipServer, - 0 + 0, + net::OK }; } // namespace |