Skip to content

Commit 2934b6f

Browse files
committed
http: add V8 Fast API to http parser
Add V8 Fast API support for the following HTTP parser methods: - free(): triggers destroy callbacks - remove(): removes parser from connections list - pause(): pauses the HTTP parser - resume(): resumes the HTTP parser - unconsume(): removes parser as stream listener The following methods were intentionally not converted: - close(): deletes the parser object, unsafe in Fast API - consume(): requires unwrapping StreamBase from JS object This improves performance by bypassing V8's argument marshaling overhead for these frequently called methods.
1 parent 286828b commit 2934b6f

File tree

1 file changed

+89
-11
lines changed

1 file changed

+89
-11
lines changed

src/node_http_parser.cc

Lines changed: 89 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
#include "node.h"
2323
#include "node_buffer.h"
24+
#include "node_debug.h"
2425
#include "util.h"
2526

2627
#include "async_wrap-inl.h"
@@ -52,6 +53,7 @@ namespace http_parser { // NOLINT(build/namespaces)
5253

5354
using v8::Array;
5455
using v8::Boolean;
56+
using v8::CFunction;
5557
using v8::Context;
5658
using v8::EscapableHandleScope;
5759
using v8::Exception;
@@ -604,30 +606,49 @@ class Parser : public AsyncWrap, public StreamListener {
604606
new Parser(binding_data, args.This());
605607
}
606608

607-
// TODO(@anonrig): Add V8 Fast API
609+
// NOTE: Close() deletes the parser object, which is not safe in Fast API
610+
// because the calling convention assumes the receiver remains valid.
608611
static void Close(const FunctionCallbackInfo<Value>& args) {
609612
Parser* parser;
610613
ASSIGN_OR_RETURN_UNWRAP(&parser, args.This());
611614

612615
delete parser;
613616
}
614617

615-
// TODO(@anonrig): Add V8 Fast API
616618
static void Free(const FunctionCallbackInfo<Value>& args) {
617619
Parser* parser;
618620
ASSIGN_OR_RETURN_UNWRAP(&parser, args.This());
621+
FreeImpl(parser);
622+
}
623+
624+
static void FastFree(Local<Object> receiver) {
625+
TRACK_V8_FAST_API_CALL("http_parser.free");
626+
Parser* parser;
627+
ASSIGN_OR_RETURN_UNWRAP(&parser, receiver);
628+
FreeImpl(parser);
629+
}
619630

631+
static void FreeImpl(Parser* parser) {
620632
// Since the Parser destructor isn't going to run the destroy() callbacks
621633
// it needs to be triggered manually.
622634
parser->EmitTraceEventDestroy();
623635
parser->EmitDestroy();
624636
}
625637

626-
// TODO(@anonrig): Add V8 Fast API
627638
static void Remove(const FunctionCallbackInfo<Value>& args) {
628639
Parser* parser;
629640
ASSIGN_OR_RETURN_UNWRAP(&parser, args.This());
641+
RemoveImpl(parser);
642+
}
643+
644+
static void FastRemove(Local<Object> receiver) {
645+
TRACK_V8_FAST_API_CALL("http_parser.remove");
646+
Parser* parser;
647+
ASSIGN_OR_RETURN_UNWRAP(&parser, receiver);
648+
RemoveImpl(parser);
649+
}
630650

651+
static void RemoveImpl(Parser* parser) {
631652
if (parser->connectionsList_ != nullptr) {
632653
parser->connectionsList_->Pop(parser);
633654
parser->connectionsList_->PopActive(parser);
@@ -736,23 +757,39 @@ class Parser : public AsyncWrap, public StreamListener {
736757
}
737758
}
738759

739-
// TODO(@anonrig): Add V8 Fast API
740760
template <bool should_pause>
741761
static void Pause(const FunctionCallbackInfo<Value>& args) {
742762
Environment* env = Environment::GetCurrent(args);
743763
Parser* parser;
744764
ASSIGN_OR_RETURN_UNWRAP(&parser, args.This());
745765
// Should always be called from the same context.
746766
CHECK_EQ(env, parser->env());
767+
PauseImpl<should_pause>(parser);
768+
}
769+
770+
template <bool should_pause>
771+
static void FastPause(Local<Object> receiver) {
772+
if constexpr (should_pause) {
773+
TRACK_V8_FAST_API_CALL("http_parser.pause");
774+
} else {
775+
TRACK_V8_FAST_API_CALL("http_parser.resume");
776+
}
777+
Parser* parser;
778+
ASSIGN_OR_RETURN_UNWRAP(&parser, receiver);
779+
PauseImpl<should_pause>(parser);
780+
}
747781

782+
template <bool should_pause>
783+
static void PauseImpl(Parser* parser) {
748784
if constexpr (should_pause) {
749785
llhttp_pause(&parser->parser_);
750786
} else {
751787
llhttp_resume(&parser->parser_);
752788
}
753789
}
754790

755-
// TODO(@anonrig): Add V8 Fast API
791+
// NOTE: Consume() requires unwrapping a StreamBase from a JS object argument,
792+
// which involves V8 API calls that are not supported in Fast API.
756793
static void Consume(const FunctionCallbackInfo<Value>& args) {
757794
Parser* parser;
758795
ASSIGN_OR_RETURN_UNWRAP(&parser, args.This());
@@ -762,11 +799,20 @@ class Parser : public AsyncWrap, public StreamListener {
762799
stream->PushStreamListener(parser);
763800
}
764801

765-
// TODO(@anonrig): Add V8 Fast API
766802
static void Unconsume(const FunctionCallbackInfo<Value>& args) {
767803
Parser* parser;
768804
ASSIGN_OR_RETURN_UNWRAP(&parser, args.This());
805+
UnconsumeImpl(parser);
806+
}
769807

808+
static void FastUnconsume(Local<Object> receiver) {
809+
TRACK_V8_FAST_API_CALL("http_parser.unconsume");
810+
Parser* parser;
811+
ASSIGN_OR_RETURN_UNWRAP(&parser, receiver);
812+
UnconsumeImpl(parser);
813+
}
814+
815+
static void UnconsumeImpl(Parser* parser) {
770816
// Already unconsumed
771817
if (parser->stream_ == nullptr)
772818
return;
@@ -1086,9 +1132,22 @@ class Parser : public AsyncWrap, public StreamListener {
10861132
typedef int (Parser::*Call)();
10871133
typedef int (Parser::*DataCall)(const char* at, size_t length);
10881134

1135+
public:
10891136
static const llhttp_settings_t settings;
1137+
1138+
static CFunction fast_free_;
1139+
static CFunction fast_remove_;
1140+
static CFunction fast_pause_;
1141+
static CFunction fast_resume_;
1142+
static CFunction fast_unconsume_;
10901143
};
10911144

1145+
CFunction Parser::fast_free_(CFunction::Make(Parser::FastFree));
1146+
CFunction Parser::fast_remove_(CFunction::Make(Parser::FastRemove));
1147+
CFunction Parser::fast_pause_(CFunction::Make(Parser::FastPause<true>));
1148+
CFunction Parser::fast_resume_(CFunction::Make(Parser::FastPause<false>));
1149+
CFunction Parser::fast_unconsume_(CFunction::Make(Parser::FastUnconsume));
1150+
10921151
bool ParserComparator::operator()(const Parser* lhs, const Parser* rhs) const {
10931152
if (lhs->last_message_start_ == 0 && rhs->last_message_start_ == 0) {
10941153
// When both parsers are idle, guarantee strict order by
@@ -1330,16 +1389,30 @@ void CreatePerIsolateProperties(IsolateData* isolate_data,
13301389
Integer::NewFromUnsigned(isolate, kLenientAll));
13311390

13321391
t->Inherit(AsyncWrap::GetConstructorTemplate(isolate_data));
1392+
Local<ObjectTemplate> instance = t->InstanceTemplate();
13331393
SetProtoMethod(isolate, t, "close", Parser::Close);
1334-
SetProtoMethod(isolate, t, "free", Parser::Free);
1335-
SetProtoMethod(isolate, t, "remove", Parser::Remove);
1394+
SetFastMethod(isolate, instance, "free", Parser::Free, &Parser::fast_free_);
1395+
SetFastMethod(
1396+
isolate, instance, "remove", Parser::Remove, &Parser::fast_remove_);
13361397
SetProtoMethod(isolate, t, "execute", Parser::Execute);
13371398
SetProtoMethod(isolate, t, "finish", Parser::Finish);
13381399
SetProtoMethod(isolate, t, "initialize", Parser::Initialize);
1339-
SetProtoMethod(isolate, t, "pause", Parser::Pause<true>);
1340-
SetProtoMethod(isolate, t, "resume", Parser::Pause<false>);
1400+
SetFastMethod(isolate,
1401+
instance,
1402+
"pause",
1403+
Parser::Pause<true>,
1404+
&Parser::fast_pause_);
1405+
SetFastMethod(isolate,
1406+
instance,
1407+
"resume",
1408+
Parser::Pause<false>,
1409+
&Parser::fast_resume_);
13411410
SetProtoMethod(isolate, t, "consume", Parser::Consume);
1342-
SetProtoMethod(isolate, t, "unconsume", Parser::Unconsume);
1411+
SetFastMethod(isolate,
1412+
instance,
1413+
"unconsume",
1414+
Parser::Unconsume,
1415+
&Parser::fast_unconsume_);
13431416
SetProtoMethod(isolate, t, "getCurrentBuffer", Parser::GetCurrentBuffer);
13441417

13451418
SetConstructorFunction(isolate, target, "HTTPParser", t);
@@ -1401,14 +1474,19 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
14011474
registry->Register(Parser::New);
14021475
registry->Register(Parser::Close);
14031476
registry->Register(Parser::Free);
1477+
registry->Register(Parser::fast_free_);
14041478
registry->Register(Parser::Remove);
1479+
registry->Register(Parser::fast_remove_);
14051480
registry->Register(Parser::Execute);
14061481
registry->Register(Parser::Finish);
14071482
registry->Register(Parser::Initialize);
14081483
registry->Register(Parser::Pause<true>);
1484+
registry->Register(Parser::fast_pause_);
14091485
registry->Register(Parser::Pause<false>);
1486+
registry->Register(Parser::fast_resume_);
14101487
registry->Register(Parser::Consume);
14111488
registry->Register(Parser::Unconsume);
1489+
registry->Register(Parser::fast_unconsume_);
14121490
registry->Register(Parser::GetCurrentBuffer);
14131491
registry->Register(ConnectionsList::New);
14141492
registry->Register(ConnectionsList::All);

0 commit comments

Comments
 (0)