quic: reduce boilerplate and other minor cleanups

While I get that macros aren't the most loved thing in
the world, they do help reduce boilerplate, and there's
a lot of boilerplate in the QUIC code. This commit cleans
up some of that boilerplate, particularly around the
use of v8 APIs.

PR-URL: https://github.com/nodejs/node/pull/59342
Reviewed-By: Stephen Belanger <admin@stephenbelanger.com>
Reviewed-By: Ethan Arrowood <ethan@arrowood.dev>
This commit is contained in:
James M Snell 2025-08-10 08:29:59 -07:00
parent cd9fd09a27
commit b4af647920
33 changed files with 291 additions and 418 deletions

View File

@ -1826,16 +1826,6 @@ added: v21.2.0
Disable exposition of [Navigator API][] on the global scope.
### `--no-experimental-quic`
<!-- YAML
added: REPLACEME
-->
> Stability: 1.1 - Active Development
Use this flag to disable QUIC.
### `--no-experimental-repl-await`
<!-- YAML
@ -3462,6 +3452,7 @@ one is included in the list below.
* `--experimental-loader`
* `--experimental-modules`
* `--experimental-print-required-tla`
* `--experimental-quic`
* `--experimental-require-module`
* `--experimental-shadow-realm`
* `--experimental-specifier-resolution`

View File

@ -226,9 +226,6 @@ flag is no longer required as WASI is enabled by default.
.It Fl -experimental-quic
Enable the experimental QUIC support.
.
.It Fl -no-experimental-quic
Disable the experimental QUIC support.
.
.It Fl -experimental-inspector-network-resource
Enable experimental support for inspector network resources.
.

View File

@ -15,11 +15,14 @@ const {
Uint8Array,
} = primordials;
// QUIC requires that Node.js be compiled with crypto support.
const {
assertCrypto,
} = require('internal/util');
assertCrypto();
getOptionValue,
} = require('internal/options');
// QUIC requires that Node.js be compiled with crypto support.
if (!process.features.quic || !getOptionValue('--experimental-quic')) {
return;
}
const { inspect } = require('internal/util/inspect');

View File

