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 24002d75215d56bcc5081c22af3d4369806159e8..d74cf0bddfda97b9498732396cc4644278a37e59 100644 |
| --- a/net/http/http_cache_transaction.cc |
| +++ b/net/http/http_cache_transaction.cc |
| @@ -11,7 +11,9 @@ |
| #endif |
| #include <algorithm> |
| +#include <limits> |
| #include <string> |
| +#include <utility> |
| #include "base/bind.h" |
| #include "base/callback_helpers.h" |
| @@ -38,6 +40,8 @@ |
| #include "net/cert/cert_status_flags.h" |
| #include "net/cert/x509_certificate.h" |
| #include "net/disk_cache/disk_cache.h" |
| +#include "net/http/http_cache_data_access.h" |
| +#include "net/http/http_cache_shared_writers.h" |
| #include "net/http/http_network_session.h" |
| #include "net/http/http_request_info.h" |
| #include "net/http/http_util.h" |
| @@ -181,6 +185,9 @@ HttpCache::Transaction::Transaction(RequestPriority priority, HttpCache* cache) |
| couldnt_conditionalize_request_(false), |
| bypass_lock_for_test_(false), |
| fail_conditionalization_for_test_(false), |
| + shared_(false), |
| + initiate_shared_writing_(false), |
| + shared_read_write_failure_result_(0), |
| io_buf_len_(0), |
| read_offset_(0), |
| effective_load_flags_(0), |
| @@ -190,6 +197,7 @@ HttpCache::Transaction::Transaction(RequestPriority priority, HttpCache* cache) |
| total_received_bytes_(0), |
| total_sent_bytes_(0), |
| websocket_handshake_stream_base_create_helper_(NULL), |
| + have_full_request_headers_(false), |
| weak_factory_(this) { |
| TRACE_EVENT0("io", "HttpCacheTransaction::Transaction"); |
| static_assert(HttpCache::Transaction::kNumValidationHeaders == |
| @@ -202,6 +210,13 @@ HttpCache::Transaction::Transaction(RequestPriority priority, HttpCache* cache) |
| HttpCache::Transaction::~Transaction() { |
| TRACE_EVENT0("io", "HttpCacheTransaction::~Transaction"); |
| + |
| + if (shared_) { |
| + // Remove transaction from shared writers and do any cleanup, if needed, for |
| + // entry_->shared_writers and entry_. |
| + RemoveTransactionFromSharedWriters(); |
| + } |
| + |
| // We may have to issue another IO, but we should never invoke the callback_ |
| // after this point. |
| callback_.Reset(); |
| @@ -216,7 +231,6 @@ HttpCache::Transaction::~Transaction() { |
| cancel_request &= (response_.headers->response_code() == 200); |
| } |
| } |
| - |
| cache_->DoneWithEntry(entry_, this, cancel_request); |
| } else if (cache_pending_) { |
| cache_->RemovePendingTransaction(this); |
| @@ -224,6 +238,35 @@ HttpCache::Transaction::~Transaction() { |
| } |
| } |
| +void HttpCache::Transaction::RemoveTransactionFromSharedWriters() { |
| + // Current shared data_access_ consumer. |
| + if (next_state_ == STATE_SHARED_NETWORK_READ_COMPLETE || |
| + next_state_ == STATE_SHARED_CACHE_WRITE_DATA_COMPLETE) { |
| + cache_->RemoveCurrentSharedWriter(this, entry_); |
| + } else if (next_state_ == STATE_SHARED_NETWORK_READ_WAIT_COMPLETE) { |
| + // Waiting on data_access_ operation to be completed by another |
| + // transaction. |
| + entry_->shared_writers->RemoveWaitingWriter(this); |
| + } else if (next_state_ != STATE_NONE && |
| + next_state_ <= STATE_ADD_TO_ENTRY_COMPLETE) { |
| + // Pending transaction not yet to added to an entry. |
| + cache_->RemovePendingTransaction(this); |
| + // Set cache_pending_ to false so that more cleanup is not attempted in |
| + // the destructor. |
| + cache_pending_ = false; |
| + } else if (validating_shared()) { |
| + cache_->RemoveValidatingTransSharedWriters(this, entry_); |
| + } else { |
| + cache_->RemoveIdleSharedWriter(this, entry_); |
| + } |
| + |
| + DCHECK(!shared_); |
| + |
| + // Since any cleanup needed in entry_ and shared_writers is already done, |
| + // set entry_ to null. |
| + entry_ = nullptr; |
| +} |
| + |
| int HttpCache::Transaction::WriteMetadata(IOBuffer* buf, int buf_len, |
| const CompletionCallback& callback) { |
| DCHECK(buf); |
| @@ -247,7 +290,7 @@ bool HttpCache::Transaction::AddTruncatedFlag() { |
| if (partial_ && !truncated_) |
| return true; |
| - if (!CanResume(true)) |
| + if (!cache_->CanResumeEntry(true, request_->method, &response_, entry_)) |
| return false; |
| // We may have received the whole resource already. |
| @@ -261,8 +304,9 @@ bool HttpCache::Transaction::AddTruncatedFlag() { |
| } |
| LoadState HttpCache::Transaction::GetWriterLoadState() const { |
| - if (network_trans_.get()) |
| - return network_trans_->GetLoadState(); |
| + HttpTransaction* network_transaction = GetCurrentNetworkTransaction(); |
| + if (network_transaction) |
| + return network_transaction->GetLoadState(); |
| if (entry_ || !request_) |
| return LOAD_STATE_IDLE; |
| return LOAD_STATE_WAITING_FOR_CACHE; |
| @@ -372,6 +416,15 @@ bool HttpCache::Transaction::IsReadyToRestartForAuth() { |
| int HttpCache::Transaction::Read(IOBuffer* buf, int buf_len, |
| const CompletionCallback& callback) { |
| + if (next_state_ == STATE_SHARED_READ_WRITE_FAILED) { |
| + if (net_log_.IsCapturing()) |
| + net_log_.EndEventWithNetErrorCode( |
| + NetLogEventType::HTTP_CACHE_SHARED_READ_WRITE_FAILED, |
| + shared_read_write_failure_result_); |
| + |
| + return shared_read_write_failure_result_; |
| + } |
| + |
| DCHECK_EQ(next_state_, STATE_NONE); |
| DCHECK(buf); |
| DCHECK_GT(buf_len, 0); |
| @@ -395,13 +448,25 @@ int HttpCache::Transaction::Read(IOBuffer* buf, int buf_len, |
| reading_ = true; |
| read_buf_ = buf; |
| io_buf_len_ = buf_len; |
| - if (network_trans_) { |
| - DCHECK(mode_ == WRITE || mode_ == NONE || |
| - (mode_ == READ_WRITE && partial_)); |
| - next_state_ = STATE_NETWORK_READ; |
| - } else { |
| - DCHECK(mode_ == READ || (mode_ == READ_WRITE && partial_)); |
| - next_state_ = STATE_CACHE_READ_DATA; |
| + |
| + if (shared_) { |
| + int disk_entry_size = |
| + entry_->disk_entry->GetDataSize(kResponseContentIndex); |
| + if (read_offset_ == disk_entry_size) { |
| + next_state_ = STATE_SHARED_NETWORK_READ; |
| + } else { |
| + DCHECK_LT(read_offset_, disk_entry_size); |
| + next_state_ = STATE_CACHE_READ_DATA; |
| + } |
| + } else { // not shared. |
| + if (network_trans_ || data_access_) { |
| + DCHECK(mode_ == WRITE || mode_ == NONE || |
| + (mode_ == READ_WRITE && partial_)); |
| + next_state_ = STATE_NETWORK_READ; |
| + } else { // read-only |
| + DCHECK(mode_ == READ || (mode_ == READ_WRITE && partial_)); |
| + next_state_ = STATE_CACHE_READ_DATA; |
| + } |
| } |
| int rv = DoLoop(OK); |
| @@ -425,40 +490,62 @@ void HttpCache::Transaction::StopCaching() { |
| // 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_) { |
| + if (shared_) { |
| + // This might or might not stop caching based on whether other consumers |
| + // exist for this resource or not. If it does, shared_ will be set to false. |
| + cache_->StopCachingSharedWriters(this, entry_); |
|
Randy Smith (Not in Mondays)
2017/01/19 00:53:43
Why not delegate to entry_->shared_writers? That
shivanisha
2017/01/19 21:13:06
Going through HttpCache in this case basically ens
Randy Smith (Not in Mondays)
2017/01/20 19:54:31
Meaning that StopCachingSharedWriters() might dest
shivanisha
2017/01/25 19:46:13
Done. (Response in detail in the higher level feed
|
| + } |
| + if (!shared_ && cache_.get() && entry_ && (mode_ & WRITE) && |
| + network_trans_.get() && !is_sparse_ && !range_requested_) { |
| mode_ = NONE; |
| } |
| } |
| bool HttpCache::Transaction::GetFullRequestHeaders( |
| HttpRequestHeaders* headers) const { |
| - if (network_trans_) |
| - return network_trans_->GetFullRequestHeaders(headers); |
| - |
| + HttpTransaction* network_transaction = GetCurrentNetworkTransaction(); |
| + if (network_transaction) { |
| + return network_transaction->GetFullRequestHeaders(headers); |
| + } else if (have_full_request_headers_) { |
| + *headers = full_request_headers_; |
| + return true; |
| + } |
| // TODO(juliatuttle): Read headers from cache. |
| return false; |
| } |
| int64_t HttpCache::Transaction::GetTotalReceivedBytes() const { |
| int64_t total_received_bytes = total_received_bytes_; |
| - if (network_trans_) |
| - total_received_bytes += network_trans_->GetTotalReceivedBytes(); |
| + HttpTransaction* network_transaction = GetCurrentNetworkTransaction(); |
| + if (network_transaction) |
| + total_received_bytes += network_transaction->GetTotalReceivedBytes(); |
| return total_received_bytes; |
| } |
| int64_t HttpCache::Transaction::GetTotalSentBytes() const { |
| int64_t total_sent_bytes = total_sent_bytes_; |
| - if (network_trans_) |
| - total_sent_bytes += network_trans_->GetTotalSentBytes(); |
| + HttpTransaction* network_transaction = GetCurrentNetworkTransaction(); |
| + if (network_transaction) |
| + total_sent_bytes += network_transaction->GetTotalSentBytes(); |
| return total_sent_bytes; |
| } |
| void HttpCache::Transaction::DoneReading() { |
| if (cache_.get() && entry_) { |
| DCHECK_NE(mode_, UPDATE); |
| + bool perform_entry_cleanup = true; |
| + if (shared_) { |
| + cache_->DoneReadingSharedWriters(this, entry_); |
| + |
| + // shared_ should have been set to false. |
| + DCHECK(!shared_); |
| + |
| + // entry_ cleanup already performed, if any. |
| + perform_entry_cleanup = false; |
| + } |
| + |
| if (mode_ & WRITE) { |
| - DoneWritingToEntry(true); |
| + DoneWritingToEntry(true, perform_entry_cleanup); |
| } 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 |
| @@ -497,8 +584,9 @@ void HttpCache::Transaction::SetQuicServerInfo( |
| bool HttpCache::Transaction::GetLoadTimingInfo( |
| LoadTimingInfo* load_timing_info) const { |
| - if (network_trans_) |
| - return network_trans_->GetLoadTimingInfo(load_timing_info); |
| + HttpTransaction* network_transaction = GetCurrentNetworkTransaction(); |
| + if (network_transaction) |
| + return network_transaction->GetLoadTimingInfo(load_timing_info); |
| if (old_network_trans_load_timing_) { |
| *load_timing_info = *old_network_trans_load_timing_; |
| @@ -517,8 +605,9 @@ bool HttpCache::Transaction::GetLoadTimingInfo( |
| } |
| bool HttpCache::Transaction::GetRemoteEndpoint(IPEndPoint* endpoint) const { |
| - if (network_trans_) |
| - return network_trans_->GetRemoteEndpoint(endpoint); |
| + HttpTransaction* network_transaction = GetCurrentNetworkTransaction(); |
| + if (network_transaction) |
| + return network_transaction->GetRemoteEndpoint(endpoint); |
| if (!old_remote_endpoint_.address().empty()) { |
| *endpoint = old_remote_endpoint_; |
| @@ -530,22 +619,25 @@ bool HttpCache::Transaction::GetRemoteEndpoint(IPEndPoint* endpoint) const { |
| void HttpCache::Transaction::PopulateNetErrorDetails( |
| NetErrorDetails* details) const { |
| - if (network_trans_) |
| - return network_trans_->PopulateNetErrorDetails(details); |
| + HttpTransaction* network_transaction = GetCurrentNetworkTransaction(); |
| + if (network_transaction) |
| + network_transaction->PopulateNetErrorDetails(details); |
| return; |
| } |
| void HttpCache::Transaction::SetPriority(RequestPriority priority) { |
| priority_ = priority; |
| - if (network_trans_) |
| - network_trans_->SetPriority(priority_); |
| + HttpTransaction* network_transaction = GetCurrentNetworkTransaction(); |
| + if (network_transaction) |
| + network_transaction->SetPriority(priority_); |
| } |
| void HttpCache::Transaction::SetWebSocketHandshakeStreamCreateHelper( |
| WebSocketHandshakeStreamBase::CreateHelper* create_helper) { |
| websocket_handshake_stream_base_create_helper_ = create_helper; |
| - if (network_trans_) |
| - network_trans_->SetWebSocketHandshakeStreamCreateHelper(create_helper); |
| + HttpTransaction* network_transaction = GetCurrentNetworkTransaction(); |
| + if (network_transaction) |
| + network_transaction->SetWebSocketHandshakeStreamCreateHelper(create_helper); |
| } |
| void HttpCache::Transaction::SetBeforeNetworkStartCallback( |
| @@ -561,16 +653,18 @@ void HttpCache::Transaction::SetBeforeHeadersSentCallback( |
| } |
| int HttpCache::Transaction::ResumeNetworkStart() { |
| - if (network_trans_) |
| - return network_trans_->ResumeNetworkStart(); |
| + HttpTransaction* network_transaction = GetCurrentNetworkTransaction(); |
| + if (network_transaction) |
| + network_transaction->ResumeNetworkStart(); |
| return ERR_UNEXPECTED; |
| } |
| void HttpCache::Transaction::GetConnectionAttempts( |
| ConnectionAttempts* out) const { |
| ConnectionAttempts new_connection_attempts; |
| - if (network_trans_) |
| - network_trans_->GetConnectionAttempts(&new_connection_attempts); |
| + HttpTransaction* network_transaction = GetCurrentNetworkTransaction(); |
| + if (network_transaction) |
| + network_transaction->GetConnectionAttempts(&new_connection_attempts); |
| out->swap(new_connection_attempts); |
| out->insert(out->begin(), old_connection_attempts_.begin(), |
| @@ -588,18 +682,29 @@ void HttpCache::Transaction::GetConnectionAttempts( |
| // CacheWriteResponse* -> TruncateCachedData* -> TruncateCachedMetadata* -> |
| // PartialHeadersReceived |
| // |
| -// Read(): |
| +// Read(): (For transactions that are not eligible for shared writing) |
| // NetworkRead* -> CacheWriteData* |
| // |
| +// Read(): (For transactions that are eligible for shared writing) |
| +// SharedNetworkRead* -> SharedCacheWriteData* |
| +// |
| +// Read(): (For a transaction that is shared and another read is already in |
| +// progress) |
| +// SharedNetworkRead -> SharedNetworkReadWaitComplete |
| +// |
| // 2. Cached entry, no validation: |
| // Start(): |
| // GetBackend* -> InitEntry -> OpenEntry* -> AddToEntry* -> CacheReadResponse* |
| // -> CacheDispatchValidation -> BeginPartialCacheValidation() -> |
| // BeginCacheValidation() -> SetupEntryForRead() |
| // |
| -// Read(): |
| +// Read(): (When response is already written to the cache.) |
| // CacheReadData* |
| // |
| +// Read(): (When response is currently being written to the cache by shared |
| +// writing.) |
| +// SharedNetworkRead* -> SharedCacheWriteData* |
| +// |
| // 3. Cached entry, validation (304): |
| // Start(): |
| // GetBackend* -> InitEntry -> OpenEntry* -> AddToEntry* -> CacheReadResponse* |
| @@ -609,9 +714,13 @@ void HttpCache::Transaction::GetConnectionAttempts( |
| // UpdateCachedResponseComplete -> OverwriteCachedResponse -> |
| // PartialHeadersReceived |
| // |
| -// Read(): |
| +// Read(): () (When response is already written to the cache.) |
| // CacheReadData* |
| // |
| +// Read(): (When response is currently being written to the cache by shared |
| +// writing.) |
| +// SharedNetworkRead* -> SharedCacheWriteData* |
| +// |
| // 4. Cached entry, validation and replace (200): |
| // Start(): |
| // GetBackend* -> InitEntry -> OpenEntry* -> AddToEntry* -> CacheReadResponse* |
| @@ -706,6 +815,7 @@ void HttpCache::Transaction::GetConnectionAttempts( |
| // 14. Cached entry more than 5 minutes old, unused_since_prefetch is true: |
| // Like examples 2-4, only CacheToggleUnusedSincePrefetch* is inserted between |
| // CacheReadResponse* and CacheDispatchValidation. |
| +// |
| int HttpCache::Transaction::DoLoop(int result) { |
| DCHECK(next_state_ != STATE_NONE); |
| @@ -873,6 +983,25 @@ int HttpCache::Transaction::DoLoop(int result) { |
| case STATE_CACHE_WRITE_TRUNCATED_RESPONSE_COMPLETE: |
| rv = DoCacheWriteTruncatedResponseComplete(rv); |
| break; |
| + case STATE_SHARED_NETWORK_READ: |
| + DCHECK_EQ(OK, rv); |
| + rv = DoSharedNetworkRead(); |
| + break; |
| + case STATE_SHARED_NETWORK_READ_COMPLETE: |
| + rv = DoSharedNetworkReadComplete(rv); |
| + break; |
| + case STATE_SHARED_NETWORK_READ_WAIT_COMPLETE: |
| + rv = DoSharedNetworkReadWaitComplete(rv); |
| + break; |
| + case STATE_SHARED_CACHE_WRITE_DATA: |
| + rv = DoSharedCacheWriteData(rv); |
| + break; |
| + case STATE_SHARED_CACHE_WRITE_DATA_COMPLETE: |
| + rv = DoSharedCacheWriteDataComplete(rv); |
| + break; |
| + case STATE_SHARED_READ_WRITE_FAILED: |
| + rv = DoSharedReadWriteFailed(); |
| + break; |
| default: |
| NOTREACHED() << "bad state"; |
| rv = ERR_FAILED; |
| @@ -1510,6 +1639,26 @@ int HttpCache::Transaction::DoSuccessfulSendRequest() { |
| return OK; |
| } |
| + // If the transaction is shared and it is a 200 or an error response, then |
| + // doom the entry and let this transaction continue without writing to the |
| + // cache if shared writers contain more transactions. If not, continue |
| + // writing to the cache and also transfer the network transaction to shared |
| + // writers. |
| + if (shared_ && new_response->headers->response_code() != 304) { |
| + network_trans_ = cache_->ValidationNoMatchSharedWriters( |
| + cache_key_, this, std::move(network_trans_), priority_, entry_); |
| + if (!shared_) { |
| + DCHECK(network_trans_); |
| + if (cache_entry_status_ == CacheEntryStatus::ENTRY_UNDEFINED) { |
| + UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_DOOM_SHARED_WRITING); |
| + } |
| + DoneWritingToEntry(false, false); |
| + return OK; |
| + } else { |
| + DCHECK(!network_trans_); |
| + } |
| + } |
| + |
| // Are we expecting a response to a conditional query? |
| if (mode_ == READ_WRITE || mode_ == UPDATE) { |
| if (new_response->headers->response_code() == 304 || handling_206_) { |
| @@ -1632,7 +1781,8 @@ int HttpCache::Transaction::DoOverwriteCachedResponse() { |
| return OK; |
| } |
| - if (handling_206_ && !CanResume(false)) { |
| + if (handling_206_ && |
| + !cache_->CanResumeEntry(false, request_->method, &response_, entry_)) { |
| // 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); |
| @@ -1658,6 +1808,26 @@ int HttpCache::Transaction::DoCacheWriteResponseComplete(int result) { |
| return OnWriteResponseInfoToEntryComplete(result); |
| } |
| +void HttpCache::Transaction::ResetShared(bool continue_network_reading, |
| + bool continue_cache_reading) { |
| + if (!continue_network_reading) { |
| + SaveSharedNetworkTransactionInfo(); |
| + } |
| + if (continue_cache_reading) { |
| + mode_ = READ; |
| + } |
| + shared_ = false; |
| +} |
| + |
| +void HttpCache::Transaction::SetShared() { |
| + shared_ = true; |
| +} |
| + |
| +bool HttpCache::Transaction::validating_shared() const { |
| + return shared_ && next_state_ != STATE_NONE && |
| + next_state_ <= STATE_START_STATE_MACHINE_FINAL; |
| +} |
| + |
| int HttpCache::Transaction::DoTruncateCachedData() { |
| TRACE_EVENT0("io", "HttpCacheTransaction::DoTruncateCachedData"); |
| next_state_ = STATE_TRUNCATE_CACHED_DATA_COMPLETE; |
| @@ -1706,11 +1876,53 @@ int HttpCache::Transaction::DoTruncateCachedMetadataComplete(int result) { |
| return OK; |
| } |
| +void HttpCache::Transaction::ProcessForSharedWriting() { |
| + // Should not be already reading. |
| + if (reading_) |
| + return; |
| + |
| + // If not already part of SharedWriters, then check if one should be |
| + // created. |
| + if (!shared_ && !IsEligibleForSharedWriting()) |
| + return; |
| + |
| + if (shared_) { |
| + // Non 304 case is already handled in DoSuccessfulSendRequest. |
| + if (response_.headers->response_code() != 304) { |
| + return; |
| + } |
| + UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_JOIN_SHARED_WRITING); |
| + entry_->shared_writers->OnValidationMatch(this, priority_); |
| + network_trans_.reset(); |
| + return; |
| + } |
| + |
| + // Do not create a SharedWriters if its a Redirect response. |
| + if (response_.headers->response_code() != 200) |
| + return; |
| + |
| + // or if its a no-store response. |
| + if (!entry_) |
| + return; |
| + |
| + DCHECK(cache_entry_status_ == CacheEntryStatus::ENTRY_NOT_IN_CACHE || |
| + cache_entry_status_ == CacheEntryStatus::ENTRY_UPDATED || |
| + cache_entry_status_ == CacheEntryStatus::ENTRY_CANT_CONDITIONALIZE || |
| + cache_entry_status_ == CacheEntryStatus::ENTRY_OTHER); |
| + // Measure how many requests out of the total of "not cached" and "updated" |
| + // are initiating Shared Writing. |
| + initiate_shared_writing_ = true; |
| + DCHECK(entry_ && network_trans_); |
| + cache_->CreateSharedWriters(this, std::move(network_trans_), priority_); |
| +} |
| + |
| int HttpCache::Transaction::DoPartialHeadersReceived() { |
| new_response_ = NULL; |
| if (entry_ && !partial_ && entry_->disk_entry->GetDataSize(kMetadataIndex)) |
| next_state_ = STATE_CACHE_READ_METADATA; |
| + ProcessForSharedWriting(); |
| + |
| if (!partial_) |
| return OK; |
| @@ -1756,11 +1968,21 @@ int HttpCache::Transaction::DoCacheReadMetadataComplete(int result) { |
| int HttpCache::Transaction::DoNetworkRead() { |
| TRACE_EVENT0("io", "HttpCacheTransaction::DoNetworkRead"); |
| next_state_ = STATE_NETWORK_READ_COMPLETE; |
| - return network_trans_->Read(read_buf_.get(), io_buf_len_, io_callback_); |
| + if (!data_access_) { |
| + DCHECK(network_trans_); |
| + // Create a DataAccess object if not already done. |
| + data_access_ = |
| + base::MakeUnique<DataAccess>(std::move(network_trans_), entry_); |
| + } |
| + return data_access_->Read(read_buf_, io_buf_len_, io_callback_); |
| } |
| int HttpCache::Transaction::DoNetworkReadComplete(int result) { |
| TRACE_EVENT0("io", "HttpCacheTransaction::DoNetworkReadComplete"); |
| + if (net_log_.IsCapturing()) |
| + net_log_.EndEventWithNetErrorCode( |
| + NetLogEventType::HTTP_CACHE_NETWORK_READ_COMPLETE, result); |
| + |
| DCHECK(mode_ & WRITE || mode_ == NONE); |
| if (!cache_.get()) |
| @@ -1775,6 +1997,94 @@ int HttpCache::Transaction::DoNetworkReadComplete(int result) { |
| return result; |
| } |
| +int HttpCache::Transaction::DoSharedNetworkRead() { |
| + TRACE_EVENT0("io", "HttpCacheTransaction::DoSharedNetworkRead"); |
| + if (net_log_.IsCapturing()) |
| + net_log_.BeginEvent(NetLogEventType::HTTP_CACHE_SHARED_NETWORK_READ); |
| + |
| + next_state_ = STATE_SHARED_NETWORK_READ_COMPLETE; |
| + bool shared_read_in_progress = false; |
| + int result = entry_->shared_writers->Read( |
| + read_buf_, io_buf_len_, io_callback_, this, &shared_read_in_progress); |
| + if (shared_read_in_progress) { |
| + next_state_ = STATE_SHARED_NETWORK_READ_WAIT_COMPLETE; |
| + } |
| + return result; |
| +} |
| + |
| +int HttpCache::Transaction::DoSharedNetworkReadComplete(int result) { |
| + TRACE_EVENT0("io", "HttpCacheTransaction::DoSharedNetworkReadComplete"); |
| + if (net_log_.IsCapturing()) |
| + net_log_.EndEventWithNetErrorCode( |
| + NetLogEventType::HTTP_CACHE_SHARED_NETWORK_READ_COMPLETE, result); |
| + |
| + if (result <= 0) { |
| + // Set entry as null so that the destructor does not invoke DoneWithEntry |
| + // again as entry_ is already cleaned up by SharedWriters. |
| + entry_ = nullptr; |
| + return result; |
| + } |
| + |
| + read_offset_ += result; |
| + |
| + next_state_ = STATE_SHARED_CACHE_WRITE_DATA; |
| + return result; |
| +} |
| + |
| +int HttpCache::Transaction::DoSharedCacheWriteData(int result) { |
| + TRACE_EVENT0("io", "HttpCacheTransaction::DoSharedCacheWriteData"); |
| + if (net_log_.IsCapturing()) |
| + net_log_.BeginEvent(NetLogEventType::HTTP_CACHE_SHARED_CACHE_WRITE_DATA); |
| + |
| + next_state_ = STATE_SHARED_CACHE_WRITE_DATA_COMPLETE; |
| + write_len_ = result; |
| + return entry_->shared_writers->CacheWrite(read_buf_, write_len_, io_callback_, |
| + this); |
| +} |
| + |
| +int HttpCache::Transaction::DoSharedCacheWriteDataComplete(int result) { |
| + TRACE_EVENT0("io", "HttpCacheTransaction::DoSharedCacheWriteDataComplete"); |
| + if (net_log_.IsCapturing()) |
| + net_log_.EndEventWithNetErrorCode( |
| + NetLogEventType::HTTP_CACHE_SHARED_CACHE_WRITE_DATA_COMPLETE, result); |
| + |
| + if (result != write_len_) { |
| + DoneWritingToEntry(false, false); |
| + // Consumer need not know that it was a failure since this transaction can |
| + // continue reading from the network. |
| + result = write_len_; |
| + } else { |
| + done_reading_ = cache_->IsResponseCompleted(entry_, &response_); |
| + if (result == 0) { |
| + DoneWritingToEntry(true, false); |
| + } |
| + } |
| + return result; |
| +} |
| + |
| +int HttpCache::Transaction::DoSharedNetworkReadWaitComplete(int result) { |
| + TRACE_EVENT0("io", "HttpCacheTransaction::DoSharedNetworkReadWaitComplete"); |
| + if (net_log_.IsCapturing()) |
| + net_log_.EndEventWithNetErrorCode( |
| + NetLogEventType::HTTP_CACHE_SHARED_NETWORK_READ_WAIT_COMPLETE, result); |
| + |
| + // If its a network read failure or cache write failure, we just return the |
| + // result. |
| + // If its a cache write success, read_buf_ would have been filled |
| + // with the read data by SharedWriters. |
| + if (result == 0) { // response is complete. |
| + DoneWritingToEntry(true, false); // changes the mode_ to NONE. |
| + } else if (result > 0) { |
| + read_offset_ += result; |
| + } else { |
| + // Set entry as null so that the destructor does not invoke DoneWithEntry |
| + // again as entry_ is already cleaned up by SharedWriters. |
| + entry_ = nullptr; |
| + } |
| + |
| + return result; |
| +} |
| + |
| int HttpCache::Transaction::DoCacheReadData() { |
| TRACE_EVENT0("io", "HttpCacheTransaction::DoCacheReadData"); |
| if (request_->method == "HEAD") |
| @@ -1836,9 +2146,12 @@ int HttpCache::Transaction::DoCacheWriteData(int num_bytes) { |
| if (!entry_ || !num_bytes) |
| return num_bytes; |
| - int current_size = entry_->disk_entry->GetDataSize(kResponseContentIndex); |
| - return WriteToEntry(kResponseContentIndex, current_size, read_buf_.get(), |
| - num_bytes, io_callback_); |
| + if (partial_) { |
| + return partial_->CacheWrite(entry_->disk_entry, read_buf_.get(), num_bytes, |
| + io_callback_); |
| + } |
| + DCHECK(data_access_); |
| + return data_access_->CacheWrite(read_buf_, num_bytes, io_callback_); |
| } |
| int HttpCache::Transaction::DoCacheWriteDataComplete(int result) { |
| @@ -1860,10 +2173,7 @@ int HttpCache::Transaction::DoCacheWriteDataComplete(int result) { |
| // the network. |
| result = write_len_; |
| } else if (!done_reading_ && entry_ && (!partial_ || truncated_)) { |
| - int current_size = entry_->disk_entry->GetDataSize(kResponseContentIndex); |
| - int64_t body_size = response_.headers->GetContentLength(); |
| - if (body_size >= 0 && body_size <= current_size) |
| - done_reading_ = true; |
| + done_reading_ = cache_->IsResponseCompleted(entry_, &response_); |
| } |
| if (partial_) { |
| @@ -1874,13 +2184,12 @@ int HttpCache::Transaction::DoCacheWriteDataComplete(int result) { |
| } |
| } |
| + // 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 (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) { |
| + response_.headers->GetContentLength() <= 0) |
| DoneWritingToEntry(true); |
| - } |
| } |
| return result; |
| @@ -1898,6 +2207,15 @@ int HttpCache::Transaction::DoCacheWriteTruncatedResponseComplete(int result) { |
| return OnWriteResponseInfoToEntryComplete(result); |
| } |
| +int HttpCache::Transaction::DoSharedReadWriteFailed() { |
| + if (net_log_.IsCapturing()) |
| + net_log_.EndEventWithNetErrorCode( |
| + NetLogEventType::HTTP_CACHE_SHARED_READ_WRITE_FAILED, |
| + shared_read_write_failure_result_); |
| + |
| + return shared_read_write_failure_result_; |
| +} |
| + |
| //----------------------------------------------------------------------------- |
| void HttpCache::Transaction::SetRequest(const NetLogWithSource& net_log, |
| @@ -2091,8 +2409,13 @@ int HttpCache::Transaction::BeginCacheValidation() { |
| } |
| if (skip_validation) { |
| - UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_USED); |
| - return SetupEntryForRead(); |
| + if (shared_) { |
| + UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_JOIN_SHARED_WRITING); |
| + entry_->shared_writers->OnValidationMatch(this, priority_); |
| + } else { |
| + UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_USED); |
| + return SetupEntryForRead(); |
| + } |
| } else { |
| // Make the network request conditional, to see if we may reuse our cached |
| // response. If we cannot do so, then we just resort to a normal fetch. |
| @@ -2611,15 +2934,8 @@ int HttpCache::Transaction::WriteResponseInfoToEntry(bool truncated) { |
| if (truncated) |
| DCHECK_EQ(200, response_.headers->response_code()); |
| - // When writing headers, we normally only write the non-transient headers. |
| - bool skip_transient_headers = true; |
| - scoped_refptr<PickledIOBuffer> data(new PickledIOBuffer()); |
| - response_.Persist(data->pickle(), skip_transient_headers, truncated); |
| - data->Done(); |
| - |
| - io_buf_len_ = data->pickle()->size(); |
| - return entry_->disk_entry->WriteData(kResponseInfoIndex, 0, data.get(), |
| - io_buf_len_, io_callback_, true); |
| + return cache_->WriteResponseInfo(entry_, &response_, io_callback_, truncated, |
| + &io_buf_len_); |
| } |
| int HttpCache::Transaction::OnWriteResponseInfoToEntryComplete(int result) { |
| @@ -2637,17 +2953,30 @@ int HttpCache::Transaction::OnWriteResponseInfoToEntryComplete(int result) { |
| return OK; |
| } |
| -void HttpCache::Transaction::DoneWritingToEntry(bool success) { |
| +void HttpCache::Transaction::DoneWritingToEntry(bool success, |
| + bool perform_entry_cleanup) { |
| if (!entry_) |
| return; |
| RecordHistograms(); |
| - cache_->DoneWritingToEntry(entry_, success); |
| - entry_ = NULL; |
| + if (perform_entry_cleanup) { |
| + cache_->DoneWritingToEntry(entry_, success); |
| + } |
| + entry_ = nullptr; |
| mode_ = NONE; // switch to 'pass through' mode |
| } |
| +void HttpCache::Transaction::ContinueWithoutSharedWriting( |
| + std::unique_ptr<DataAccess> data_access, |
| + bool needs_entry) { |
| + shared_ = false; |
| + if (!needs_entry) |
| + entry_ = nullptr; |
| + mode_ = NONE; |
| + data_access_ = std::move(data_access); |
| +} |
| + |
| int HttpCache::Transaction::OnCacheReadError(int result, bool restart) { |
| DLOG(ERROR) << "ReadData failed: " << result; |
| const int result_for_histogram = std::max(0, -result); |
| @@ -2666,7 +2995,11 @@ int HttpCache::Transaction::OnCacheReadError(int result, bool restart) { |
| if (restart) { |
| DCHECK(!reading_); |
| DCHECK(!network_trans_.get()); |
| - cache_->DoneWithEntry(entry_, this, false); |
| + if (shared_) { |
| + cache_->RemoveValidatingTransSharedWriters(this, entry_); |
| + } else { |
| + cache_->DoneWithEntry(entry_, this, false); |
| + } |
| entry_ = NULL; |
| is_sparse_ = false; |
| partial_.reset(); |
| @@ -2691,6 +3024,8 @@ void HttpCache::Transaction::OnAddToEntryTimeout(base::TimeTicks start_time) { |
| } |
| void HttpCache::Transaction::DoomPartialEntry(bool delete_object) { |
| + // Partial requests not supported with shared writing as of now. |
| + DCHECK(!shared_); |
| DVLOG(2) << "DoomPartialEntry"; |
| int rv = cache_->DoomEntry(cache_key_, NULL); |
| DCHECK_EQ(OK, rv); |
| @@ -2707,7 +3042,7 @@ int HttpCache::Transaction::DoPartialNetworkReadCompleted(int result) { |
| if (result == 0) { |
| // We need to move on to the next range. |
| - ResetNetworkTransaction(); |
| + ResetDataAccess(); |
| next_state_ = STATE_START_PARTIAL_CACHE_VALIDATION; |
| } |
| return result; |
| @@ -2751,53 +3086,63 @@ void HttpCache::Transaction::ResetPartialState(bool delete_object) { |
| } |
| } |
| -void HttpCache::Transaction::ResetNetworkTransaction() { |
| +void HttpCache::Transaction::SaveNetworkTransactionInfo( |
| + const HttpTransaction* transaction) { |
| DCHECK(!old_network_trans_load_timing_); |
| - DCHECK(network_trans_); |
| + DCHECK(transaction); |
| LoadTimingInfo load_timing; |
| - if (network_trans_->GetLoadTimingInfo(&load_timing)) |
| + if (transaction->GetLoadTimingInfo(&load_timing)) |
| old_network_trans_load_timing_.reset(new LoadTimingInfo(load_timing)); |
| - total_received_bytes_ += network_trans_->GetTotalReceivedBytes(); |
| - total_sent_bytes_ += network_trans_->GetTotalSentBytes(); |
| + total_received_bytes_ += transaction->GetTotalReceivedBytes(); |
| + total_sent_bytes_ += transaction->GetTotalSentBytes(); |
| ConnectionAttempts attempts; |
| - network_trans_->GetConnectionAttempts(&attempts); |
| + transaction->GetConnectionAttempts(&attempts); |
| for (const auto& attempt : attempts) |
| old_connection_attempts_.push_back(attempt); |
| old_remote_endpoint_ = IPEndPoint(); |
| - network_trans_->GetRemoteEndpoint(&old_remote_endpoint_); |
| - network_trans_.reset(); |
| + transaction->GetRemoteEndpoint(&old_remote_endpoint_); |
| } |
| -// Histogram data from the end of 2010 show the following distribution of |
| -// response headers: |
| -// |
| -// Content-Length............... 87% |
| -// Date......................... 98% |
| -// Last-Modified................ 49% |
| -// Etag......................... 19% |
| -// Accept-Ranges: bytes......... 25% |
| -// Accept-Ranges: none.......... 0.4% |
| -// Strong Validator............. 50% |
| -// Strong Validator + ranges.... 24% |
| -// Strong Validator + CL........ 49% |
| -// |
| -bool HttpCache::Transaction::CanResume(bool has_data) { |
| - // Double check that there is something worth keeping. |
| - if (has_data && !entry_->disk_entry->GetDataSize(kResponseContentIndex)) |
| - return false; |
| +void HttpCache::Transaction::SaveSharedNetworkTransactionInfo() { |
| + // If network_trans_ is still present, this transaction has not started using |
| + // the "shared" network transaction. |
| + if (network_trans_) { |
| + SaveNetworkTransactionInfo(network_trans_.get()); |
| + return; |
| + } |
| - if (request_->method != "GET") |
| - return false; |
| + DCHECK(!old_network_trans_load_timing_); |
| - // Note that if this is a 206, content-length was already fixed after calling |
| - // PartialData::ResponseHeadersOK(). |
| - if (response_.headers->GetContentLength() <= 0 || |
| - response_.headers->HasHeaderValue("Accept-Ranges", "none") || |
| - !response_.headers->HasStrongValidators()) { |
| - return false; |
| - } |
| + // If the transaction is being deleted while still in the waiting queue, |
| + // entry_ is not yet set, do nothing. |
| + if (!entry_) |
| + return; |
| + DCHECK(entry_->shared_writers); |
| + HttpTransaction* network_transaction = |
| + entry_->shared_writers->data_access_->network_transaction_.get(); |
| + SaveNetworkTransactionInfo(network_transaction); |
| + have_full_request_headers_ = |
| + network_transaction->GetFullRequestHeaders(&full_request_headers_); |
| +} |
| - return true; |
| +void HttpCache::Transaction::ResetNetworkTransaction() { |
| + SaveNetworkTransactionInfo(network_trans_.get()); |
| + network_trans_.reset(); |
| +} |
| + |
| +void HttpCache::Transaction::ResetDataAccess() { |
| + SaveNetworkTransactionInfo(data_access_->network_transaction_.get()); |
| + data_access_.reset(); |
| +} |
| + |
| +HttpTransaction* HttpCache::Transaction::GetCurrentNetworkTransaction() const { |
| + if (network_trans_) |
| + return network_trans_.get(); |
| + if (data_access_) |
| + return data_access_->network_transaction_.get(); |
| + if (shared_ && entry_ && entry_->shared_writers) |
| + return entry_->shared_writers->data_access_->network_transaction_.get(); |
| + return nullptr; |
| } |
| void HttpCache::Transaction::SetResponse(const HttpResponseInfo& response) { |
| @@ -2923,8 +3268,10 @@ void HttpCache::Transaction::RecordHistograms() { |
| validation_cause_, VALIDATION_CAUSE_MAX); |
| } |
| + // This may also exclude shared writing counts for these cases. |
| if (cache_entry_status_ == CacheEntryStatus::ENTRY_OTHER) |
| return; |
| + |
| DCHECK(!range_requested_); |
| DCHECK(!first_cache_access_since_.is_null()); |
| @@ -2932,19 +3279,33 @@ void HttpCache::Transaction::RecordHistograms() { |
| UMA_HISTOGRAM_TIMES("HttpCache.AccessToDone", total_time); |
| + if (cache_entry_status_ == CacheEntryStatus::ENTRY_JOIN_SHARED_WRITING) { |
| + UMA_HISTOGRAM_TIMES("HttpCache.AccessToDone.JoinSharedWriting", total_time); |
| + } else if (cache_entry_status_ == |
| + CacheEntryStatus::ENTRY_DOOM_SHARED_WRITING) { |
| + UMA_HISTOGRAM_TIMES("HttpCache.AccessToDone.DoomSharedWriting", total_time); |
| + } else if (initiate_shared_writing_) { |
| + UMA_HISTOGRAM_TIMES("HttpCache.AccessToDone.InitiateSharedWriting", |
| + total_time); |
| + } |
| + |
| bool did_send_request = !send_request_since_.is_null(); |
| DCHECK( |
| (did_send_request && |
| (cache_entry_status_ == CacheEntryStatus::ENTRY_NOT_IN_CACHE || |
| cache_entry_status_ == CacheEntryStatus::ENTRY_VALIDATED || |
| cache_entry_status_ == CacheEntryStatus::ENTRY_UPDATED || |
| - cache_entry_status_ == CacheEntryStatus::ENTRY_CANT_CONDITIONALIZE)) || |
| + cache_entry_status_ == CacheEntryStatus::ENTRY_CANT_CONDITIONALIZE || |
| + cache_entry_status_ == CacheEntryStatus::ENTRY_JOIN_SHARED_WRITING || |
| + cache_entry_status_ == CacheEntryStatus::ENTRY_DOOM_SHARED_WRITING)) || |
| (!did_send_request && |
| - cache_entry_status_ == CacheEntryStatus::ENTRY_USED)); |
| + (cache_entry_status_ == CacheEntryStatus::ENTRY_USED || |
| + cache_entry_status_ == CacheEntryStatus::ENTRY_JOIN_SHARED_WRITING))); |
| if (!did_send_request) { |
| - DCHECK(cache_entry_status_ == CacheEntryStatus::ENTRY_USED); |
| - UMA_HISTOGRAM_TIMES("HttpCache.AccessToDone.Used", total_time); |
| + if (cache_entry_status_ == CacheEntryStatus::ENTRY_USED) { |
| + UMA_HISTOGRAM_TIMES("HttpCache.AccessToDone.Used", total_time); |
| + } |
| return; |
| } |
| @@ -2969,12 +3330,31 @@ void HttpCache::Transaction::RecordHistograms() { |
| before_send_time); |
| UMA_HISTOGRAM_PERCENTAGE("HttpCache.PercentBeforeSend.CantConditionalize", |
| before_send_sample); |
| + if (initiate_shared_writing_) { |
| + UMA_HISTOGRAM_TIMES( |
| + "HttpCache.BeforeSend.CantConditionalize.InitiateSharedWriting", |
| + before_send_time); |
| + UMA_HISTOGRAM_PERCENTAGE( |
| + "HttpCache.PercentBeforeSend.CantConditionalize." |
| + "InitiateSharedWriting", |
| + before_send_sample); |
| + } |
| + |
| break; |
| } |
| case CacheEntryStatus::ENTRY_NOT_IN_CACHE: { |
| UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend.NotCached", before_send_time); |
| UMA_HISTOGRAM_PERCENTAGE("HttpCache.PercentBeforeSend.NotCached", |
| before_send_sample); |
| + if (initiate_shared_writing_) { |
| + UMA_HISTOGRAM_TIMES( |
| + "HttpCache.BeforeSend.NotCached.InitiateSharedWriting", |
| + before_send_time); |
| + UMA_HISTOGRAM_PERCENTAGE( |
| + "HttpCache.PercentBeforeSend.NotCached.InitiateSharedWriting", |
| + before_send_sample); |
| + } |
| + |
| break; |
| } |
| case CacheEntryStatus::ENTRY_VALIDATED: { |
| @@ -2987,6 +3367,28 @@ void HttpCache::Transaction::RecordHistograms() { |
| UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend.Updated", before_send_time); |
| UMA_HISTOGRAM_PERCENTAGE("HttpCache.PercentBeforeSend.Updated", |
| before_send_sample); |
| + if (initiate_shared_writing_) { |
| + UMA_HISTOGRAM_TIMES( |
| + "HttpCache.BeforeSend.Updated.InitiateSharedWriting", |
| + before_send_time); |
| + UMA_HISTOGRAM_PERCENTAGE( |
| + "HttpCache.PercentBeforeSend.Updated.InitiateSharedWriting", |
| + before_send_sample); |
| + } |
| + break; |
| + } |
| + case CacheEntryStatus::ENTRY_JOIN_SHARED_WRITING: { |
| + UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend.JoinSharedWriting", |
| + before_send_time); |
| + UMA_HISTOGRAM_PERCENTAGE("HttpCache.PercentBeforeSend.JoinSharedWriting", |
| + before_send_sample); |
| + break; |
| + } |
| + case CacheEntryStatus::ENTRY_DOOM_SHARED_WRITING: { |
| + UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend.DoomSharedWriting", |
| + before_send_time); |
| + UMA_HISTOGRAM_PERCENTAGE("HttpCache.PercentBeforeSend.DoomSharedWriting", |
| + before_send_sample); |
| break; |
| } |
| default: |
| @@ -2994,6 +3396,26 @@ void HttpCache::Transaction::RecordHistograms() { |
| } |
| } |
| +bool HttpCache::Transaction::IsEligibleForSharedWriting() const { |
| + if (!(mode_ & WRITE)) |
| + return false; |
| + |
| + DCHECK(request_); |
| + if (request_->method != "GET") |
| + return false; |
| + |
| + if (partial_ || range_requested_) |
| + return false; |
| + |
| + return true; |
| +} |
| + |
| +void HttpCache::Transaction::SetSharedWritingFailState(int result) { |
| + next_state_ = STATE_SHARED_READ_WRITE_FAILED; |
| + shared_read_write_failure_result_ = result; |
| + entry_ = nullptr; |
| +} |
| + |
| void HttpCache::Transaction::OnIOComplete(int result) { |
| DoLoop(result); |
| } |