// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// HttpRequestHeaders manages the request headers.
// It maintains these in a vector of header key/value pairs, thereby maintaining
// the order of the headers.  This means that any lookups are linear time
// operations.

#ifndef NET_HTTP_HTTP_REQUEST_HEADERS_H_
#define NET_HTTP_HTTP_REQUEST_HEADERS_H_

#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <vector>

#include "base/containers/flat_set.h"
#include "base/values.h"
#include "net/base/net_export.h"
#include "net/filter/source_stream_type.h"
#include "net/log/net_log_capture_mode.h"
#include "url/gurl.h"

namespace net {

class NET_EXPORT HttpRequestHeaders {
 public:
  struct NET_EXPORT HeaderKeyValuePair {
    HeaderKeyValuePair();
    HeaderKeyValuePair(std::string_view key, std::string_view value);
    HeaderKeyValuePair(std::string_view key, std::string&& value);
    // Inline to take advantage of the std::string_view constructor being
    // constexpr.
    HeaderKeyValuePair(std::string_view key, const char* value)
        : HeaderKeyValuePair(key, std::string_view(value)) {}

    bool operator==(const HeaderKeyValuePair& other) const = default;

    std::string key;
    std::string value;
  };

  typedef std::vector<HeaderKeyValuePair> HeaderVector;

  class NET_EXPORT Iterator {
   public:
    explicit Iterator(const HttpRequestHeaders& headers);

    Iterator(const Iterator&) = delete;
    Iterator& operator=(const Iterator&) = delete;

    ~Iterator();

    // Advances the iterator to the next header, if any.  Returns true if there
    // is a next header.  Use name() and value() methods to access the resultant
    // header name and value.
    bool GetNext();

    // These two accessors are only valid if GetNext() returned true.
    const std::string& name() const { return curr_->key; }
    const std::string& value() const { return curr_->value; }

   private:
    bool started_ = false;
    HttpRequestHeaders::HeaderVector::const_iterator curr_;
    const HttpRequestHeaders::HeaderVector::const_iterator end_;
  };

  static constexpr char kConnectMethod[] = "CONNECT";
  static constexpr char kDeleteMethod[] = "DELETE";
  static constexpr char kGetMethod[] = "GET";
  static constexpr char kHeadMethod[] = "HEAD";
  static constexpr char kOptionsMethod[] = "OPTIONS";
  static constexpr char kPatchMethod[] = "PATCH";
  static constexpr char kPostMethod[] = "POST";
  static constexpr char kPutMethod[] = "PUT";
  static constexpr char kTraceMethod[] = "TRACE";
  static constexpr char kTrackMethod[] = "TRACK";

  static constexpr char kAccept[] = "Accept";
  static constexpr char kAcceptCharset[] = "Accept-Charset";
  static constexpr char kAcceptEncoding[] = "Accept-Encoding";
  static constexpr char kAcceptLanguage[] = "Accept-Language";
  static constexpr char kAuthorization[] = "Authorization";
  static constexpr char kCacheControl[] = "Cache-Control";
  static constexpr char kConnection[] = "Connection";
  static constexpr char kContentLength[] = "Content-Length";
  static constexpr char kContentType[] = "Content-Type";
  static constexpr char kCookie[] = "Cookie";
    static constexpr char kDNT[] = "DNT";
  static constexpr char kHost[] = "Host";
  static constexpr char kIfMatch[] = "If-Match";
  static constexpr char kIfModifiedSince[] = "If-Modified-Since";
  static constexpr char kIfNoneMatch[] = "If-None-Match";
  static constexpr char kIfRange[] = "If-Range";
  static constexpr char kIfUnmodifiedSince[] = "If-Unmodified-Since";
  static constexpr char kOrigin[] = "Origin";
  static constexpr char kPragma[] = "Pragma";
  static constexpr char kPriority[] = "Priority";
  static constexpr char kProxyAuthorization[] = "Proxy-Authorization";
  static constexpr char kProxyConnection[] = "Proxy-Connection";
  static constexpr char kRange[] = "Range";
  static constexpr char kReferer[] = "Referer";
  static constexpr char kSecPurpose[] = "Sec-Purpose";
  static constexpr char kTransferEncoding[] = "Transfer-Encoding";
  static constexpr char kUpgradeInsecureRequests[] =
      "Upgrade-Insecure-Requests";
  static constexpr char kUserAgent[] = "User-Agent";

