| Index: net/http/http_cache_transaction.cc
|
| diff --git a/net/http/http_cache_transaction.cc b/net/http/http_cache_transaction.cc
|
| index 518944650747e6a3dc7f39c58b4b4d7cb1610758..a4e00493ae1e84a880e2caeedbb086ca3a78d2c8 100644
|
| --- a/net/http/http_cache_transaction.cc
|
| +++ b/net/http/http_cache_transaction.cc
|
| @@ -196,16 +196,8 @@ HttpCache::Transaction::~Transaction() {
|
|
|
| if (cache_) {
|
| if (entry_) {
|
| - bool cancel_request = reading_ && response_.headers.get();
|
| - if (cancel_request) {
|
| - if (partial_) {
|
| - entry_->disk_entry->CancelSparseIO();
|
| - } else {
|
| - cancel_request &= (response_.headers->response_code() == 200);
|
| - }
|
| - }
|
| -
|
| - cache_->DoneWithEntry(entry_, this, cancel_request);
|
| + cache_->DoneWithEntry(entry_, this, true /* process_cancel */,
|
| + partial_ != nullptr);
|
| } else if (cache_pending_) {
|
| cache_->RemovePendingTransaction(this);
|
| }
|
| @@ -451,7 +443,8 @@ void HttpCache::Transaction::DoneReading() {
|
| // It is necessary to check mode_ & READ because it is possible
|
| // for mode_ to be NONE and entry_ non-NULL with a write entry
|
| // if StopCaching was called.
|
| - cache_->DoneReadingFromEntry(entry_, this);
|
| + cache_->DoneWithEntry(entry_, this, false /* process_cancel */,
|
| + partial_ != nullptr);
|
| entry_ = NULL;
|
| }
|
| }
|
| @@ -565,6 +558,12 @@ void HttpCache::Transaction::GetConnectionAttempts(
|
| old_connection_attempts_.end());
|
| }
|
|
|
| +void HttpCache::Transaction::SetValidatingCannotProceed() {
|
| + DCHECK(!reading_);
|
| + next_state_ = STATE_HEADERS_PHASE_CANNOT_PROCEED;
|
| + entry_ = nullptr;
|
| +}
|
| +
|
| size_t HttpCache::Transaction::EstimateMemoryUsage() const {
|
| // TODO(xunjieli): Consider improving the coverage. crbug.com/669108.
|
| return 0;
|
| @@ -579,7 +578,7 @@ size_t HttpCache::Transaction::EstimateMemoryUsage() const {
|
| // GetBackend* -> InitEntry -> OpenEntry* -> CreateEntry* -> AddToEntry* ->
|
| // SendRequest* -> SuccessfulSendRequest -> OverwriteCachedResponse ->
|
| // CacheWriteResponse* -> TruncateCachedData* -> TruncateCachedMetadata* ->
|
| -// PartialHeadersReceived
|
| +// PartialHeadersReceived -> FinishHeaders*
|
| //
|
| // Read():
|
| // NetworkRead* -> CacheWriteData*
|
| @@ -588,7 +587,7 @@ size_t HttpCache::Transaction::EstimateMemoryUsage() const {
|
| // Start():
|
| // GetBackend* -> InitEntry -> OpenEntry* -> AddToEntry* -> CacheReadResponse*
|
| // -> CacheDispatchValidation -> BeginPartialCacheValidation() ->
|
| -// BeginCacheValidation() -> SetupEntryForRead()
|
| +// BeginCacheValidation() -> SetupEntryForRead() -> FinishHeaders*
|
| //
|
| // Read():
|
| // CacheReadData*
|
| @@ -600,7 +599,7 @@ size_t HttpCache::Transaction::EstimateMemoryUsage() const {
|
| // BeginCacheValidation() -> SendRequest* -> SuccessfulSendRequest ->
|
| // UpdateCachedResponse -> CacheWriteUpdatedResponse* ->
|
| // UpdateCachedResponseComplete -> OverwriteCachedResponse ->
|
| -// PartialHeadersReceived
|
| +// PartialHeadersReceived -> FinishHeaders*
|
| //
|
| // Read():
|
| // CacheReadData*
|
| @@ -611,7 +610,7 @@ size_t HttpCache::Transaction::EstimateMemoryUsage() const {
|
| // -> CacheDispatchValidation -> BeginPartialCacheValidation() ->
|
| // BeginCacheValidation() -> SendRequest* -> SuccessfulSendRequest ->
|
| // OverwriteCachedResponse -> CacheWriteResponse* -> DoTruncateCachedData* ->
|
| -// TruncateCachedMetadata* -> PartialHeadersReceived
|
| +// TruncateCachedMetadata* -> PartialHeadersReceived -> FinishHeaders*
|
| //
|
| // Read():
|
| // NetworkRead* -> CacheWriteData*
|
| @@ -625,7 +624,7 @@ size_t HttpCache::Transaction::EstimateMemoryUsage() const {
|
| // BeginCacheValidation() -> SendRequest* -> SuccessfulSendRequest ->
|
| // UpdateCachedResponse -> CacheWriteUpdatedResponse* ->
|
| // UpdateCachedResponseComplete -> OverwriteCachedResponse ->
|
| -// PartialHeadersReceived
|
| +// PartialHeadersReceived -> FinishHeaders*
|
| //
|
| // Read() 1:
|
| // NetworkRead* -> CacheWriteData*
|
| @@ -666,7 +665,7 @@ size_t HttpCache::Transaction::EstimateMemoryUsage() const {
|
| // GetBackend* -> InitEntry -> OpenEntry* -> AddToEntry* -> CacheReadResponse*
|
| // -> CacheDispatchValidation -> BeginPartialCacheValidation() ->
|
| // BeginCacheValidation() -> SendRequest* -> SuccessfulSendRequest ->
|
| -// OverwriteCachedResponse
|
| +// OverwriteCachedResponse -> FinishHeaders*
|
| //
|
| // 10. HEAD. Sparse entry, partially cached:
|
| // Serve the request from the cache, as long as it doesn't require
|
| @@ -691,7 +690,7 @@ size_t HttpCache::Transaction::EstimateMemoryUsage() const {
|
| // GetBackend* -> InitEntry -> OpenEntry* -> AddToEntry* -> CacheReadResponse*
|
| // -> CacheToggleUnusedSincePrefetch* -> CacheDispatchValidation ->
|
| // BeginPartialCacheValidation() -> BeginCacheValidation() ->
|
| -// SetupEntryForRead()
|
| +// SetupEntryForRead() -> FinishHeaders*
|
| //
|
| // Read():
|
| // CacheReadData*
|
| @@ -705,8 +704,9 @@ int HttpCache::Transaction::DoLoop(int result) {
|
| DCHECK(!in_do_loop_);
|
|
|
| int rv = result;
|
| + State state = next_state_;
|
| do {
|
| - State state = next_state_;
|
| + state = next_state_;
|
| next_state_ = STATE_UNSET;
|
| base::AutoReset<bool> scoped_in_do_loop(&in_do_loop_, true);
|
|
|
| @@ -843,6 +843,15 @@ int HttpCache::Transaction::DoLoop(int result) {
|
| case STATE_CACHE_READ_METADATA_COMPLETE:
|
| rv = DoCacheReadMetadataComplete(rv);
|
| break;
|
| + case STATE_HEADERS_PHASE_CANNOT_PROCEED:
|
| + rv = DoHeadersPhaseCannotProceed();
|
| + break;
|
| + case STATE_FINISH_HEADERS:
|
| + rv = DoFinishHeaders(rv);
|
| + break;
|
| + case STATE_FINISH_HEADERS_COMPLETE:
|
| + rv = DoFinishHeadersComplete(rv);
|
| + break;
|
| case STATE_NETWORK_READ:
|
| DCHECK_EQ(OK, rv);
|
| rv = DoNetworkRead();
|
| @@ -879,8 +888,13 @@ int HttpCache::Transaction::DoLoop(int result) {
|
|
|
| } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
|
|
|
| + // Assert Start() state machine's allowed last state in successful cases when
|
| + // caching is happening.
|
| + DCHECK(reading_ || rv != OK || !entry_ ||
|
| + state == STATE_FINISH_HEADERS_COMPLETE);
|
| +
|
| if (rv != ERR_IO_PENDING && !callback_.is_null()) {
|
| - read_buf_ = NULL; // Release the buffer before invoking the callback.
|
| + read_buf_ = nullptr; // Release the buffer before invoking the callback.
|
| base::ResetAndReturn(&callback_).Run(rv);
|
| }
|
|
|
| @@ -907,7 +921,7 @@ int HttpCache::Transaction::DoGetBackendComplete(int result) {
|
| if (effective_load_flags_ & LOAD_ONLY_FROM_CACHE) {
|
| if (effective_load_flags_ & LOAD_BYPASS_CACHE) {
|
| // The client has asked for nonsense.
|
| - TransitionToState(STATE_NONE);
|
| + TransitionToState(STATE_FINISH_HEADERS);
|
| return ERR_CACHE_MISS;
|
| }
|
| mode_ = READ;
|
| @@ -930,8 +944,8 @@ int HttpCache::Transaction::DoGetBackendComplete(int result) {
|
| }
|
|
|
| // Use PUT and DELETE only to invalidate existing stored entries.
|
| - if ((request_->method == "PUT" || request_->method == "DELETE") &&
|
| - mode_ != READ_WRITE && mode_ != WRITE) {
|
| + if ((method_ == "PUT" || method_ == "DELETE") && mode_ != READ_WRITE &&
|
| + mode_ != WRITE) {
|
| mode_ = NONE;
|
| }
|
|
|
| @@ -940,13 +954,13 @@ int HttpCache::Transaction::DoGetBackendComplete(int result) {
|
| // was not modified, the entry is updated and a response is not returned from
|
| // the cache. If we receive 200, it doesn't matter if there was a validation
|
| // header or not.
|
| - if (request_->method == "HEAD" && mode_ == WRITE)
|
| + if (method_ == "HEAD" && mode_ == WRITE)
|
| mode_ = NONE;
|
|
|
| // If must use cache, then we must fail. This can happen for back/forward
|
| // navigations to a page generated via a form post.
|
| if (!(mode_ & READ) && effective_load_flags_ & LOAD_ONLY_FROM_CACHE) {
|
| - TransitionToState(STATE_NONE);
|
| + TransitionToState(STATE_FINISH_HEADERS);
|
| return ERR_CACHE_MISS;
|
| }
|
|
|
| @@ -963,6 +977,9 @@ int HttpCache::Transaction::DoGetBackendComplete(int result) {
|
| // This is only set if we have something to do with the response.
|
| range_requested_ = (partial_.get() != NULL);
|
|
|
| + // mode_ may change later, save the initial mode in case we need to restart
|
| + // this request.
|
| + restart_info_.mode = mode_;
|
| return OK;
|
| }
|
|
|
| @@ -971,7 +988,7 @@ int HttpCache::Transaction::DoInitEntry() {
|
| DCHECK(!new_entry_);
|
|
|
| if (!cache_.get()) {
|
| - TransitionToState(STATE_NONE);
|
| + TransitionToState(STATE_FINISH_HEADERS);
|
| return ERR_UNEXPECTED;
|
| }
|
|
|
| @@ -1008,13 +1025,13 @@ int HttpCache::Transaction::DoOpenEntryComplete(int result) {
|
| }
|
|
|
| if (result == ERR_CACHE_RACE) {
|
| - TransitionToState(STATE_INIT_ENTRY);
|
| + TransitionToState(STATE_HEADERS_PHASE_CANNOT_PROCEED);
|
| return OK;
|
| }
|
|
|
| - if (request_->method == "PUT" || request_->method == "DELETE" ||
|
| - (request_->method == "HEAD" && mode_ == READ_WRITE)) {
|
| - DCHECK(mode_ == READ_WRITE || mode_ == WRITE || request_->method == "HEAD");
|
| + if (method_ == "PUT" || method_ == "DELETE" ||
|
| + (method_ == "HEAD" && mode_ == READ_WRITE)) {
|
| + DCHECK(mode_ == READ_WRITE || mode_ == WRITE || method_ == "HEAD");
|
| mode_ = NONE;
|
| TransitionToState(STATE_SEND_REQUEST);
|
| return OK;
|
| @@ -1034,7 +1051,7 @@ int HttpCache::Transaction::DoOpenEntryComplete(int result) {
|
|
|
| // The entry does not exist, and we are not permitted to create a new entry,
|
| // so we must fail.
|
| - TransitionToState(STATE_NONE);
|
| + TransitionToState(STATE_FINISH_HEADERS);
|
| return ERR_CACHE_MISS;
|
| }
|
|
|
| @@ -1053,8 +1070,9 @@ int HttpCache::Transaction::DoDoomEntryComplete(int result) {
|
| net_log_.EndEventWithNetErrorCode(NetLogEventType::HTTP_CACHE_DOOM_ENTRY,
|
| result);
|
| cache_pending_ = false;
|
| - TransitionToState(result == ERR_CACHE_RACE ? STATE_INIT_ENTRY
|
| - : STATE_CREATE_ENTRY);
|
| + TransitionToState(result == ERR_CACHE_RACE
|
| + ? STATE_HEADERS_PHASE_CANNOT_PROCEED
|
| + : STATE_CREATE_ENTRY);
|
| return OK;
|
| }
|
|
|
| @@ -1081,7 +1099,7 @@ int HttpCache::Transaction::DoCreateEntryComplete(int result) {
|
| break;
|
|
|
| case ERR_CACHE_RACE:
|
| - TransitionToState(STATE_INIT_ENTRY);
|
| + TransitionToState(STATE_HEADERS_PHASE_CANNOT_PROCEED);
|
| break;
|
|
|
| default:
|
| @@ -1162,13 +1180,13 @@ int HttpCache::Transaction::DoAddToEntryComplete(int result) {
|
| new_entry_ = NULL;
|
|
|
| if (result == ERR_CACHE_RACE) {
|
| - TransitionToState(STATE_INIT_ENTRY);
|
| + TransitionToState(STATE_HEADERS_PHASE_CANNOT_PROCEED);
|
| return OK;
|
| }
|
|
|
| if (result == ERR_CACHE_LOCK_TIMEOUT) {
|
| if (mode_ == READ) {
|
| - TransitionToState(STATE_NONE);
|
| + TransitionToState(STATE_FINISH_HEADERS);
|
| return ERR_CACHE_MISS;
|
| }
|
|
|
| @@ -1182,12 +1200,16 @@ int HttpCache::Transaction::DoAddToEntryComplete(int result) {
|
| return OK;
|
| }
|
|
|
| - open_entry_last_used_ = entry_->disk_entry->GetLastUsed();
|
| + // TODO(crbug.com/713354) Access timestamp for histograms only if entry is
|
| + // already written, to avoid data race since cache thread can also access
|
| + // this.
|
| + if (!cache_->IsWritingInProgress(entry_))
|
| + open_entry_last_used_ = entry_->disk_entry->GetLastUsed();
|
|
|
| // TODO(jkarlin): We should either handle the case or DCHECK.
|
| if (result != OK) {
|
| NOTREACHED();
|
| - TransitionToState(STATE_NONE);
|
| + TransitionToState(STATE_FINISH_HEADERS);
|
| return result;
|
| }
|
|
|
| @@ -1226,29 +1248,37 @@ int HttpCache::Transaction::DoCacheReadResponseComplete(int result) {
|
| return OnCacheReadError(result, true);
|
| }
|
|
|
| - int current_size = entry_->disk_entry->GetDataSize(kResponseContentIndex);
|
| - int64_t full_response_length = response_.headers->GetContentLength();
|
| -
|
| - // Some resources may have slipped in as truncated when they're not.
|
| - if (full_response_length == current_size)
|
| - truncated_ = false;
|
| -
|
| - // The state machine's handling of StopCaching unfortunately doesn't deal well
|
| - // with resources that are larger than 2GB when there is a truncated or sparse
|
| - // cache entry. While the state machine is reworked to resolve this, the
|
| - // following logic is put in place to defer such requests to the network. The
|
| - // cache should not be storing multi gigabyte resources. See
|
| - // http://crbug.com/89567.
|
| - if ((truncated_ || response_.headers->response_code() == 206) &&
|
| - !range_requested_ &&
|
| - full_response_length > std::numeric_limits<int32_t>::max()) {
|
| - // Does not release the cache entry. If another transaction wants to use
|
| - // this cache entry while this transaction is active, the second transaction
|
| - // will fall back to the network after the timeout.
|
| - DCHECK(!partial_);
|
| - mode_ = NONE;
|
| - TransitionToState(STATE_SEND_REQUEST);
|
| - return OK;
|
| + // TODO(crbug.com/713354) Only get data size if there is no other transaction
|
| + // currently writing the response body due to the data race mentioned in the
|
| + // associated bug.
|
| + if (!cache_->IsWritingInProgress(entry_)) {
|
| + int current_size = entry_->disk_entry->GetDataSize(kResponseContentIndex);
|
| + int64_t full_response_length = response_.headers->GetContentLength();
|
| +
|
| + // Some resources may have slipped in as truncated when they're not.
|
| + if (full_response_length == current_size)
|
| + truncated_ = false;
|
| +
|
| + // The state machine's handling of StopCaching unfortunately doesn't deal
|
| + // well with resources that are larger than 2GB when there is a truncated or
|
| + // sparse cache entry. While the state machine is reworked to resolve this,
|
| + // the following logic is put in place to defer such requests to the
|
| + // network. The cache should not be storing multi gigabyte resources. See
|
| + // http://crbug.com/89567.
|
| + if ((truncated_ || response_.headers->response_code() == 206) &&
|
| + !range_requested_ &&
|
| + full_response_length > std::numeric_limits<int32_t>::max()) {
|
| + DCHECK(!partial_);
|
| +
|
| + // Doom the entry so that no other transaction gets added to this entry
|
| + // and avoid a race of not being able to check this condition because
|
| + // writing is in progress.
|
| + cache_->DoneWritingToEntry(entry_, false, this);
|
| + entry_ = nullptr;
|
| + mode_ = NONE;
|
| + TransitionToState(STATE_SEND_REQUEST);
|
| + return OK;
|
| + }
|
| }
|
|
|
| if (response_.unused_since_prefetch !=
|
| @@ -1330,7 +1360,7 @@ int HttpCache::Transaction::DoCacheQueryData() {
|
| int HttpCache::Transaction::DoCacheQueryDataComplete(int result) {
|
| DCHECK_EQ(OK, result);
|
| if (!cache_.get()) {
|
| - TransitionToState(STATE_NONE);
|
| + TransitionToState(STATE_FINISH_HEADERS);
|
| return ERR_UNEXPECTED;
|
| }
|
|
|
| @@ -1340,7 +1370,7 @@ int HttpCache::Transaction::DoCacheQueryDataComplete(int result) {
|
| // We may end up here multiple times for a given request.
|
| int HttpCache::Transaction::DoStartPartialCacheValidation() {
|
| if (mode_ == NONE) {
|
| - TransitionToState(STATE_NONE);
|
| + TransitionToState(STATE_FINISH_HEADERS);
|
| return OK;
|
| }
|
|
|
| @@ -1354,15 +1384,16 @@ int HttpCache::Transaction::DoCompletePartialCacheValidation(int result) {
|
| if (mode_ & WRITE) {
|
| DoneWritingToEntry(true);
|
| } else {
|
| - cache_->DoneReadingFromEntry(entry_, this);
|
| + cache_->DoneWithEntry(entry_, this, false /* process_cancel */,
|
| + partial_ != nullptr);
|
| entry_ = NULL;
|
| }
|
| - TransitionToState(STATE_NONE);
|
| + TransitionToState(STATE_FINISH_HEADERS);
|
| return result;
|
| }
|
|
|
| if (result < 0) {
|
| - TransitionToState(STATE_NONE);
|
| + TransitionToState(STATE_FINISH_HEADERS);
|
| return result;
|
| }
|
|
|
| @@ -1388,7 +1419,7 @@ int HttpCache::Transaction::DoSendRequest() {
|
| int rv =
|
| cache_->network_layer_->CreateTransaction(priority_, &network_trans_);
|
| if (rv != OK) {
|
| - TransitionToState(STATE_NONE);
|
| + TransitionToState(STATE_FINISH_HEADERS);
|
| return rv;
|
| }
|
| network_trans_->SetBeforeNetworkStartCallback(before_network_start_callback_);
|
| @@ -1410,7 +1441,7 @@ int HttpCache::Transaction::DoSendRequest() {
|
| int HttpCache::Transaction::DoSendRequestComplete(int result) {
|
| TRACE_EVENT0("io", "HttpCacheTransaction::DoSendRequestComplete");
|
| if (!cache_.get()) {
|
| - TransitionToState(STATE_NONE);
|
| + TransitionToState(STATE_FINISH_HEADERS);
|
| return ERR_UNEXPECTED;
|
| }
|
|
|
| @@ -1441,7 +1472,7 @@ int HttpCache::Transaction::DoSendRequestComplete(int result) {
|
| DoneWritingToEntry(true);
|
| }
|
|
|
| - TransitionToState(STATE_NONE);
|
| + TransitionToState(STATE_FINISH_HEADERS);
|
| return result;
|
| }
|
|
|
| @@ -1455,7 +1486,7 @@ int HttpCache::Transaction::DoSuccessfulSendRequest() {
|
| new_response->headers->response_code() == 407) {
|
| SetAuthResponse(*new_response);
|
| if (!reading_) {
|
| - TransitionToState(STATE_NONE);
|
| + TransitionToState(STATE_FINISH_HEADERS);
|
| return OK;
|
| }
|
|
|
| @@ -1480,7 +1511,7 @@ int HttpCache::Transaction::DoSuccessfulSendRequest() {
|
| mode_ = NONE;
|
| partial_.reset();
|
| ResetNetworkTransaction();
|
| - TransitionToState(STATE_NONE);
|
| + TransitionToState(STATE_FINISH_HEADERS);
|
| return ERR_CACHE_AUTH_FAILURE_AFTER_READ;
|
| }
|
|
|
| @@ -1512,20 +1543,18 @@ int HttpCache::Transaction::DoSuccessfulSendRequest() {
|
| }
|
|
|
| // Invalidate any cached GET with a successful PUT or DELETE.
|
| - if (mode_ == WRITE &&
|
| - (request_->method == "PUT" || request_->method == "DELETE")) {
|
| + if (mode_ == WRITE && (method_ == "PUT" || method_ == "DELETE")) {
|
| if (NonErrorResponse(new_response->headers->response_code())) {
|
| int ret = cache_->DoomEntry(cache_key_, NULL);
|
| DCHECK_EQ(OK, ret);
|
| }
|
| - cache_->DoneWritingToEntry(entry_, true);
|
| + cache_->DoneWritingToEntry(entry_, true, this);
|
| entry_ = NULL;
|
| mode_ = NONE;
|
| }
|
|
|
| // Invalidate any cached GET with a successful POST.
|
| - if (!(effective_load_flags_ & LOAD_DISABLE_CACHE) &&
|
| - request_->method == "POST" &&
|
| + if (!(effective_load_flags_ & LOAD_DISABLE_CACHE) && method_ == "POST" &&
|
| NonErrorResponse(new_response->headers->response_code())) {
|
| cache_->DoomMainEntryForUrl(request_->url);
|
| }
|
| @@ -1533,10 +1562,10 @@ int HttpCache::Transaction::DoSuccessfulSendRequest() {
|
| RecordNoStoreHeaderHistogram(request_->load_flags, new_response);
|
|
|
| if (new_response_->headers->response_code() == 416 &&
|
| - (request_->method == "GET" || request_->method == "POST")) {
|
| + (method_ == "GET" || method_ == "POST")) {
|
| // If there is an active entry it may be destroyed with this transaction.
|
| SetResponse(*new_response_);
|
| - TransitionToState(STATE_NONE);
|
| + TransitionToState(STATE_FINISH_HEADERS);
|
| return OK;
|
| }
|
|
|
| @@ -1624,7 +1653,6 @@ int HttpCache::Transaction::DoUpdateCachedResponseComplete(int result) {
|
| } else if (entry_ && !handling_206_) {
|
| DCHECK_EQ(READ_WRITE, mode_);
|
| if (!partial_ || partial_->IsLastRange()) {
|
| - cache_->ConvertWriterToReader(entry_);
|
| mode_ = READ;
|
| }
|
| // We no longer need the network transaction, so destroy it.
|
| @@ -1657,12 +1685,12 @@ int HttpCache::Transaction::DoOverwriteCachedResponse() {
|
|
|
| SetResponse(*new_response_);
|
|
|
| - if (request_->method == "HEAD") {
|
| + if (method_ == "HEAD") {
|
| // This response is replacing the cached one.
|
| DoneWritingToEntry(false);
|
| mode_ = NONE;
|
| new_response_ = NULL;
|
| - TransitionToState(STATE_NONE);
|
| + TransitionToState(STATE_FINISH_HEADERS);
|
| return OK;
|
| }
|
|
|
| @@ -1683,6 +1711,19 @@ 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
|
| + // the network without writing to the backend.
|
| + bool is_match = response_.headers->response_code() == 304;
|
| + if (entry_ && response_.headers &&
|
| + !cache_->CanTransactionWriteResponseHeaders(entry_, this, is_match)) {
|
| + cache_->DoneWritingToEntry(entry_, false, this);
|
| + entry_ = nullptr;
|
| + mode_ = NONE;
|
| + return OK;
|
| + }
|
| +
|
| return WriteResponseInfoToEntry(truncated_);
|
| }
|
|
|
| @@ -1744,10 +1785,11 @@ int HttpCache::Transaction::DoPartialHeadersReceived() {
|
| new_response_ = NULL;
|
|
|
| if (!partial_) {
|
| - if (entry_ && entry_->disk_entry->GetDataSize(kMetadataIndex))
|
| + if (entry_ && entry_->disk_entry->GetDataSize(kMetadataIndex)) {
|
| TransitionToState(STATE_CACHE_READ_METADATA);
|
| - else
|
| - TransitionToState(STATE_NONE);
|
| + } else {
|
| + TransitionToState(STATE_FINISH_HEADERS);
|
| + }
|
| return OK;
|
| }
|
|
|
| @@ -1761,13 +1803,59 @@ int HttpCache::Transaction::DoPartialHeadersReceived() {
|
| // We are about to return the headers for a byte-range request to the user,
|
| // so let's fix them.
|
| partial_->FixResponseHeaders(response_.headers.get(), true);
|
| - TransitionToState(STATE_NONE);
|
| + TransitionToState(STATE_FINISH_HEADERS);
|
| } else {
|
| - TransitionToState(STATE_NONE);
|
| + TransitionToState(STATE_FINISH_HEADERS);
|
| }
|
| return OK;
|
| }
|
|
|
| +int HttpCache::Transaction::DoHeadersPhaseCannotProceed() {
|
| + // If its the Start state machine and it cannot proceed due to a cache
|
| + // failure, restart this transaction.
|
| + DCHECK(!reading_);
|
| + TransitionToState(STATE_INIT_ENTRY);
|
| + cache_entry_status_ = restart_info_.cache_entry_status;
|
| + entry_ = nullptr;
|
| + mode_ = restart_info_.mode;
|
| + if (network_trans_)
|
| + network_trans_.reset();
|
| +
|
| + return OK;
|
| +}
|
| +
|
| +int HttpCache::Transaction::DoFinishHeaders(int result) {
|
| + if (!entry_ || result != OK) {
|
| + TransitionToState(STATE_NONE);
|
| + return result;
|
| + }
|
| +
|
| + TransitionToState(STATE_FINISH_HEADERS_COMPLETE);
|
| +
|
| + // If it was an auth failure or 416, this transaction should continue to be
|
| + // headers_transaction till consumer takes an action, so no need to do
|
| + // anything now.
|
| + if (auth_response_.headers.get() ||
|
| + (new_response_ && new_response_->headers &&
|
| + new_response_->headers->response_code() == 416))
|
| + return OK;
|
| +
|
| + // If the transaction needs to wait because another transaction is still
|
| + // writing the response body, it will return ERR_IO_PENDING now and the
|
| + // io_callback_ will be invoked when the wait is done.
|
| + return cache_->DoneWithResponseHeaders(entry_, this, partial_ != nullptr);
|
| +}
|
| +
|
| +int HttpCache::Transaction::DoFinishHeadersComplete(int rv) {
|
| + if (rv == ERR_CACHE_RACE) {
|
| + TransitionToState(STATE_HEADERS_PHASE_CANNOT_PROCEED);
|
| + return OK;
|
| + }
|
| +
|
| + TransitionToState(STATE_NONE);
|
| + return rv;
|
| +}
|
| +
|
| int HttpCache::Transaction::DoCacheReadMetadata() {
|
| TRACE_EVENT0("io", "HttpCacheTransaction::DoCacheReadMetadata");
|
| DCHECK(entry_);
|
| @@ -1790,7 +1878,8 @@ int HttpCache::Transaction::DoCacheReadMetadataComplete(int result) {
|
| result);
|
| if (result != response_.metadata->size())
|
| return OnCacheReadError(result, false);
|
| - TransitionToState(STATE_NONE);
|
| +
|
| + TransitionToState(STATE_FINISH_HEADERS);
|
| return OK;
|
| }
|
|
|
| @@ -1823,7 +1912,7 @@ int HttpCache::Transaction::DoNetworkReadComplete(int result) {
|
| int HttpCache::Transaction::DoCacheReadData() {
|
| TRACE_EVENT0("io", "HttpCacheTransaction::DoCacheReadData");
|
|
|
| - if (request_->method == "HEAD") {
|
| + if (method_ == "HEAD") {
|
| TransitionToState(STATE_NONE);
|
| return 0;
|
| }
|
| @@ -1961,6 +2050,7 @@ void HttpCache::Transaction::SetRequest(const NetLogWithSource& net_log,
|
| net_log_ = net_log;
|
| request_ = request;
|
| effective_load_flags_ = request_->load_flags;
|
| + method_ = request_->method;
|
|
|
| if (cache_->mode() == DISABLE)
|
| effective_load_flags_ |= LOAD_DISABLE_CACHE;
|
| @@ -2040,7 +2130,7 @@ void HttpCache::Transaction::SetRequest(const NetLogWithSource& net_log,
|
| if (range_found && !(effective_load_flags_ & LOAD_DISABLE_CACHE)) {
|
| UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_OTHER);
|
| partial_.reset(new PartialData);
|
| - if (request_->method == "GET" && partial_->Init(request_->extra_headers)) {
|
| + if (method_ == "GET" && partial_->Init(request_->extra_headers)) {
|
| // We will be modifying the actual range requested to the server, so
|
| // let's remove the header here.
|
| custom_request_.reset(new HttpRequestInfo(*request_));
|
| @@ -2054,6 +2144,7 @@ void HttpCache::Transaction::SetRequest(const NetLogWithSource& net_log,
|
| partial_.reset(NULL);
|
| }
|
| }
|
| + restart_info_.cache_entry_status = cache_entry_status_;
|
| }
|
|
|
| bool HttpCache::Transaction::ShouldPassThrough() {
|
| @@ -2065,18 +2156,18 @@ bool HttpCache::Transaction::ShouldPassThrough() {
|
| if (effective_load_flags_ & LOAD_DISABLE_CACHE)
|
| return true;
|
|
|
| - if (request_->method == "GET" || request_->method == "HEAD")
|
| + if (method_ == "GET" || method_ == "HEAD")
|
| return false;
|
|
|
| - if (request_->method == "POST" && request_->upload_data_stream &&
|
| + if (method_ == "POST" && request_->upload_data_stream &&
|
| request_->upload_data_stream->identifier()) {
|
| return false;
|
| }
|
|
|
| - if (request_->method == "PUT" && request_->upload_data_stream)
|
| + if (method_ == "PUT" && request_->upload_data_stream)
|
| return false;
|
|
|
| - if (request_->method == "DELETE")
|
| + if (method_ == "DELETE")
|
| return false;
|
|
|
| return true;
|
| @@ -2087,28 +2178,28 @@ int HttpCache::Transaction::BeginCacheRead() {
|
| // TODO(jkarlin): Either handle this case or DCHECK.
|
| if (response_.headers->response_code() == 206 || partial_) {
|
| NOTREACHED();
|
| - TransitionToState(STATE_NONE);
|
| + TransitionToState(STATE_FINISH_HEADERS);
|
| return ERR_CACHE_MISS;
|
| }
|
|
|
| // We don't have the whole resource.
|
| if (truncated_) {
|
| - TransitionToState(STATE_NONE);
|
| + TransitionToState(STATE_FINISH_HEADERS);
|
| return ERR_CACHE_MISS;
|
| }
|
|
|
| if (RequiresValidation()) {
|
| - TransitionToState(STATE_NONE);
|
| + TransitionToState(STATE_FINISH_HEADERS);
|
| return ERR_CACHE_MISS;
|
| }
|
|
|
| - if (request_->method == "HEAD")
|
| + if (method_ == "HEAD")
|
| FixHeadersForHead();
|
|
|
| if (entry_->disk_entry->GetDataSize(kMetadataIndex))
|
| TransitionToState(STATE_CACHE_READ_METADATA);
|
| else
|
| - TransitionToState(STATE_NONE);
|
| + TransitionToState(STATE_FINISH_HEADERS);
|
|
|
| return OK;
|
| }
|
| @@ -2118,7 +2209,7 @@ int HttpCache::Transaction::BeginCacheValidation() {
|
|
|
| bool skip_validation = !RequiresValidation();
|
|
|
| - if (request_->method == "HEAD" &&
|
| + if (method_ == "HEAD" &&
|
| (truncated_ || response_.headers->response_code() == 206)) {
|
| DCHECK(!partial_);
|
| if (skip_validation)
|
| @@ -2176,7 +2267,7 @@ int HttpCache::Transaction::BeginPartialCacheValidation() {
|
|
|
| // Partial requests should not be recorded in histograms.
|
| UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_OTHER);
|
| - if (request_->method == "HEAD")
|
| + if (method_ == "HEAD")
|
| return BeginCacheValidation();
|
|
|
| if (!range_requested_) {
|
| @@ -2314,7 +2405,7 @@ bool HttpCache::Transaction::RequiresValidation() {
|
| return true;
|
| }
|
|
|
| - if (request_->method == "PUT" || request_->method == "DELETE")
|
| + if (method_ == "PUT" || method_ == "DELETE")
|
| return true;
|
|
|
| bool validation_required_by_headers = response_.headers->RequiresValidation(
|
| @@ -2340,7 +2431,7 @@ bool HttpCache::Transaction::RequiresValidation() {
|
| bool HttpCache::Transaction::ConditionalizeRequest() {
|
| DCHECK(response_.headers.get());
|
|
|
| - if (request_->method == "PUT" || request_->method == "DELETE")
|
| + if (method_ == "PUT" || method_ == "DELETE")
|
| return false;
|
|
|
| // This only makes sense for cached 200 or 206 responses.
|
| @@ -2434,7 +2525,7 @@ bool HttpCache::Transaction::ValidatePartialResponse() {
|
| bool partial_response = (response_code == 206);
|
| handling_206_ = false;
|
|
|
| - if (!entry_ || request_->method != "GET")
|
| + if (!entry_ || method_ != "GET")
|
| return true;
|
|
|
| if (invalid_range_) {
|
| @@ -2541,7 +2632,8 @@ void HttpCache::Transaction::IgnoreRangeRequest() {
|
| if (mode_ & WRITE)
|
| DoneWritingToEntry(mode_ != WRITE);
|
| else if (mode_ & READ && entry_)
|
| - cache_->DoneReadingFromEntry(entry_, this);
|
| + cache_->DoneWithEntry(entry_, this, false /* process_cancel */,
|
| + partial_ != nullptr);
|
|
|
| partial_.reset(NULL);
|
| entry_ = NULL;
|
| @@ -2568,16 +2660,16 @@ int HttpCache::Transaction::SetupEntryForRead() {
|
| partial_.reset();
|
| }
|
| }
|
| - cache_->ConvertWriterToReader(entry_);
|
| +
|
| mode_ = READ;
|
|
|
| - if (request_->method == "HEAD")
|
| + if (method_ == "HEAD")
|
| FixHeadersForHead();
|
|
|
| if (entry_->disk_entry->GetDataSize(kMetadataIndex))
|
| TransitionToState(STATE_CACHE_READ_METADATA);
|
| else
|
| - TransitionToState(STATE_NONE);
|
| + TransitionToState(STATE_FINISH_HEADERS);
|
| return OK;
|
| }
|
|
|
| @@ -2656,7 +2748,7 @@ void HttpCache::Transaction::DoneWritingToEntry(bool success) {
|
|
|
| RecordHistograms();
|
|
|
| - cache_->DoneWritingToEntry(entry_, success);
|
| + cache_->DoneWritingToEntry(entry_, success, this);
|
| entry_ = NULL;
|
| mode_ = NONE; // switch to 'pass through' mode
|
| }
|
| @@ -2679,7 +2771,8 @@ int HttpCache::Transaction::OnCacheReadError(int result, bool restart) {
|
| if (restart) {
|
| DCHECK(!reading_);
|
| DCHECK(!network_trans_.get());
|
| - cache_->DoneWithEntry(entry_, this, false);
|
| + cache_->DoneWithEntry(entry_, this, false /* process_cancel */,
|
| + partial_ != nullptr);
|
| entry_ = NULL;
|
| is_sparse_ = false;
|
| partial_.reset();
|
| @@ -2708,7 +2801,8 @@ void HttpCache::Transaction::DoomPartialEntry(bool delete_object) {
|
| DVLOG(2) << "DoomPartialEntry";
|
| int rv = cache_->DoomEntry(cache_key_, NULL);
|
| DCHECK_EQ(OK, rv);
|
| - cache_->DoneWithEntry(entry_, this, false);
|
| + cache_->DoneWithEntry(entry_, this, false /* process_cancel */,
|
| + partial_ != nullptr);
|
| entry_ = NULL;
|
| is_sparse_ = false;
|
| truncated_ = false;
|
| @@ -2804,7 +2898,7 @@ bool HttpCache::Transaction::CanResume(bool has_data) {
|
| if (has_data && !entry_->disk_entry->GetDataSize(kResponseContentIndex))
|
| return false;
|
|
|
| - if (request_->method != "GET")
|
| + if (method_ != "GET")
|
| return false;
|
|
|
| // Note that if this is a 206, content-length was already fixed after calling
|
| @@ -2853,7 +2947,7 @@ void HttpCache::Transaction::RecordHistograms() {
|
| DCHECK_NE(CacheEntryStatus::ENTRY_UNDEFINED, cache_entry_status_);
|
| if (!cache_.get() || !cache_->GetCurrentBackend() ||
|
| cache_->GetCurrentBackend()->GetCacheType() != DISK_CACHE ||
|
| - cache_->mode() != NORMAL || request_->method != "GET") {
|
| + cache_->mode() != NORMAL || method_ != "GET") {
|
| return;
|
| }
|
|
|
| @@ -2867,10 +2961,14 @@ void HttpCache::Transaction::RecordHistograms() {
|
| cache_entry_status_ == CacheEntryStatus::ENTRY_CANT_CONDITIONALIZE);
|
| int64_t freshness_periods_since_last_used = 0;
|
|
|
| - if (stale_request) {
|
| + if (stale_request && !open_entry_last_used_.is_null()) {
|
| + // Note that we are not able to capture those transactions' histograms which
|
| + // when added to entry, the response was being written by another
|
| + // transaction because getting the last used timestamp might lead to a data
|
| + // race in that case. TODO(crbug.com/713354).
|
| +
|
| // For stale entries, record how many freshness periods have elapsed since
|
| // the entry was last used.
|
| - DCHECK(!open_entry_last_used_.is_null());
|
| DCHECK(!stale_entry_freshness_.is_zero());
|
| base::TimeDelta time_since_use = base::Time::Now() - open_entry_last_used_;
|
| freshness_periods_since_last_used =
|
| @@ -2903,7 +3001,7 @@ void HttpCache::Transaction::RecordHistograms() {
|
| // response header mime type, which could be incorrect, so this is just an
|
| // estimate.
|
| if (mime_type == "text/html" &&
|
| - (request_->load_flags & LOAD_MAIN_FRAME_DEPRECATED)) {
|
| + (effective_load_flags_ & LOAD_MAIN_FRAME_DEPRECATED)) {
|
| CACHE_STATUS_HISTOGRAMS(".MainFrameHTML");
|
| } else if (mime_type == "text/html") {
|
| CACHE_STATUS_HISTOGRAMS(".NonMainFrameHTML");
|
| @@ -2943,7 +3041,8 @@ void HttpCache::Transaction::RecordHistograms() {
|
|
|
| if (cache_entry_status_ == CacheEntryStatus::ENTRY_OTHER)
|
| return;
|
| - DCHECK(!range_requested_);
|
| +
|
| + DCHECK(!range_requested_) << "Cache entry status " << cache_entry_status_;
|
| DCHECK(!first_cache_access_since_.is_null());
|
|
|
| TimeDelta total_time = base::TimeTicks::Now() - first_cache_access_since_;
|
|
|