blob: e914ab1026e78f1102ead1e21192e3c232ee72bf [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 <map>
#include <memory>
#include "base/memory/weak_ptr.h"
#include "net/base/completion_once_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. The writers object must be
// deleted when HttpCache::WritersDoneWritingToEntry is called as it doesn't
// expect any of its ongoing IO transactions (e.g., network reads or cache
// writers) to complete after that point and won't know what to do with them.
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,
CompletionOnceCallback 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 {
// 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;
CompletionOnceCallback callback;
WaitingForRead(scoped_refptr<IOBuffer> read_buf,
int len,
CompletionOnceCallback consumer_callback);
using WaitingForReadMap = std::map<Transaction*, WaitingForRead>;
using TransactionMap = std::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);
// Helper functions for callback.
void OnNetworkReadFailure(int result);
void OnCacheWriteFailure();
void OnDataReceived(int result);
// Completes any pending IO_PENDING read operations by copying any received
// bytes from read_buf_ to the given buffer and posts a task to run the
// callback with |result|.
void CompleteWaitingForReadTransactions(int result);
// Removes idle writers, passing |result| which is to be used for any
// subsequent read transaction.
void RemoveIdleWriters(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();
// Enqueues a truncation operation to the entry. Ignores the response.
void TruncateEntry();
// 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;
CompletionOnceCallback 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