  HttpRequestHeaders();
  HttpRequestHeaders(const HttpRequestHeaders& other);
  HttpRequestHeaders(HttpRequestHeaders&& other);
  ~HttpRequestHeaders();

  HttpRequestHeaders& operator=(const HttpRequestHeaders& other);
  HttpRequestHeaders& operator=(HttpRequestHeaders&& other);

  bool IsEmpty() const { return headers_.empty(); }

  bool HasHeader(std::string_view key) const {
    return FindHeader(key) != headers_.end();
  }

  // Gets the first header that matches `key`, if one exists. If none exist,
  // returns std::nullopt.
  std::optional<std::string_view> GetHeaderView(std::string_view key) const;
  std::optional<std::string> GetHeader(std::string_view key) const;

  // Clears all the headers.
  void Clear();

  // Sets the header value pair for |key| and |value|.  If |key| already exists,
  // then the header value is modified, but the key is untouched, and the order
  // in the vector remains the same.  When comparing |key|, case is ignored.
  // The caller must ensure that |key| passes HttpUtil::IsValidHeaderName() and
  // |value| passes HttpUtil::IsValidHeaderValue().
  void SetHeader(std::string_view key, std::string_view value);
  void SetHeader(std::string_view key, std::string&& value);
  // Inline to take advantage of the std::string_view constructor being
  // constexpr.
  void SetHeader(std::string_view key, const char* value) {
    SetHeader(key, std::string_view(value));
  }

  // Does the same as above but without internal DCHECKs for validations.
  void SetHeaderWithoutCheckForTesting(std::string_view key,
                                       std::string_view value);

  // Sets the header value pair for |key| and |value|, if |key| does not exist.
  // If |key| already exists, the call is a no-op.
  // When comparing |key|, case is ignored.
  //
  // The caller must ensure that |key| passes HttpUtil::IsValidHeaderName() and
  // |value| passes HttpUtil::IsValidHeaderValue().
  void SetHeaderIfMissing(std::string_view key, std::string_view value);

  // Removes the first header that matches (case insensitive) |key|.
  void RemoveHeader(std::string_view key);

  // Parses the header from a string and calls SetHeader() with it.  This string
  // should not contain any CRLF.  As per RFC7230 Section 3.2, the format is:
  //
  // header-field   = field-name ":" OWS field-value OWS
  //
  // field-name     = token
  // field-value    = *( field-content / obs-fold )
  // field-content  = field-vchar [ 1*( SP / HTAB ) field-vchar ]
  // field-vchar    = VCHAR / obs-text
  //
  // obs-fold       = CRLF 1*( SP / HTAB )
  //                ; obsolete line folding
  //                ; see Section 3.2.4
  //
  // AddHeaderFromString() will trim any LWS surrounding the
  // field-content.
  void AddHeaderFromString(std::string_view header_line);

  // Same thing as AddHeaderFromString() except that |headers| is a "\r\n"
  // delimited string of header lines.  It will split up the string by "\r\n"
  // and call AddHeaderFromString() on each.
  void AddHeadersFromString(std::string_view headers);

  // Calls SetHeader() on each header from |other|, maintaining order.
  void MergeFrom(const HttpRequestHeaders& other);

  void Swap(HttpRequestHeaders* other) { headers_.swap(other->headers_); }

  // Serializes HttpRequestHeaders to a string representation.  Joins all the
  // header keys and values with ": ", and inserts "\r\n" between each header
  // line, and adds the trailing "\r\n".
  std::string ToString() const;

  // Takes in the request line and returns a Value for use with the NetLog
  // containing both the request line and all headers fields.
  base::Value::Dict NetLogParams(const std::string& request_line,
                                 NetLogCaptureMode capture_mode) const;

  const HeaderVector& GetHeaderVector() const { return headers_; }

  // Sets Accept-Encoding header based on `url` and `accepted_stream_types`, if
  // it does not exist. "br" is appended only when `enable_brotli` is true.
  void SetAcceptEncodingIfMissing(
      const GURL& url,
      const std::optional<base::flat_set<SourceStreamType>>&
          accepted_stream_types,
      bool enable_brotli,
      bool enable_zstd);

 private:
  HeaderVector::iterator FindHeader(std::string_view key);
  HeaderVector::const_iterator FindHeader(std::string_view key) const;

  void SetHeaderInternal(std::string_view key, std::string&& value);

  HeaderVector headers_;
};

}  // namespace net

#endif  // NET_HTTP_HTTP_REQUEST_HEADERS_H_