%PDF- %PDF-
| Direktori : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/src/quic/ |
| Current File : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/src/quic/session.h |
#pragma once
#include <sys/types.h>
#include "quic/tokens.h"
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
#if HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC
#include <async_wrap.h>
#include <base_object.h>
#include <env.h>
#include <memory_tracker.h>
#include <ngtcp2/ngtcp2.h>
#include <node_http_common.h>
#include <node_sockaddr.h>
#include <timer_wrap.h>
#include <util.h>
#include <optional>
#include "bindingdata.h"
#include "cid.h"
#include "data.h"
#include "defs.h"
#include "logstream.h"
#include "packet.h"
#include "preferredaddress.h"
#include "sessionticket.h"
#include "streams.h"
#include "tlscontext.h"
#include "transportparams.h"
namespace node {
namespace quic {
class Endpoint;
// A Session represents one half of a persistent connection between two QUIC
// peers. Every Session is established first by performing a TLS handshake in
// which the client sends an initial packet to the server containing a TLS
// client hello. Once the TLS handshake has been completed, the Session can be
// used to open one or more Streams for the actual data flow back and forth.
//
// While client and server Sessions are created in slightly different ways,
// their lifecycles are generally identical:
//
// A Session is either acting as a Client or as a Server.
//
// Client Sessions are always created using Endpoint::Connect()
//
// Server Sessions are always created by an Endpoint receiving a valid initial
// request received from a remote client.
//
// As soon as Sessions of either type are created, they will immediately start
// working through the TLS handshake to establish the crypographic keys used to
// secure the communication. Once those keys are established, the Session can be
// used to open Streams. Based on how the Session is configured, any number of
// Streams can exist concurrently on a single Session.
class Session final : public AsyncWrap, private SessionTicket::AppData::Source {
public:
// For simplicity, we use the same Application::Options struct for all
// Application types. This may change in the future. Not all of the options
// are going to be relevant for all Application types.
struct Application_Options final : public MemoryRetainer {
// The maximum number of header pairs permitted for a Stream.
// Only relevant if the selected application supports headers.
uint64_t max_header_pairs = DEFAULT_MAX_HEADER_LIST_PAIRS;
// The maximum total number of header bytes (including header
// name and value) permitted for a Stream.
// Only relevant if the selected application supports headers.
uint64_t max_header_length = DEFAULT_MAX_HEADER_LENGTH;
// HTTP/3 specific options.
uint64_t max_field_section_size = 0;
uint64_t qpack_max_dtable_capacity = 0;
uint64_t qpack_encoder_max_dtable_capacity = 0;
uint64_t qpack_blocked_streams = 0;
bool enable_connect_protocol = true;
bool enable_datagrams = true;
operator const nghttp3_settings() const;
SET_NO_MEMORY_INFO()
SET_MEMORY_INFO_NAME(Application::Options)
SET_SELF_SIZE(Options)
static v8::Maybe<Application_Options> From(Environment* env,
v8::Local<v8::Value> value);
std::string ToString() const;
static const Application_Options kDefault;
};
// An Application implements the ALPN-protocol specific semantics on behalf
// of a QUIC Session.
class Application;
// The options used to configure a session. Most of these deal directly with
// the transport parameters that are exchanged with the remote peer during
// handshake.
struct Options final : public MemoryRetainer {
// The QUIC protocol version requested for the session.
uint32_t version = NGTCP2_PROTO_VER_MAX;
// Te minimum QUIC protocol version supported by this session.
uint32_t min_version = NGTCP2_PROTO_VER_MIN;
// By default a client session will use the preferred address advertised by
// the the server. This option is only relevant for client sessions.
PreferredAddress::Policy preferred_address_strategy =
PreferredAddress::Policy::USE_PREFERRED_ADDRESS;
TransportParams::Options transport_params =
TransportParams::Options::kDefault;
TLSContext::Options tls_options = TLSContext::Options::kDefault;
Application_Options application_options = Application_Options::kDefault;
// A reference to the CID::Factory used to generate CID instances
// for this session.
const CID::Factory* cid_factory = &CID::Factory::random();
// If the CID::Factory is a base object, we keep a reference to it
// so that it cannot be garbage collected.
BaseObjectPtr<BaseObject> cid_factory_ref = BaseObjectPtr<BaseObject>();
// When true, QLog output will be enabled for the session.
bool qlog = false;
void MemoryInfo(MemoryTracker* tracker) const override;
SET_MEMORY_INFO_NAME(Session::Options)
SET_SELF_SIZE(Options)
static v8::Maybe<Options> From(Environment* env,
v8::Local<v8::Value> value);
std::string ToString() const;
};
// The additional configuration settings used to create a specific session.
// while the Options above can be used to configure multiple sessions, a
// single Config is used to create a single session, which is why they are
// kept separate.
struct Config final : MemoryRetainer {
// Is the Session acting as a client or a server?
Side side;
// The options to use for this session.
Options options;
// The actual QUIC version identified for this session.
uint32_t version;
SocketAddress local_address;
SocketAddress remote_address;
// The destination CID, identifying the remote peer. This value is always
// provided by the remote peer.
CID dcid = CID::kInvalid;
// The source CID, identifying this session. This value is always created
// locally.
CID scid = CID::kInvalid;
// Used only by client sessions to identify the original DCID
// used to initiate the connection.
CID ocid = CID::kInvalid;
CID retry_scid = CID::kInvalid;
CID preferred_address_cid = CID::kInvalid;
// If this is a client session, the session_ticket is used to resume
// a TLS session using a previously established session ticket.
std::optional<SessionTicket> session_ticket = std::nullopt;
ngtcp2_settings settings = {};
operator ngtcp2_settings*() { return &settings; }
operator const ngtcp2_settings*() const { return &settings; }
Config(Side side,
const Endpoint& endpoint,
const Options& options,
uint32_t version,
const SocketAddress& local_address,
const SocketAddress& remote_address,
const CID& dcid,
const CID& scid,
std::optional<SessionTicket> session_ticket = std::nullopt,
const CID& ocid = CID::kInvalid);
Config(const Endpoint& endpoint,
const Options& options,
const SocketAddress& local_address,
const SocketAddress& remote_address,
std::optional<SessionTicket> session_ticket = std::nullopt,
const CID& ocid = CID::kInvalid);
void set_token(const uint8_t* token,
size_t len,
ngtcp2_token_type type = NGTCP2_TOKEN_TYPE_UNKNOWN);
void set_token(const RetryToken& token);
void set_token(const RegularToken& token);
void MemoryInfo(MemoryTracker* tracker) const override;
SET_MEMORY_INFO_NAME(Session::Config)
SET_SELF_SIZE(Config)
std::string ToString() const;
};
static bool HasInstance(Environment* env, v8::Local<v8::Value> value);
static v8::Local<v8::FunctionTemplate> GetConstructorTemplate(
Environment* env);
static void InitPerIsolate(IsolateData* isolate_data,
v8::Local<v8::ObjectTemplate> target);
static void InitPerContext(Realm* env, v8::Local<v8::Object> target);
static void RegisterExternalReferences(ExternalReferenceRegistry* registry);
static BaseObjectPtr<Session> Create(Endpoint* endpoint,
const Config& config);
// Really should be private but MakeDetachedBaseObject needs visibility.
Session(Endpoint* endpoint,
v8::Local<v8::Object> object,
const Config& config);
~Session() override;
uint32_t version() const;
Endpoint& endpoint() const;
TLSContext& tls_context();
Application& application();
const Config& config() const;
const Options& options() const;
const SocketAddress& remote_address() const;
const SocketAddress& local_address() const;
bool is_closing() const;
bool is_graceful_closing() const;
bool is_silent_closing() const;
bool is_destroyed() const;
bool is_server() const;
void set_priority_supported(bool on = true);
std::string diagnostic_name() const override;
// Use the configured CID::Factory to generate a new CID.
CID new_cid(size_t len = CID::kMaxLength) const;
void HandleQlog(uint32_t flags, const void* data, size_t len);
TransportParams GetLocalTransportParams() const;
TransportParams GetRemoteTransportParams() const;
void MemoryInfo(MemoryTracker* tracker) const override;
SET_MEMORY_INFO_NAME(Session)
SET_SELF_SIZE(Session)
struct State;
struct Stats;
operator ngtcp2_conn*() const;
BaseObjectPtr<Stream> FindStream(int64_t id) const;
BaseObjectPtr<Stream> CreateStream(int64_t id);
BaseObjectPtr<Stream> OpenStream(Direction direction);
void ExtendStreamOffset(int64_t id, size_t amount);
void ExtendOffset(size_t amount);
void SetLastError(QuicError&& error);
uint64_t max_data_left() const;
enum class CloseMethod {
// Roundtrip through JavaScript, causing all currently opened streams
// to be closed. An attempt will be made to send a CONNECTION_CLOSE
// frame to the peer. If closing while within the ngtcp2 callback scope,
// sending the CONNECTION_CLOSE will be deferred until the scope exits.
DEFAULT,
// The connected peer will not be notified.
SILENT,
// Closing gracefully disables the ability to open or accept new streams for
// this Session. Existing streams are allowed to close naturally on their
// own.
// Once called, the Session will be immediately closed once there are no
// remaining streams. No notification is given to the connected peer that we
// are in a graceful closing state. A CONNECTION_CLOSE will be sent only
// once
// Close() is called.
GRACEFUL
};
void Close(CloseMethod method = CloseMethod::DEFAULT);
struct SendPendingDataScope {
Session* session;
explicit SendPendingDataScope(Session* session);
explicit SendPendingDataScope(const BaseObjectPtr<Session>& session);
SendPendingDataScope(const SendPendingDataScope&) = delete;
SendPendingDataScope(SendPendingDataScope&&) = delete;
SendPendingDataScope& operator=(const SendPendingDataScope&) = delete;
SendPendingDataScope& operator=(SendPendingDataScope&&) = delete;
~SendPendingDataScope();
};
private:
struct Impl;
struct MaybeCloseConnectionScope;
using StreamsMap = std::unordered_map<int64_t, BaseObjectPtr<Stream>>;
using QuicConnectionPointer = DeleteFnPtr<ngtcp2_conn, ngtcp2_conn_del>;
struct PathValidationFlags {
bool preferredAddress = false;
};
struct DatagramReceivedFlags {
bool early = false;
};
void Destroy();
bool Receive(Store&& store,
const SocketAddress& local_address,
const SocketAddress& remote_address);
void Send(Packet* packet);
void Send(Packet* packet, const PathStorage& path);
uint64_t SendDatagram(Store&& data);
void AddStream(const BaseObjectPtr<Stream>& stream);
void RemoveStream(int64_t id);
void ResumeStream(int64_t id);
void ShutdownStream(int64_t id, QuicError error);
void StreamDataBlocked(int64_t id);
void ShutdownStreamWrite(int64_t id, QuicError code = QuicError());
// Implementation of SessionTicket::AppData::Source
void CollectSessionTicketAppData(
SessionTicket::AppData* app_data) const override;
SessionTicket::AppData::Status ExtractSessionTicketAppData(
const SessionTicket::AppData& app_data,
SessionTicket::AppData::Source::Flag flag) override;
// Returns true if the Session has entered the closing period after sending a
// CONNECTION_CLOSE. While true, the Session is only permitted to transmit
// CONNECTION_CLOSE frames until either the idle timeout period elapses or
// until the Session is explicitly destroyed.
bool is_in_closing_period() const;
// Returns true if the Session has received a CONNECTION_CLOSE frame from the
// peer. Once in the draining period, the Session is not permitted to send any
// frames to the peer. The Session will be silently closed after either the
// idle timeout period elapses or until the Session is explicitly destroyed.
bool is_in_draining_period() const;
// Returns false if the Session is currently in a state where it is unable to
// transmit any packets.
bool can_send_packets() const;
// Returns false if the Session is currently in a state where it cannot create
// new streams.
bool can_create_streams() const;
uint64_t max_local_streams_uni() const;
uint64_t max_local_streams_bidi() const;
bool wants_session_ticket() const;
void SetStreamOpenAllowed();
// It's a terrible name but "wrapped" here means that the Session has been
// passed out to JavaScript and should be "wrapped" by whatever handler is
// defined there to manage it.
void set_wrapped();
void DoClose(bool silent = false);
void UpdateDataStats();
void SendConnectionClose();
void OnTimeout();
void UpdateTimer();
bool StartClosingPeriod();
// JavaScript callouts
void EmitClose(const QuicError& error = QuicError());
void EmitDatagram(Store&& datagram, DatagramReceivedFlags flag);
void EmitDatagramStatus(uint64_t id, DatagramStatus status);
void EmitHandshakeComplete();
void EmitKeylog(const char* line);
struct ValidatedPath {
std::shared_ptr<SocketAddress> local;
std::shared_ptr<SocketAddress> remote;
};
void EmitPathValidation(PathValidationResult result,
PathValidationFlags flags,
const ValidatedPath& newPath,
const std::optional<ValidatedPath>& oldPath);
void EmitSessionTicket(Store&& ticket);
void EmitStream(BaseObjectPtr<Stream> stream);
void EmitVersionNegotiation(const ngtcp2_pkt_hd& hd,
const uint32_t* sv,
size_t nsv);
void DatagramStatus(uint64_t datagramId, DatagramStatus status);
void DatagramReceived(const uint8_t* data,
size_t datalen,
DatagramReceivedFlags flag);
bool GenerateNewConnectionId(ngtcp2_cid* cid, size_t len, uint8_t* token);
bool HandshakeCompleted();
void HandshakeConfirmed();
void SelectPreferredAddress(PreferredAddress* preferredAddress);
void UpdatePath(const PathStorage& path);
QuicConnectionPointer InitConnection();
std::unique_ptr<Application> select_application();
AliasedStruct<Stats> stats_;
AliasedStruct<State> state_;
ngtcp2_mem allocator_;
BaseObjectWeakPtr<Endpoint> endpoint_;
Config config_;
SocketAddress local_address_;
SocketAddress remote_address_;
QuicConnectionPointer connection_;
TLSContext tls_context_;
std::unique_ptr<Application> application_;
StreamsMap streams_;
TimerWrapHandle timer_;
size_t send_scope_depth_ = 0;
size_t connection_close_depth_ = 0;
QuicError last_error_;
Packet* conn_closebuf_;
BaseObjectPtr<LogStream> qlog_stream_;
BaseObjectPtr<LogStream> keylog_stream_;
friend class Application;
friend class DefaultApplication;
friend class Endpoint;
friend struct Impl;
friend struct MaybeCloseConnectionScope;
friend struct SendPendingDataScope;
friend class Stream;
friend class TLSContext;
friend class TransportParams;
};
} // namespace quic
} // namespace node
#endif // HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS