Skip to content

Commit

Permalink
src: refactor http parser binding initialization
Browse files Browse the repository at this point in the history
- Use the per-isolate template to build constants and functions
  which is faster
- Use Array::New() with prebuilt vectors which is faster
- Register external references so the binding can be included
  in the snapshot

PR-URL: #54276
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Paolo Insogna <paolo@cowtech.it>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Robert Nagy <ronagy@icloud.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Ethan Arrowood <ethan@arrowood.dev>
  • Loading branch information
joyeecheung authored and RafaelGSS committed Aug 21, 2024
1 parent 409d9eb commit 4e4d1de
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 93 deletions.
1 change: 1 addition & 0 deletions src/node_binding.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ static_assert(static_cast<int>(NM_F_LINKED) ==
V(encoding_binding) \
V(fs) \
V(fs_dir) \
V(http_parser) \
V(messaging) \
V(mksnapshot) \
V(modules) \
Expand Down
1 change: 1 addition & 0 deletions src/node_external_reference.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ class ExternalReferenceRegistry {
V(fs_event_wrap) \
V(handle_wrap) \
V(heap_utils) \
V(http_parser) \
V(internal_only_v8) \
V(messaging) \
V(mksnapshot) \
Expand Down
219 changes: 126 additions & 93 deletions src/node_http_parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,11 @@

#include "async_wrap-inl.h"
#include "env-inl.h"
#include "llhttp.h"
#include "memory_tracker-inl.h"
#include "node_external_reference.h"
#include "stream_base-inl.h"
#include "v8.h"
#include "llhttp.h"

#include <cstdlib> // free()
#include <cstring> // strdup(), strchr()
Expand All @@ -47,7 +48,7 @@


namespace node {
namespace { // NOLINT(build/namespaces)
namespace http_parser { // NOLINT(build/namespaces)

using v8::Array;
using v8::Boolean;
Expand All @@ -65,6 +66,7 @@ using v8::Local;
using v8::MaybeLocal;
using v8::Number;
using v8::Object;
using v8::ObjectTemplate;
using v8::String;
using v8::Uint32;
using v8::Undefined;
Expand Down Expand Up @@ -1243,97 +1245,59 @@ const llhttp_settings_t Parser::settings = {
nullptr,
};

void InitializeHttpParser(Local<Object> target,
Local<Value> unused,
Local<Context> context,
void* priv) {
Realm* realm = Realm::GetCurrent(context);
Environment* env = realm->env();
Isolate* isolate = env->isolate();
BindingData* const binding_data = realm->AddBindingData<BindingData>(target);
if (binding_data == nullptr) return;
void CreatePerIsolateProperties(IsolateData* isolate_data,
Local<ObjectTemplate> target) {
Isolate* isolate = isolate_data->isolate();

Local<FunctionTemplate> t = NewFunctionTemplate(isolate, Parser::New);
t->InstanceTemplate()->SetInternalFieldCount(Parser::kInternalFieldCount);

t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "REQUEST"),
Integer::New(env->isolate(), HTTP_REQUEST));
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "RESPONSE"),
Integer::New(env->isolate(), HTTP_RESPONSE));
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnMessageBegin"),
Integer::NewFromUnsigned(env->isolate(), kOnMessageBegin));
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnHeaders"),
Integer::NewFromUnsigned(env->isolate(), kOnHeaders));
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnHeadersComplete"),
Integer::NewFromUnsigned(env->isolate(), kOnHeadersComplete));
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnBody"),
Integer::NewFromUnsigned(env->isolate(), kOnBody));
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnMessageComplete"),
Integer::NewFromUnsigned(env->isolate(), kOnMessageComplete));
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnExecute"),
Integer::NewFromUnsigned(env->isolate(), kOnExecute));
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnTimeout"),
Integer::NewFromUnsigned(env->isolate(), kOnTimeout));

t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kLenientNone"),
Integer::NewFromUnsigned(env->isolate(), kLenientNone));
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kLenientHeaders"),
Integer::NewFromUnsigned(env->isolate(), kLenientHeaders));
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kLenientChunkedLength"),
Integer::NewFromUnsigned(env->isolate(), kLenientChunkedLength));
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kLenientKeepAlive"),
Integer::NewFromUnsigned(env->isolate(), kLenientKeepAlive));
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kLenientTransferEncoding"),
Integer::NewFromUnsigned(env->isolate(), kLenientTransferEncoding));
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kLenientVersion"),
Integer::NewFromUnsigned(env->isolate(), kLenientVersion));
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kLenientDataAfterClose"),
Integer::NewFromUnsigned(env->isolate(), kLenientDataAfterClose));
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kLenientOptionalLFAfterCR"),
Integer::NewFromUnsigned(env->isolate(), kLenientOptionalLFAfterCR));
t->Set(
FIXED_ONE_BYTE_STRING(env->isolate(), "kLenientOptionalCRLFAfterChunk"),
Integer::NewFromUnsigned(env->isolate(), kLenientOptionalCRLFAfterChunk));
t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kLenientOptionalCRBeforeLF"),
Integer::NewFromUnsigned(env->isolate(), kLenientOptionalCRBeforeLF));
t->Set(
FIXED_ONE_BYTE_STRING(env->isolate(), "kLenientSpacesAfterChunkSize"),
Integer::NewFromUnsigned(env->isolate(), kLenientSpacesAfterChunkSize));

