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_transaction.h" | 5 #include "net/http/http_cache_transaction.h" |
6 | 6 |
7 #include "build/build_config.h" // For OS_POSIX | 7 #include "build/build_config.h" // For OS_POSIX |
8 | 8 |
9 #if defined(OS_POSIX) | 9 #if defined(OS_POSIX) |
10 #include <unistd.h> | 10 #include <unistd.h> |
(...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
157 new_response_(NULL), | 157 new_response_(NULL), |
158 mode_(NONE), | 158 mode_(NONE), |
159 reading_(false), | 159 reading_(false), |
160 invalid_range_(false), | 160 invalid_range_(false), |
161 truncated_(false), | 161 truncated_(false), |
162 is_sparse_(false), | 162 is_sparse_(false), |
163 range_requested_(false), | 163 range_requested_(false), |
164 handling_206_(false), | 164 handling_206_(false), |
165 cache_pending_(false), | 165 cache_pending_(false), |
166 done_reading_(false), | 166 done_reading_(false), |
| 167 done_headers_create_new_entry_(false), |
167 vary_mismatch_(false), | 168 vary_mismatch_(false), |
168 couldnt_conditionalize_request_(false), | 169 couldnt_conditionalize_request_(false), |
169 bypass_lock_for_test_(false), | 170 bypass_lock_for_test_(false), |
170 fail_conditionalization_for_test_(false), | 171 fail_conditionalization_for_test_(false), |
171 io_buf_len_(0), | 172 io_buf_len_(0), |
172 read_offset_(0), | 173 read_offset_(0), |
173 effective_load_flags_(0), | 174 effective_load_flags_(0), |
174 write_len_(0), | 175 write_len_(0), |
175 cache_entry_status_(CacheEntryStatus::ENTRY_UNDEFINED), | 176 cache_entry_status_(CacheEntryStatus::ENTRY_UNDEFINED), |
176 validation_cause_(VALIDATION_CAUSE_UNDEFINED), | 177 validation_cause_(VALIDATION_CAUSE_UNDEFINED), |
(...skipping 569 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
746 case STATE_CREATE_ENTRY_COMPLETE: | 747 case STATE_CREATE_ENTRY_COMPLETE: |
747 rv = DoCreateEntryComplete(rv); | 748 rv = DoCreateEntryComplete(rv); |
748 break; | 749 break; |
749 case STATE_ADD_TO_ENTRY: | 750 case STATE_ADD_TO_ENTRY: |
750 DCHECK_EQ(OK, rv); | 751 DCHECK_EQ(OK, rv); |
751 rv = DoAddToEntry(); | 752 rv = DoAddToEntry(); |
752 break; | 753 break; |
753 case STATE_ADD_TO_ENTRY_COMPLETE: | 754 case STATE_ADD_TO_ENTRY_COMPLETE: |
754 rv = DoAddToEntryComplete(rv); | 755 rv = DoAddToEntryComplete(rv); |
755 break; | 756 break; |
| 757 case STATE_DONE_HEADERS_ADD_TO_ENTRY_COMPLETE: |
| 758 rv = DoDoneHeadersAddToEntryComplete(rv); |
| 759 break; |
756 case STATE_CACHE_READ_RESPONSE: | 760 case STATE_CACHE_READ_RESPONSE: |
757 DCHECK_EQ(OK, rv); | 761 DCHECK_EQ(OK, rv); |
758 rv = DoCacheReadResponse(); | 762 rv = DoCacheReadResponse(); |
759 break; | 763 break; |
760 case STATE_CACHE_READ_RESPONSE_COMPLETE: | 764 case STATE_CACHE_READ_RESPONSE_COMPLETE: |
761 rv = DoCacheReadResponseComplete(rv); | 765 rv = DoCacheReadResponseComplete(rv); |
762 break; | 766 break; |
763 case STATE_TOGGLE_UNUSED_SINCE_PREFETCH: | 767 case STATE_TOGGLE_UNUSED_SINCE_PREFETCH: |
764 DCHECK_EQ(OK, rv); | 768 DCHECK_EQ(OK, rv); |
765 rv = DoCacheToggleUnusedSincePrefetch(); | 769 rv = DoCacheToggleUnusedSincePrefetch(); |
(...skipping 338 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1104 case ERR_CACHE_RACE: | 1108 case ERR_CACHE_RACE: |
1105 TransitionToState(STATE_HEADERS_PHASE_CANNOT_PROCEED); | 1109 TransitionToState(STATE_HEADERS_PHASE_CANNOT_PROCEED); |
1106 break; | 1110 break; |
1107 | 1111 |
1108 default: | 1112 default: |
1109 // We have a race here: Maybe we failed to open the entry and decided to | 1113 // We have a race here: Maybe we failed to open the entry and decided to |
1110 // create one, but by the time we called create, another transaction | 1114 // create one, but by the time we called create, another transaction |
1111 // already created the entry. If we want to eliminate this issue, we | 1115 // already created the entry. If we want to eliminate this issue, we |
1112 // need an atomic OpenOrCreate() method exposed by the disk cache. | 1116 // need an atomic OpenOrCreate() method exposed by the disk cache. |
1113 DLOG(WARNING) << "Unable to create cache entry"; | 1117 DLOG(WARNING) << "Unable to create cache entry"; |
| 1118 // Switching into passing through data directly from the network, avoiding |
| 1119 // the cache entry. |
1114 mode_ = NONE; | 1120 mode_ = NONE; |
1115 if (partial_) | 1121 if (!done_headers_create_new_entry_) { |
1116 partial_->RestoreHeaders(&custom_request_->extra_headers); | 1122 if (partial_) |
1117 TransitionToState(STATE_SEND_REQUEST); | 1123 partial_->RestoreHeaders(&custom_request_->extra_headers); |
| 1124 TransitionToState(STATE_SEND_REQUEST); |
| 1125 return OK; |
| 1126 } |
| 1127 // The headers have already been received as a result of validation, |
| 1128 // triggering the doom of the old entry. So no network request needs to |
| 1129 // be sent. Note that since mode_ is set to pass-through, response will |
| 1130 // not be written to the cache but moving to state |
| 1131 // STATE_CACHE_WRITE_RESPONSE for consistency. |
| 1132 done_headers_create_new_entry_ = false; |
| 1133 TransitionToState(STATE_CACHE_WRITE_RESPONSE); |
1118 } | 1134 } |
1119 return OK; | 1135 return OK; |
1120 } | 1136 } |
1121 | 1137 |
1122 int HttpCache::Transaction::DoAddToEntry() { | 1138 int HttpCache::Transaction::DoAddToEntry() { |
1123 TRACE_EVENT0("io", "HttpCacheTransaction::DoAddToEntry"); | 1139 TRACE_EVENT0("io", "HttpCacheTransaction::DoAddToEntry"); |
1124 DCHECK(new_entry_); | 1140 DCHECK(new_entry_); |
1125 cache_pending_ = true; | 1141 cache_pending_ = true; |
1126 TransitionToState(STATE_ADD_TO_ENTRY_COMPLETE); | |
1127 net_log_.BeginEvent(NetLogEventType::HTTP_CACHE_ADD_TO_ENTRY); | 1142 net_log_.BeginEvent(NetLogEventType::HTTP_CACHE_ADD_TO_ENTRY); |
1128 DCHECK(entry_lock_waiting_since_.is_null()); | 1143 DCHECK(entry_lock_waiting_since_.is_null()); |
| 1144 int rv = cache_->AddTransactionToEntry(new_entry_, this); |
| 1145 DCHECK_EQ(rv, ERR_IO_PENDING); |
| 1146 |
| 1147 // If headers phase is already done and we are here because of validation not |
| 1148 // matching and creating a new entry, then this transaction should be the |
| 1149 // first transaction of that entry and thus it should not be subject |
| 1150 // to any cache lock delays, thus returning early from here. |
| 1151 if (done_headers_create_new_entry_) { |
| 1152 DCHECK_EQ(mode_, WRITE); |
| 1153 TransitionToState(STATE_DONE_HEADERS_ADD_TO_ENTRY_COMPLETE); |
| 1154 return rv; |
| 1155 } |
| 1156 |
| 1157 TransitionToState(STATE_ADD_TO_ENTRY_COMPLETE); |
1129 entry_lock_waiting_since_ = TimeTicks::Now(); | 1158 entry_lock_waiting_since_ = TimeTicks::Now(); |
1130 int rv = cache_->AddTransactionToEntry(new_entry_, this); | 1159 |
1131 if (rv == ERR_IO_PENDING) { | 1160 if (bypass_lock_for_test_) { |
1132 if (bypass_lock_for_test_) { | 1161 base::ThreadTaskRunnerHandle::Get()->PostTask( |
1133 base::ThreadTaskRunnerHandle::Get()->PostTask( | 1162 FROM_HERE, |
1134 FROM_HERE, | 1163 base::Bind(&HttpCache::Transaction::OnAddToEntryTimeout, |
1135 base::Bind(&HttpCache::Transaction::OnAddToEntryTimeout, | 1164 weak_factory_.GetWeakPtr(), entry_lock_waiting_since_)); |
1136 weak_factory_.GetWeakPtr(), entry_lock_waiting_since_)); | 1165 } else { |
1137 } else { | 1166 int timeout_milliseconds = 20 * 1000; |
1138 int timeout_milliseconds = 20 * 1000; | 1167 if (partial_ && new_entry_->writer && |
1139 if (partial_ && new_entry_->writer && | 1168 new_entry_->writer->range_requested_) { |
1140 new_entry_->writer->range_requested_) { | 1169 // Quickly timeout and bypass the cache if we're a range request and |
1141 // Quickly timeout and bypass the cache if we're a range request and | 1170 // we're blocked by the reader/writer lock. Doing so eliminates a long |
1142 // we're blocked by the reader/writer lock. Doing so eliminates a long | 1171 // running issue, http://crbug.com/31014, where two of the same media |
1143 // running issue, http://crbug.com/31014, where two of the same media | 1172 // resources could not be played back simultaneously due to one locking |
1144 // resources could not be played back simultaneously due to one locking | 1173 // the cache entry until the entire video was downloaded. |
1145 // the cache entry until the entire video was downloaded. | 1174 // |
1146 // | 1175 // Bypassing the cache is not ideal, as we are now ignoring the cache |
1147 // Bypassing the cache is not ideal, as we are now ignoring the cache | 1176 // entirely for all range requests to a resource beyond the first. This |
1148 // entirely for all range requests to a resource beyond the first. This | 1177 // is however a much more succinct solution than the alternatives, which |
1149 // is however a much more succinct solution than the alternatives, which | 1178 // would require somewhat significant changes to the http caching logic. |
1150 // would require somewhat significant changes to the http caching logic. | 1179 // |
1151 // | 1180 // Allow some timeout slack for the entry addition to complete in case |
1152 // Allow some timeout slack for the entry addition to complete in case | 1181 // the writer lock is imminently released; we want to avoid skipping |
1153 // the writer lock is imminently released; we want to avoid skipping | 1182 // the cache if at all possible. See http://crbug.com/408765 |
1154 // the cache if at all possible. See http://crbug.com/408765 | 1183 timeout_milliseconds = 25; |
1155 timeout_milliseconds = 25; | |
1156 } | |
1157 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( | |
1158 FROM_HERE, | |
1159 base::Bind(&HttpCache::Transaction::OnAddToEntryTimeout, | |
1160 weak_factory_.GetWeakPtr(), entry_lock_waiting_since_), | |
1161 TimeDelta::FromMilliseconds(timeout_milliseconds)); | |
1162 } | 1184 } |
| 1185 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( |
| 1186 FROM_HERE, |
| 1187 base::Bind(&HttpCache::Transaction::OnAddToEntryTimeout, |
| 1188 weak_factory_.GetWeakPtr(), entry_lock_waiting_since_), |
| 1189 TimeDelta::FromMilliseconds(timeout_milliseconds)); |
1163 } | 1190 } |
| 1191 |
1164 return rv; | 1192 return rv; |
1165 } | 1193 } |
1166 | 1194 |
1167 int HttpCache::Transaction::DoAddToEntryComplete(int result) { | 1195 int HttpCache::Transaction::DoAddToEntryComplete(int result) { |
1168 TRACE_EVENT0("io", "HttpCacheTransaction::DoAddToEntryComplete"); | 1196 TRACE_EVENT0("io", "HttpCacheTransaction::DoAddToEntryComplete"); |
1169 net_log_.EndEventWithNetErrorCode(NetLogEventType::HTTP_CACHE_ADD_TO_ENTRY, | 1197 net_log_.EndEventWithNetErrorCode(NetLogEventType::HTTP_CACHE_ADD_TO_ENTRY, |
1170 result); | 1198 result); |
1171 const TimeDelta entry_lock_wait = | 1199 const TimeDelta entry_lock_wait = |
1172 TimeTicks::Now() - entry_lock_waiting_since_; | 1200 TimeTicks::Now() - entry_lock_waiting_since_; |
1173 UMA_HISTOGRAM_TIMES("HttpCache.EntryLockWait", entry_lock_wait); | 1201 UMA_HISTOGRAM_TIMES("HttpCache.EntryLockWait", entry_lock_wait); |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1221 partial_->RestoreHeaders(&custom_request_->extra_headers); | 1249 partial_->RestoreHeaders(&custom_request_->extra_headers); |
1222 TransitionToState(STATE_SEND_REQUEST); | 1250 TransitionToState(STATE_SEND_REQUEST); |
1223 } else { | 1251 } else { |
1224 // We have to read the headers from the cached entry. | 1252 // We have to read the headers from the cached entry. |
1225 DCHECK(mode_ & READ_META); | 1253 DCHECK(mode_ & READ_META); |
1226 TransitionToState(STATE_CACHE_READ_RESPONSE); | 1254 TransitionToState(STATE_CACHE_READ_RESPONSE); |
1227 } | 1255 } |
1228 return OK; | 1256 return OK; |
1229 } | 1257 } |
1230 | 1258 |
| 1259 int HttpCache::Transaction::DoDoneHeadersAddToEntryComplete(int result) { |
| 1260 // This state is reached when |this| has already completed validation leading |
| 1261 // to a no-match with original entry which was doomed and |new_entry_| was |
| 1262 // created. A response of no-match from a validation request also includes the |
| 1263 // full contents of the URL, so go ahead and write the response to the newly |
| 1264 // created entry. |
| 1265 |
| 1266 DCHECK_EQ(result, OK); |
| 1267 DCHECK_EQ(mode_, WRITE); |
| 1268 DCHECK(new_entry_); |
| 1269 DCHECK(response_.headers); |
| 1270 |
| 1271 cache_pending_ = false; |
| 1272 entry_ = new_entry_; |
| 1273 done_headers_create_new_entry_ = false; |
| 1274 bool is_match = response_.headers->response_code() == 304; |
| 1275 DCHECK(cache_->CanTransactionWriteResponseHeaders(entry_, this, is_match)); |
| 1276 TransitionToState(STATE_CACHE_WRITE_RESPONSE); |
| 1277 return OK; |
| 1278 } |
| 1279 |
1231 int HttpCache::Transaction::DoCacheReadResponse() { | 1280 int HttpCache::Transaction::DoCacheReadResponse() { |
1232 TRACE_EVENT0("io", "HttpCacheTransaction::DoCacheReadResponse"); | 1281 TRACE_EVENT0("io", "HttpCacheTransaction::DoCacheReadResponse"); |
1233 DCHECK(entry_); | 1282 DCHECK(entry_); |
1234 TransitionToState(STATE_CACHE_READ_RESPONSE_COMPLETE); | 1283 TransitionToState(STATE_CACHE_READ_RESPONSE_COMPLETE); |
1235 | 1284 |
1236 io_buf_len_ = entry_->disk_entry->GetDataSize(kResponseInfoIndex); | 1285 io_buf_len_ = entry_->disk_entry->GetDataSize(kResponseInfoIndex); |
1237 read_buf_ = new IOBuffer(io_buf_len_); | 1286 read_buf_ = new IOBuffer(io_buf_len_); |
1238 | 1287 |
1239 net_log_.BeginEvent(NetLogEventType::HTTP_CACHE_READ_INFO); | 1288 net_log_.BeginEvent(NetLogEventType::HTTP_CACHE_READ_INFO); |
1240 return entry_->disk_entry->ReadData(kResponseInfoIndex, 0, read_buf_.get(), | 1289 return entry_->disk_entry->ReadData(kResponseInfoIndex, 0, read_buf_.get(), |
(...skipping 465 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1706 TransitionToState(STATE_PARTIAL_HEADERS_RECEIVED); | 1755 TransitionToState(STATE_PARTIAL_HEADERS_RECEIVED); |
1707 return OK; | 1756 return OK; |
1708 } | 1757 } |
1709 | 1758 |
1710 TransitionToState(STATE_CACHE_WRITE_RESPONSE); | 1759 TransitionToState(STATE_CACHE_WRITE_RESPONSE); |
1711 return OK; | 1760 return OK; |
1712 } | 1761 } |
1713 | 1762 |
1714 int HttpCache::Transaction::DoCacheWriteResponse() { | 1763 int HttpCache::Transaction::DoCacheWriteResponse() { |
1715 TRACE_EVENT0("io", "HttpCacheTransaction::DoCacheWriteResponse"); | 1764 TRACE_EVENT0("io", "HttpCacheTransaction::DoCacheWriteResponse"); |
1716 TransitionToState(STATE_CACHE_WRITE_RESPONSE_COMPLETE); | |
1717 | 1765 |
1718 // Invalidate any current entry with a successful response if this transaction | 1766 // Invalidate any current entry with a successful response if this transaction |
1719 // cannot write to this entry. This transaction then continues to read from | 1767 // cannot write to this entry. This transaction then continues to read from |
1720 // the network without writing to the backend. | 1768 // the network without writing to the backend. |
1721 bool is_match = response_.headers->response_code() == 304; | 1769 bool is_match = response_.headers->response_code() == 304; |
1722 if (entry_ && response_.headers && | 1770 if (entry_ && response_.headers && |
1723 !cache_->CanTransactionWriteResponseHeaders(entry_, this, is_match)) { | 1771 !cache_->CanTransactionWriteResponseHeaders(entry_, this, is_match)) { |
1724 cache_->DoneWritingToEntry(entry_, false, this); | 1772 done_headers_create_new_entry_ = true; |
| 1773 |
| 1774 // This transaction should not add itself to any other existing entry but |
| 1775 // create a new entry. Going to state STATE_INIT_ENTRY and setting mode_ to |
| 1776 // WRITE will take care of dooming if any other entry exists. |
| 1777 mode_ = WRITE; |
| 1778 TransitionToState(STATE_INIT_ENTRY); |
| 1779 cache_->DoomEntryValidationNoMatch(entry_, this); |
1725 entry_ = nullptr; | 1780 entry_ = nullptr; |
1726 mode_ = NONE; | |
1727 return OK; | 1781 return OK; |
1728 } | 1782 } |
1729 | 1783 |
| 1784 TransitionToState(STATE_CACHE_WRITE_RESPONSE_COMPLETE); |
1730 return WriteResponseInfoToEntry(truncated_); | 1785 return WriteResponseInfoToEntry(truncated_); |
1731 } | 1786 } |
1732 | 1787 |
1733 int HttpCache::Transaction::DoCacheWriteResponseComplete(int result) { | 1788 int HttpCache::Transaction::DoCacheWriteResponseComplete(int result) { |
1734 TRACE_EVENT0("io", "HttpCacheTransaction::DoCacheWriteResponseComplete"); | 1789 TRACE_EVENT0("io", "HttpCacheTransaction::DoCacheWriteResponseComplete"); |
1735 TransitionToState(STATE_TRUNCATE_CACHED_DATA); | 1790 TransitionToState(STATE_TRUNCATE_CACHED_DATA); |
1736 return OnWriteResponseInfoToEntryComplete(result); | 1791 return OnWriteResponseInfoToEntryComplete(result); |
1737 } | 1792 } |
1738 | 1793 |
1739 int HttpCache::Transaction::DoTruncateCachedData() { | 1794 int HttpCache::Transaction::DoTruncateCachedData() { |
(...skipping 1379 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3119 } | 3174 } |
3120 | 3175 |
3121 void HttpCache::Transaction::TransitionToState(State state) { | 3176 void HttpCache::Transaction::TransitionToState(State state) { |
3122 // Ensure that the state is only set once per Do* state. | 3177 // Ensure that the state is only set once per Do* state. |
3123 DCHECK(in_do_loop_); | 3178 DCHECK(in_do_loop_); |
3124 DCHECK_EQ(STATE_UNSET, next_state_) << "Next state is " << state; | 3179 DCHECK_EQ(STATE_UNSET, next_state_) << "Next state is " << state; |
3125 next_state_ = state; | 3180 next_state_ = state; |
3126 } | 3181 } |
3127 | 3182 |
3128 } // namespace net | 3183 } // namespace net |
OLD | NEW |