Skip to content

Commit 81a7056

Browse files
jasnellrvagg
authored andcommitted
http2: set js callbacks once
Make the http2 binding a bit more efficient by setting the callback functions once when the module is loaded rather than for each `Http2Session` and `Http2Stream`. PR-URL: #24063 Reviewed-By: Matteo Collina <[email protected]> Note: Landed with one collaborator approval after PR was open for 18 days
1 parent cd7df56 commit 81a7056

File tree

3 files changed

+100
-63
lines changed

3 files changed

+100
-63
lines changed

lib/internal/http2/core.js

+33-24
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ const kProceed = Symbol('proceed');
159159
const kProtocol = Symbol('protocol');
160160
const kProxySocket = Symbol('proxy-socket');
161161
const kRemoteSettings = Symbol('remote-settings');
162+
const kSelectPadding = Symbol('select-padding');
162163
const kSentHeaders = Symbol('sent-headers');
163164
const kSentTrailers = Symbol('sent-trailers');
164165
const kServer = Symbol('server');
@@ -368,8 +369,8 @@ function onPing(payload) {
368369
// longer usable so destroy it also.
369370
function onStreamClose(code) {
370371
const stream = this[kOwner];
371-
if (stream.destroyed)
372-
return;
372+
if (!stream || stream.destroyed)
373+
return false;
373374

374375
debug(`Http2Stream ${stream[kID]} [Http2Session ` +
375376
`${sessionName(stream[kSession][kType])}]: closed with code ${code}`);
@@ -399,6 +400,7 @@ function onStreamClose(code) {
399400
else
400401
stream.read(0);
401402
}
403+
return true;
402404
}
403405

404406
// Called when the remote peer settings have been updated.
@@ -523,12 +525,14 @@ function onGoawayData(code, lastStreamID, buf) {
523525
// on the options. It is invoked with two arguments, the frameLen, and the
524526
// maxPayloadLen. The method must return a numeric value within the range
525527
// frameLen <= n <= maxPayloadLen.
526-
function onSelectPadding(fn) {
527-
return function getPadding() {
528-
const frameLen = paddingBuffer[PADDING_BUF_FRAME_LENGTH];
529-
const maxFramePayloadLen = paddingBuffer[PADDING_BUF_MAX_PAYLOAD_LENGTH];
530-
paddingBuffer[PADDING_BUF_RETURN_VALUE] = fn(frameLen, maxFramePayloadLen);
531-
};
528+
function onSelectPadding() {
529+
const session = this[kOwner];
530+
if (session.destroyed)
531+
return;
532+
const fn = session[kSelectPadding];
533+
const frameLen = paddingBuffer[PADDING_BUF_FRAME_LENGTH];
534+
const maxFramePayloadLen = paddingBuffer[PADDING_BUF_MAX_PAYLOAD_LENGTH];
535+
paddingBuffer[PADDING_BUF_RETURN_VALUE] = fn(frameLen, maxFramePayloadLen);
532536
}
533537

534538
// When a ClientHttp2Session is first created, the socket may not yet be
@@ -826,28 +830,20 @@ function setupHandle(socket, type, options) {
826830
process.nextTick(emit, this, 'connect', this, socket);
827831
return;
828832
}
833+
834+
assert(socket._handle !== undefined,
835+
'Internal HTTP/2 Failure. The socket is not connected. Please ' +
836+
'report this as a bug in Node.js');
837+
829838
debug(`Http2Session ${sessionName(type)}: setting up session handle`);
830839
this[kState].flags |= SESSION_FLAGS_READY;
831840

832841
updateOptionsBuffer(options);
833842
const handle = new binding.Http2Session(type);
834843
handle[kOwner] = this;
835-
handle.error = onSessionInternalError;
836-
handle.onpriority = onPriority;
837-
handle.onsettings = onSettings;
838-
handle.onping = onPing;
839-
handle.onheaders = onSessionHeaders;
840-
handle.onframeerror = onFrameError;
841-
handle.ongoawaydata = onGoawayData;
842-
handle.onaltsvc = onAltSvc;
843-
handle.onorigin = onOrigin;
844844

845845
if (typeof options.selectPadding === 'function')
846-
handle.ongetpadding = onSelectPadding(options.selectPadding);
847-
848-
assert(socket._handle !== undefined,
849-
'Internal HTTP/2 Failure. The socket is not connected. Please ' +
850-
'report this as a bug in Node.js');
846+
this[kSelectPadding] = options.selectPadding;
851847
handle.consume(socket._handle._externalStream);
852848

853849
this[kHandle] = handle;
@@ -1654,8 +1650,6 @@ class Http2Stream extends Duplex {
16541650
this[async_id_symbol] = handle.getAsyncId();
16551651
handle[kOwner] = this;
16561652
this[kHandle] = handle;
1657-
handle.ontrailers = onStreamTrailers;
1658-
handle.onstreamclose = onStreamClose;
16591653
handle.onread = onStreamRead;
16601654
this.uncork();
16611655
this.emit('ready');
@@ -2899,6 +2893,21 @@ function getUnpackedSettings(buf, options = {}) {
28992893
return settings;
29002894
}
29012895

2896+
binding.setCallbackFunctions(
2897+
onSessionInternalError,
2898+
onPriority,
2899+
onSettings,
2900+
onPing,
2901+
onSessionHeaders,
2902+
onFrameError,
2903+
onGoawayData,
2904+
onAltSvc,
2905+
onOrigin,
2906+
onSelectPadding,
2907+
onStreamTrailers,
2908+
onStreamClose
2909+
);
2910+
29022911
// Exports
29032912
module.exports = {
29042913
connect,

src/env.h

+12-11
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,6 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2;
212212
V(nistcurve_string, "nistCurve") \
213213
V(nsname_string, "nsname") \
214214
V(ocsp_request_string, "OCSPRequest") \
215-
V(onaltsvc_string, "onaltsvc") \
216215
V(oncertcb_string, "oncertcb") \
217216
V(onchange_string, "onchange") \
218217
V(onclienthello_string, "onclienthello") \
@@ -221,26 +220,16 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2;
221220
V(ondone_string, "ondone") \
222221
V(onerror_string, "onerror") \
223222
V(onexit_string, "onexit") \
224-
V(onframeerror_string, "onframeerror") \
225-
V(ongetpadding_string, "ongetpadding") \
226-
V(ongoawaydata_string, "ongoawaydata") \
227223
V(onhandshakedone_string, "onhandshakedone") \
228224
V(onhandshakestart_string, "onhandshakestart") \
229-
V(onheaders_string, "onheaders") \
230225
V(onmessage_string, "onmessage") \
231226
V(onnewsession_string, "onnewsession") \
232227
V(onocspresponse_string, "onocspresponse") \
233-
V(onorigin_string, "onorigin") \
234-
V(onping_string, "onping") \
235-
V(onpriority_string, "onpriority") \
236228
V(onread_string, "onread") \
237229
V(onreadstart_string, "onreadstart") \
238230
V(onreadstop_string, "onreadstop") \
239-
V(onsettings_string, "onsettings") \
240231
V(onshutdown_string, "onshutdown") \
241232
V(onsignal_string, "onsignal") \
242-
V(onstreamclose_string, "onstreamclose") \
243-
V(ontrailers_string, "ontrailers") \
244233
V(onunpipe_string, "onunpipe") \
245234
V(onwrite_string, "onwrite") \
246235
V(openssl_error_stack, "opensslErrorStack") \
@@ -343,6 +332,18 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2;
343332
V(host_import_module_dynamically_callback, v8::Function) \
344333
V(host_initialize_import_meta_object_callback, v8::Function) \
345334
V(http2ping_constructor_template, v8::ObjectTemplate) \
335+
V(http2session_on_altsvc_function, v8::Function) \
336+
V(http2session_on_error_function, v8::Function) \
337+
V(http2session_on_frame_error_function, v8::Function) \
338+
V(http2session_on_goaway_data_function, v8::Function) \
339+
V(http2session_on_headers_function, v8::Function) \
340+
V(http2session_on_origin_function, v8::Function) \
341+
V(http2session_on_ping_function, v8::Function) \
342+
V(http2session_on_priority_function, v8::Function) \
343+
V(http2session_on_select_padding_function, v8::Function) \
344+
V(http2session_on_settings_function, v8::Function) \
345+
V(http2session_on_stream_close_function, v8::Function) \
346+
V(http2session_on_stream_trailers_function, v8::Function) \
346347
V(http2settings_constructor_template, v8::ObjectTemplate) \
347348
V(http2stream_constructor_template, v8::ObjectTemplate) \
348349
V(immediate_callback_function, v8::Function) \

src/node_http2.cc

+55-28
Original file line numberDiff line numberDiff line change
@@ -851,7 +851,7 @@ ssize_t Http2Session::OnCallbackPadding(size_t frameLen,
851851
buffer[PADDING_BUF_FRAME_LENGTH] = frameLen;
852852
buffer[PADDING_BUF_MAX_PAYLOAD_LENGTH] = maxPayloadLen;
853853
buffer[PADDING_BUF_RETURN_VALUE] = frameLen;
854-
MakeCallback(env()->ongetpadding_string(), 0, nullptr);
854+
MakeCallback(env()->http2session_on_select_padding_function(), 0, nullptr);
855855
uint32_t retval = buffer[PADDING_BUF_RETURN_VALUE];
856856
retval = std::min(retval, static_cast<uint32_t>(maxPayloadLen));
857857
retval = std::max(retval, static_cast<uint32_t>(frameLen));
@@ -1017,7 +1017,7 @@ int Http2Session::OnInvalidFrame(nghttp2_session* handle,
10171017
Local<Context> context = env->context();
10181018
Context::Scope context_scope(context);
10191019
Local<Value> arg = Integer::New(isolate, lib_error_code);
1020-
session->MakeCallback(env->error_string(), 1, &arg);
1020+
session->MakeCallback(env->http2session_on_error_function(), 1, &arg);
10211021
}
10221022
return 0;
10231023
}
@@ -1054,7 +1054,9 @@ int Http2Session::OnFrameNotSent(nghttp2_session* handle,
10541054
Integer::New(isolate, frame->hd.type),
10551055
Integer::New(isolate, error_code)
10561056
};
1057-
session->MakeCallback(env->onframeerror_string(), arraysize(argv), argv);
1057+
session->MakeCallback(
1058+
env->http2session_on_frame_error_function(),
1059+
arraysize(argv), argv);
10581060
return 0;
10591061
}
10601062

@@ -1085,22 +1087,19 @@ int Http2Session::OnStreamClose(nghttp2_session* handle,
10851087
return 0;
10861088

10871089
stream->Close(code);
1090+
10881091
// It is possible for the stream close to occur before the stream is
1089-
// ever passed on to the javascript side. If that happens, skip straight
1090-
// to destroying the stream. We can check this by looking for the
1091-
// onstreamclose function. If it exists, then the stream has already
1092-
// been passed on to javascript.
1093-
Local<Value> fn =
1094-
stream->object()->Get(context, env->onstreamclose_string())
1095-
.ToLocalChecked();
1096-
1097-
if (!fn->IsFunction()) {
1092+
// ever passed on to the javascript side. If that happens, the callback
1093+
// will return false.
1094+
Local<Value> arg = Integer::NewFromUnsigned(isolate, code);
1095+
MaybeLocal<Value> answer =
1096+
stream->MakeCallback(env->http2session_on_stream_close_function(),
1097+
1, &arg);
1098+
if (answer.IsEmpty() ||
1099+
!(answer.ToLocalChecked()->BooleanValue(env->context()).FromJust())) {
1100+
// Skip to destroy
10981101
stream->Destroy();
1099-
return 0;
11001102
}
1101-
1102-
Local<Value> arg = Integer::NewFromUnsigned(isolate, code);
1103-
stream->MakeCallback(fn.As<Function>(), 1, &arg);
11041103
return 0;
11051104
}
11061105

@@ -1233,7 +1232,7 @@ int Http2Session::OnNghttpError(nghttp2_session* handle,
12331232
Local<Context> context = env->context();
12341233
Context::Scope context_scope(context);
12351234
Local<Value> arg = Integer::New(isolate, NGHTTP2_ERR_PROTO);
1236-
session->MakeCallback(env->error_string(), 1, &arg);
1235+
session->MakeCallback(env->http2session_on_error_function(), 1, &arg);
12371236
}
12381237
return 0;
12391238
}
@@ -1317,7 +1316,8 @@ void Http2Session::HandleHeadersFrame(const nghttp2_frame* frame) {
13171316
Integer::New(isolate, stream->headers_category()),
13181317
Integer::New(isolate, frame->hd.flags),
13191318
Array::New(isolate, headers_v.data(), headers_size * 2)};
1320-
MakeCallback(env()->onheaders_string(), arraysize(args), args);
1319+
MakeCallback(env()->http2session_on_headers_function(),
1320+
arraysize(args), args);
13211321
}
13221322

13231323

@@ -1343,7 +1343,8 @@ void Http2Session::HandlePriorityFrame(const nghttp2_frame* frame) {
13431343
Integer::New(isolate, spec.weight),
13441344
Boolean::New(isolate, spec.exclusive)
13451345
};
1346-
MakeCallback(env()->onpriority_string(), arraysize(argv), argv);
1346+
MakeCallback(env()->http2session_on_priority_function(),
1347+
arraysize(argv), argv);
13471348
}
13481349

13491350

@@ -1383,7 +1384,8 @@ void Http2Session::HandleGoawayFrame(const nghttp2_frame* frame) {
13831384
length).ToLocalChecked();
13841385
}
13851386

1386-
MakeCallback(env()->ongoawaydata_string(), arraysize(argv), argv);
1387+
MakeCallback(env()->http2session_on_goaway_data_function(),
1388+
arraysize(argv), argv);
13871389
}
13881390

