blob: 2b45c1e0b85654157432afe0395137deb14d547b [file] [log] [blame]
// Copyright (c) 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <list>
#include <memory>
#include "base/memory/weak_ptr.h"
#include "net/base/completion_callback.h"
#include "net/http/http_cache.h"
namespace net {
class HttpResponseInfo;
class PartialData;
// If multiple HttpCache::Transactions are accessing the same cache entry
// simultaneously, their access to the data read from network is synchronized
// by HttpCache::Writers. This enables each of those transactions to drive
// reading the response body from the network ensuring a slow consumer does not
// starve other consumers of the same resource.
// Writers represents the set of all HttpCache::Transactions that are
// reading from the network using the same network transaction and writing to
// the same cache entry. It is owned by the ActiveEntry.
class NET_EXPORT_PRIVATE HttpCache::Writers {
// This is the information maintained by Writers in the context of each
// transaction.
// |partial| is owned by the transaction and to be sure there are no
// dangling pointers, it must be ensured that transaction's reference and
// this information will be removed from writers once the transaction is
// deleted.
struct NET_EXPORT_PRIVATE TransactionInfo {
TransactionInfo(PartialData* partial,
bool truncated,
HttpResponseInfo info);
TransactionInfo& operator=(const TransactionInfo&);
TransactionInfo(const TransactionInfo&);
PartialData* partial;
bool truncated;
HttpResponseInfo response_info;
// |cache| and |entry| must outlive this object.
Writers(HttpCache* cache, HttpCache::ActiveEntry* entry);
// Retrieves data from the network transaction associated with the Writers
// object. This may be done directly (via a network read into |*buf->data()|)
// or indirectly (by copying from another transactions buffer into
// |*buf->data()| on network read completion) depending on whether or not a
// read is currently in progress. May return the result synchronously or
// return ERR_IO_PENDING: if ERR_IO_PENDING is returned, |callback| will be
// run to inform the consumer of the result of the Read().
// |transaction| may be removed while Read() is ongoing. In that case Writers
// will still complete the Read() processing but will not invoke the
// |callback|.
int Read(scoped_refptr<IOBuffer> buf,
int buf_len,
const CompletionCallback& callback,
Transaction* transaction);
// Invoked when StopCaching is called on a member transaction.
// It stops caching only if there are no other transactions. Returns true if
// caching can be stopped.
// |keep_entry| should be true if the entry needs to be preserved after
// truncation.
bool StopCaching(bool keep_entry);
// Membership functions like AddTransaction and RemoveTransaction are invoked
// by HttpCache on behalf of the HttpCache::Transaction.
// Adds an HttpCache::Transaction to Writers.
// Should only be invoked if CanAddWriters() returns true.
// |parallel_writing_pattern| governs whether writing is an exclusive
// operation implying that Writers can contain at most one transaction till
// the completion of the response body. It is illegal to invoke with
// |parallel_writing_pattern| as PARALLEL_WRITING_NOT_JOIN* if there is
// already a transaction present.
// |transaction| can be destroyed at any point and it should invoke
// HttpCache::DoneWithEntry() during its destruction. This will also ensure
// any pointers in |info| are not accessed after the transaction is destroyed.
void AddTransaction(Transaction* transaction,
ParallelWritingPattern initial_writing_pattern,
RequestPriority priority,
const TransactionInfo& info);
// Invoked when the transaction is done working with the entry.
void RemoveTransaction(Transaction* transaction, bool success);
// Invoked when there is a change in a member transaction's priority or a
// member transaction is removed.
void UpdatePriority();
// Returns true if this object is empty.
bool IsEmpty() const { return all_writers_.empty(); }
// Invoked during HttpCache's destruction.
void Clear() { all_writers_.clear(); }
// Returns true if |transaction| is part of writers.
bool HasTransaction(const Transaction* transaction) const {
return all_writers_.count(const_cast<Transaction*>(transaction)) > 0;
// Returns true if more writers can be added for shared writing. Also fills in
// the |reason| for why a transaction cannot be added.
bool CanAddWriters(ParallelWritingPattern* reason);
// Returns if only one transaction can be a member of writers.
bool IsExclusive() const { return is_exclusive_; }
// Returns the network transaction which may be nullptr for range requests.
const HttpTransaction* network_transaction() const {
return network_transaction_.get();
// Returns the load state of the |network_transaction_| if present else
// returns LOAD_STATE_IDLE.
LoadState GetLoadState() const;
// Sets the network transaction argument to |network_transaction_|. Must be
// invoked before Read can be invoked.
void SetNetworkTransaction(
Transaction* transaction,
std::unique_ptr<HttpTransaction> network_transaction);
// Resets the network transaction to nullptr. Required for range requests as
// they might use the current network transaction only for part of the
// request. Must only be invoked for range requests.
void ResetNetworkTransaction();
// Returns if response is only being read from the network.
bool network_read_only() const { return network_read_only_; }
int GetTransactionsCount() const { return all_writers_.size(); }
friend class WritersTest;
enum class State {
// This state is used when an attempt to truncate races with an ongoing
// network read/cache write. In this case we transition from
// ASYNC_OP_COMPLETE_PRE_TRUNCATE. We will then process the truncation
// after the ongoing operation completes.
// These transactions are waiting on Read. After the active transaction
// completes writing the data to the cache, their buffer would be filled with
// the data and their callback will be invoked.
struct WaitingForRead {
scoped_refptr<IOBuffer> read_buf;
int read_buf_len;
int write_len;
const CompletionCallback callback;
WaitingForRead(scoped_refptr<IOBuffer> read_buf,
int len,
const CompletionCallback& consumer_callback);
WaitingForRead(const WaitingForRead&);
using WaitingForReadMap = std::unordered_map<Transaction*, WaitingForRead>;
using TransactionMap = std::unordered_map<Transaction*, TransactionInfo>;
// Runs the state transition loop. Resets and calls |callback_| on exit,
// unless the return value is ERR_IO_PENDING.
int DoLoop(int result);
// State machine functions.
int DoNetworkRead();
int DoNetworkReadComplete(int result);
int DoCacheWriteData(int num_bytes);
int DoCacheWriteDataComplete(int result);
int DoAsyncOpCompletePreTruncate(int result);
int DoCacheWriteTruncatedResponse();
int DoCacheWriteTruncatedResponseComplete(int result);
// Helper functions for callback.
void OnNetworkReadFailure(int result);
void OnCacheWriteFailure();
void OnDataReceived(int result);
// Notifies the transactions waiting on Read of the result, by posting a task
// for each of them.
void ProcessWaitingForReadTransactions(int result);
// Sets the state to FAIL_READ so that any subsequent Read on an idle
// transaction fails.
void SetIdleWritersFailState(int result);
// Invoked when |active_transaction_| fails to read from network or write to
// cache. |error| indicates network read error code or cache write error.
void ProcessFailure(int error);
// Returns true if |this| only contains idle writers. Idle writers are those
// that are waiting for Read to be invoked by the consumer.
bool ContainsOnlyIdleWriters() const;
// Returns true if its worth marking the entry as truncated.
// TODO(shivanisha): Refactor this so that it could be const.
bool ShouldTruncate();
// Invoked for truncating the entry either internally within DoLoop or through
// the API RemoveTransaction.
// Sets |should_keep_entry_| as true if either the entry is marked as
// truncated or the entry is already completely written and false if the entry
// cannot be resumed later so no need to mark truncated.
// Returns true if the function initiated truncation of the entry by changing
// the next state to transition to CACHE_WRITE_TRUNCATED_RESPONSE or
// If a network read/cache write operation is still going on, this will not
// initiate truncation but it will be done after the ongoing operation is
// complete.
// Note that if this function returns a true, its possible that |this| may
// have been deleted and thus no members should be touched after calling this
// function.
bool InitiateTruncateEntry();
// Remove the transaction.
void EraseTransaction(Transaction* transaction, int result);
TransactionMap::iterator EraseTransaction(TransactionMap::iterator it,
int result);
void SetCacheCallback(bool success, const TransactionSet& make_readers);
// IO Completion callback function.
void OnIOComplete(int result);
State next_state_ = State::NONE;
// True if only reading from network and not writing to cache.
bool network_read_only_ = false;
HttpCache* cache_ = nullptr;
// Owner of |this|.
ActiveEntry* entry_ = nullptr;
std::unique_ptr<HttpTransaction> network_transaction_ = nullptr;
scoped_refptr<IOBuffer> read_buf_ = nullptr;
int io_buf_len_ = 0;
int write_len_ = 0;
// The cache transaction that is the current consumer of network_transaction_
// ::Read or writing to the entry and is waiting for the operation to be
// completed. This is used to ensure there is at most one consumer of
// network_transaction_ at a time.
Transaction* active_transaction_ = nullptr;
// Transactions whose consumers have invoked Read, but another transaction is
// currently the |active_transaction_|. After the network read and cache write
// is complete, the waiting transactions will be notified.
WaitingForReadMap waiting_for_read_;
// Includes all transactions. ResetStateForEmptyWriters should be invoked
// whenever all_writers_ becomes empty.
TransactionMap all_writers_;
// True if multiple transactions are not allowed e.g. for partial requests.
bool is_exclusive_ = false;
ParallelWritingPattern parallel_writing_pattern_ = PARALLEL_WRITING_NONE;
// Current priority of the request. It is always the maximum of all the writer
// transactions.
RequestPriority priority_ = MINIMUM_PRIORITY;
// Response info of the most recent transaction added to Writers will be used
// to write back the headers along with the truncated bit set. This is done so
// that we don't overwrite headers written by a more recent transaction with
// older headers while truncating.
HttpResponseInfo response_info_truncation_;
// Do not mark a partial request as truncated if it is not already a truncated
// entry to start with.
bool partial_do_not_truncate_ = false;
// True if the entry should be kept, even if the response was not completely
// written.
bool should_keep_entry_ = true;
// If truncation happens after a current network read/cache write then the
// result returned to the consumer should be the one returned by the network
// read/cache write operation.
int post_truncate_result_ = OK;
// True if currently processing the DoLoop.
bool in_do_loop_ = false;
CompletionCallback callback_; // Callback for active_transaction_.
// Since cache_ can destroy |this|, |cache_callback_| is only invoked at the
// end of DoLoop().
base::OnceClosure cache_callback_; // Callback for cache_.
base::WeakPtrFactory<Writers> weak_factory_;
} // namespace net