Index: net/http/http_cache_transaction.cc |
diff --git a/net/http/http_cache_transaction.cc b/net/http/http_cache_transaction.cc |
index a2d536b05dbd28c9b3512dc7e9e03d510916b7eb..10b6d9136c7b83246b80e84c002bda02a6553519 100644 |
--- a/net/http/http_cache_transaction.cc |
+++ b/net/http/http_cache_transaction.cc |
@@ -165,6 +165,7 @@ HttpCache::Transaction::Transaction(RequestPriority priority, HttpCache* cache) |
handling_206_(false), |
cache_pending_(false), |
done_reading_(false), |
+ done_headers_create_new_entry_(false), |
vary_mismatch_(false), |
couldnt_conditionalize_request_(false), |
bypass_lock_for_test_(false), |
@@ -760,6 +761,9 @@ int HttpCache::Transaction::DoLoop(int result) { |
case STATE_ADD_TO_ENTRY_COMPLETE: |
rv = DoAddToEntryComplete(rv); |
break; |
+ case STATE_DONE_HEADERS_ADD_TO_ENTRY_COMPLETE: |
+ rv = DoDoneHeadersAddToEntryComplete(rv); |
+ break; |
case STATE_CACHE_READ_RESPONSE: |
DCHECK_EQ(OK, rv); |
rv = DoCacheReadResponse(); |
@@ -1119,10 +1123,23 @@ int HttpCache::Transaction::DoCreateEntryComplete(int result) { |
// already created the entry. If we want to eliminate this issue, we |
// need an atomic OpenOrCreate() method exposed by the disk cache. |
DLOG(WARNING) << "Unable to create cache entry"; |
+ |
+ // Set the mode to NONE in order to bypass the cache entry and read from |
+ // the network directly. |
mode_ = NONE; |
- if (partial_) |
- partial_->RestoreHeaders(&custom_request_->extra_headers); |
- TransitionToState(STATE_SEND_REQUEST); |
+ if (!done_headers_create_new_entry_) { |
+ if (partial_) |
+ partial_->RestoreHeaders(&custom_request_->extra_headers); |
+ TransitionToState(STATE_SEND_REQUEST); |
+ return OK; |
+ } |
+ // The headers have already been received as a result of validation, |
+ // triggering the doom of the old entry. So no network request needs to |
+ // be sent. Note that since mode_ is NONE, the response won't be written |
+ // to cache. Transition to STATE_CACHE_WRITE_RESPONSE as that's the state |
+ // the transaction left off on when it tried to create the new entry. |
+ done_headers_create_new_entry_ = false; |
+ TransitionToState(STATE_CACHE_WRITE_RESPONSE); |
} |
return OK; |
} |
@@ -1131,13 +1148,25 @@ int HttpCache::Transaction::DoAddToEntry() { |
TRACE_EVENT0("io", "HttpCacheTransaction::DoAddToEntry"); |
DCHECK(new_entry_); |
cache_pending_ = true; |
- TransitionToState(STATE_ADD_TO_ENTRY_COMPLETE); |
net_log_.BeginEvent(NetLogEventType::HTTP_CACHE_ADD_TO_ENTRY); |
DCHECK(entry_lock_waiting_since_.is_null()); |
- entry_lock_waiting_since_ = TimeTicks::Now(); |
int rv = cache_->AddTransactionToEntry(new_entry_, this); |
- if (rv == ERR_IO_PENDING) |
- AddCacheLockTimeoutHandler(new_entry_); |
+ DCHECK_EQ(rv, ERR_IO_PENDING); |
+ |
+ // If headers phase is already done then we are here because of validation not |
+ // matching and creating a new entry. This transaction should be the |
+ // first transaction of that new entry and thus it should not be subject |
+ // to any cache lock delays, thus returning early from here. |
+ if (done_headers_create_new_entry_) { |
+ DCHECK_EQ(mode_, WRITE); |
+ TransitionToState(STATE_DONE_HEADERS_ADD_TO_ENTRY_COMPLETE); |
+ return rv; |
+ } |
+ |
+ TransitionToState(STATE_ADD_TO_ENTRY_COMPLETE); |
+ |
+ entry_lock_waiting_since_ = TimeTicks::Now(); |
+ AddCacheLockTimeoutHandler(new_entry_); |
return rv; |
} |
@@ -1154,7 +1183,6 @@ void HttpCache::Transaction::AddCacheLockTimeoutHandler(ActiveEntry* entry) { |
} else { |
int timeout_milliseconds = 20 * 1000; |
if (partial_ && entry->writer && entry->writer->range_requested_) { |
- // Quickly timeout and bypass the cache if we're a range request and |
// we're blocked by the reader/writer lock. Doing so eliminates a long |
// running issue, http://crbug.com/31014, where two of the same media |
// resources could not be played back simultaneously due to one locking |
@@ -1242,6 +1270,26 @@ int HttpCache::Transaction::DoAddToEntryComplete(int result) { |
return OK; |
} |
+int HttpCache::Transaction::DoDoneHeadersAddToEntryComplete(int result) { |
+ // This transaction's response headers did not match its ActiveEntry so it |
+ // created a new ActiveEntry (new_entry_) to write to (and doomed the old |
+ // one). Now that the new entry has been created, start writing the response. |
+ |
+ DCHECK_EQ(result, OK); |
+ DCHECK_EQ(mode_, WRITE); |
+ DCHECK(new_entry_); |
+ DCHECK(response_.headers); |
+ |
+ cache_pending_ = false; |
+ entry_ = new_entry_; |
+ done_headers_create_new_entry_ = false; |
+ DCHECK_NE(response_.headers->response_code(), 304); |
+ DCHECK(cache_->CanTransactionWriteResponseHeaders( |
+ entry_, this, partial_ != nullptr, false)); |
+ TransitionToState(STATE_CACHE_WRITE_RESPONSE); |
+ return OK; |
+} |
+ |
int HttpCache::Transaction::DoCacheReadResponse() { |
TRACE_EVENT0("io", "HttpCacheTransaction::DoCacheReadResponse"); |
DCHECK(entry_); |
@@ -1727,7 +1775,6 @@ int HttpCache::Transaction::DoOverwriteCachedResponse() { |
int HttpCache::Transaction::DoCacheWriteResponse() { |
TRACE_EVENT0("io", "HttpCacheTransaction::DoCacheWriteResponse"); |
- TransitionToState(STATE_CACHE_WRITE_RESPONSE_COMPLETE); |
// Invalidate any current entry with a successful response if this transaction |
// cannot write to this entry. This transaction then continues to read from |
@@ -1736,12 +1783,21 @@ int HttpCache::Transaction::DoCacheWriteResponse() { |
if (entry_ && response_.headers && |
!cache_->CanTransactionWriteResponseHeaders( |
entry_, this, partial_ != nullptr, is_match)) { |
- cache_->DoneWritingToEntry(entry_, false, this); |
+ done_headers_create_new_entry_ = true; |
+ |
+ // The transaction needs to overwrite this response. Doom the current entry, |
+ // create a new one (by going to STATE_INIT_ENTRY), and then jump straight |
+ // to writing out the response, bypassing the headers checks. The mode_ is |
+ // set to WRITE in order to doom any other existing entries that might exist |
+ // so that this transaction can go straight to writing a response. |
+ mode_ = WRITE; |
+ TransitionToState(STATE_INIT_ENTRY); |
+ cache_->DoomEntryValidationNoMatch(entry_); |
entry_ = nullptr; |
- mode_ = NONE; |
return OK; |
} |
+ TransitionToState(STATE_CACHE_WRITE_RESPONSE_COMPLETE); |
return WriteResponseInfoToEntry(truncated_); |
} |