t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kLenientAll"),
Integer::NewFromUnsigned(env->isolate(), kLenientAll));

Local<Array> methods = Array::New(env->isolate());
Local<Array> all_methods = Array::New(env->isolate());
size_t method_index = -1;
size_t all_method_index = -1;
#define V(num, name, string) \
methods \
->Set(env->context(), \
++method_index, \
FIXED_ONE_BYTE_STRING(env->isolate(), #string)) \
.Check();
HTTP_METHOD_MAP(V)
#undef V
#define V(num, name, string) \
all_methods \
->Set(env->context(), \
++all_method_index, \
FIXED_ONE_BYTE_STRING(env->isolate(), #string)) \
.Check();
HTTP_ALL_METHOD_MAP(V)
#undef V

target->Set(env->context(),
FIXED_ONE_BYTE_STRING(env->isolate(), "methods"),
methods).Check();
target
->Set(env->context(),
FIXED_ONE_BYTE_STRING(env->isolate(), "allMethods"),
all_methods)
.Check();

t->Inherit(AsyncWrap::GetConstructorTemplate(env));
t->Set(FIXED_ONE_BYTE_STRING(isolate, "REQUEST"),
Integer::New(isolate, HTTP_REQUEST));
t->Set(FIXED_ONE_BYTE_STRING(isolate, "RESPONSE"),
Integer::New(isolate, HTTP_RESPONSE));
t->Set(FIXED_ONE_BYTE_STRING(isolate, "kOnMessageBegin"),
Integer::NewFromUnsigned(isolate, kOnMessageBegin));
t->Set(FIXED_ONE_BYTE_STRING(isolate, "kOnHeaders"),
Integer::NewFromUnsigned(isolate, kOnHeaders));
t->Set(FIXED_ONE_BYTE_STRING(isolate, "kOnHeadersComplete"),
Integer::NewFromUnsigned(isolate, kOnHeadersComplete));
t->Set(FIXED_ONE_BYTE_STRING(isolate, "kOnBody"),
Integer::NewFromUnsigned(isolate, kOnBody));
t->Set(FIXED_ONE_BYTE_STRING(isolate, "kOnMessageComplete"),
Integer::NewFromUnsigned(isolate, kOnMessageComplete));
t->Set(FIXED_ONE_BYTE_STRING(isolate, "kOnExecute"),
Integer::NewFromUnsigned(isolate, kOnExecute));
t->Set(FIXED_ONE_BYTE_STRING(isolate, "kOnTimeout"),
Integer::NewFromUnsigned(isolate, kOnTimeout));

t->Set(FIXED_ONE_BYTE_STRING(isolate, "kLenientNone"),
Integer::NewFromUnsigned(isolate, kLenientNone));
t->Set(FIXED_ONE_BYTE_STRING(isolate, "kLenientHeaders"),
Integer::NewFromUnsigned(isolate, kLenientHeaders));
t->Set(FIXED_ONE_BYTE_STRING(isolate, "kLenientChunkedLength"),
Integer::NewFromUnsigned(isolate, kLenientChunkedLength));
t->Set(FIXED_ONE_BYTE_STRING(isolate, "kLenientKeepAlive"),
Integer::NewFromUnsigned(isolate, kLenientKeepAlive));
t->Set(FIXED_ONE_BYTE_STRING(isolate, "kLenientTransferEncoding"),
Integer::NewFromUnsigned(isolate, kLenientTransferEncoding));
t->Set(FIXED_ONE_BYTE_STRING(isolate, "kLenientVersion"),
Integer::NewFromUnsigned(isolate, kLenientVersion));
t->Set(FIXED_ONE_BYTE_STRING(isolate, "kLenientDataAfterClose"),
Integer::NewFromUnsigned(isolate, kLenientDataAfterClose));
t->Set(FIXED_ONE_BYTE_STRING(isolate, "kLenientOptionalLFAfterCR"),
Integer::NewFromUnsigned(isolate, kLenientOptionalLFAfterCR));
t->Set(FIXED_ONE_BYTE_STRING(isolate, "kLenientOptionalCRLFAfterChunk"),
Integer::NewFromUnsigned(isolate, kLenientOptionalCRLFAfterChunk));
t->Set(FIXED_ONE_BYTE_STRING(isolate, "kLenientOptionalCRBeforeLF"),
Integer::NewFromUnsigned(isolate, kLenientOptionalCRBeforeLF));
t->Set(FIXED_ONE_BYTE_STRING(isolate, "kLenientSpacesAfterChunkSize"),
Integer::NewFromUnsigned(isolate, kLenientSpacesAfterChunkSize));

t->Set(FIXED_ONE_BYTE_STRING(isolate, "kLenientAll"),
Integer::NewFromUnsigned(isolate, kLenientAll));

t->Inherit(AsyncWrap::GetConstructorTemplate(isolate_data));
SetProtoMethod(isolate, t, "close", Parser::Close);
SetProtoMethod(isolate, t, "free", Parser::Free);
SetProtoMethod(isolate, t, "remove", Parser::Remove);
Expand All @@ -1348,7 +1312,7 @@ void InitializeHttpParser(Local<Object> target,
SetProtoMethod(isolate, t, "duration", Parser::Duration);
SetProtoMethod(isolate, t, "headersCompleted", Parser::HeadersCompleted);

SetConstructorFunction(context, target, "HTTPParser", t);
SetConstructorFunction(isolate, target, "HTTPParser", t);

Local<FunctionTemplate> c =
NewFunctionTemplate(isolate, ConnectionsList::New);
Expand All @@ -1358,10 +1322,79 @@ void InitializeHttpParser(Local<Object> target,
SetProtoMethod(isolate, c, "idle", ConnectionsList::Idle);
SetProtoMethod(isolate, c, "active", ConnectionsList::Active);
SetProtoMethod(isolate, c, "expired", ConnectionsList::Expired);
SetConstructorFunction(context, target, "ConnectionsList", c);
SetConstructorFunction(isolate, target, "ConnectionsList", c);
}

void CreatePerContextProperties(Local<Object> target,
Local<Value> unused,
Local<Context> context,
void* priv) {
Realm* realm = Realm::GetCurrent(context);
Environment* env = realm->env();
Isolate* isolate = env->isolate();
BindingData* const binding_data = realm->AddBindingData<BindingData>(target);
if (binding_data == nullptr) return;

std::vector<Local<Value>> methods_val;
std::vector<Local<Value>> all_methods_val;

#define V(num, name, string) \
methods_val.push_back(FIXED_ONE_BYTE_STRING(isolate, #string));
HTTP_METHOD_MAP(V)
#undef V
#define V(num, name, string) \
all_methods_val.push_back(FIXED_ONE_BYTE_STRING(isolate, #string));
HTTP_ALL_METHOD_MAP(V)
#undef V

Local<Array> methods =
Array::New(isolate, methods_val.data(), methods_val.size());
Local<Array> all_methods =
Array::New(isolate, all_methods_val.data(), all_methods_val.size());
if (!target
->Set(env->context(),
FIXED_ONE_BYTE_STRING(isolate, "methods"),
methods)
.IsJust()) {
return;
}
if (target
->Set(env->context(),
FIXED_ONE_BYTE_STRING(isolate, "allMethods"),
all_methods)
.IsJust()) {
return;
}
}

void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
registry->Register(Parser::New);
registry->Register(Parser::Close);
registry->Register(Parser::Free);
registry->Register(Parser::Remove);
registry->Register(Parser::Execute);
registry->Register(Parser::Finish);
registry->Register(Parser::Initialize);
registry->Register(Parser::Pause<true>);
registry->Register(Parser::Pause<false>);
registry->Register(Parser::Consume);
registry->Register(Parser::Unconsume);
registry->Register(Parser::GetCurrentBuffer);
registry->Register(Parser::Duration);
registry->Register(Parser::HeadersCompleted);
registry->Register(ConnectionsList::New);
registry->Register(ConnectionsList::All);
registry->Register(ConnectionsList::Idle);
registry->Register(ConnectionsList::Active);
registry->Register(ConnectionsList::Expired);
}

} // anonymous namespace
} // namespace http_parser
} // namespace node

NODE_BINDING_CONTEXT_AWARE_INTERNAL(http_parser, node::InitializeHttpParser)
NODE_BINDING_CONTEXT_AWARE_INTERNAL(
http_parser, node::http_parser::CreatePerContextProperties)
NODE_BINDING_PER_ISOLATE_INIT(http_parser,
node::http_parser::CreatePerIsolateProperties)
NODE_BINDING_EXTERNAL_REFERENCE(http_parser,
node::http_parser::RegisterExternalReferences)

0 comments on commit 4e4d1de

Please sign in to comment.