// Copyright 2017 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef NET_HTTP_HTTP_CACHE_WRITERS_H_ #define NET_HTTP_HTTP_CACHE_WRITERS_H_ #include #include #include "base/memory/raw_ptr.h" #include "base/memory/weak_ptr.h" #include "net/base/completion_once_callback.h" #include "net/http/http_cache.h" #include "net/http/http_response_info.h" namespace crypto { class SecureHash; } namespace net { class HttpResponseInfo; class IOBuffer; 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 { public: // 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(); TransactionInfo& operator=(const TransactionInfo&); TransactionInfo(const TransactionInfo&); raw_ptr partial; bool truncated; HttpResponseInfo response_info; }; // |cache| and |entry| must outlive this object. Writers(HttpCache* cache, HttpCache::ActiveEntry* entry); Writers(const Writers&) = delete; Writers& operator=(const Writers&) = delete; ~Writers(); // 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 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(); } // Returns true if |transaction| is part of writers. bool HasTransaction(const Transaction* transaction) const { return all_writers_.count(const_cast(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(); } void CloseConnectionOnDestruction(); // 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. If |checksum| is set it will be // validated and the cache entry will be marked unusable if it doesn't match. void SetNetworkTransaction( Transaction* transaction, std::unique_ptr network_transaction, std::unique_ptr checksum); // 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(); } private: friend class WritersTest; enum class State { UNSET, NONE, NETWORK_READ, NETWORK_READ_COMPLETE, CACHE_WRITE_DATA, CACHE_WRITE_DATA_COMPLETE, MARK_SINGLE_KEYED_CACHE_ENTRY_UNUSABLE, MARK_SINGLE_KEYED_CACHE_ENTRY_UNUSABLE_COMPLETE, }; // 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 read_buf; int read_buf_len; int write_len = 0; CompletionOnceCallback callback; WaitingForRead(scoped_refptr read_buf, int len, CompletionOnceCallback consumer_callback); ~WaitingForRead(); WaitingForRead(WaitingForRead&&); }; using WaitingForReadMap = std::map; using TransactionMap = std::map; // 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 DoMarkSingleKeyedCacheEntryUnusable(); int DoMarkSingleKeyedCacheEntryUnusableComplete(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; raw_ptr const cache_ = nullptr; // Owner of |this|. raw_ptr const entry_ = nullptr; std::unique_ptr network_transaction_; scoped_refptr read_buf_; 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. raw_ptr 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; // Set if we are currently calculating a checksum of the resource to validate // it against the expected checksum for the single-keyed cache. Initialised // with selected headers and accumulates the body of the response. std::unique_ptr checksum_; 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 weak_factory_{this}; }; } // namespace net #endif // NET_HTTP_HTTP_CACHE_WRITERS_H_