// 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_
