Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

quic: add more QUIC implementation #47494

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions node.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -341,16 +341,20 @@
'src/quic/cid.cc',
'src/quic/data.cc',
'src/quic/logstream.cc',
'src/quic/packet.cc',
'src/quic/preferredaddress.cc',
'src/quic/sessionticket.cc',
'src/quic/tlscontext.cc',
'src/quic/tokens.cc',
'src/quic/transportparams.cc',
'src/quic/bindingdata.h',
'src/quic/cid.h',
'src/quic/data.h',
'src/quic/logstream.h',
'src/quic/packet.h',
'src/quic/preferredaddress.h',
'src/quic/sessionticket.h',
'src/quic/tlscontext.h',
'src/quic/tokens.h',
'src/quic/transportparams.h',
],
Expand Down
1 change: 1 addition & 0 deletions src/async_wrap.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ namespace node {
V(PROMISE) \
V(QUERYWRAP) \
V(QUIC_LOGSTREAM) \
V(QUIC_PACKET) \
V(SHUTDOWNWRAP) \
V(SIGNALWRAP) \
V(STATWATCHER) \
Expand Down
2 changes: 2 additions & 0 deletions src/node_errors.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ void OOMErrorHandler(const char* location, const v8::OOMDetails& details);
V(ERR_DLOPEN_FAILED, Error) \
V(ERR_ENCODING_INVALID_ENCODED_DATA, TypeError) \
V(ERR_EXECUTION_ENVIRONMENT_NOT_AVAILABLE, Error) \
V(ERR_ILLEGAL_CONSTRUCTOR, Error) \
V(ERR_INVALID_ADDRESS, Error) \
V(ERR_INVALID_ARG_VALUE, TypeError) \
V(ERR_OSSL_EVP_INVALID_DIGEST, Error) \
Expand Down Expand Up @@ -156,6 +157,7 @@ ERRORS_WITH_CODE(V)
V(ERR_DLOPEN_FAILED, "DLOpen failed") \
V(ERR_EXECUTION_ENVIRONMENT_NOT_AVAILABLE, \
"Context not associated with Node.js environment") \
V(ERR_ILLEGAL_CONSTRUCTOR, "Illegal constructor") \
V(ERR_INVALID_ADDRESS, "Invalid socket address") \
V(ERR_INVALID_MODULE, "No such module") \
V(ERR_INVALID_STATE, "Invalid state") \
Expand Down
46 changes: 45 additions & 1 deletion src/quic/bindingdata.cc
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,15 @@ void BindingData::DecreaseAllocatedSize(size_t size) {

void BindingData::Initialize(Environment* env, Local<Object> target) {
SetMethod(env->context(), target, "setCallbacks", SetCallbacks);
SetMethod(env->context(), target, "flushPacketFreelist", FlushPacketFreelist);
Realm::GetCurrent(env->context())
->AddBindingData<BindingData>(env->context(), target);
}

void BindingData::RegisterExternalReferences(
ExternalReferenceRegistry* registry) {
registry->Register(SetCallbacks);
registry->Register(FlushPacketFreelist);
}

BindingData::BindingData(Realm* realm, Local<Object> object)
Expand Down Expand Up @@ -140,7 +142,7 @@ QUIC_JS_CALLBACKS(V)
void BindingData::SetCallbacks(const FunctionCallbackInfo<Value>& args) {
auto env = Environment::GetCurrent(args);
auto isolate = env->isolate();
BindingData& state = BindingData::Get(env);
auto& state = BindingData::Get(env);
CHECK(args[0]->IsObject());
Local<Object> obj = args[0].As<Object>();

Expand All @@ -159,6 +161,48 @@ void BindingData::SetCallbacks(const FunctionCallbackInfo<Value>& args) {
#undef V
}

void BindingData::FlushPacketFreelist(const FunctionCallbackInfo<Value>& args) {
auto env = Environment::GetCurrent(args);
auto& state = BindingData::Get(env);
state.packet_freelist.clear();
}

NgTcp2CallbackScope::NgTcp2CallbackScope(Environment* env) : env(env) {
auto& binding = BindingData::Get(env);
CHECK(!binding.in_ngtcp2_callback_scope);
binding.in_ngtcp2_callback_scope = true;
}

NgTcp2CallbackScope::~NgTcp2CallbackScope() {
auto& binding = BindingData::Get(env);
binding.in_ngtcp2_callback_scope = false;
}

bool NgTcp2CallbackScope::in_ngtcp2_callback(Environment* env) {
auto& binding = BindingData::Get(env);
return binding.in_ngtcp2_callback_scope;
}

NgHttp3CallbackScope::NgHttp3CallbackScope(Environment* env) : env(env) {
auto& binding = BindingData::Get(env);
CHECK(!binding.in_nghttp3_callback_scope);
binding.in_nghttp3_callback_scope = true;
}

NgHttp3CallbackScope::~NgHttp3CallbackScope() {
auto& binding = BindingData::Get(env);
binding.in_nghttp3_callback_scope = false;
}

bool NgHttp3CallbackScope::in_nghttp3_callback(Environment* env) {
auto& binding = BindingData::Get(env);
return binding.in_nghttp3_callback_scope;
}

void IllegalConstructor(const FunctionCallbackInfo<Value>& args) {
THROW_ERR_ILLEGAL_CONSTRUCTOR(Environment::GetCurrent(args));
}

} // namespace quic
} // namespace node

Expand Down
51 changes: 44 additions & 7 deletions src/quic/bindingdata.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@
#include <node.h>
#include <node_mem.h>
#include <v8.h>
#include <vector>

namespace node {
namespace quic {

class Endpoint;
class Packet;

enum class Side {
CLIENT = NGTCP2_CRYPTO_SIDE_CLIENT,
Expand Down Expand Up @@ -64,23 +66,37 @@ constexpr size_t kDefaultMaxPacketLength = NGTCP2_MAX_UDP_PAYLOAD_SIZE;
#define QUIC_STRINGS(V) \
V(ack_delay_exponent, "ackDelayExponent") \
V(active_connection_id_limit, "activeConnectionIDLimit") \
V(alpn, "alpn") \
V(ca, "ca") \
V(certs, "certs") \
V(crl, "crl") \
V(ciphers, "ciphers") \
V(disable_active_migration, "disableActiveMigration") \
V(enable_tls_trace, "tlsTrace") \
V(endpoint, "Endpoint") \
V(endpoint_udp, "Endpoint::UDP") \
V(groups, "groups") \
V(hostname, "hostname") \
V(http3_alpn, &NGHTTP3_ALPN_H3[1]) \
V(initial_max_data, "initialMaxData") \
V(initial_max_stream_data_bidi_local, "initialMaxStreamDataBidiLocal") \
V(initial_max_stream_data_bidi_remote, "initialMaxStreamDataBidiRemote") \
V(initial_max_stream_data_uni, "initialMaxStreamDataUni") \
V(initial_max_streams_bidi, "initialMaxStreamsBidi") \
V(initial_max_streams_uni, "initialMaxStreamsUni") \
V(keylog, "keylog") \
V(keys, "keys") \
V(logstream, "LogStream") \
V(max_ack_delay, "maxAckDelay") \
V(max_datagram_frame_size, "maxDatagramFrameSize") \
V(max_idle_timeout, "maxIdleTimeout") \
V(packetwrap, "PacketWrap") \
V(reject_unauthorized, "rejectUnauthorized") \
V(request_peer_certificate, "requestPeerCertificate") \
V(session, "Session") \
V(stream, "Stream")
V(session_id_ctx, "sessionIDContext") \
V(stream, "Stream") \
V(verify_hostname_identity, "verifyHostnameIdentity")

// =============================================================================
// The BindingState object holds state for the internalBinding('quic') binding
Expand Down Expand Up @@ -115,12 +131,14 @@ class BindingData final
// bridge out to the JS API.
static void SetCallbacks(const v8::FunctionCallbackInfo<v8::Value>& args);

// TODO(@jasnell) This will be added when Endpoint is implemented.
// // A set of listening Endpoints. We maintain this to ensure that the
// Endpoint
// // cannot be gc'd while it is still listening and there are active
// // connections.
// std::unordered_map<Endpoint*, BaseObjectPtr<Endpoint>> listening_endpoints;
std::vector<BaseObjectPtr<BaseObject>> packet_freelist;

// Purge the packet free list to free up memory.
static void FlushPacketFreelist(
const v8::FunctionCallbackInfo<v8::Value>& args);

bool in_ngtcp2_callback_scope = false;
bool in_nghttp3_callback_scope = false;

// The following set up various storage and accessors for common strings,
// construction templates, and callbacks stored on the BindingData. These
Expand Down Expand Up @@ -166,6 +184,25 @@ class BindingData final
#undef V
};

void IllegalConstructor(const v8::FunctionCallbackInfo<v8::Value>& args);

// The ngtcp2 and nghttp3 callbacks have certain restrictions
// that forbid re-entry. We provide the following scopes for
// use in those to help protect against it.
struct NgTcp2CallbackScope {
Environment* env;
NgTcp2CallbackScope(Environment* env);
~NgTcp2CallbackScope();
static bool in_ngtcp2_callback(Environment* env);
};

struct NgHttp3CallbackScope {
Environment* env;
NgHttp3CallbackScope(Environment* env);
~NgHttp3CallbackScope();
static bool in_nghttp3_callback(Environment* env);
};

} // namespace quic
} // namespace node

Expand Down
48 changes: 48 additions & 0 deletions src/quic/defs.h
Original file line number Diff line number Diff line change
@@ -1,12 +1,28 @@
#pragma once

#include <aliased_struct.h>
#include <env.h>
#include <node_errors.h>
#include <uv.h>
#include <v8.h>

namespace node {
namespace quic {

template <typename Opt, std::string Opt::*member>
bool SetOption(Environment* env,
Opt* options,
const v8::Local<v8::Object>& object,
const v8::Local<v8::String>& name) {
v8::Local<v8::Value> value;
if (!object->Get(env->context(), name).ToLocal(&value)) return false;
if (!value->IsUndefined()) {
Utf8Value utf8(env->isolate(), value);
options->*member = *utf8;
}
return true;
}

template <typename Opt, bool Opt::*member>
bool SetOption(Environment* env,
Opt* options,
Expand Down Expand Up @@ -50,5 +66,37 @@ bool SetOption(Environment* env,
return true;
}

// Utilities used to update the stats for Endpoint, Session, and Stream
// objects. The stats themselves are maintained in an AliasedStruct within
// each of the relevant classes.

template <typename Stats, uint64_t Stats::*member>
void IncrementStat(Stats* stats, uint64_t amt = 1) {
stats->*member += amt;
}

template <typename Stats, uint64_t Stats::*member>
void RecordTimestampStat(Stats* stats) {
stats->*member = uv_hrtime();
}

template <typename Stats, uint64_t Stats::*member>
void SetStat(Stats* stats, uint64_t val) {
stats->*member = val;
}

template <typename Stats, uint64_t Stats::*member>
uint64_t GetStat(Stats* stats) {
return stats->*member;
}

#define STAT_INCREMENT(Type, name) IncrementStat<Type, &Type::name>(&stats_);
#define STAT_INCREMENT_N(Type, name, amt) \
IncrementStat<Type, &Type::name>(&stats_, amt);
#define STAT_RECORD_TIMESTAMP(Type, name) \
RecordTimestampStat<Type, &Type::name>(&stats_);
#define STAT_SET(Type, name, val) SetStat<Type, &Type::name>(&stats_, val);
#define STAT_GET(Type, name) GetStat<Type, &Type::name>(&stats_);

} // namespace quic
} // namespace node
Loading