13891391
// Called by OnFrameReceived when a complete ALTSVC frame has been received.
@@ -1411,7 +1413,8 @@ void Http2Session::HandleAltSvcFrame(const nghttp2_frame* frame) {
14111413
altsvc->field_value_len).ToLocalChecked(),
14121414
};
14131415

1414-
MakeCallback(env()->onaltsvc_string(), arraysize(argv), argv);
1416+
MakeCallback(env()->http2session_on_altsvc_function(),
1417+
arraysize(argv), argv);
14151418
}
14161419

14171420
void Http2Session::HandleOriginFrame(const nghttp2_frame* frame) {
@@ -1436,7 +1439,7 @@ void Http2Session::HandleOriginFrame(const nghttp2_frame* frame) {
14361439
.ToLocalChecked();
14371440
}
14381441
Local<Value> holder = Array::New(isolate, origin_v.data(), origin_v.size());
1439-
MakeCallback(env()->onorigin_string(), 1, &holder);
1442+
MakeCallback(env()->http2session_on_origin_function(), 1, &holder);
14401443
}
14411444

14421445
// Called by OnFrameReceived when a complete PING frame has been received.
@@ -1457,7 +1460,7 @@ void Http2Session::HandlePingFrame(const nghttp2_frame* frame) {
14571460
// is buggy or malicious, and we're not going to tolerate such
14581461
// nonsense.
14591462
arg = Integer::New(isolate, NGHTTP2_ERR_PROTO);
1460-
MakeCallback(env()->error_string(), 1, &arg);
1463+
MakeCallback(env()->http2session_on_error_function(), 1, &arg);
14611464
return;
14621465
}
14631466

