Index: net/http/http_cache_transaction.cc |
diff --git a/net/http/http_cache_transaction.cc b/net/http/http_cache_transaction.cc |
index 6ee76a7f673718f4c662cff46909c6fb21486c12..78dd629fefcb30836e82fe74e04681e9f4c9c67c 100644 |
--- a/net/http/http_cache_transaction.cc |
+++ b/net/http/http_cache_transaction.cc |
@@ -146,6 +146,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), |
@@ -200,6 +201,11 @@ HttpCache::Transaction::~Transaction() { |
// We could still have a cache read or write in progress, so we just null the |
// cache_ pointer to signal that we are dead. See DoCacheReadCompleted. |
cache_.reset(); |
+ |
+#if !defined(NDEBUG) |
+ // Dump the state transitions. |
+ DumpStatePath(); |
+#endif |
} |
int HttpCache::Transaction::WriteMetadata(IOBuffer* buf, int buf_len, |
@@ -563,8 +569,11 @@ int HttpCache::Transaction::HandleResult(int rv) { |
int HttpCache::Transaction::DoLoop(int result) { |
DCHECK(next_state_ != STATE_NONE); |
+ |
int rv = result; |
do { |
+ state_path_.push_back(next_state_); |
+ |
State state = next_state_; |
next_state_ = STATE_NONE; |
switch (state) { |
@@ -811,6 +820,36 @@ 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_CACHE_RETURN_IF_OFFLINE && |
+ mode_ == READ_WRITE && entry_ && !partial_ && result != OK) { |
+ switch (result) { |
+ // Errors that mean we didn't have a connection to the host. |
+ // Go ahead and read from the cache anyway. |
+ case ERR_NAME_NOT_RESOLVED: |
+ case ERR_INTERNET_DISCONNECTED: |
+ case ERR_ADDRESS_UNREACHABLE: |
+ case ERR_CONNECTION_TIMED_OUT: |
+ network_trans_.reset(); |
+ cache_->ConvertWriterToReader(entry_); |
+ mode_ = READ; |
+ |
+ // If there is metadata, read it. Otherwise, we're done. |
+ if (entry_->disk_entry->GetDataSize(kMetadataIndex)) |
+ next_state_ = STATE_CACHE_READ_METADATA; |
+ return OK; |
+ default: |
+ break; |
+ } |
+ } |
+ |
+ // 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; |
@@ -897,6 +936,9 @@ int HttpCache::Transaction::DoSuccessfulSendRequest() { |
if (mode_ == READ_WRITE || mode_ == UPDATE) { |
if (new_response->headers->response_code() == 304 || handling_206_) { |
UpdateTransactionPattern(PATTERN_ENTRY_VALIDATED); |
+ // TODO(rdsmith): we just 304ed, so we have good data, but now we |
+ // are falling into this update state cos we want the new headers |
+ // and the freshness they can give us. |
next_state_ = STATE_UPDATE_CACHED_RESPONSE; |
return OK; |
} |
@@ -1791,12 +1833,16 @@ int HttpCache::Transaction::BeginCacheValidation() { |
// 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. |
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; |
+ // We don't want to switch mode to WRITE here as in offline |
+ // mode we may still need to read from the cache. Instead |
+ // we record and test on whether we could conditionalize the |
+ // request. |
} |
next_state_ = STATE_SEND_REQUEST; |
} |
@@ -2563,4 +2609,67 @@ int HttpCache::Transaction::ResetCacheIOStart(int return_value) { |
return return_value; |
} |
+#if !defined(NDEBUG) |
+ |
+// This list must be kept in sync with the HttpCache::Transaction::State enum. |
+ |
+static const char *state_names[] = { |
+ "NONE", |
+ "GET_BACKEND", |
+ "GET_BACKEND_COMPLETE", |
+ "SEND_REQUEST", |
+ "SEND_REQUEST_COMPLETE", |
+ "SUCCESSFUL_SEND_REQUEST", |
+ "NETWORK_READ", |
+ "NETWORK_READ_COMPLETE", |
+ "INIT_ENTRY", |
+ "OPEN_ENTRY", |
+ "OPEN_ENTRY_COMPLETE", |
+ "CREATE_ENTRY", |
+ "CREATE_ENTRY_COMPLETE", |
+ "DOOM_ENTRY", |
+ "DOOM_ENTRY_COMPLETE", |
+ "ADD_TO_ENTRY", |
+ "ADD_TO_ENTRY_COMPLETE", |
+ "ADD_TO_ENTRY_COMPLETE_AFTER_DELAY", |
+ "START_PARTIAL_CACHE_VALIDATION", |
+ "COMPLETE_PARTIAL_CACHE_VALIDATION", |
+ "UPDATE_CACHED_RESPONSE", |
+ "UPDATE_CACHED_RESPONSE_COMPLETE", |
+ "OVERWRITE_CACHED_RESPONSE", |
+ "TRUNCATE_CACHED_DATA", |
+ "TRUNCATE_CACHED_DATA_COMPLETE", |
+ "TRUNCATE_CACHED_METADATA", |
+ "TRUNCATE_CACHED_METADATA_COMPLETE", |
+ "PARTIAL_HEADERS_RECEIVED", |
+ "CACHE_READ_RESPONSE", |
+ "CACHE_READ_RESPONSE_COMPLETE", |
+ "CACHE_WRITE_RESPONSE", |
+ "CACHE_WRITE_TRUNCATED_RESPONSE", |
+ "CACHE_WRITE_RESPONSE_COMPLETE", |
+ "CACHE_READ_METADATA", |
+ "CACHE_READ_METADATA_COMPLETE", |
+ "CACHE_QUERY_DATA", |
+ "CACHE_QUERY_DATA_COMPLETE", |
+ "CACHE_READ_DATA", |
+ "CACHE_READ_DATA_COMPLETE", |
+ "CACHE_WRITE_DATA", |
+ "CACHE_WRITE_DATA_COMPLETE", |
+}; |
+ |
+ |
+void HttpCache::Transaction::DumpStatePath() { |
+ std::string entry_path; |
+ for (std::vector<int>::const_iterator it = state_path_.begin(); |
+ it != state_path_.end(); ++it) { |
+ if (it != state_path_.begin()) |
+ entry_path += " -> "; |
+ entry_path += state_names[*it]; |
+ } |
+ LOG(WARNING) << "Path state transitions for " << cache_key_ |
+ << ": " << entry_path; |
+} |
+ |
+#endif |
+ |
} // namespace net |