Skip to content

Commit

Permalink
quic: add more QUIC impl
Browse files Browse the repository at this point in the history
* add BindingData
* add LogStream
* add TransportParams

PR-URL: #47348
Reviewed-By: Yagiz Nizipli <[email protected]>
Reviewed-By: Stephen Belanger <[email protected]>
  • Loading branch information
jasnell authored and RafaelGSS committed Apr 13, 2023
1 parent 757a586 commit f8b4e26
Show file tree
Hide file tree
Showing 11 changed files with 1,017 additions and 1 deletion.
6 changes: 6 additions & 0 deletions node.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -337,16 +337,22 @@
'src/node_crypto.h',
],
'node_quic_sources': [
'src/quic/bindingdata.cc',
'src/quic/cid.cc',
'src/quic/data.cc',
'src/quic/logstream.cc',
'src/quic/preferredaddress.cc',
'src/quic/sessionticket.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/preferredaddress.h',
'src/quic/sessionticket.h',
'src/quic/tokens.h',
'src/quic/transportparams.h',
],
'node_mksnapshot_exec': '<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)node_mksnapshot<(EXECUTABLE_SUFFIX)',
'conditions': [
Expand Down
1 change: 1 addition & 0 deletions src/async_wrap.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ namespace node {
V(PROCESSWRAP) \
V(PROMISE) \
V(QUERYWRAP) \
V(QUIC_LOGSTREAM) \
V(SHUTDOWNWRAP) \
V(SIGNALWRAP) \
V(STATWATCHER) \
Expand Down
3 changes: 2 additions & 1 deletion src/base_object_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ namespace node {

#define UNSERIALIZABLE_BINDING_TYPES(V) \
V(http2_binding_data, http2::BindingData) \
V(http_parser_binding_data, http_parser::BindingData)
V(http_parser_binding_data, http_parser::BindingData) \
V(quic_binding_data, quic::BindingData)

// List of (non-binding) BaseObjects that are serializable in the snapshot.
// The first argument should match what the type passes to
Expand Down
165 changes: 165 additions & 0 deletions src/quic/bindingdata.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
#if HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC
#include "bindingdata.h"
#include <base_object-inl.h>
#include <env-inl.h>
#include <memory_tracker-inl.h>
#include <nghttp3/nghttp3.h>
#include <ngtcp2/ngtcp2.h>
#include <node.h>
#include <node_errors.h>
#include <node_external_reference.h>
#include <node_mem-inl.h>
#include <node_realm-inl.h>
#include <v8.h>

namespace node {

using v8::Function;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
using v8::Local;
using v8::Object;
using v8::String;
using v8::Value;

namespace quic {

BindingData& BindingData::Get(Environment* env) {
return *Realm::GetBindingData<BindingData>(env->context());
}

BindingData::operator ngtcp2_mem() {
return MakeAllocator();
}

BindingData::operator nghttp3_mem() {
ngtcp2_mem allocator = *this;
nghttp3_mem http3_allocator = {
allocator.user_data,
allocator.malloc,
allocator.free,
allocator.calloc,
allocator.realloc,
};
return http3_allocator;
}

void BindingData::CheckAllocatedSize(size_t previous_size) const {
CHECK_GE(current_ngtcp2_memory_, previous_size);
}

void BindingData::IncreaseAllocatedSize(size_t size) {
current_ngtcp2_memory_ += size;
}

void BindingData::DecreaseAllocatedSize(size_t size) {
current_ngtcp2_memory_ -= size;
}

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

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

BindingData::BindingData(Realm* realm, Local<Object> object)
: BaseObject(realm, object) {
MakeWeak();
}

void BindingData::MemoryInfo(MemoryTracker* tracker) const {
#define V(name, _) tracker->TrackField(#name, name##_callback());

QUIC_JS_CALLBACKS(V)

#undef V

#define V(name, _) tracker->TrackField(#name, name##_string());

QUIC_STRINGS(V)

#undef V
}

#define V(name) \
void BindingData::set_##name##_constructor_template( \
Local<FunctionTemplate> tmpl) { \
name##_constructor_template_.Reset(env()->isolate(), tmpl); \
} \
Local<FunctionTemplate> BindingData::name##_constructor_template() const { \
return PersistentToLocal::Default(env()->isolate(), \
name##_constructor_template_); \
}

QUIC_CONSTRUCTORS(V)

#undef V

#define V(name, _) \
void BindingData::set_##name##_callback(Local<Function> fn) { \
name##_callback_.Reset(env()->isolate(), fn); \
} \
Local<Function> BindingData::name##_callback() const { \
return PersistentToLocal::Default(env()->isolate(), name##_callback_); \
}

QUIC_JS_CALLBACKS(V)

#undef V

#define V(name, value) \
Local<String> BindingData::name##_string() const { \
if (name##_string_.IsEmpty()) \
name##_string_.Set(env()->isolate(), \
OneByteString(env()->isolate(), value)); \
return name##_string_.Get(env()->isolate()); \
}

QUIC_STRINGS(V)

#undef V

#define V(name, value) \
Local<String> BindingData::on_##name##_string() const { \
if (on_##name##_string_.IsEmpty()) \
on_##name##_string_.Set( \
env()->isolate(), \
FIXED_ONE_BYTE_STRING(env()->isolate(), "on" #value)); \
return on_##name##_string_.Get(env()->isolate()); \
}

QUIC_JS_CALLBACKS(V)

#undef V

void BindingData::SetCallbacks(const FunctionCallbackInfo<Value>& args) {
auto env = Environment::GetCurrent(args);
auto isolate = env->isolate();
BindingData& state = BindingData::Get(env);
CHECK(args[0]->IsObject());
Local<Object> obj = args[0].As<Object>();

#define V(name, key) \
do { \
Local<Value> val; \
if (!obj->Get(env->context(), state.on_##name##_string()).ToLocal(&val) || \
!val->IsFunction()) { \
return THROW_ERR_MISSING_ARGS(isolate, "Missing Callback: on" #key); \
} \
state.set_##name##_callback(val.As<Function>()); \
} while (0);

QUIC_JS_CALLBACKS(V)

#undef V
}

} // namespace quic
} // namespace node

#endif // HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC
173 changes: 173 additions & 0 deletions src/quic/bindingdata.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
#pragma once

#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
#if HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC

#include <base_object.h>
#include <env.h>
#include <memory_tracker.h>
#include <nghttp3/nghttp3.h>
#include <ngtcp2/ngtcp2.h>
#include <ngtcp2/ngtcp2_crypto.h>
#include <node.h>
#include <node_mem.h>
#include <v8.h>

namespace node {
namespace quic {

class Endpoint;

enum class Side {
CLIENT = NGTCP2_CRYPTO_SIDE_CLIENT,
SERVER = NGTCP2_CRYPTO_SIDE_SERVER,
};

constexpr size_t kDefaultMaxPacketLength = NGTCP2_MAX_UDP_PAYLOAD_SIZE;

// ============================================================================

// The FunctionTemplates the BindingData will store for us.
#define QUIC_CONSTRUCTORS(V) \
V(endpoint) \
V(logstream) \
V(packet) \
V(session) \
V(stream) \
V(udp)

// The callbacks are persistent v8::Function references that are set in the
// quic::BindingState used to communicate data and events back out to the JS
// environment. They are set once from the JavaScript side when the
// internalBinding('quic') is first loaded.
#define QUIC_JS_CALLBACKS(V) \
V(endpoint_close, EndpointClose) \
V(endpoint_error, EndpointError) \
V(session_new, SessionNew) \
V(session_close, SessionClose) \
V(session_error, SessionError) \
V(session_datagram, SessionDatagram) \
V(session_datagram_status, SessionDatagramStatus) \
V(session_handshake, SessionHandshake) \
V(session_ticket, SessionTicket) \
V(session_version_negotiation, SessionVersionNegotiation) \
V(session_path_validation, SessionPathValidation) \
V(stream_close, StreamClose) \
V(stream_error, StreamError) \
V(stream_created, StreamCreated) \
V(stream_reset, StreamReset) \
V(stream_headers, StreamHeaders) \
V(stream_blocked, StreamBlocked) \
V(stream_trailers, StreamTrailers)

// The various JS strings the implementation uses.
#define QUIC_STRINGS(V) \
V(ack_delay_exponent, "ackDelayExponent") \
V(active_connection_id_limit, "activeConnectionIDLimit") \
V(disable_active_migration, "disableActiveMigration") \
V(endpoint, "Endpoint") \
V(endpoint_udp, "Endpoint::UDP") \
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(logstream, "LogStream") \
V(max_ack_delay, "maxAckDelay") \
V(max_datagram_frame_size, "maxDatagramFrameSize") \
V(max_idle_timeout, "maxIdleTimeout") \
V(packetwrap, "PacketWrap") \
V(session, "Session") \
V(stream, "Stream")

// =============================================================================
// The BindingState object holds state for the internalBinding('quic') binding
// instance. It is mostly used to hold the persistent constructors, strings, and
// callback references used for the rest of the implementation.
//
// TODO(@jasnell): Make this snapshotable?
class BindingData final
: public BaseObject,
public mem::NgLibMemoryManager<BindingData, ngtcp2_mem> {
public:
SET_BINDING_ID(quic_binding_data)
static void Initialize(Environment* env, v8::Local<v8::Object> target);
static void RegisterExternalReferences(ExternalReferenceRegistry* registry);

static BindingData& Get(Environment* env);

BindingData(Realm* realm, v8::Local<v8::Object> object);

void MemoryInfo(MemoryTracker* tracker) const override;
SET_MEMORY_INFO_NAME(BindingData)
SET_SELF_SIZE(BindingData)

// NgLibMemoryManager
operator ngtcp2_mem();
operator nghttp3_mem();
void CheckAllocatedSize(size_t previous_size) const;
void IncreaseAllocatedSize(size_t size);
void DecreaseAllocatedSize(size_t size);

// Installs the set of JavaScript callback functions that are used to
// 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;

// The following set up various storage and accessors for common strings,
// construction templates, and callbacks stored on the BindingData. These
// are all defined in defs.h

#define V(name) \
void set_##name##_constructor_template( \
v8::Local<v8::FunctionTemplate> tmpl); \
v8::Local<v8::FunctionTemplate> name##_constructor_template() const;
QUIC_CONSTRUCTORS(V)
#undef V

#define V(name, _) \
void set_##name##_callback(v8::Local<v8::Function> fn); \
v8::Local<v8::Function> name##_callback() const;
QUIC_JS_CALLBACKS(V)
#undef V

#define V(name, _) v8::Local<v8::String> name##_string() const;
QUIC_STRINGS(V)
#undef V

#define V(name, _) v8::Local<v8::String> on_##name##_string() const;
QUIC_JS_CALLBACKS(V)
#undef V

size_t current_ngtcp2_memory_ = 0;

#define V(name) v8::Global<v8::FunctionTemplate> name##_constructor_template_;
QUIC_CONSTRUCTORS(V)
#undef V

#define V(name, _) v8::Global<v8::Function> name##_callback_;
QUIC_JS_CALLBACKS(V)
#undef V

#define V(name, _) mutable v8::Eternal<v8::String> name##_string_;
QUIC_STRINGS(V)
#undef V

#define V(name, _) mutable v8::Eternal<v8::String> on_##name##_string_;
QUIC_JS_CALLBACKS(V)
#undef V
};

} // namespace quic
} // namespace node

#endif // HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
Loading

0 comments on commit f8b4e26

Please sign in to comment.