Skip to content

Commit 4d3aad7

Browse files
anonrigJonas Badalic
andcommitted
http: improve http parser performance
Co-Authored-by: Jonas Badalic <[email protected]>
1 parent 8eeb8fc commit 4d3aad7

20 files changed

Lines changed: 77 additions & 504 deletions

benchmark/http/bench-parser.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@ function main({ len, n }) {
2323
bench.start();
2424
for (let i = 0; i < n; i++) {
2525
parser.execute(header, 0, header.length);
26-
parser.initialize(REQUEST, {});
26+
parser.initialize(REQUEST);
2727
}
2828
bench.end(n);
2929
}
3030

3131
function newParser(type) {
3232
const parser = new HTTPParser();
33-
parser.initialize(type, {});
33+
parser.initialize(type);
3434

3535
parser.headers = [];
3636

lib/_http_client.js

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -130,13 +130,6 @@ function validateHost(host, name) {
130130
return host;
131131
}
132132

133-
class HTTPClientAsyncResource {
134-
constructor(type, req) {
135-
this.type = type;
136-
this.req = req;
137-
}
138-
}
139-
140133
// When proxying a HTTP request, the following needs to be done:
141134
// https://datatracker.ietf.org/doc/html/rfc7230#section-5.3.2
142135
// 1. Rewrite the request path to absolute-form.
@@ -880,7 +873,6 @@ function tickOnSocket(req, socket) {
880873
const lenient = req.insecureHTTPParser === undefined ?
881874
isLenient() : req.insecureHTTPParser;
882875
parser.initialize(HTTPParser.RESPONSE,
883-
new HTTPClientAsyncResource('HTTPINCOMINGMESSAGE', req),
884876
req.maxHeaderSize || 0,
885877
lenient ? kLenientAll : kLenientNone);
886878
parser.socket = socket;

lib/_http_common.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -187,10 +187,6 @@ function freeParser(parser, req, socket) {
187187
// Make sure the parser's stack has unwound before deleting the
188188
// corresponding C++ object through .close().
189189
setImmediate(closeParserInstance, parser);
190-
} else {
191-
// Since the Parser destructor isn't going to run the destroy() callbacks
192-
// it needs to be triggered manually.
193-
parser.free();
194190
}
195191
}
196192
if (req) {

lib/_http_server.js

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -187,13 +187,6 @@ const kConnectionsCheckingInterval = Symbol('http.server.connectionsCheckingInte
187187

188188
const HTTP_SERVER_TRACE_EVENT_NAME = 'http.server.request';
189189

190-
class HTTPServerAsyncResource {
191-
constructor(type, socket) {
192-
this.type = type;
193-
this.socket = socket;
194-
}
195-
}
196-
197190
function ServerResponse(req, options) {
198191
OutgoingMessage.call(this, options);
199192

@@ -705,7 +698,6 @@ function connectionListenerInternal(server, socket) {
705698
// https://github.com/nodejs/node/pull/21313
706699
parser.initialize(
707700
HTTPParser.REQUEST,
708-
new HTTPServerAsyncResource('HTTPINCOMINGMESSAGE', socket),
709701
server.maxHeaderSize || 0,
710702
lenient ? kLenientAll : kLenientNone,
711703
server[kConnections],

src/async_wrap.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,6 @@ namespace node {
4949
V(HTTP2STREAM) \
5050
V(HTTP2PING) \
5151
V(HTTP2SETTINGS) \
52-
V(HTTPINCOMINGMESSAGE) \
53-
V(HTTPCLIENTREQUEST) \
5452
V(LOCKS) \
5553
V(JSSTREAM) \
5654
V(JSUDPWRAP) \

src/node_http_parser.cc

Lines changed: 32 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
#include "node_buffer.h"
2424
#include "util.h"
2525

26-
#include "async_wrap-inl.h"
2726
#include "env-inl.h"
2827
#include "llhttp.h"
2928
#include "memory_tracker-inl.h"
@@ -250,17 +249,16 @@ class ConnectionsList : public BaseObject {
250249
std::set<Parser*, ParserComparator> active_connections_;
251250
};
252251

253-
class Parser : public AsyncWrap, public StreamListener {
252+
class Parser : public BaseObject, public StreamListener {
254253
friend class ConnectionsList;
255254
friend struct ParserComparator;
256255

257256
public:
258257
Parser(BindingData* binding_data, Local<Object> wrap)
259-
: AsyncWrap(binding_data->env(), wrap),
258+
: BaseObject(binding_data->env(), wrap),
260259
current_buffer_len_(0),
261260
current_buffer_data_(nullptr),
262-
binding_data_(binding_data) {
263-
}
261+
binding_data_(binding_data) {}
264262

265263
SET_NO_MEMORY_INFO()
266264
SET_MEMORY_INFO_NAME(Parser)
@@ -289,13 +287,7 @@ class Parser : public AsyncWrap, public StreamListener {
289287
Local<Value> cb = object()->Get(env()->context(), kOnMessageBegin)
290288
.ToLocalChecked();
291289
if (cb->IsFunction()) {
292-
InternalCallbackScope callback_scope(
293-
this, InternalCallbackScope::kSkipTaskQueues);
294-
295-
MaybeLocal<Value> r = cb.As<Function>()->Call(
296-
env()->context(), object(), 0, nullptr);
297-
298-
if (r.IsEmpty()) callback_scope.MarkAsFailed();
290+
USE(cb.As<Function>()->Call(env()->context(), object(), 0, nullptr));
299291
}
300292

301293
return 0;
@@ -442,14 +434,8 @@ class Parser : public AsyncWrap, public StreamListener {
442434

443435
argv[A_UPGRADE] = Boolean::New(env()->isolate(), parser_.upgrade);
444436

445-
MaybeLocal<Value> head_response;
446-
{
447-
InternalCallbackScope callback_scope(
448-
this, InternalCallbackScope::kSkipTaskQueues);
449-
head_response = cb.As<Function>()->Call(
450-
env()->context(), object(), arraysize(argv), argv);
451-
if (head_response.IsEmpty()) callback_scope.MarkAsFailed();
452-
}
437+
auto head_response = cb.As<Function>()->Call(
438+
env()->context(), object(), arraysize(argv), argv);
453439

454440
int64_t val;
455441

@@ -478,9 +464,10 @@ class Parser : public AsyncWrap, public StreamListener {
478464

479465
Local<Value> buffer = Buffer::Copy(env, at, length).ToLocalChecked();
480466

481-
MaybeLocal<Value> r = MakeCallback(cb.As<Function>(), 1, &buffer);
467+
v8::TryCatch try_catch(env->isolate());
468+
USE(cb.As<Function>()->Call(env->context(), object(), 1, &buffer));
482469

483-
if (r.IsEmpty()) {
470+
if (try_catch.HasCaught()) {
484471
got_exception_ = true;
485472
llhttp_set_error_reason(&parser_, "HPE_JS_EXCEPTION:JS Exception");
486473
return HPE_USER;
@@ -516,15 +503,10 @@ class Parser : public AsyncWrap, public StreamListener {
516503
if (!cb->IsFunction())
517504
return 0;
518505

519-
MaybeLocal<Value> r;
520-
{
521-
InternalCallbackScope callback_scope(
522-
this, InternalCallbackScope::kSkipTaskQueues);
523-
r = cb.As<Function>()->Call(env()->context(), object(), 0, nullptr);
524-
if (r.IsEmpty()) callback_scope.MarkAsFailed();
525-
}
506+
v8::TryCatch try_catch(env()->isolate());
507+
USE(cb.As<Function>()->Call(env()->context(), object(), 0, nullptr));
526508

527-
if (r.IsEmpty()) {
509+
if (try_catch.HasCaught()) {
528510
got_exception_ = true;
529511
return -1;
530512
}
@@ -571,17 +553,6 @@ class Parser : public AsyncWrap, public StreamListener {
571553
delete parser;
572554
}
573555

574-
// TODO(@anonrig): Add V8 Fast API
575-
static void Free(const FunctionCallbackInfo<Value>& args) {
576-
Parser* parser;
577-
ASSIGN_OR_RETURN_UNWRAP(&parser, args.This());
578-
579-
// Since the Parser destructor isn't going to run the destroy() callbacks
580-
// it needs to be triggered manually.
581-
parser->EmitTraceEventDestroy();
582-
parser->EmitDestroy();
583-
}
584-
585556
// TODO(@anonrig): Add V8 Fast API
586557
static void Remove(const FunctionCallbackInfo<Value>& args) {
587558
Parser* parser;
@@ -639,25 +610,24 @@ class Parser : public AsyncWrap, public StreamListener {
639610
ConnectionsList* connectionsList = nullptr;
640611

641612
CHECK(args[0]->IsInt32());
642-
CHECK(args[1]->IsObject());
643613

644-
if (args.Length() > 2) {
645-
CHECK(args[2]->IsNumber());
614+
if (args.Length() > 1) {
615+
CHECK(args[1]->IsNumber());
646616
max_http_header_size =
647-
static_cast<uint64_t>(args[2].As<Number>()->Value());
617+
static_cast<uint64_t>(args[1].As<Number>()->Value());
648618
}
649619
if (max_http_header_size == 0) {
650620
max_http_header_size = env->options()->max_http_header_size;
651621
}
652622

653-
if (args.Length() > 3) {
654-
CHECK(args[3]->IsInt32());
655-
lenient_flags = args[3].As<Int32>()->Value();
623+
if (args.Length() > 2) {
624+
CHECK(args[2]->IsInt32());
625+
lenient_flags = args[2].As<Int32>()->Value();
656626
}
657627

658-
if (args.Length() > 4 && !args[4]->IsNullOrUndefined()) {
659-
CHECK(args[4]->IsObject());
660-
ASSIGN_OR_RETURN_UNWRAP(&connectionsList, args[4]);
628+
if (args.Length() > 3 && !args[3]->IsNullOrUndefined()) {
629+
CHECK(args[3]->IsObject());
630+
ASSIGN_OR_RETURN_UNWRAP(&connectionsList, args[3]);
661631
}
662632

663633
llhttp_type_t type =
@@ -669,13 +639,6 @@ class Parser : public AsyncWrap, public StreamListener {
669639
// Should always be called from the same context.
670640
CHECK_EQ(env, parser->env());
671641

672-
AsyncWrap::ProviderType provider =
673-
(type == HTTP_REQUEST ?
674-
AsyncWrap::PROVIDER_HTTPINCOMINGMESSAGE
675-
: AsyncWrap::PROVIDER_HTTPCLIENTREQUEST);
676-
677-
parser->set_provider_type(provider);
678-
parser->AsyncReset(args[1].As<Object>());
679642
parser->Init(type, max_http_header_size, lenient_flags);
680643

681644
if (connectionsList != nullptr) {
@@ -802,7 +765,13 @@ class Parser : public AsyncWrap, public StreamListener {
802765
current_buffer_len_ = nread;
803766
current_buffer_data_ = buf.base;
804767

805-
MakeCallback(cb.As<Function>(), 1, &ret);
768+
v8::TryCatch try_catch(env()->isolate());
769+
USE(cb.As<Function>()->Call(env()->context(), object(), 1, &ret));
770+
771+
if (try_catch.HasCaught()) {
772+
got_exception_ = true;
773+
return;
774+
}
806775

807776
current_buffer_len_ = 0;
808777
current_buffer_data_ = nullptr;
@@ -917,12 +886,11 @@ class Parser : public AsyncWrap, public StreamListener {
917886
url_.ToString(env())
918887
};
919888

920-
MaybeLocal<Value> r = MakeCallback(cb.As<Function>(),
921-
arraysize(argv),
922-
argv);
889+
v8::TryCatch try_catch(env()->isolate());
890+
USE(cb.As<Function>()->Call(
891+
env()->context(), object(), arraysize(argv), argv));
923892

924-
if (r.IsEmpty())
925-
got_exception_ = true;
893+
if (try_catch.HasCaught()) got_exception_ = true;
926894

927895
url_.Reset();
928896
have_flushed_ = true;
@@ -1287,9 +1255,7 @@ void CreatePerIsolateProperties(IsolateData* isolate_data,
12871255
t->Set(FIXED_ONE_BYTE_STRING(isolate, "kLenientAll"),
12881256
Integer::NewFromUnsigned(isolate, kLenientAll));
12891257

1290-
t->Inherit(AsyncWrap::GetConstructorTemplate(isolate_data));
12911258
SetProtoMethod(isolate, t, "close", Parser::Close);
1292-
SetProtoMethod(isolate, t, "free", Parser::Free);
12931259
SetProtoMethod(isolate, t, "remove", Parser::Remove);
12941260
SetProtoMethod(isolate, t, "execute", Parser::Execute);
12951261
SetProtoMethod(isolate, t, "finish", Parser::Finish);
@@ -1358,7 +1324,6 @@ void CreatePerContextProperties(Local<Object> target,
13581324
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
13591325
registry->Register(Parser::New);
13601326
registry->Register(Parser::Close);
1361-
registry->Register(Parser::Free);
13621327
registry->Register(Parser::Remove);
13631328
registry->Register(Parser::Execute);
13641329
registry->Register(Parser::Finish);

test/async-hooks/test-graph.http.js

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,7 @@ process.on('exit', () => {
3636
{ type: 'TCPCONNECTWRAP',
3737
id: 'tcpconnect:1',
3838
triggerAsyncId: 'tcp:1' },
39-
{ type: 'HTTPCLIENTREQUEST',
40-
id: 'httpclientrequest:1',
41-
triggerAsyncId: 'tcpserver:1' },
4239
{ type: 'TCPWRAP', id: 'tcp:2', triggerAsyncId: 'tcpserver:1' },
43-
{ type: 'HTTPINCOMINGMESSAGE',
44-
id: 'httpincomingmessage:1',
45-
triggerAsyncId: 'tcp:2' },
4640
{ type: 'Timeout',
4741
id: 'timeout:1',
4842
triggerAsyncId: null },

test/async-hooks/test-httpparser-reuse.js

Lines changed: 0 additions & 75 deletions
This file was deleted.

0 commit comments

Comments
 (0)