@ -10,6 +10,14 @@ const {
JSONStringify,
} = primordials;
const {
getOptionValue,
} = require('internal/options');
if (!process.features.quic || !getOptionValue('--experimental-quic')) {
return;
}
const {
codes: {
ERR_ILLEGAL_CONSTRUCTOR,

View File

@ -5,6 +5,14 @@ const {
JSONStringify,
} = primordials;
const {
getOptionValue,
} = require('internal/options');
if (!process.features.quic || !getOptionValue('--experimental-quic')) {
return;
}
const {
isArrayBuffer,
} = require('util/types');

View File

@ -4,6 +4,14 @@ const {
Symbol,
} = primordials;
const {
getOptionValue,
} = require('internal/options');
if (!process.features.quic || !getOptionValue('--experimental-quic')) {
return;
}
const {
customInspectSymbol: kInspect,
} = require('internal/util');

View File

@ -60,6 +60,8 @@ namespace node {
V(simdutf) \
V(ada) \
V(nbytes) \
V(ngtcp2) \
V(nghttp3) \
NODE_VERSIONS_KEY_AMARO(V) \
NODE_VERSIONS_KEY_UNDICI(V) \
V(cjs_module_lexer)
@ -80,14 +82,6 @@ namespace node {
#define NODE_VERSIONS_KEY_INTL(V)
#endif // NODE_HAVE_I18N_SUPPORT
#ifndef OPENSSL_NO_QUIC
#define NODE_VERSIONS_KEY_QUIC(V) \
V(ngtcp2) \
V(nghttp3)
#else
#define NODE_VERSIONS_KEY_QUIC(V)
#endif
#if HAVE_SQLITE
#define NODE_VERSIONS_KEY_SQLITE(V) V(sqlite)
#else
@ -98,7 +92,6 @@ namespace node {
NODE_VERSIONS_KEYS_BASE(V) \
NODE_VERSIONS_KEY_CRYPTO(V) \
NODE_VERSIONS_KEY_INTL(V) \
NODE_VERSIONS_KEY_QUIC(V) \
NODE_VERSIONS_KEY_SQLITE(V)
#define V(key) +1

View File

@ -551,10 +551,11 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
kAllowedInEnvvar,
true);
AddOption("--experimental-quic",
"" /* undocumented until its development */,
#ifndef OPENSSL_NO_QUIC
"experimental QUIC support",
&EnvironmentOptions::experimental_quic,
#else
"" /* undocumented when no-op */,
NoOp{},
#endif
kAllowedInEnvvar);

View File

@ -17,7 +17,6 @@
namespace node {
using v8::Function;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
using v8::Local;
using v8::Object;
@ -144,7 +143,7 @@ QUIC_JS_CALLBACKS(V)
#undef V
void BindingData::SetCallbacks(const FunctionCallbackInfo<Value>& args) {
JS_METHOD_IMPL(BindingData::SetCallbacks) {
auto env = Environment::GetCurrent(args);
auto isolate = env->isolate();
auto& state = Get(env);
@ -166,7 +165,7 @@ void BindingData::SetCallbacks(const FunctionCallbackInfo<Value>& args) {
#undef V
}
void BindingData::FlushPacketFreelist(const FunctionCallbackInfo<Value>& args) {
JS_METHOD_IMPL(BindingData::FlushPacketFreelist) {
auto env = Environment::GetCurrent(args);
auto& state = Get(env);
state.packet_freelist.clear();
@ -217,7 +216,7 @@ CallbackScopeBase::~CallbackScopeBase() {
}
}
void IllegalConstructor(const FunctionCallbackInfo<Value>& args) {
JS_METHOD_IMPL(IllegalConstructor) {
THROW_ERR_ILLEGAL_CONSTRUCTOR(Environment::GetCurrent(args));
}

View File

@ -167,16 +167,15 @@ class BindingData final
// 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);
JS_METHOD(SetCallbacks);
// Purge the packet free list to free up memory.
JS_METHOD(FlushPacketFreelist);
std::vector<BaseObjectPtr<BaseObject>> packet_freelist;
std::unordered_map<Endpoint*, BaseObjectPtr<BaseObject>> listening_endpoints;
// 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;
size_t current_ngtcp2_memory_ = 0;
@ -223,7 +222,7 @@ class BindingData final
#undef V
};
void IllegalConstructor(const v8::FunctionCallbackInfo<v8::Value>& args);
JS_METHOD_IMPL(IllegalConstructor);
// The ngtcp2 and nghttp3 callbacks have certain restrictions
// that forbid re-entry. We provide the following scopes for

View File

@ -375,8 +375,6 @@ QuicError QuicError::FromConnectionClose(ngtcp2_conn* session) {
return QuicError(ngtcp2_conn_get_ccerr(session));
}
const QuicError QuicError::TRANSPORT_NO_ERROR =
ForTransport(TransportError::NO_ERROR_);
#define V(name) \
const QuicError QuicError::TRANSPORT_##name = \
ForTransport(TransportError::name);

View File

@ -156,9 +156,6 @@ class QuicError final : public MemoryRetainer {
// it as is.
NO_ERROR_ = NGTCP2_NO_ERROR,
#define V(name) name = NGTCP2_##name,
// The NO_ERROR here has to be treated as a special case since there
// is a NO_ERROR macro defined in Windows that conflicts.
NO_ERROR_ = NGTCP2_NO_ERROR,
QUIC_TRANSPORT_ERRORS(V)
#undef V
};

View File

@ -161,8 +161,96 @@ uint64_t GetStat(Stats* stats) {
name##_STATS(STAT_FIELD) \
};
#define JS_METHOD(name) \
static void name(const v8::FunctionCallbackInfo<v8::Value>& args)
#define JS_METHOD_IMPL(name) \
void name(const v8::FunctionCallbackInfo<v8::Value>& args)
#define JS_METHOD(name) static JS_METHOD_IMPL(name)
#define JS_CONSTRUCTOR(name) \
inline static bool HasInstance(Environment* env, \
v8::Local<v8::Value> value) { \
return GetConstructorTemplate(env)->HasInstance(value); \
} \
static v8::Local<v8::FunctionTemplate> GetConstructorTemplate( \
Environment* env)
#define JS_CONSTRUCTOR_IMPL(name, template, body) \
v8::Local<v8::FunctionTemplate> name::GetConstructorTemplate( \
Environment* env) { \
auto& state = BindingData::Get(env); \
auto tmpl = state.template(); \
if (tmpl.IsEmpty()) { \
body state.set_##template(tmpl); \
} \
return tmpl; \
}
#define JS_ILLEGAL_CONSTRUCTOR() \
tmpl = NewFunctionTemplate(env->isolate(), IllegalConstructor)
#define JS_NEW_CONSTRUCTOR() tmpl = NewFunctionTemplate(env->isolate(), New)
#define JS_INHERIT(name) tmpl->Inherit(name::GetConstructorTemplate(env));
#define JS_CLASS(name) \
tmpl->InstanceTemplate()->SetInternalFieldCount(kInternalFieldCount); \
tmpl->SetClassName(state.name##_string())
#define JS_CLASS_FIELDS(name, fields) \
tmpl->InstanceTemplate()->SetInternalFieldCount(fields); \
tmpl->SetClassName(state.name##_string())
#define JS_NEW_INSTANCE_OR_RETURN(env, name, ret) \
v8::Local<v8::Object> name; \
if (!GetConstructorTemplate(env) \
->InstanceTemplate() \
->NewInstance(env->context()) \
.ToLocal(&obj)) { \
return ret; \
}
#define JS_NEW_INSTANCE(env, name) \
v8::Local<v8::Object> name; \
if (!GetConstructorTemplate(env) \
->InstanceTemplate() \
->NewInstance(env->context()) \
.ToLocal(&obj)) { \
return; \
}
#define JS_BINDING_INIT_BOILERPLATE() \
static void InitPerIsolate(IsolateData* isolate_data, \
v8::Local<v8::ObjectTemplate> target); \
static void InitPerContext(Realm* realm, v8::Local<v8::Object> target); \
static void RegisterExternalReferences(ExternalReferenceRegistry* registry)
#define JS_TRY_ALLOCATE_BACKING(env, name, len) \
auto name = v8::ArrayBuffer::NewBackingStore( \
env->isolate(), \
len, \
v8::BackingStoreInitializationMode::kUninitialized, \
v8::BackingStoreOnFailureMode::kReturnNull); \
if (!name) { \
THROW_ERR_MEMORY_ALLOCATION_FAILED(env); \
return; \
}
#define JS_TRY_ALLOCATE_BACKING_OR_RETURN(env, name, len, ret) \
auto name = v8::ArrayBuffer::NewBackingStore( \
env->isolate(), \
len, \
v8::BackingStoreInitializationMode::kUninitialized, \
v8::BackingStoreOnFailureMode::kReturnNull); \
if (!name) { \
THROW_ERR_MEMORY_ALLOCATION_FAILED(env); \
return ret; \
}
#define JS_DEFINE_READONLY_PROPERTY(env, target, name, value) \
target \
->DefineOwnProperty( \
env->context(), name, value, v8::PropertyAttribute::ReadOnly) \
.Check();
enum class Side : uint8_t {
CLIENT,
@ -222,7 +310,7 @@ CC_ALGOS(V)
#undef V
constexpr size_t kDefaultMaxPacketLength = NGTCP2_MAX_UDP_PAYLOAD_SIZE;
constexpr size_t kMaxSizeT = std::numeric_limits<size_t>::max();
constexpr uint64_t kMaxSizeT = std::numeric_limits<size_t>::max();
constexpr uint64_t kMaxSafeJsInteger = 9007199254740991;
constexpr auto kSocketAddressInfoTimeout = 60 * NGTCP2_SECONDS;
constexpr size_t kMaxVectorCount = 16;

View File

@ -27,8 +27,6 @@ namespace node {
using v8::ArrayBufferView;
using v8::BackingStore;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
using v8::HandleScope;
using v8::Integer;
using v8::Just;
@ -38,7 +36,6 @@ using v8::Nothing;
using v8::Number;
using v8::Object;
using v8::ObjectTemplate;
using v8::PropertyAttribute;
using v8::String;
using v8::Uint32;
using v8::Value;
@ -284,27 +281,10 @@ std::string Endpoint::Options::ToString() const {
class Endpoint::UDP::Impl final : public HandleWrap {
public:
static Local<FunctionTemplate> GetConstructorTemplate(Environment* env) {
auto& state = BindingData::Get(env);
auto tmpl = state.udp_constructor_template();
if (tmpl.IsEmpty()) {
tmpl = NewFunctionTemplate(env->isolate(), IllegalConstructor);
tmpl->Inherit(HandleWrap::GetConstructorTemplate(env));
tmpl->InstanceTemplate()->SetInternalFieldCount(kInternalFieldCount);
tmpl->SetClassName(state.endpoint_udp_string());
state.set_udp_constructor_template(tmpl);
}
return tmpl;
}
JS_CONSTRUCTOR(Impl);
static Impl* Create(Endpoint* endpoint) {
Local<Object> obj;
if (!GetConstructorTemplate(endpoint->env())
->InstanceTemplate()
->NewInstance(endpoint->env()->context())
.ToLocal(&obj)) {
return nullptr;
}
JS_NEW_INSTANCE_OR_RETURN(endpoint->env(), obj, nullptr);
return new Impl(endpoint, obj);
}
@ -367,6 +347,12 @@ class Endpoint::UDP::Impl final : public HandleWrap {
friend class UDP;
};
JS_CONSTRUCTOR_IMPL(Endpoint::UDP::Impl, udp_constructor_template, {
JS_ILLEGAL_CONSTRUCTOR();
JS_INHERIT(HandleWrap);
JS_CLASS(endpoint_udp);
})
Endpoint::UDP::UDP(Endpoint* endpoint) : impl_(Impl::Create(endpoint)) {
DCHECK(impl_);
// The endpoint starts in an inactive, unref'd state. It will be ref'd when
@ -512,29 +498,18 @@ void Endpoint::UDP::MemoryInfo(MemoryTracker* tracker) const {
// ============================================================================
bool Endpoint::HasInstance(Environment* env, Local<Value> value) {
return GetConstructorTemplate(env)->HasInstance(value);
}
Local<FunctionTemplate> Endpoint::GetConstructorTemplate(Environment* env) {
auto& state = BindingData::Get(env);
auto tmpl = state.endpoint_constructor_template();
if (tmpl.IsEmpty()) {
auto isolate = env->isolate();
tmpl = NewFunctionTemplate(isolate, New);
tmpl->Inherit(AsyncWrap::GetConstructorTemplate(env));
tmpl->SetClassName(state.endpoint_string());
tmpl->InstanceTemplate()->SetInternalFieldCount(kInternalFieldCount);
SetProtoMethod(isolate, tmpl, "listen", DoListen);
SetProtoMethod(isolate, tmpl, "closeGracefully", DoCloseGracefully);
SetProtoMethod(isolate, tmpl, "connect", DoConnect);
SetProtoMethod(isolate, tmpl, "markBusy", MarkBusy);
SetProtoMethod(isolate, tmpl, "ref", Ref);
SetProtoMethodNoSideEffect(isolate, tmpl, "address", LocalAddress);
state.set_endpoint_constructor_template(tmpl);
}
return tmpl;
}
JS_CONSTRUCTOR_IMPL(Endpoint, endpoint_constructor_template, {
auto isolate = env->isolate();
JS_NEW_CONSTRUCTOR();
JS_INHERIT(AsyncWrap);
JS_CLASS(endpoint);
SetProtoMethod(isolate, tmpl, "listen", DoListen);
SetProtoMethod(isolate, tmpl, "closeGracefully", DoCloseGracefully);
SetProtoMethod(isolate, tmpl, "connect", DoConnect);
SetProtoMethod(isolate, tmpl, "markBusy", MarkBusy);
SetProtoMethod(isolate, tmpl, "ref", Ref);
SetProtoMethodNoSideEffect(isolate, tmpl, "address", LocalAddress);
})
void Endpoint::InitPerIsolate(IsolateData* data, Local<ObjectTemplate> target) {
// TODO(@jasnell): Implement the per-isolate state
@ -576,17 +551,17 @@ void Endpoint::InitPerContext(Realm* realm, Local<Object> target) {
NODE_DEFINE_CONSTANT(target, DEFAULT_MAX_PACKET_LENGTH);
static constexpr auto CLOSECONTEXT_CLOSE =
static_cast<int>(CloseContext::CLOSE);
static_cast<uint8_t>(CloseContext::CLOSE);
static constexpr auto CLOSECONTEXT_BIND_FAILURE =
static_cast<int>(CloseContext::BIND_FAILURE);
static_cast<uint8_t>(CloseContext::BIND_FAILURE);
static constexpr auto CLOSECONTEXT_LISTEN_FAILURE =
static_cast<int>(CloseContext::LISTEN_FAILURE);
static_cast<uint8_t>(CloseContext::LISTEN_FAILURE);
static constexpr auto CLOSECONTEXT_RECEIVE_FAILURE =
static_cast<int>(CloseContext::RECEIVE_FAILURE);
static_cast<uint8_t>(CloseContext::RECEIVE_FAILURE);
static constexpr auto CLOSECONTEXT_SEND_FAILURE =
static_cast<int>(CloseContext::SEND_FAILURE);
static_cast<uint8_t>(CloseContext::SEND_FAILURE);
static constexpr auto CLOSECONTEXT_START_FAILURE =
static_cast<int>(CloseContext::START_FAILURE);
static_cast<uint8_t>(CloseContext::START_FAILURE);
NODE_DEFINE_CONSTANT(target, CLOSECONTEXT_CLOSE);
NODE_DEFINE_CONSTANT(target, CLOSECONTEXT_BIND_FAILURE);
NODE_DEFINE_CONSTANT(target, CLOSECONTEXT_LISTEN_FAILURE);
@ -628,15 +603,10 @@ Endpoint::Endpoint(Environment* env,
Debug(this, "Endpoint created. Options %s", options.ToString());
}
const auto defineProperty = [&](auto name, auto value) {
object
->DefineOwnProperty(
env->context(), name, value, PropertyAttribute::ReadOnly)
.Check();
};
defineProperty(env->state_string(), state_.GetArrayBuffer());
defineProperty(env->stats_string(), stats_.GetArrayBuffer());
JS_DEFINE_READONLY_PROPERTY(
env, object, env->state_string(), state_.GetArrayBuffer());
JS_DEFINE_READONLY_PROPERTY(
env, object, env->stats_string(), stats_.GetArrayBuffer());
}
SocketAddress Endpoint::local_address() const {
@ -1676,7 +1646,7 @@ void Endpoint::EmitClose(CloseContext context, int status) {
// ======================================================================================
// Endpoint JavaScript API
void Endpoint::New(const FunctionCallbackInfo<Value>& args) {
JS_METHOD_IMPL(Endpoint::New) {
DCHECK(args.IsConstructCall());
auto env = Environment::GetCurrent(args);
Options options;
@ -1689,7 +1659,7 @@ void Endpoint::New(const FunctionCallbackInfo<Value>& args) {
new Endpoint(env, args.This(), options);
}
void Endpoint::DoConnect(const FunctionCallbackInfo<Value>& args) {
JS_METHOD_IMPL(Endpoint::DoConnect) {
auto env = Environment::GetCurrent(args);
Endpoint* endpoint;
ASSIGN_OR_RETURN_UNWRAP(&endpoint, args.This());
@ -1723,7 +1693,7 @@ void Endpoint::DoConnect(const FunctionCallbackInfo<Value>& args) {
if (session) args.GetReturnValue().Set(session->object());
}
void Endpoint::DoListen(const FunctionCallbackInfo<Value>& args) {
JS_METHOD_IMPL(Endpoint::DoListen) {
Endpoint* endpoint;
ASSIGN_OR_RETURN_UNWRAP(&endpoint, args.This());
auto env = Environment::GetCurrent(args);
@ -1734,19 +1704,19 @@ void Endpoint::DoListen(const FunctionCallbackInfo<Value>& args) {
}
}
void Endpoint::MarkBusy(const FunctionCallbackInfo<Value>& args) {
JS_METHOD_IMPL(Endpoint::MarkBusy) {
Endpoint* endpoint;
ASSIGN_OR_RETURN_UNWRAP(&endpoint, args.This());
endpoint->MarkAsBusy(args[0]->IsTrue());
}
void Endpoint::DoCloseGracefully(const FunctionCallbackInfo<Value>& args) {
JS_METHOD_IMPL(Endpoint::DoCloseGracefully) {
Endpoint* endpoint;
ASSIGN_OR_RETURN_UNWRAP(&endpoint, args.This());
endpoint->CloseGracefully();
}
void Endpoint::LocalAddress(const FunctionCallbackInfo<Value>& args) {
JS_METHOD_IMPL(Endpoint::LocalAddress) {
auto env = Environment::GetCurrent(args);
Endpoint* endpoint;
ASSIGN_OR_RETURN_UNWRAP(&endpoint, args.This());
@ -1756,7 +1726,7 @@ void Endpoint::LocalAddress(const FunctionCallbackInfo<Value>& args) {
if (addr) args.GetReturnValue().Set(addr->object());
}
void Endpoint::Ref(const FunctionCallbackInfo<Value>& args) {
JS_METHOD_IMPL(Endpoint::Ref) {
Endpoint* endpoint;
ASSIGN_OR_RETURN_UNWRAP(&endpoint, args.This());
auto env = Environment::GetCurrent(args);

View File

@ -24,7 +24,7 @@ namespace node::quic {
class Endpoint final : public AsyncWrap, public Packet::Listener {
public:
static constexpr uint64_t DEFAULT_MAX_CONNECTIONS =
std::min<uint64_t>(kMaxSizeT, static_cast<uint64_t>(kMaxSafeJsInteger));
std::min<uint64_t>(kMaxSizeT, kMaxSafeJsInteger);
static constexpr uint64_t DEFAULT_MAX_CONNECTIONS_PER_HOST = 100;
static constexpr uint64_t DEFAULT_MAX_SOCKETADDRESS_LRU_SIZE =
(DEFAULT_MAX_CONNECTIONS_PER_HOST * 10);
@ -143,13 +143,8 @@ class Endpoint final : public AsyncWrap, public Packet::Listener {
std::string ToString() const;
};
bool HasInstance(Environment* env, v8::Local<v8::Value> value);
static v8::Local<v8::FunctionTemplate> GetConstructorTemplate(
Environment* env);
static void InitPerIsolate(IsolateData* data,
v8::Local<v8::ObjectTemplate> target);
static void InitPerContext(Realm* realm, v8::Local<v8::Object> target);
static void RegisterExternalReferences(ExternalReferenceRegistry* registry);
JS_CONSTRUCTOR(Endpoint);
JS_BINDING_INIT_BOILERPLATE();
Endpoint(Environment* env,
v8::Local<v8::Object> object,
@ -300,7 +295,7 @@ class Endpoint final : public AsyncWrap, public Packet::Listener {
void MaybeDestroy();
// Specifies the general reason the endpoint is being destroyed.
enum class CloseContext {
enum class CloseContext : uint8_t {
CLOSE,
BIND_FAILURE,
START_FAILURE,
@ -330,7 +325,7 @@ class Endpoint final : public AsyncWrap, public Packet::Listener {
// Create a new Endpoint.
// @param Endpoint::Options options - Options to configure the Endpoint.
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
JS_METHOD(New);
// Methods on the Endpoint instance:
@ -341,31 +336,30 @@ class Endpoint final : public AsyncWrap, public Packet::Listener {
// the Session.
// @param v8::ArrayBufferView remote_transport_params - The remote transport
// params.
static void DoConnect(const v8::FunctionCallbackInfo<v8::Value>& args);
JS_METHOD(DoConnect);
// Start listening as a QUIC server
// @param Session::Options options - Options to configure the Session.
static void DoListen(const v8::FunctionCallbackInfo<v8::Value>& args);
JS_METHOD(DoListen);
// Mark the Endpoint as busy, temporarily pausing handling of new initial
// packets.
// @param bool on - If true, mark the Endpoint as busy.
static void MarkBusy(const v8::FunctionCallbackInfo<v8::Value>& args);
JS_METHOD(MarkBusy);
static void FastMarkBusy(v8::Local<v8::Object> receiver, bool on);
// DoCloseGracefully is the signal that endpoint should close. Any packets
// that are already in the queue or in flight will be allowed to finish, but
// the EndpoingWrap will be otherwise no longer able to receive or send
// packets.
static void DoCloseGracefully(
const v8::FunctionCallbackInfo<v8::Value>& args);
JS_METHOD(DoCloseGracefully);
// Get the local address of the Endpoint.
// @return node::SocketAddress - The local address of the Endpoint.
static void LocalAddress(const v8::FunctionCallbackInfo<v8::Value>& args);
JS_METHOD(LocalAddress);
// Ref() causes a listening Endpoint to keep the event loop active.
static void Ref(const v8::FunctionCallbackInfo<v8::Value>& args);
JS_METHOD(Ref);
static void FastRef(v8::Local<v8::Object> receiver, bool on);
void Receive(const uv_buf_t& buf, const SocketAddress& from);

View File

@ -20,34 +20,18 @@
namespace node {
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
using v8::Local;
using v8::Object;
using v8::ObjectTemplate;
using v8::Value;
namespace quic {
// ============================================================================
bool Http3Application::HasInstance(Environment* env, Local<Value> value) {
return GetConstructorTemplate(env)->HasInstance(value);
}
Local<FunctionTemplate> Http3Application::GetConstructorTemplate(
Environment* env) {
auto& state = BindingData::Get(env);
auto tmpl = state.http3application_constructor_template();
if (tmpl.IsEmpty()) {
auto isolate = env->isolate();
tmpl = NewFunctionTemplate(isolate, New);
tmpl->SetClassName(state.http3application_string());
tmpl->InstanceTemplate()->SetInternalFieldCount(kInternalFieldCount);
state.set_http3application_constructor_template(tmpl);
}
return tmpl;
}
JS_CONSTRUCTOR_IMPL(Http3Application, http3application_constructor_template, {
JS_NEW_CONSTRUCTOR();
JS_CLASS(http3application);
})
void Http3Application::InitPerIsolate(IsolateData* isolate_data,
Local<ObjectTemplate> target) {
@ -73,17 +57,11 @@ Http3Application::Http3Application(Environment* env,
MakeWeak();
}
void Http3Application::New(const FunctionCallbackInfo<Value>& args) {
JS_METHOD_IMPL(Http3Application::New) {
Environment* env = Environment::GetCurrent(args);
CHECK(args.IsConstructCall());
Local<Object> obj;
if (!GetConstructorTemplate(env)
->InstanceTemplate()
->NewInstance(env->context())
.ToLocal(&obj)) {
return;
}
JS_NEW_INSTANCE(env, obj);
Session::Application::Options options;
if (!args[0]->IsUndefined() &&

View File

@ -11,13 +11,8 @@ namespace node::quic {
// Provides an implementation of the HTTP/3 Application implementation
class Http3Application final : public Session::ApplicationProvider {
public:
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* realm, v8::Local<v8::Object> target);
static void RegisterExternalReferences(ExternalReferenceRegistry* registry);
JS_CONSTRUCTOR(Http3Application);
JS_BINDING_INIT_BOILERPLATE();
Http3Application(Environment* env,
v8::Local<v8::Object> object,
@ -32,7 +27,7 @@ class Http3Application final : public Session::ApplicationProvider {
std::string ToString() const;
private:
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
JS_METHOD(New);
Session::Application_Options options_;
};

View File

@ -20,29 +20,15 @@ using v8::Object;
namespace quic {
Local<FunctionTemplate> LogStream::GetConstructorTemplate(Environment* env) {
auto& state = BindingData::Get(env);
auto tmpl = state.logstream_constructor_template();
if (tmpl.IsEmpty()) {
tmpl = FunctionTemplate::New(env->isolate());
tmpl->Inherit(AsyncWrap::GetConstructorTemplate(env));
tmpl->InstanceTemplate()->SetInternalFieldCount(
StreamBase::kInternalFieldCount);
tmpl->SetClassName(state.logstream_string());
StreamBase::AddMethods(env, tmpl);
state.set_logstream_constructor_template(tmpl);
}
return tmpl;
}
JS_CONSTRUCTOR_IMPL(LogStream, logstream_constructor_template, {
tmpl = FunctionTemplate::New(env->isolate());
JS_INHERIT(AsyncWrap);
JS_CLASS_FIELDS(logstream, StreamBase::kInternalFieldCount);
StreamBase::AddMethods(env, tmpl);
})
BaseObjectPtr<LogStream> LogStream::Create(Environment* env) {
Local<Object> obj;
if (!GetConstructorTemplate(env)
->InstanceTemplate()
->NewInstance(env->context())
.ToLocal(&obj)) {
return {};
}
JS_NEW_INSTANCE_OR_RETURN(env, obj, nullptr);
return MakeDetachedBaseObject<LogStream>(env, obj);
}

View File

@ -7,6 +7,7 @@
#include <env.h>
#include <stream_base.h>
#include <list>
#include "defs.h"
namespace node::quic {
@ -14,8 +15,7 @@ namespace node::quic {
// and Keylog diagnostic data (one instance for each).
class LogStream final : public AsyncWrap, public StreamBase {
public:
static v8::Local<v8::FunctionTemplate> GetConstructorTemplate(
Environment* env);
JS_CONSTRUCTOR(LogStream);
static BaseObjectPtr<LogStream> Create(Environment* env);

View File

@ -20,7 +20,6 @@
namespace node {
using v8::FunctionTemplate;
using v8::Local;
using v8::Object;
@ -97,18 +96,11 @@ void Packet::Truncate(size_t len) {
data_->data_.SetLength(len);
}
Local<FunctionTemplate> Packet::GetConstructorTemplate(Environment* env) {
auto& state = BindingData::Get(env);
Local<FunctionTemplate> tmpl = state.packet_constructor_template();
if (tmpl.IsEmpty()) {
tmpl = NewFunctionTemplate(env->isolate(), IllegalConstructor);
tmpl->Inherit(ReqWrap<uv_udp_send_t>::GetConstructorTemplate(env));
tmpl->InstanceTemplate()->SetInternalFieldCount(kInternalFieldCount);
tmpl->SetClassName(state.packetwrap_string());
state.set_packet_constructor_template(tmpl);
}
return tmpl;
}
JS_CONSTRUCTOR_IMPL(Packet, packet_constructor_template, {
JS_ILLEGAL_CONSTRUCTOR();
JS_INHERIT(ReqWrap<uv_udp_send_t>);
JS_CLASS(packetwrap);
})
BaseObjectPtr<Packet> Packet::Create(Environment* env,
Listener* listener,
@ -116,14 +108,7 @@ BaseObjectPtr<Packet> Packet::Create(Environment* env,
size_t length,
const char* diagnostic_label) {
if (BindingData::Get(env).packet_freelist.empty()) {
Local<Object> obj;
if (!GetConstructorTemplate(env)
->InstanceTemplate()
->NewInstance(env->context())
.ToLocal(&obj)) [[unlikely]] {
return {};
}
JS_NEW_INSTANCE_OR_RETURN(env, obj, {});
return MakeBaseObject<Packet>(
env, listener, obj, destination, length, diagnostic_label);
}
@ -137,14 +122,7 @@ BaseObjectPtr<Packet> Packet::Create(Environment* env,
BaseObjectPtr<Packet> Packet::Clone() const {
auto& binding = BindingData::Get(env());
if (binding.packet_freelist.empty()) {
Local<Object> obj;
if (!GetConstructorTemplate(env())
->InstanceTemplate()
->NewInstance(env()->context())
.ToLocal(&obj)) [[unlikely]] {
return {};
}
JS_NEW_INSTANCE_OR_RETURN(env(), obj, {});
return MakeBaseObject<Packet>(env(), listener_, obj, destination_, data_);
}

View File

@ -49,8 +49,7 @@ class Packet final : public ReqWrap<uv_udp_send_t> {
struct Data;
public:
static v8::Local<v8::FunctionTemplate> GetConstructorTemplate(
Environment* env);
JS_CONSTRUCTOR(Packet);
class Listener {
public:

View File

@ -37,13 +37,9 @@
namespace node {
using v8::Array;
using v8::ArrayBuffer;
using v8::ArrayBufferView;
using v8::BackingStoreInitializationMode;
using v8::BigInt;
using v8::Boolean;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
using v8::HandleScope;
using v8::Int32;
using v8::Integer;
@ -55,9 +51,7 @@ using v8::MaybeLocal;
using v8::Nothing;
using v8::Object;
using v8::ObjectTemplate;
using v8::PropertyAttribute;
using v8::String;
using v8::Uint32;
using v8::Undefined;
using v8::Value;
@ -319,7 +313,7 @@ bool SetOption(Environment* env,
"The cc_algorithm option is invalid");
return false;
}
algo = static_cast<ngtcp2_cc_algo>(num->Value());
algo = FromV8Value<ngtcp2_cc_algo>(num);
}
options->*member = algo;
}
@ -653,7 +647,7 @@ struct Session::Impl final : public MemoryRetainer {
// JavaScript APIs
static void Destroy(const FunctionCallbackInfo<Value>& args) {
JS_METHOD(Destroy) {
auto env = Environment::GetCurrent(args);
Session* session;
ASSIGN_OR_RETURN_UNWRAP(&session, args.This());
@ -663,7 +657,7 @@ struct Session::Impl final : public MemoryRetainer {
session->Destroy();
}
static void GetRemoteAddress(const FunctionCallbackInfo<Value>& args) {
JS_METHOD(GetRemoteAddress) {
auto env = Environment::GetCurrent(args);
Session* session;
ASSIGN_OR_RETURN_UNWRAP(&session, args.This());
@ -678,7 +672,7 @@ struct Session::Impl final : public MemoryRetainer {
->object());
}
static void GetCertificate(const FunctionCallbackInfo<Value>& args) {
JS_METHOD(GetCertificate) {
auto env = Environment::GetCurrent(args);
Session* session;
ASSIGN_OR_RETURN_UNWRAP(&session, args.This());
@ -692,7 +686,7 @@ struct Session::Impl final : public MemoryRetainer {
args.GetReturnValue().Set(ret);
}
static void GetEphemeralKeyInfo(const FunctionCallbackInfo<Value>& args) {
JS_METHOD(GetEphemeralKeyInfo) {
auto env = Environment::GetCurrent(args);
Session* session;
ASSIGN_OR_RETURN_UNWRAP(&session, args.This());
@ -707,7 +701,7 @@ struct Session::Impl final : public MemoryRetainer {
args.GetReturnValue().Set(ret);
}
static void GetPeerCertificate(const FunctionCallbackInfo<Value>& args) {
JS_METHOD(GetPeerCertificate) {
auto env = Environment::GetCurrent(args);
Session* session;
ASSIGN_OR_RETURN_UNWRAP(&session, args.This());
@ -721,7 +715,7 @@ struct Session::Impl final : public MemoryRetainer {
args.GetReturnValue().Set(ret);
}
static void GracefulClose(const FunctionCallbackInfo<Value>& args) {
JS_METHOD(GracefulClose) {
auto env = Environment::GetCurrent(args);
Session* session;
ASSIGN_OR_RETURN_UNWRAP(&session, args.This());
@ -733,7 +727,7 @@ struct Session::Impl final : public MemoryRetainer {
session->Close(CloseMethod::GRACEFUL);
}
static void SilentClose(const FunctionCallbackInfo<Value>& args) {
JS_METHOD(SilentClose) {
// This is exposed for testing purposes only!
auto env = Environment::GetCurrent(args);
Session* session;
@ -746,7 +740,7 @@ struct Session::Impl final : public MemoryRetainer {
session->Close(CloseMethod::SILENT);
}
static void UpdateKey(const FunctionCallbackInfo<Value>& args) {
JS_METHOD(UpdateKey) {
auto env = Environment::GetCurrent(args);
Session* session;
ASSIGN_OR_RETURN_UNWRAP(&session, args.This());
@ -763,7 +757,7 @@ struct Session::Impl final : public MemoryRetainer {
args.GetReturnValue().Set(session->tls_session().InitiateKeyUpdate());
}
static void OpenStream(const FunctionCallbackInfo<Value>& args) {
JS_METHOD(OpenStream) {
auto env = Environment::GetCurrent(args);
Session* session;
ASSIGN_OR_RETURN_UNWRAP(&session, args.This());
@ -782,7 +776,7 @@ struct Session::Impl final : public MemoryRetainer {
}
SendPendingDataScope send_scope(session);
auto direction = static_cast<Direction>(args[0].As<Uint32>()->Value());
auto direction = FromV8Value<Direction>(args[0]);
Local<Object> stream;
if (session->OpenStream(direction, std::move(data_source)).ToLocal(&stream))
[[likely]] {
@ -790,7 +784,7 @@ struct Session::Impl final : public MemoryRetainer {
}
}
static void SendDatagram(const FunctionCallbackInfo<Value>& args) {
JS_METHOD(SendDatagram) {
auto env = Environment::GetCurrent(args);
Session* session;
ASSIGN_OR_RETURN_UNWRAP(&session, args.This());
@ -1276,23 +1270,12 @@ Session::SendPendingDataScope::~SendPendingDataScope() {
}
// ============================================================================
bool Session::HasInstance(Environment* env, Local<Value> value) {
return GetConstructorTemplate(env)->HasInstance(value);
}
BaseObjectPtr<Session> Session::Create(
Endpoint* endpoint,
const Config& config,
TLSContext* tls_context,
const std::optional<SessionTicket>& ticket) {
Local<Object> obj;
if (!GetConstructorTemplate(endpoint->env())
->InstanceTemplate()
->NewInstance(endpoint->env()->context())
.ToLocal(&obj)) {
return {};
}
JS_NEW_INSTANCE_OR_RETURN(endpoint->env(), obj, {});
return MakeDetachedBaseObject<Session>(
endpoint, obj, config, tls_context, ticket);
}
@ -1311,27 +1294,23 @@ Session::Session(Endpoint* endpoint,
DCHECK(impl_);
MakeWeak();
Debug(this, "Session created.");
const auto defineProperty = [&](auto name, auto value) {
object
->DefineOwnProperty(
env()->context(), name, value, PropertyAttribute::ReadOnly)
.Check();
};
defineProperty(env()->state_string(), impl_->state_.GetArrayBuffer());
defineProperty(env()->stats_string(), impl_->stats_.GetArrayBuffer());
auto& binding = BindingData::Get(env());
JS_DEFINE_READONLY_PROPERTY(
env(), object, env()->stats_string(), impl_->stats_.GetArrayBuffer());
JS_DEFINE_READONLY_PROPERTY(
env(), object, env()->state_string(), impl_->state_.GetArrayBuffer());
if (config.options.qlog) [[unlikely]] {
qlog_stream_ = LogStream::Create(env());
defineProperty(binding.qlog_string(), qlog_stream_->object());
JS_DEFINE_READONLY_PROPERTY(
env(), object, binding.qlog_string(), qlog_stream_->object());
}
if (config.options.tls_options.keylog) [[unlikely]] {
keylog_stream_ = LogStream::Create(env());
defineProperty(binding.keylog_string(), keylog_stream_->object());
JS_DEFINE_READONLY_PROPERTY(
env(), object, binding.keylog_string(), keylog_stream_->object());
}
UpdateDataStats();
@ -2348,10 +2327,7 @@ void Session::DatagramReceived(const uint8_t* data,
Debug(this, "Session is receiving datagram of size %zu", datalen);
auto& stats_ = impl_->stats_;
STAT_INCREMENT(Stats, datagrams_received);
auto backing = ArrayBuffer::NewBackingStore(
env()->isolate(),
datalen,
BackingStoreInitializationMode::kUninitialized);
JS_TRY_ALLOCATE_BACKING(env(), backing, datalen)
memcpy(backing->Data(), data, datalen);
EmitDatagram(Store(std::move(backing), datalen), flag);
}
@ -2777,28 +2753,19 @@ void Session::EmitKeylog(const char* line) {
// ============================================================================
Local<FunctionTemplate> Session::GetConstructorTemplate(Environment* env) {
auto& state = BindingData::Get(env);
auto tmpl = state.session_constructor_template();
if (tmpl.IsEmpty()) {
auto isolate = env->isolate();
tmpl = NewFunctionTemplate(isolate, IllegalConstructor);
tmpl->SetClassName(state.session_string());
tmpl->Inherit(AsyncWrap::GetConstructorTemplate(env));
tmpl->InstanceTemplate()->SetInternalFieldCount(kInternalFieldCount);
#define V(name, key, no_side_effect) \
if (no_side_effect) { \
SetProtoMethodNoSideEffect(isolate, tmpl, #key, Impl::name); \
SetProtoMethodNoSideEffect(env->isolate(), tmpl, #key, Impl::name); \
} else { \
SetProtoMethod(isolate, tmpl, #key, Impl::name); \
SetProtoMethod(env->isolate(), tmpl, #key, Impl::name); \
}
SESSION_JS_METHODS(V)
JS_CONSTRUCTOR_IMPL(Session, session_constructor_template, {
JS_ILLEGAL_CONSTRUCTOR();
JS_INHERIT(AsyncWrap);
JS_CLASS(session);
SESSION_JS_METHODS(V)
})
#undef V
state.set_session_constructor_template(tmpl);
}
return tmpl;
}
void Session::RegisterExternalReferences(ExternalReferenceRegistry* registry) {
#define V(name, _, __) registry->Register(Impl::name);
@ -2823,9 +2790,9 @@ void Session::InitPerContext(Realm* realm, Local<Object> target) {
PreferredAddress::Initialize(realm->env(), target);
static constexpr auto STREAM_DIRECTION_BIDIRECTIONAL =
static_cast<uint32_t>(Direction::BIDIRECTIONAL);
static_cast<uint8_t>(Direction::BIDIRECTIONAL);
static constexpr auto STREAM_DIRECTION_UNIDIRECTIONAL =
static_cast<uint32_t>(Direction::UNIDIRECTIONAL);
static_cast<uint8_t>(Direction::UNIDIRECTIONAL);
static constexpr auto QUIC_PROTO_MAX = NGTCP2_PROTO_VER_MAX;
static constexpr auto QUIC_PROTO_MIN = NGTCP2_PROTO_VER_MIN;

View File

@ -250,13 +250,8 @@ class Session final : public AsyncWrap, private SessionTicket::AppData::Source {
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);
JS_CONSTRUCTOR(Session);
JS_BINDING_INIT_BOILERPLATE();
static BaseObjectPtr<Session> Create(
Endpoint* endpoint,
@ -334,7 +329,7 @@ class Session final : public AsyncWrap, private SessionTicket::AppData::Source {
// A non-const variation to allow certain modifications.
Config& config();
enum class CreateStreamOption {
enum class CreateStreamOption : uint8_t {
NOTIFY,
DO_NOT_NOTIFY,
};
@ -423,7 +418,7 @@ class Session final : public AsyncWrap, private SessionTicket::AppData::Source {
// defined there to manage it.
void set_wrapped();
enum class CloseMethod {
enum class CloseMethod : uint8_t {
// Immediate close with a 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

View File

@ -65,7 +65,7 @@ class SessionTicket final : public MemoryRetainer {
// server-side of the Session::Application that sets it.
class SessionTicket::AppData final {
public:
enum class Status {
enum class Status : uint8_t {
TICKET_IGNORE = SSL_TICKET_RETURN_IGNORE,
TICKET_IGNORE_RENEW = SSL_TICKET_RETURN_IGNORE_RENEW,
TICKET_USE = SSL_TICKET_RETURN_USE,
@ -83,7 +83,7 @@ class SessionTicket::AppData final {
// Session.
class Source {
public:
enum class Flag { STATUS_NONE, STATUS_RENEW };
enum class Flag : uint8_t { STATUS_NONE, STATUS_RENEW };
// Collect application data into the given AppData instance.
virtual void CollectSessionTicketAppData(AppData* app_data) const = 0;

View File

@ -20,10 +20,7 @@ namespace node {
using v8::Array;
using v8::ArrayBuffer;
using v8::ArrayBufferView;
using v8::BackingStoreInitializationMode;
using v8::BigInt;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
using v8::Global;
using v8::Integer;
using v8::Just;
@ -32,9 +29,7 @@ using v8::Maybe;
using v8::Nothing;
using v8::Object;
using v8::ObjectTemplate;
using v8::PropertyAttribute;
using v8::SharedArrayBuffer;
using v8::Uint32;
using v8::Value;
namespace quic {
@ -185,7 +180,7 @@ Maybe<std::shared_ptr<DataQueue>> Stream::GetDataQueueFromSource(
// Stream object.
struct Stream::Impl {
// Attaches an outbound data source to the stream.
static void AttachSource(const FunctionCallbackInfo<Value>& args) {
JS_METHOD(AttachSource) {
Environment* env = Environment::GetCurrent(args);
std::shared_ptr<DataQueue> dataqueue;
@ -196,7 +191,7 @@ struct Stream::Impl {
}
}
static void Destroy(const FunctionCallbackInfo<Value>& args) {
JS_METHOD(Destroy) {
Stream* stream;
ASSIGN_OR_RETURN_UNWRAP(&stream, args.This());
if (args.Length() > 1) {
@ -209,17 +204,16 @@ struct Stream::Impl {
}
}
static void SendHeaders(const FunctionCallbackInfo<Value>& args) {
JS_METHOD(SendHeaders) {
Stream* stream;
ASSIGN_OR_RETURN_UNWRAP(&stream, args.This());
CHECK(args[0]->IsUint32()); // Kind
CHECK(args[1]->IsArray()); // Headers
CHECK(args[2]->IsUint32()); // Flags
HeadersKind kind = static_cast<HeadersKind>(args[0].As<Uint32>()->Value());
HeadersKind kind = FromV8Value<HeadersKind>(args[0]);
Local<Array> headers = args[1].As<Array>();
HeadersFlags flags =
static_cast<HeadersFlags>(args[2].As<Uint32>()->Value());
HeadersFlags flags = FromV8Value<HeadersFlags>(args[2]);
// If the stream is pending, the headers will be queued until the
// stream is opened, at which time the queued header block will be
@ -236,7 +230,7 @@ struct Stream::Impl {
// Tells the peer to stop sending data for this stream. This has the effect
// of shutting down the readable side of the stream for this peer. Any data
// that has already been received is still readable.
static void StopSending(const FunctionCallbackInfo<Value>& args) {
JS_METHOD(StopSending) {
Stream* stream;
ASSIGN_OR_RETURN_UNWRAP(&stream, args.This());
uint64_t code = 0;
@ -261,7 +255,7 @@ struct Stream::Impl {
// more data for this stream. This has the effect of shutting down the
// writable side of the stream for this peer. Any data that is held in the
// outbound queue will be dropped. The stream may still be readable.
static void ResetStream(const FunctionCallbackInfo<Value>& args) {
JS_METHOD(ResetStream) {
Stream* stream;
ASSIGN_OR_RETURN_UNWRAP(&stream, args.This());
uint64_t code = 0;
@ -288,16 +282,14 @@ struct Stream::Impl {
}
}
static void SetPriority(const FunctionCallbackInfo<Value>& args) {
JS_METHOD(SetPriority) {
Stream* stream;
ASSIGN_OR_RETURN_UNWRAP(&stream, args.This());
CHECK(args[0]->IsUint32()); // Priority
CHECK(args[1]->IsUint32()); // Priority flag
StreamPriority priority =
static_cast<StreamPriority>(args[0].As<Uint32>()->Value());
StreamPriorityFlags flags =
static_cast<StreamPriorityFlags>(args[1].As<Uint32>()->Value());
StreamPriority priority = FromV8Value<StreamPriority>(args[0]);
StreamPriorityFlags flags = FromV8Value<StreamPriorityFlags>(args[1]);
if (stream->is_pending()) {
stream->pending_priority_ = PendingPriority{
@ -310,7 +302,7 @@ struct Stream::Impl {
}
}
static void GetPriority(const FunctionCallbackInfo<Value>& args) {
JS_METHOD(GetPriority) {
Stream* stream;
ASSIGN_OR_RETURN_UNWRAP(&stream, args.This());
@ -323,7 +315,7 @@ struct Stream::Impl {
args.GetReturnValue().Set(static_cast<uint32_t>(priority));
}
static void GetReader(const FunctionCallbackInfo<Value>& args) {
JS_METHOD(GetReader) {
Stream* stream;
ASSIGN_OR_RETURN_UNWRAP(&stream, args.This());
BaseObjectPtr<Blob::Reader> reader = stream->get_reader();
@ -697,33 +689,19 @@ class Stream::Outbound final : public MemoryRetainer {
// ============================================================================
bool Stream::HasInstance(Environment* env, Local<Value> value) {
return GetConstructorTemplate(env)->HasInstance(value);
}
Local<FunctionTemplate> Stream::GetConstructorTemplate(Environment* env) {
auto& state = BindingData::Get(env);
auto tmpl = state.stream_constructor_template();
if (tmpl.IsEmpty()) {
auto isolate = env->isolate();
tmpl = NewFunctionTemplate(isolate, IllegalConstructor);
tmpl->SetClassName(state.stream_string());
tmpl->Inherit(AsyncWrap::GetConstructorTemplate(env));
tmpl->InstanceTemplate()->SetInternalFieldCount(kInternalFieldCount);
#define V(name, key, no_side_effect) \
if (no_side_effect) { \
SetProtoMethodNoSideEffect(isolate, tmpl, #key, Impl::name); \
SetProtoMethodNoSideEffect(env->isolate(), tmpl, #key, Impl::name); \
} else { \
SetProtoMethod(isolate, tmpl, #key, Impl::name); \
SetProtoMethod(env->isolate(), tmpl, #key, Impl::name); \
}
STREAM_JS_METHODS(V)
JS_CONSTRUCTOR_IMPL(Stream, stream_constructor_template, {
JS_ILLEGAL_CONSTRUCTOR();
JS_INHERIT(AsyncWrap);
JS_CLASS(stream);
STREAM_JS_METHODS(V)
})
#undef V
state.set_stream_constructor_template(tmpl);
}
return tmpl;
}
void Stream::RegisterExternalReferences(ExternalReferenceRegistry* registry) {
#define V(name, _, __) registry->Register(Impl::name);
@ -755,16 +733,16 @@ void Stream::InitPerContext(Realm* realm, Local<Object> target) {
#undef V
constexpr int QUIC_STREAM_HEADERS_KIND_HINTS =
static_cast<int>(HeadersKind::HINTS);
static_cast<uint8_t>(HeadersKind::HINTS);
constexpr int QUIC_STREAM_HEADERS_KIND_INITIAL =
static_cast<int>(HeadersKind::INITIAL);
static_cast<uint8_t>(HeadersKind::INITIAL);
constexpr int QUIC_STREAM_HEADERS_KIND_TRAILING =
static_cast<int>(HeadersKind::TRAILING);
static_cast<uint8_t>(HeadersKind::TRAILING);
constexpr int QUIC_STREAM_HEADERS_FLAGS_NONE =
static_cast<int>(HeadersFlags::NONE);
static_cast<uint8_t>(HeadersFlags::NONE);
constexpr int QUIC_STREAM_HEADERS_FLAGS_TERMINAL =
static_cast<int>(HeadersFlags::TERMINAL);
static_cast<uint8_t>(HeadersFlags::TERMINAL);
NODE_DEFINE_CONSTANT(target, QUIC_STREAM_HEADERS_KIND_HINTS);
NODE_DEFINE_CONSTANT(target, QUIC_STREAM_HEADERS_KIND_INITIAL);
@ -784,14 +762,7 @@ BaseObjectPtr<Stream> Stream::Create(Session* session,
std::shared_ptr<DataQueue> source) {
DCHECK_GE(id, 0);
DCHECK_NOT_NULL(session);
Local<Object> obj;
if (!GetConstructorTemplate(session->env())
->InstanceTemplate()
->NewInstance(session->env()->context())
.ToLocal(&obj)) {
return {};
}
JS_NEW_INSTANCE_OR_RETURN(session->env(), obj, {});
return MakeDetachedBaseObject<Stream>(
BaseObjectWeakPtr<Session>(session), obj, id, std::move(source));
}
@ -800,14 +771,7 @@ BaseObjectPtr<Stream> Stream::Create(Session* session,
Direction direction,
std::shared_ptr<DataQueue> source) {
DCHECK_NOT_NULL(session);
Local<Object> obj;
if (!GetConstructorTemplate(session->env())
->InstanceTemplate()
->NewInstance(session->env()->context())
.ToLocal(&obj)) {
return {};
}
JS_NEW_INSTANCE_OR_RETURN(session->env(), obj, {});
return MakeBaseObject<Stream>(
BaseObjectWeakPtr<Session>(session), obj, direction, std::move(source));
}
@ -829,15 +793,10 @@ Stream::Stream(BaseObjectWeakPtr<Session> session,
// inbound queue so that we can update the stream flow control.
inbound_->addBackpressureListener(this);
const auto defineProperty = [&](auto name, auto value) {
object
->DefineOwnProperty(
env()->context(), name, value, PropertyAttribute::ReadOnly)
.Check();
};
defineProperty(env()->state_string(), state_.GetArrayBuffer());
defineProperty(env()->stats_string(), stats_.GetArrayBuffer());
JS_DEFINE_READONLY_PROPERTY(
env(), object, env()->state_string(), state_.GetArrayBuffer());
JS_DEFINE_READONLY_PROPERTY(
env(), object, env()->stats_string(), stats_.GetArrayBuffer());
set_outbound(std::move(source));
@ -866,15 +825,10 @@ Stream::Stream(BaseObjectWeakPtr<Session> session,
// inbound queue so that we can update the stream flow control.
inbound_->addBackpressureListener(this);
const auto defineProperty = [&](auto name, auto value) {
object
->DefineOwnProperty(
env()->context(), name, value, PropertyAttribute::ReadOnly)
.Check();
};
defineProperty(env()->state_string(), state_.GetArrayBuffer());
defineProperty(env()->stats_string(), stats_.GetArrayBuffer());
JS_DEFINE_READONLY_PROPERTY(
env(), object, env()->state_string(), state_.GetArrayBuffer());
JS_DEFINE_READONLY_PROPERTY(
env(), object, env()->stats_string(), stats_.GetArrayBuffer());
set_outbound(std::move(source));
@ -1203,8 +1157,7 @@ void Stream::ReceiveData(const uint8_t* data,
STAT_INCREMENT_N(Stats, bytes_received, len);
STAT_RECORD_TIMESTAMP(Stats, received_at);
auto backing = ArrayBuffer::NewBackingStore(
env()->isolate(), len, BackingStoreInitializationMode::kUninitialized);
JS_TRY_ALLOCATE_BACKING(env(), backing, len)
memcpy(backing->Data(), data, len);
inbound_->append(DataQueue::CreateInMemoryEntryFromBackingStore(
std::move(backing), 0, len));

View File

@ -147,13 +147,8 @@ class Stream final : public AsyncWrap,
static Stream* From(void* stream_user_data);
static bool HasInstance(Environment* env, v8::Local<v8::Value> value);
static v8::Local<v8::FunctionTemplate> GetConstructorTemplate(
Environment* env);
static void InitPerIsolate(IsolateData* data,
v8::Local<v8::ObjectTemplate> target);
static void InitPerContext(Realm* realm, v8::Local<v8::Object> target);
static void RegisterExternalReferences(ExternalReferenceRegistry* registry);
JS_CONSTRUCTOR(Stream);
JS_BINDING_INIT_BOILERPLATE();
// Creates a new non-pending stream.
static BaseObjectPtr<Stream> Create(

View File

@ -29,8 +29,6 @@ using ncrypto::SSLPointer;
using ncrypto::SSLSessionPointer;
using ncrypto::X509Pointer;
using v8::ArrayBuffer;
using v8::BackingStoreInitializationMode;
using v8::BackingStoreOnFailureMode;
using v8::Just;
using v8::Local;
using v8::Maybe;
@ -361,12 +359,7 @@ int TLSContext::OnNewSession(SSL* ssl, SSL_SESSION* sess) {
// enough memory to allocate the backing store, then we ignore it
// and continue without emitting the sessionticket event.
if (size > 0 && size <= crypto::SecureContext::kMaxSessionSize) {
auto ticket = ArrayBuffer::NewBackingStore(
session.env()->isolate(),
size,
BackingStoreInitializationMode::kUninitialized,
BackingStoreOnFailureMode::kReturnNull);
if (!ticket) return 0;
JS_TRY_ALLOCATE_BACKING_OR_RETURN(session.env(), ticket, size, 0);
auto data = reinterpret_cast<unsigned char*>(ticket->Data());
if (i2d_SSL_SESSION(sess, &data) > 0) {
session.EmitSessionTicket(Store(std::move(ticket), size));

View File

@ -15,9 +15,6 @@
namespace node {
using v8::ArrayBuffer;
using v8::BackingStoreInitializationMode;
using v8::BackingStoreOnFailureMode;
using v8::Just;
using v8::Local;
using v8::Maybe;
@ -195,14 +192,7 @@ Store TransportParams::Encode(Environment* env, int version) const {
return {};
}
auto result = ArrayBuffer::NewBackingStore(
env->isolate(),
size,
BackingStoreInitializationMode::kUninitialized,
BackingStoreOnFailureMode::kReturnNull);
if (!result) {
return {};
}
JS_TRY_ALLOCATE_BACKING_OR_RETURN(env, result, size, {});
auto ret = ngtcp2_transport_params_encode_versioned(
static_cast<uint8_t*>(result->Data()), size, version, &params_);

View File

@ -142,7 +142,4 @@ for (const [, envVar, config] of nodeOptionsCC.matchAll(addOptionRE)) {
// add alias handling
manPagesOptions.delete('-trace-events-enabled');
// TODO(@jasnell): Need to determine why the documentation for this option is not being recognized
manPagesOptions.delete('-no-experimental-quic');
assert.strictEqual(manPagesOptions.size, 0, `Man page options not documented: ${[...manPagesOptions]}`);

View File

@ -21,8 +21,8 @@ if (!common.hasIntl) {
common.skip('missing Intl');
}
if (process.config.variables.node_quic) {
common.skip('this test assumes default configuration options');
if (!common.hasQuic) {
common.skip('this test requires QUIC');
}
const {

View File

@ -130,8 +130,6 @@ assert(undocumented.delete('--verify-base-objects'));
assert(undocumented.delete('--no-verify-base-objects'));
assert(undocumented.delete('--trace-promises'));
assert(undocumented.delete('--no-trace-promises'));
assert(undocumented.delete('--experimental-quic'));
assert(undocumented.delete('--no-experimental-quic'));
// Remove negated versions of the flags.
for (const flag of undocumented) {

View File

@ -10,7 +10,7 @@ const expectedKeys = new Map([
['uv', ['boolean']],
['ipv6', ['boolean']],
['openssl_is_boringssl', ['boolean']],
['quic', ['boolean']],
['quic', ['boolean', 'undefined']],
['tls_alpn', ['boolean']],
['tls_sni', ['boolean']],
['tls_ocsp', ['boolean']],

View File

@ -46,11 +46,6 @@ if (common.hasCrypto) {
expected_keys.push('ncrypto');
}
if (common.hasQuic) {
expected_keys.push('ngtcp2');
expected_keys.push('nghttp3');
}
if (common.hasIntl) {
expected_keys.push('icu');
expected_keys.push('cldr');