@@ -1469,15 +1472,15 @@ void Http2Session::HandlePingFrame(const nghttp2_frame* frame) {
14691472
arg = Buffer::Copy(env(),
14701473
reinterpret_cast<const char*>(frame->ping.opaque_data),
14711474
8).ToLocalChecked();
1472-
MakeCallback(env()->onping_string(), 1, &arg);
1475+
MakeCallback(env()->http2session_on_ping_function(), 1, &arg);
14731476
}
14741477

14751478
// Called by OnFrameReceived when a complete SETTINGS frame has been received.
14761479
void Http2Session::HandleSettingsFrame(const nghttp2_frame* frame) {
14771480
bool ack = frame->hd.flags & NGHTTP2_FLAG_ACK;
14781481
if (!ack) {
14791482
// This is not a SETTINGS acknowledgement, notify and return
1480-
MakeCallback(env()->onsettings_string(), 0, nullptr);
1483+
MakeCallback(env()->http2session_on_settings_function(), 0, nullptr);
14811484
return;
14821485
}
14831486

@@ -1502,7 +1505,7 @@ void Http2Session::HandleSettingsFrame(const nghttp2_frame* frame) {
15021505
Local<Context> context = env()->context();
15031506
Context::Scope context_scope(context);
15041507
Local<Value> arg = Integer::New(isolate, NGHTTP2_ERR_PROTO);
1505-
MakeCallback(env()->error_string(), 1, &arg);
1508+
MakeCallback(env()->http2session_on_error_function(), 1, &arg);
15061509
}
15071510

15081511
// Callback used when data has been written to the stream.
@@ -1827,7 +1830,7 @@ void Http2Session::OnStreamRead(ssize_t nread, const uv_buf_t& buf) {
18271830
if (UNLIKELY(ret < 0)) {
18281831
Debug(this, "fatal error receiving data: %d", ret);
18291832
Local<Value> arg = Integer::New(isolate, ret);
1830-
MakeCallback(env()->error_string(), 1, &arg);
1833+
MakeCallback(env()->http2session_on_error_function(), 1, &arg);
18311834
return;
18321835
}
18331836

@@ -2032,7 +2035,7 @@ void Http2Stream::OnTrailers() {
20322035
Local<Context> context = env()->context();
20332036
Context::Scope context_scope(context);
20342037
flags_ &= ~NGHTTP2_STREAM_FLAG_TRAILERS;
2035-
MakeCallback(env()->ontrailers_string(), 0, nullptr);
2038+
MakeCallback(env()->http2session_on_stream_trailers_function(), 0, nullptr);
20362039
}
20372040

20382041
// Submit informational headers for a stream.
@@ -2898,6 +2901,29 @@ void nghttp2_header::MemoryInfo(MemoryTracker* tracker) const {
28982901
tracker->TrackFieldWithSize("value", nghttp2_rcbuf_get_buf(value).len);
28992902
}
29002903

2904+
void SetCallbackFunctions(const FunctionCallbackInfo<Value>& args) {
2905+
Environment* env = Environment::GetCurrent(args);
2906+
CHECK_EQ(args.Length(), 12);
2907+
2908+
#define SET_FUNCTION(arg, name) \
2909+
CHECK(args[arg]->IsFunction()); \
2910+
env->set_http2session_on_ ## name ## _function(args[arg].As<Function>());
2911+
2912+
SET_FUNCTION(0, error)
2913+
SET_FUNCTION(1, priority)
2914+
SET_FUNCTION(2, settings)
2915+
SET_FUNCTION(3, ping)
2916+
SET_FUNCTION(4, headers)
2917+
SET_FUNCTION(5, frame_error)
2918+
SET_FUNCTION(6, goaway_data)
2919+
SET_FUNCTION(7, altsvc)
2920+
SET_FUNCTION(8, origin)
2921+
SET_FUNCTION(9, select_padding)
2922+
SET_FUNCTION(10, stream_trailers)
2923+
SET_FUNCTION(11, stream_close)
2924+
2925+
#undef SET_FUNCTION
2926+
}
29012927

29022928
// Set up the process.binding('http2') binding.
29032929
void Initialize(Local<Object> target,
@@ -3106,6 +3132,7 @@ HTTP_STATUS_CODES(V)
31063132

31073133
env->SetMethod(target, "refreshDefaultSettings", RefreshDefaultSettings);
31083134
env->SetMethod(target, "packSettings", PackSettings);
3135+
env->SetMethod(target, "setCallbackFunctions", SetCallbackFunctions);
31093136

31103137
target->Set(context,
31113138
env->constants_string(),

0 commit comments

Comments
 (0)