| // 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 <map> |
| #include <memory> |
| |
| #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<PartialData> 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<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(); } |
| |
| // 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(); |
| } |
| |
| 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<HttpTransaction> network_transaction, |
| std::unique_ptr<crypto::SecureHash> 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<IOBuffer> read_buf; |
| int read_buf_len; |
| int write_len = 0; |
| CompletionOnceCallback callback; |
| WaitingForRead(scoped_refptr<IOBuffer> read_buf, |
| int len, |
| CompletionOnceCallback consumer_callback); |
| ~WaitingForRead(); |
| WaitingForRead(WaitingForRead&&); |
| }; |
| 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); |
| 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<HttpCache> const cache_ = nullptr; |
| |
| // Owner of |this|. |
| raw_ptr<ActiveEntry, DanglingUntriaged> const entry_ = nullptr; |
| |
| std::unique_ptr<HttpTransaction> network_transaction_; |
| |
| scoped_refptr<IOBuffer> 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<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; |
| |
| // The latest time `this` starts writing data to the disk cache. |
| base::TimeTicks last_disk_cache_access_start_time_; |
| |
| // 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<crypto::SecureHash> 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<Writers> weak_factory_{this}; |
| }; |
| |
| } // namespace net |
| |
| #endif // NET_HTTP_HTTP_CACHE_WRITERS_H_ |