| // 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. |
| |
| #ifndef NET_HTTP_HTTP_CACHE_WRITERS_H_ |
| #define NET_HTTP_HTTP_CACHE_WRITERS_H_ |
| |
| #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 { |
| 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&); |
| |
| PartialData* partial; |
| bool truncated; |
| HttpResponseInfo response_info; |
| }; |
| |
| // |cache| and |entry| must outlive this object. |
| Writers(HttpCache* cache, HttpCache::ActiveEntry* entry); |
| ~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, |
| 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. |
| // If |is_exclusive| is true, it makes writing 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 is_exclusive |
| // as true 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, |
| bool is_exclusive, |
| 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. |
| bool CanAddWriters(); |
| |
| // 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_; } |
| |
| // Returns true if entry is worth keeping in its current state. It may be |
| // truncated or complete. Returns false if entry is incomplete and cannot be |
| // resumed, it will not be marked as truncated and will not be preserved. |
| bool ShouldKeepEntry() const { return should_keep_entry_; } |
| |
| 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, |
| // This state is used when an attempt to truncate races with an ongoing |
| // network read/cache write. In this case we transition from |
| // NETWORK_READ_COMPLETE or CACHE_WRITE_DATA_COMPLETE to |
| // ASYNC_OP_COMPLETE_PRE_TRUNCATE. We will then process the truncation |
| // after the ongoing operation completes. |
| ASYNC_OP_COMPLETE_PRE_TRUNCATE, |
| CACHE_WRITE_TRUNCATED_RESPONSE, |
| CACHE_WRITE_TRUNCATED_RESPONSE_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; |
| const CompletionCallback callback; |
| WaitingForRead(scoped_refptr<IOBuffer> read_buf, |
| int len, |
| const CompletionCallback& consumer_callback); |
| ~WaitingForRead(); |
| 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 |
| // ASYNC_OP_COMPLETE_PRE_TRUNCATE. |
| // 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; |
| |
| // 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_; |
| DISALLOW_COPY_AND_ASSIGN(Writers); |
| }; |
| |
| } // namespace net |
| |
| #endif // NET_HTTP_HTTP_CACHE_WRITERS_H_ |