Chromium Code Reviews| Index: net/http/http_cache_transaction.cc |
| diff --git a/net/http/http_cache_transaction.cc b/net/http/http_cache_transaction.cc |
| index db3f4968911b47ec29167190cf429a69007e00d7..9da268ffe51f0dd238e5d9acea601841103264cd 100644 |
| --- a/net/http/http_cache_transaction.cc |
| +++ b/net/http/http_cache_transaction.cc |
| @@ -266,8 +266,10 @@ HttpCache::Transaction::Transaction(RequestPriority priority, HttpCache* cache) |
| done_reading_(false), |
| vary_mismatch_(false), |
| couldnt_conditionalize_request_(false), |
| + stopped_caching_(false), |
| bypass_lock_for_test_(false), |
| fail_conditionalization_for_test_(false), |
| + read_buf_len_(0), |
| io_buf_len_(0), |
| read_offset_(0), |
| effective_load_flags_(0), |
| @@ -290,22 +292,28 @@ HttpCache::Transaction::~Transaction() { |
| // after this point. |
| callback_.Reset(); |
| - 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); |
| - } |
| - } |
| + RecordHistograms(); |
| + |
| + if (!cache_) |
| + return; |
| - cache_->DoneWithEntry(entry_, this, cancel_request); |
| - } else if (cache_pending_) { |
| + if (!entry_) { |
| + if (cache_pending_) |
| cache_->RemovePendingTransaction(this); |
| - } |
| + return; |
| + } |
| + |
| + bool cancel_request = reading_ && response_.headers.get() && |
| + (response_.headers->response_code() == 200 || partial_); |
| + if (cancel_request) { |
| + if (partial_) |
| + entry_->disk_entry->CancelSparseIO(); |
| + |
| + AbandonCacheEntry(CompletionCallback()); |
| + return; |
| } |
| + |
| + ReleaseCacheEntry(EntryState::DOOM); |
| } |
| int HttpCache::Transaction::WriteMetadata(IOBuffer* buf, int buf_len, |
| @@ -324,26 +332,6 @@ int HttpCache::Transaction::WriteMetadata(IOBuffer* buf, int buf_len, |
| callback, true); |
| } |
| -bool HttpCache::Transaction::AddTruncatedFlag() { |
| - DCHECK(mode_ & WRITE || mode_ == NONE); |
| - |
| - // Don't set the flag for sparse entries. |
| - if (partial_ && !truncated_) |
| - return true; |
| - |
| - if (!CanResume(true)) |
| - return false; |
| - |
| - // We may have received the whole resource already. |
| - if (done_reading_) |
| - return true; |
| - |
| - truncated_ = true; |
| - next_state_ = STATE_CACHE_WRITE_TRUNCATED_RESPONSE; |
| - DoLoop(OK); |
| - return true; |
| -} |
| - |
| LoadState HttpCache::Transaction::GetWriterLoadState() const { |
| if (network_trans_.get()) |
| return network_trans_->GetLoadState(); |
| @@ -458,7 +446,6 @@ int HttpCache::Transaction::Read(IOBuffer* buf, int buf_len, |
| DCHECK(buf); |
| DCHECK_GT(buf_len, 0); |
| DCHECK(!callback.is_null()); |
| - |
| DCHECK(callback_.is_null()); |
| if (!cache_.get()) |
| @@ -470,14 +457,17 @@ int HttpCache::Transaction::Read(IOBuffer* buf, int buf_len, |
| if (auth_response_.headers.get() && mode_ != NONE) { |
| UpdateTransactionPattern(PATTERN_NOT_COVERED); |
| DCHECK(mode_ & WRITE); |
| - DoneWritingToEntry(mode_ == READ_WRITE); |
| - mode_ = NONE; |
| + ReleaseCacheEntry(mode_ == READ_WRITE ? EntryState::KEEP |
| + : EntryState::DOOM); |
| } |
| reading_ = true; |
| read_buf_ = buf; |
| - io_buf_len_ = buf_len; |
| - if (network_trans_) { |
| + read_buf_len_ = buf_len; |
| + if (stopped_caching_ && entry_) { |
| + DCHECK(mode_ & WRITE); |
| + next_state_ = STATE_SWITCH_TO_NETWORK; |
| + } else if (network_trans_) { |
| DCHECK(mode_ == WRITE || mode_ == NONE || |
| (mode_ == READ_WRITE && partial_)); |
| next_state_ = STATE_NETWORK_READ; |
| @@ -496,21 +486,14 @@ int HttpCache::Transaction::Read(IOBuffer* buf, int buf_len, |
| } |
| void HttpCache::Transaction::StopCaching() { |
| - // We really don't know where we are now. Hopefully there is no operation in |
| - // progress, but nothing really prevents this method to be called after we |
| - // returned ERR_IO_PENDING. We cannot attempt to truncate the entry at this |
| - // point because we need the state machine for that (and even if we are really |
| - // free, that would be an asynchronous operation). In other words, keep the |
| - // entry how it is (it will be marked as truncated at destruction), and let |
| - // the next piece of code that executes know that we are now reading directly |
| - // from the net. |
| - // TODO(mmenke): This doesn't release the lock on the cache entry, so a |
| - // future request for the resource will be blocked on this one. |
| - // Fix this. |
| - if (cache_.get() && entry_ && (mode_ & WRITE) && network_trans_.get() && |
| - !is_sparse_ && !range_requested_) { |
| - mode_ = NONE; |
| - } |
| + DCHECK(callback_.is_null()); |
| + DCHECK(request_); |
| + |
| + // StopCaching requires being able to restart partial network requests. Doing |
| + // so is only reliable if the request is idempotent. In addition, StopCaching |
| + // is only effective if the transaction is writing to the cache. |
| + if ((!partial_ || request_->method == "GET") && (mode_ & WRITE)) |
| + stopped_caching_ = true; |
|
rvargas (doing something else)
2015/09/21 22:06:00
It looks like we set the flag when method != GET.
|
| } |
| bool HttpCache::Transaction::GetFullRequestHeaders( |
| @@ -537,18 +520,11 @@ int64_t HttpCache::Transaction::GetTotalSentBytes() const { |
| } |
| void HttpCache::Transaction::DoneReading() { |
| - if (cache_.get() && entry_) { |
| - DCHECK_NE(mode_, UPDATE); |
| - if (mode_ & WRITE) { |
| - DoneWritingToEntry(true); |
| - } else if (mode_ & READ) { |
| - // 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); |
| - entry_ = NULL; |
| - } |
| - } |
| + DCHECK_NE(mode_, UPDATE); |
| + if (!cache_ || !entry_) |
| + return; |
| + done_reading_ = true; |
| + ReleaseCacheEntry(EntryState::KEEP); |
| } |
| const HttpResponseInfo* HttpCache::Transaction::GetResponseInfo() const { |
| @@ -910,6 +886,9 @@ int HttpCache::Transaction::DoLoop(int result) { |
| case STATE_CACHE_READ_METADATA_COMPLETE: |
| rv = DoCacheReadMetadataComplete(rv); |
| break; |
| + case STATE_SWITCH_TO_NETWORK: |
| + rv = DoSwitchToNetwork(); |
| + break; |
| case STATE_NETWORK_READ: |
| DCHECK_EQ(OK, rv); |
| rv = DoNetworkRead(); |
| @@ -930,13 +909,6 @@ int HttpCache::Transaction::DoLoop(int result) { |
| case STATE_CACHE_WRITE_DATA_COMPLETE: |
| rv = DoCacheWriteDataComplete(rv); |
| break; |
| - case STATE_CACHE_WRITE_TRUNCATED_RESPONSE: |
| - DCHECK_EQ(OK, rv); |
| - rv = DoCacheWriteTruncatedResponse(); |
| - break; |
| - case STATE_CACHE_WRITE_TRUNCATED_RESPONSE_COMPLETE: |
| - rv = DoCacheWriteTruncatedResponseComplete(rv); |
| - break; |
| default: |
| NOTREACHED() << "bad state"; |
| rv = ERR_FAILED; |
| @@ -945,7 +917,8 @@ int HttpCache::Transaction::DoLoop(int result) { |
| } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); |
| 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. |
| + read_buf_len_ = 0; |
| base::ResetAndReturn(&callback_).Run(rv); |
| } |
| @@ -1077,6 +1050,7 @@ int HttpCache::Transaction::DoOpenEntryComplete(int result) { |
| next_state_ = STATE_CREATE_ENTRY; |
| return OK; |
| } |
| + |
| if (mode_ == UPDATE) { |
| // There is no cache entry to update; proceed without caching. |
| mode_ = NONE; |
| @@ -1294,7 +1268,7 @@ int HttpCache::Transaction::DoCacheToggleUnusedSincePrefetch() { |
| "422516 HttpCache::Transaction::DoCacheToggleUnusedSincePrefetch")); |
| next_state_ = STATE_TOGGLE_UNUSED_SINCE_PREFETCH_COMPLETE; |
| - return WriteResponseInfoToEntry(false); |
| + return WriteResponseInfoToEntry(false, io_callback_); |
| } |
| int HttpCache::Transaction::DoCacheToggleUnusedSincePrefetchComplete( |
| @@ -1362,14 +1336,8 @@ int HttpCache::Transaction::DoStartPartialCacheValidation() { |
| int HttpCache::Transaction::DoCompletePartialCacheValidation(int result) { |
| if (!result) { |
| - // This is the end of the request. |
| - if (mode_ & WRITE) { |
| - DoneWritingToEntry(true); |
| - } else { |
| - cache_->DoneReadingFromEntry(entry_, this); |
| - entry_ = NULL; |
| - } |
| - return result; |
| + ReleaseCacheEntry(EntryState::KEEP); |
| + return 0; |
| } |
| if (result < 0) |
| @@ -1441,7 +1409,7 @@ int HttpCache::Transaction::DoSendRequestComplete(int result) { |
| DCHECK(response); |
| response_.cert_request_info = response->cert_request_info; |
| } else if (response_.was_cached) { |
| - DoneWritingToEntry(true); |
| + ReleaseCacheEntry(EntryState::KEEP); |
| } |
| return result; |
| @@ -1497,11 +1465,27 @@ int HttpCache::Transaction::DoSuccessfulSendRequest() { |
| return OK; |
| } |
| + if (handling_206_ && stopped_caching_ && partial_ && |
| + partial_->IsLastRange() && !partial_->IsCurrentRangeCached() && |
| + (mode_ & WRITE) && entry_) { |
| + // The current state of the transaction is: |
| + // * The client has requested that no addtional data be cached. |
| + // * The current network transaction spans the entire remainder of the |
| + // requested range. |
| + // * A cache entry exists and was being written to or updated. |
| + // * The current range is not cached. |
| + // |
| + // At this point it is safe to abandon the cache entry and proceed with just |
| + // the network transaction. |
| + next_state_ = STATE_NETWORK_READ; |
| + return AbandonCacheEntry(io_callback_); |
| + } |
| + |
| if (handling_206_ && mode_ == READ_WRITE && !truncated_ && !is_sparse_) { |
| // We have stored the full entry, but it changed and the server is |
| // sending a range. We have to delete the old entry. |
| UpdateTransactionPattern(PATTERN_NOT_COVERED); |
| - DoneWritingToEntry(false); |
| + ReleaseCacheEntry(EntryState::DOOM); |
| } |
| if (mode_ == WRITE && |
| @@ -1516,9 +1500,7 @@ int HttpCache::Transaction::DoSuccessfulSendRequest() { |
| int ret = cache_->DoomEntry(cache_key_, NULL); |
| DCHECK_EQ(OK, ret); |
| } |
| - cache_->DoneWritingToEntry(entry_, true); |
| - entry_ = NULL; |
| - mode_ = NONE; |
| + ReleaseCacheEntry(EntryState::KEEP); |
| } |
| // Invalidate any cached GET with a successful POST. |
| @@ -1597,7 +1579,7 @@ int HttpCache::Transaction::DoCacheWriteUpdatedResponse() { |
| "422516 HttpCache::Transaction::DoCacheWriteUpdatedResponse")); |
| next_state_ = STATE_CACHE_WRITE_UPDATED_RESPONSE_COMPLETE; |
| - return WriteResponseInfoToEntry(false); |
| + return WriteResponseInfoToEntry(false, io_callback_); |
| } |
| int HttpCache::Transaction::DoCacheWriteUpdatedResponseComplete(int result) { |
| @@ -1613,7 +1595,7 @@ int HttpCache::Transaction::DoUpdateCachedResponseComplete(int result) { |
| // |
| // By closing the cached entry now, we make sure that the 304 rather than |
| // the cached 200 response, is what will be returned to the user. |
| - DoneWritingToEntry(true); |
| + ReleaseCacheEntry(EntryState::KEEP); |
| } else if (entry_ && !handling_206_) { |
| DCHECK_EQ(READ_WRITE, mode_); |
| if (!partial_ || partial_->IsLastRange()) { |
| @@ -1652,16 +1634,15 @@ int HttpCache::Transaction::DoOverwriteCachedResponse() { |
| if (request_->method == "HEAD") { |
| // This response is replacing the cached one. |
| - DoneWritingToEntry(false); |
| - mode_ = NONE; |
| - new_response_ = NULL; |
| + ReleaseCacheEntry(EntryState::DOOM); |
| + new_response_ = nullptr; |
| return OK; |
| } |
| if (handling_206_ && !CanResume(false)) { |
| // There is no point in storing this resource because it will never be used. |
| // This may change if we support LOAD_ONLY_FROM_CACHE with sparse entries. |
| - DoneWritingToEntry(false); |
| + ReleaseCacheEntry(EntryState::DOOM); |
| if (partial_) |
| partial_->FixResponseHeaders(response_.headers.get(), true); |
| next_state_ = STATE_PARTIAL_HEADERS_RECEIVED; |
| @@ -1679,7 +1660,7 @@ int HttpCache::Transaction::DoCacheWriteResponse() { |
| "422516 HttpCache::Transaction::DoCacheWriteResponse")); |
| next_state_ = STATE_CACHE_WRITE_RESPONSE_COMPLETE; |
| - return WriteResponseInfoToEntry(truncated_); |
| + return WriteResponseInfoToEntry(truncated_, io_callback_); |
| } |
| int HttpCache::Transaction::DoCacheWriteResponseComplete(int result) { |
| @@ -1775,7 +1756,73 @@ int HttpCache::Transaction::DoCacheReadMetadataComplete(int result) { |
| return OK; |
| } |
| +int HttpCache::Transaction::DoSwitchToNetwork() { |
| + DCHECK(entry_); |
| + DCHECK(mode_ & WRITE); |
| + |
| + // If the transaction is currently reading from the cache, then it should have |
| + // a valid partial state. I.e. only part of the requested resource could be |
| + // fulfiled via the cache. If the entire request was being fulfiled via the |
| + // cache, the transaction shouldn't be trying to switch to the network at this |
| + // point, and mode_ should be READ. |
| + DCHECK_IMPLIES(!network_trans_, partial_); |
| + |
| + // The network transaction can be trivially reused if it can promise to |
| + // deliver all the bits necessary to fulfil the original request. If so, |
| + // abandon the cache entry and continue with a network read. |
| + if (network_trans_ && (!partial_ || partial_->IsLastRange())) { |
| + next_state_ = STATE_NETWORK_READ; |
| + return AbandonCacheEntry(io_callback_); |
| + } |
| + |
| + if (request_->method != "GET") { |
| + // We have a non-GET request that the cache or network can partially fulfil. |
| + // In other words, at some point the transaction is going to have to issue a |
| + // partial network request to fulfil this non-GET request. Let's just ignore |
| + // the StopCaching() directive for now. |
| + stopped_caching_ = false; |
| + next_state_ = network_trans_ ? STATE_NETWORK_READ : STATE_CACHE_READ_DATA; |
| + return OK; |
| + } |
| + |
| + // The existing network transaction doesn't cover the entire range we wanted. |
| + // Let's discard the network transaction and create a new one that will cover |
| + // the entire range we need. |
| + if (network_trans_) |
| + ResetNetworkTransaction(); |
| + |
| + if (!partial_->SkipCacheForRemainder()) { |
| + // PartialData only refuses to switch to network reads if the entire |
| + // resource has been accounted for. There's nothing more to do. |
| + return OK; |
| + } |
| + |
| + // The new network request that's going to be issued has to be validated |
| + // against the existing cache entry. By extension, this also validates the new |
| + // request against the portion that has already been sent to our client. |
| + partial_->PrepareCacheValidation(entry_->disk_entry, |
| + &custom_request_->extra_headers); |
| + |
| + // Since we called SkipCacheForRemainder(), the partial_ state should now |
| + // assume that the remainder of the resource is not cached. |
| + DCHECK(!partial_->IsCurrentRangeCached()); |
| + DCHECK(partial_->IsLastRange()); |
| + |
| + // READ shouldn't reach here at all. WRITE/UPDATE require a network_trans_ |
| + // that spans the entire request range and is handled at the top of this |
| + // function. At this point we should be left with just READ_WRITE. |
| + DCHECK_EQ(mode_, READ_WRITE); |
| + |
| + // Start validating the cache. DoSuccessfulSendRequest() will now bear the |
| + // responsibility of abandoning the cache entry if the new network transaction |
| + // is successfully validated. |
| + return BeginCacheValidation(); |
| +} |
| + |
| int HttpCache::Transaction::DoNetworkRead() { |
| + DCHECK_GT(read_buf_len_, 0); |
| + |
| + io_buf_len_ = read_buf_len_; |
| next_state_ = STATE_NETWORK_READ_COMPLETE; |
| return network_trans_->Read(read_buf_.get(), io_buf_len_, io_callback_); |
| } |
| @@ -1796,11 +1843,14 @@ int HttpCache::Transaction::DoNetworkReadComplete(int result) { |
| } |
| int HttpCache::Transaction::DoCacheReadData() { |
| + DCHECK_GT(read_buf_len_, 0); |
| + |
| if (request_->method == "HEAD") |
| return 0; |
| DCHECK(entry_); |
| next_state_ = STATE_CACHE_READ_DATA_COMPLETE; |
| + io_buf_len_ = read_buf_len_; |
| if (net_log_.IsCapturing()) |
| net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_READ_DATA); |
| @@ -1830,15 +1880,13 @@ int HttpCache::Transaction::DoCacheReadDataComplete(int result) { |
| return DoPartialCacheReadCompleted(result); |
| } |
| - if (result > 0) { |
| - read_offset_ += result; |
| - } else if (result == 0) { // End of file. |
| - RecordHistograms(); |
| - cache_->DoneReadingFromEntry(entry_, this); |
| - entry_ = NULL; |
| - } else { |
| + if (result < 0) |
| return OnCacheReadError(result, false); |
| - } |
| + |
| + if (result == 0) |
| + ReleaseCacheEntry(EntryState::KEEP); |
| + |
| + read_offset_ += result; |
| return result; |
| } |
| @@ -1859,22 +1907,27 @@ int HttpCache::Transaction::DoCacheWriteData(int num_bytes) { |
| } |
| int HttpCache::Transaction::DoCacheWriteDataComplete(int result) { |
| - if (entry_) { |
| - if (net_log_.IsCapturing()) { |
| - net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_WRITE_DATA, |
| - result); |
| - } |
| + if (entry_ && net_log_.IsCapturing()) { |
| + net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_WRITE_DATA, |
| + result); |
| } |
| + |
| if (!cache_.get()) |
| return ERR_UNEXPECTED; |
| if (result != write_len_) { |
| DLOG(ERROR) << "failed to write response data to cache"; |
| - DoneWritingToEntry(false); |
| + ReleaseCacheEntry(EntryState::DOOM); |
| // We want to ignore errors writing to disk and just keep reading from |
| // the network. |
| result = write_len_; |
| + |
| + // TODO(asanka): More needs to be done to ignore the error and keep reading |
| + // from the network. E.g. if the cache entry was sparse, it is possible that |
| + // the current network_trans_ can't fulfil the entire requested range. |
| + // Consider setting up state to perform an immediate STATE_SWITCH_TO_NETWORK |
| + // at the next Read() instead of releasing the entry now. |
| } else if (!done_reading_ && entry_ && (!partial_ || truncated_)) { |
| int current_size = entry_->disk_entry->GetDataSize(kResponseContentIndex); |
| int64 body_size = response_.headers->GetContentLength(); |
| @@ -1883,34 +1936,53 @@ int HttpCache::Transaction::DoCacheWriteDataComplete(int result) { |
| } |
| if (partial_) { |
| - // This may be the last request. |
| - if (result != 0 || truncated_ || |
| - !(partial_->IsLastRange() || mode_ == WRITE)) { |
| + // The transaction needs to update partial state under the following |
| + // circumstances: |
| + const bool need_to_update_partial_state = |
| + // Still reading. The partial state needs to be updated to account for |
| + // the data read so far. |
| + result != 0 || |
| + |
| + // Reached the end of one network request, but more transactions may be |
| + // necessary to fulfil the request. DoPartialNetworkReadCompleted() |
| + // initiates additional network transactions as needed. |
| + truncated_ || |
| + |
| + // If this isn't the last range, then more network transactions may be |
| + // necessary. But skip this step for new cache entries (mode_ == WRITE). |
| + // Range requests with new cache entries will have |
| + // partial_->IsLastRange() set to false. IsLastRange() is only set to |
| + // true when validating the last range of an existing cache entry. |
| + (!partial_->IsLastRange() && mode_ != WRITE); |
| + |
| + if (need_to_update_partial_state) |
| return DoPartialNetworkReadCompleted(result); |
| - } |
| } |
| if (result == 0) { |
| - // End of file. This may be the result of a connection problem so see if we |
| - // have to keep the entry around to be flagged as truncated later on. |
| - if (done_reading_ || !entry_ || partial_ || |
| - response_.headers->GetContentLength() <= 0) { |
| - DoneWritingToEntry(true); |
| + // If the entire response body was successfully read, then the transaction |
| + // should release the cache entry. |
| + // |
| + // While response == 0 typically indicates EOF, it is possible for that to |
| + // happen due to a connection problem. In the latter case the resulting |
|
rvargas (doing something else)
2015/09/21 22:06:00
tiny nit: maybe remove "the resulting" and "call"
|
| + // AbandonCacheEntry() call marks the cache entry as truncated. |
| + // |
| + // A succesful receipt of the response body is considered to be indicated by |
| + // one of: |
| + // * done_reading_ : Received response body was of expected size. |
| + // * Content-Length <= 0 : Response body size was not known. |
| + // * partial_ : |
| + if (entry_ && (done_reading_ || partial_ || |
| + response_.headers->GetContentLength() <= 0)) { |
| + ReleaseCacheEntry(EntryState::KEEP); |
| + } else { |
| + AbandonCacheEntry(CompletionCallback()); |
| } |
| } |
| return result; |
| } |
| -int HttpCache::Transaction::DoCacheWriteTruncatedResponse() { |
| - next_state_ = STATE_CACHE_WRITE_TRUNCATED_RESPONSE_COMPLETE; |
| - return WriteResponseInfoToEntry(true); |
| -} |
| - |
| -int HttpCache::Transaction::DoCacheWriteTruncatedResponseComplete(int result) { |
| - return OnWriteResponseInfoToEntryComplete(result); |
| -} |
| - |
| //----------------------------------------------------------------------------- |
| void HttpCache::Transaction::ReadCertChain() { |
| @@ -2168,6 +2240,11 @@ int HttpCache::Transaction::BeginCacheValidation() { |
| // know we won't be falling back to using the cache entry in the |
| // LOAD_FROM_CACHE_IF_OFFLINE case. |
| if (!ConditionalizeRequest()) { |
| + // Conditionalization should only depend on the state of the cache prior |
| + // to starting the request and the request method. Failure is unexpected |
| + // after the client has started reading the stream. |
| + DCHECK(!reading_); |
| + |
| couldnt_conditionalize_request_ = true; |
| UpdateTransactionPattern(PATTERN_ENTRY_CANT_CONDITIONALIZE); |
| if (partial_) |
| @@ -2246,7 +2323,7 @@ int HttpCache::Transaction::BeginExternallyConditionalizedRequest() { |
| // The externally conditionalized request is not a validation request |
| // for our existing cache entry. Proceed with caching disabled. |
| UpdateTransactionPattern(PATTERN_NOT_COVERED); |
| - DoneWritingToEntry(true); |
| + ReleaseCacheEntry(EntryState::KEEP); |
| } |
| } |
| @@ -2574,14 +2651,8 @@ void HttpCache::Transaction::IgnoreRangeRequest() { |
| // using the cache and see what happens. Most likely this is the first |
| // response from the server (it's not changing its mind midway, right?). |
| UpdateTransactionPattern(PATTERN_NOT_COVERED); |
| - if (mode_ & WRITE) |
| - DoneWritingToEntry(mode_ != WRITE); |
| - else if (mode_ & READ && entry_) |
| - cache_->DoneReadingFromEntry(entry_, this); |
| - |
| - partial_.reset(NULL); |
| - entry_ = NULL; |
| - mode_ = NONE; |
| + ReleaseCacheEntry(mode_ == WRITE ? EntryState::DOOM : EntryState::KEEP); |
| + partial_.reset(); |
| } |
| void HttpCache::Transaction::FixHeadersForHead() { |
| @@ -2631,7 +2702,9 @@ int HttpCache::Transaction::WriteToEntry(int index, int offset, |
| return rv; |
| } |
| -int HttpCache::Transaction::WriteResponseInfoToEntry(bool truncated) { |
| +int HttpCache::Transaction::WriteResponseInfoToEntry( |
| + bool truncated, |
| + const CompletionCallback& callback) { |
| if (!entry_) |
| return OK; |
| @@ -2649,7 +2722,7 @@ int HttpCache::Transaction::WriteResponseInfoToEntry(bool truncated) { |
| // status to a net error and replay the net error. |
| if ((response_.headers->HasHeaderValue("cache-control", "no-store")) || |
| IsCertStatusError(response_.ssl_info.cert_status)) { |
| - DoneWritingToEntry(false); |
| + ReleaseCacheEntry(EntryState::DOOM); |
| if (net_log_.IsCapturing()) |
| net_log_.EndEvent(NetLog::TYPE_HTTP_CACHE_WRITE_INFO); |
| return OK; |
| @@ -2670,7 +2743,7 @@ int HttpCache::Transaction::WriteResponseInfoToEntry(bool truncated) { |
| io_buf_len_ = data->pickle()->size(); |
| return entry_->disk_entry->WriteData(kResponseInfoIndex, 0, data.get(), |
| - io_buf_len_, io_callback_, true); |
| + io_buf_len_, callback, true); |
| } |
| int HttpCache::Transaction::OnWriteResponseInfoToEntryComplete(int result) { |
| @@ -2683,20 +2756,18 @@ int HttpCache::Transaction::OnWriteResponseInfoToEntryComplete(int result) { |
| if (result != io_buf_len_) { |
| DLOG(ERROR) << "failed to write response info to cache"; |
| - DoneWritingToEntry(false); |
| + ReleaseCacheEntry(EntryState::DOOM); |
| } |
| return OK; |
| } |
| -void HttpCache::Transaction::DoneWritingToEntry(bool success) { |
| - if (!entry_) |
| +void HttpCache::Transaction::ReleaseCacheEntry(EntryState entry_state) { |
| + if (!cache_ || !entry_) |
| return; |
| - RecordHistograms(); |
| - |
| - cache_->DoneWritingToEntry(entry_, success); |
| - entry_ = NULL; |
| - mode_ = NONE; // switch to 'pass through' mode |
| + cache_->DoneWithEntry(entry_, this, entry_state); |
| + entry_ = nullptr; |
| + mode_ = NONE; |
| } |
| int HttpCache::Transaction::OnCacheReadError(int result, bool restart) { |
| @@ -2717,8 +2788,7 @@ int HttpCache::Transaction::OnCacheReadError(int result, bool restart) { |
| if (restart) { |
| DCHECK(!reading_); |
| DCHECK(!network_trans_.get()); |
| - cache_->DoneWithEntry(entry_, this, false); |
| - entry_ = NULL; |
| + ReleaseCacheEntry(EntryState::DOOM); |
| is_sparse_ = false; |
| partial_.reset(); |
| next_state_ = STATE_GET_BACKEND; |
| @@ -2743,14 +2813,11 @@ void HttpCache::Transaction::OnAddToEntryTimeout(base::TimeTicks start_time) { |
| 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); |
| - entry_ = NULL; |
| + ReleaseCacheEntry(EntryState::DOOM); |
| is_sparse_ = false; |
| truncated_ = false; |
| if (delete_object) |
| - partial_.reset(NULL); |
| + partial_.reset(); |
| } |
| int HttpCache::Transaction::DoPartialNetworkReadCompleted(int result) { |
| @@ -2782,8 +2849,8 @@ int HttpCache::Transaction::DoRestartPartialRequest() { |
| // WRITE + Doom + STATE_INIT_ENTRY == STATE_CREATE_ENTRY (without an attempt |
| // to Doom the entry again). |
| - mode_ = WRITE; |
| ResetPartialState(!range_requested_); |
| + mode_ = WRITE; |
| next_state_ = STATE_CREATE_ENTRY; |
| return OK; |
| } |
| @@ -2859,14 +2926,14 @@ void HttpCache::Transaction::UpdateTransactionPattern( |
| } |
| void HttpCache::Transaction::RecordHistograms() { |
| - DCHECK_NE(PATTERN_UNDEFINED, transaction_pattern_); |
| - if (!cache_.get() || !cache_->GetCurrentBackend() || |
| + if (transaction_pattern_ == PATTERN_UNDEFINED || !cache_.get() || |
| + !cache_->GetCurrentBackend() || |
| cache_->GetCurrentBackend()->GetCacheType() != DISK_CACHE || |
| cache_->mode() != NORMAL || request_->method != "GET") { |
| return; |
| } |
| - UMA_HISTOGRAM_ENUMERATION( |
| - "HttpCache.Pattern", transaction_pattern_, PATTERN_MAX); |
| + UMA_HISTOGRAM_ENUMERATION("HttpCache.Pattern", transaction_pattern_, |
| + PATTERN_MAX); |
| if (transaction_pattern_ == PATTERN_NOT_COVERED) |
| return; |
| DCHECK(!range_requested_); |
| @@ -2940,4 +3007,58 @@ void HttpCache::Transaction::OnIOComplete(int result) { |
| DoLoop(result); |
| } |
| +int HttpCache::Transaction::AbandonCacheEntry( |
| + const CompletionCallback& callback) { |
| + DCHECK(abandon_cache_entry_callback_.is_null()); |
| + // Is the cache entry is sparse or if the entry is complete? Then the entry |
|
rvargas (doing something else)
2015/09/21 22:06:00
needs some rewording :). I guess you were going fo
|
| + // doesn't need to be marked as truncated. |
| + // |
| + // * done_reading_: All pertinent response data has been written. |
| + // * !(mode_ & WRITE): This transaction wasn't writing to the entry. Leave |
| + // it be. |
| + // * is_sparse_: Pre-existing sparse entry. |
| + // * (partial_ && !truncated_): New sparse entry. |
| + if (done_reading_ || !(mode_ & WRITE) || is_sparse_ || |
| + (partial_ && !truncated_)) { |
| + ReleaseCacheEntry(EntryState::KEEP); |
| + return OK; |
| + } |
| + |
| + // If the caller hasn't started reading (implies that no response data was |
| + // written) or if the request can't be resumed, then another transaction is |
| + // unlikely to be able to use a partial cache entry for this request. |
| + if (!reading_ || !CanResume(true)) { |
| + ReleaseCacheEntry(EntryState::DOOM); |
| + return OK; |
| + } |
| + |
| + // If we get here, the cache entry only contains part of the response and can |
| + // be resumed in the future. Mark it as truncated so it'll be handled |
| + // correctly. |
| + // TODO(asanka): Let's get rid of a persistent truncated flag and make it |
| + // implicit based on what we find in the cache the next time the entry is |
| + // opened. |
| + truncated_ = true; |
| + int result = WriteResponseInfoToEntry( |
| + true, base::Bind(&HttpCache::Transaction::OnAbandonCacheEntryIOComplete, |
| + weak_factory_.GetWeakPtr())); |
| + if (result < 0 && result != ERR_IO_PENDING) |
| + return result; |
| + |
| + if (callback.is_null()) { |
| + ReleaseCacheEntry(EntryState::KEEP); |
| + return OK; |
| + } |
| + |
| + abandon_cache_entry_callback_ = callback; |
| + return result; |
| +} |
| + |
| +void HttpCache::Transaction::OnAbandonCacheEntryIOComplete(int result) { |
| + result = OnWriteResponseInfoToEntryComplete(result); |
| + ReleaseCacheEntry(EntryState::KEEP); |
| + if (!abandon_cache_entry_callback_.is_null()) |
| + base::ResetAndReturn(&abandon_cache_entry_callback_).Run(result); |
| +} |
| + |
| } // namespace net |