@ 261106.2
current
#pragma once

#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS

#include <env.h>
#include <memory_tracker.h>
#include <ngtcp2/ngtcp2.h>
#include <node_sockaddr.h>
#include <optional>
#include "bindingdata.h"
#include "cid.h"
#include "data.h"
#include "tokens.h"

namespace node::quic {

class Endpoint;
class Session;

// The Transport Params are the set of configuration options that are sent to
// the remote peer. They communicate the protocol options the other peer
// should use when communicating with this session.
class TransportParams final {
 public:
  static void Initialize(Environment* env, v8::Local<v8::Object> target);

  enum class Version : int {
    V1 = NGTCP2_TRANSPORT_PARAMS_V1,
  };

  static constexpr uint64_t DEFAULT_MAX_STREAM_DATA = 256 * 1024;
  static constexpr uint64_t DEFAULT_MAX_DATA = 1 * 1024 * 1024;
  static constexpr uint64_t DEFAULT_MAX_IDLE_TIMEOUT = 10;  // seconds
  static constexpr uint64_t DEFAULT_MAX_STREAMS_BIDI = 100;
  static constexpr uint64_t DEFAULT_MAX_STREAMS_UNI = 3;
  static constexpr uint64_t DEFAULT_ACTIVE_CONNECTION_ID_LIMIT = 2;

  struct Config final {
    Side side;
    const CID& ocid;
    const CID& retry_scid;
    Config(Side side,
           const CID& ocid = CID::kInvalid,
           const CID& retry_scid = CID::kInvalid);
  };

  struct Options final : public MemoryRetainer {
    Version transportParamsVersion = Version::V1;

    // Set only on server Sessions, the preferred address communicates the IP
    // address and port that the server would prefer the client to use when
    // communicating with it. See the QUIC specification for more detail on how
    // the preferred address mechanism works:
    // https://www.rfc-editor.org/rfc/rfc9000.html#name-servers-preferred-address
    std::optional<SocketAddress> preferred_address_ipv4{};
    std::optional<SocketAddress> preferred_address_ipv6{};

    // The initial size of the flow control window of locally initiated streams.
    // This is the maximum number of bytes that the *remote* endpoint can send
    // when the connection is started.
    // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-4.16.1
    uint64_t initial_max_stream_data_bidi_local = DEFAULT_MAX_STREAM_DATA;

    // The initial size of the flow control window of remotely initiated
    // streams. This is the maximum number of bytes that the remote endpoint can
    // send when the connection is started.
    // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-4.18.1
    uint64_t initial_max_stream_data_bidi_remote = DEFAULT_MAX_STREAM_DATA;

    // The initial size of the flow control window of remotely initiated
    // unidirectional streams. This is the maximum number of bytes that the
    // remote endpoint can send when the connection is started.
    // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-4.20.1
    uint64_t initial_max_stream_data_uni = DEFAULT_MAX_STREAM_DATA;

    // The initial size of the session-level flow control window.
    // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-4.14.1
    uint64_t initial_max_data = DEFAULT_MAX_DATA;

    // The initial maximum number of concurrent bidirectional streams the remote
    // endpoint is permitted to open.
    // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-4.22.1
    uint64_t initial_max_streams_bidi = DEFAULT_MAX_STREAMS_BIDI;

    // The initial maximum number of concurrent unidirectional streams the
    // remote endpoint is permitted to open.
    // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-4.24.1
    uint64_t initial_max_streams_uni = DEFAULT_MAX_STREAMS_UNI;

    // The maximum amount of time that a Session is permitted to remain idle
    // before it is silently closed and state is discarded.
    // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-4.4.1
    uint64_t max_idle_timeout = DEFAULT_MAX_IDLE_TIMEOUT;

    // The maximum number of Connection IDs that the peer can store. A single
    // Session may have several connection IDs over it's lifetime.
    // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-6.2.1
    uint64_t active_connection_id_limit = DEFAULT_ACTIVE_CONNECTION_ID_LIMIT;

    // Establishes the exponent used in ACK Delay field in the ACK frame. See
    // the QUIC specification for details. This is an advanced option that
    // should rarely be modified and only if there is really good reason.
    // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-4.26.1
    uint64_t ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT;

    // The maximum amount of time by which the endpoint will delay sending
    // acknowledgements. This is an advanced option that should rarely be
    // modified and only if there is a really good reason. It is used to
    // determine how long a Session will wait to determine that a packet has
    // been lost.
    // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-4.28.1
    uint64_t max_ack_delay = NGTCP2_DEFAULT_MAX_ACK_DELAY;

    // The maximum size of DATAGRAM frames that the endpoint will accept.
    // Setting the value to 0 will disable DATAGRAM support.
    // https://datatracker.ietf.org/doc/html/rfc9221#section-3
    uint16_t max_datagram_frame_size = kDefaultMaxPacketLength;

    // When true, communicates that the Session does not support active
    static const Options kDefault;

    void MemoryInfo(MemoryTracker* tracker) const override;
    SET_MEMORY_INFO_NAME(TransportParams::Options)
    SET_SELF_SIZE(Options)

    static v8::Maybe<Options> From(Environment* env,
                                   v8::Local<v8::Value> value);

    std::string ToString() const;
  };

  explicit TransportParams();

  // Creates an instance of TransportParams wrapping an existing const
  // ngtcp2_transport_params pointer. Instances created this way are
  // immutable.
  TransportParams(const ngtcp2_transport_params* ptr);

  TransportParams(const Config& config, const Options& options);

  // Creates an instance of TransportParams by decoding the given buffer.
  // If the parameters cannot be successfully decoded, the bool() operator
  // will be false.
  TransportParams(const ngtcp2_vec& buf, Version version = Version::V1);

  void GenerateSessionTokens(Session* session);

  operator const ngtcp2_transport_params&() const;
  operator const ngtcp2_transport_params*() const;

  operator bool() const;

  uint64_t initial_max_streams_bidi() const;
  uint64_t initial_max_streams_uni() const;

  // Returns a Store containing the encoded transport parameters.
  // If an error occurs during encoding, or if the parameters could
  // not be encoded, an empty Store will be returned.
  Store Encode(Environment* env, Version version = Version::V1) const;

  // Returns the encoded size in bytes, or 0 on error.
  ssize_t EncodedSize(Version version = Version::V1) const;

  // Encode into a caller-provided buffer. Returns the number of bytes
  // written, or a negative value on error.
  ssize_t EncodeInto(uint8_t* buf,
                     size_t len,
                     Version version = Version::V1) const;

  // Returns a JavaScript object representing the transport parameters.
  v8::MaybeLocal<v8::Object> ToObject(Environment* env) const;

 private:
  void SetPreferredAddress(const SocketAddress& address);
  void GeneratePreferredAddressToken(Session* session);
  void GenerateStatelessResetToken(const Endpoint& endpoint, const CID& cid);

  ngtcp2_transport_params params_{};
  const ngtcp2_transport_params* ptr_;
};

}  // namespace node::quic

#endif  // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS