summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrdsmith@chromium.org <rdsmith@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-03-23 16:17:27 +0000
committerrdsmith@chromium.org <rdsmith@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-03-23 16:17:27 +0000
commit2ef5d00e499d22100618d48f302dbba25fc26633 (patch)
tree00b70dad92ba6d98914fc5c5044b6bde7242902c
parenta63e8d26092dc5dba81f25d396026a296c036999 (diff)
downloadchromium_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.js32
-rw-r--r--net/base/load_flags_list.h51
-rw-r--r--net/http/http_cache_transaction.cc70
-rw-r--r--net/http/http_cache_transaction.h4
-rw-r--r--net/http/http_cache_unittest.cc112
-rw-r--r--net/http/http_response_info.cc3
-rw-r--r--net/http/http_response_info.h5
-rw-r--r--net/http/http_transaction_unittest.cc23
-rw-r--r--net/http/http_transaction_unittest.h3
-rw-r--r--net/url_request/url_request_job_unittest.cc3
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