Skip to content

Commit 3372af1

Browse files
ShogunPandaguangwong
authored andcommitted
http: defer reentrant execution of Parser::Execute
PR-URL: nodejs/node#43369 Fixes: nodejs/node#39671 Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: Matteo Collina <[email protected]>
1 parent 762ffa3 commit 3372af1

File tree

4 files changed

+65
-73
lines changed

4 files changed

+65
-73
lines changed

lib/_http_common.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,17 +128,16 @@ function parserOnHeadersComplete(versionMajor, versionMinor, headers, method,
128128
return parser.onIncoming(incoming, shouldKeepAlive);
129129
}
130130

131-
function parserOnBody(b, start, len) {
131+
function parserOnBody(b) {
132132
const stream = this.incoming;
133133

134134
// If the stream has already been removed, then drop it.
135135
if (stream === null)
136136
return;
137137

138138
// Pretend this was the result of a stream._read call.
139-
if (len > 0 && !stream._dumped) {
140-
const slice = b.slice(start, start + len);
141-
const ret = stream.push(slice);
139+
if (!stream._dumped) {
140+
const ret = stream.push(b);
142141
if (!ret)
143142
readStop(this.socket);
144143
}

src/node_http_parser.cc

Lines changed: 9 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -199,11 +199,7 @@ class Parser : public AsyncWrap, public StreamListener {
199199
binding_data_(binding_data) {
200200
}
201201

202-
203-
void MemoryInfo(MemoryTracker* tracker) const override {
204-
tracker->TrackField("current_buffer", current_buffer_);
205-
}
206-
202+
SET_NO_MEMORY_INFO()
207203
SET_MEMORY_INFO_NAME(Parser)
208204
SET_SELF_SIZE(Parser)
209205

@@ -392,32 +388,20 @@ class Parser : public AsyncWrap, public StreamListener {
392388

393389

394390
int on_body(const char* at, size_t length) {
395-
EscapableHandleScope scope(env()->isolate());
391+
if (length == 0)
392+
return 0;
396393

397-
Local<Object> obj = object();
398-
Local<Value> cb = obj->Get(env()->context(), kOnBody).ToLocalChecked();
394+
Environment* env = this->env();
395+
HandleScope handle_scope(env->isolate());
396+
397+
Local<Value> cb = object()->Get(env->context(), kOnBody).ToLocalChecked();
399398

400399
if (!cb->IsFunction())
401400
return 0;
402401

403-
// We came from consumed stream
404-
if (current_buffer_.IsEmpty()) {
405-
// Make sure Buffer will be in parent HandleScope
406-
current_buffer_ = scope.Escape(Buffer::Copy(
407-
env()->isolate(),
408-
current_buffer_data_,
409-
current_buffer_len_).ToLocalChecked());
410-
}
402+
Local<Value> buffer = Buffer::Copy(env, at, length).ToLocalChecked();
411403

412-
Local<Value> argv[3] = {
413-
current_buffer_,
414-
Integer::NewFromUnsigned(
415-
env()->isolate(), static_cast<uint32_t>(at - current_buffer_data_)),
416-
Integer::NewFromUnsigned(env()->isolate(), length)};
417-
418-
MaybeLocal<Value> r = MakeCallback(cb.As<Function>(),
419-
arraysize(argv),
420-
argv);
404+
MaybeLocal<Value> r = MakeCallback(cb.As<Function>(), 1, &buffer);
421405

422406
if (r.IsEmpty()) {
423407
got_exception_ = true;
@@ -514,17 +498,9 @@ class Parser : public AsyncWrap, public StreamListener {
514498
static void Execute(const FunctionCallbackInfo<Value>& args) {
515499
Parser* parser;
516500
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder());
517-
CHECK(parser->current_buffer_.IsEmpty());
518-
CHECK_EQ(parser->current_buffer_len_, 0);
519-
CHECK_NULL(parser->current_buffer_data_);
520501

521502
ArrayBufferViewContents<char> buffer(args[0]);
522503

523-
// This is a hack to get the current_buffer to the callbacks with the least
524-
// amount of overhead. Nothing else will run while http_parser_execute()
525-
// runs, therefore this pointer can be set and used for the execution.
526-
parser->current_buffer_ = args[0].As<Object>();
527-
528504
Local<Value> ret = parser->Execute(buffer.data(), buffer.length());
529505

530506
if (!ret.IsEmpty())
@@ -536,7 +512,6 @@ class Parser : public AsyncWrap, public StreamListener {
536512
Parser* parser;
537513
ASSIGN_OR_RETURN_UNWRAP(&parser, args.Holder());
538514

539-
CHECK(parser->current_buffer_.IsEmpty());
540515
Local<Value> ret = parser->Execute(nullptr, 0);
541516

542517
if (!ret.IsEmpty())
@@ -600,11 +575,6 @@ class Parser : public AsyncWrap, public StreamListener {
600575
// Should always be called from the same context.
601576
CHECK_EQ(env, parser->env());
602577

603-
if (parser->execute_depth_) {
604-
parser->pending_pause_ = should_pause;
605-
return;
606-
}
607-
608578
if (should_pause) {
609579
llhttp_pause(&parser->parser_);
610580
} else {
@@ -686,7 +656,6 @@ class Parser : public AsyncWrap, public StreamListener {
686656
if (nread == 0)
687657
return;
688658

689-
current_buffer_.Clear();
690659
Local<Value> ret = Execute(buf.base, nread);
691660

692661
// Exception
@@ -737,17 +706,12 @@ class Parser : public AsyncWrap, public StreamListener {
737706

738707
llhttp_errno_t err;
739708

740-
// Do not allow re-entering `http_parser_execute()`
741-
CHECK_EQ(execute_depth_, 0);
742-
743-
execute_depth_++;
744709
if (data == nullptr) {
745710
err = llhttp_finish(&parser_);
746711
} else {
747712
err = llhttp_execute(&parser_, data, len);
748713
Save();
749714
}
750-
execute_depth_--;
751715

752716
// Calculate bytes read and resume after Upgrade/CONNECT pause
753717
size_t nread = len;
@@ -767,8 +731,6 @@ class Parser : public AsyncWrap, public StreamListener {
767731
llhttp_pause(&parser_);
768732
}
769733

770-
// Unassign the 'buffer_' variable
771-
current_buffer_.Clear();
772734
current_buffer_len_ = 0;
773735
current_buffer_data_ = nullptr;
774736

@@ -893,8 +855,6 @@ class Parser : public AsyncWrap, public StreamListener {
893855

894856

895857
int MaybePause() {
896-
CHECK_NE(execute_depth_, 0);
897-
898858
if (!pending_pause_) {
899859
return 0;
900860
}
@@ -922,10 +882,8 @@ class Parser : public AsyncWrap, public StreamListener {
922882
size_t num_values_;
923883
bool have_flushed_;
924884
bool got_exception_;
925-
Local<Object> current_buffer_;
926885
size_t current_buffer_len_;
927886
const char* current_buffer_data_;
928-
unsigned int execute_depth_ = 0;
929887
bool pending_pause_ = false;
930888
uint64_t header_nread_ = 0;
931889
uint64_t max_http_header_size_;
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const assert = require('assert');
5+
const { request } = require('http');
6+
const { Duplex } = require('stream');
7+
8+
let socket;
9+
10+
function createConnection(...args) {
11+
socket = new Duplex({
12+
read() {},
13+
write(chunk, encoding, callback) {
14+
if (chunk.toString().includes('\r\n\r\n')) {
15+
this.push('HTTP/1.1 100 Continue\r\n\r\n');
16+
}
17+
18+
callback();
19+
}
20+
});
21+
22+
return socket;
23+
}
24+
25+
const req = request('http://localhost:8080', { createConnection });
26+
27+
req.on('information', common.mustCall(({ statusCode }) => {
28+
assert.strictEqual(statusCode, 100);
29+
socket.push('HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n');
30+
socket.push(null);
31+
}));
32+
33+
req.on('response', common.mustCall(({ statusCode }) => {
34+
assert.strictEqual(statusCode, 200);
35+
}));
36+
37+
req.end();

test/parallel/test-http-parser.js

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ function newParser(type) {
6262

6363

6464
function expectBody(expected) {
65-
return mustCall(function(buf, start, len) {
66-
const body = String(buf.slice(start, start + len));
65+
return mustCall(function(buf) {
66+
const body = String(buf);
6767
assert.strictEqual(body, expected);
6868
});
6969
}
@@ -126,8 +126,8 @@ function expectBody(expected) {
126126
assert.strictEqual(statusMessage, 'OK');
127127
};
128128

129-
const onBody = (buf, start, len) => {
130-
const body = String(buf.slice(start, start + len));
129+
const onBody = (buf) => {
130+
const body = String(buf);
131131
assert.strictEqual(body, 'pong');
132132
};
133133

@@ -195,8 +195,8 @@ function expectBody(expected) {
195195
parser[kOnHeaders] = mustCall(onHeaders);
196196
};
197197

198-
const onBody = (buf, start, len) => {
199-
const body = String(buf.slice(start, start + len));
198+
const onBody = (buf) => {
199+
const body = String(buf);
200200
assert.strictEqual(body, 'ping');
201201
seen_body = true;
202202
};
@@ -291,8 +291,8 @@ function expectBody(expected) {
291291
assert.strictEqual(versionMinor, 1);
292292
};
293293

294-
const onBody = (buf, start, len) => {
295-
const body = String(buf.slice(start, start + len));
294+
const onBody = (buf) => {
295+
const body = String(buf);
296296
assert.strictEqual(body, 'foo=42&bar=1337');
297297
};
298298

@@ -332,8 +332,8 @@ function expectBody(expected) {
332332
let body_part = 0;
333333
const body_parts = ['123', '123456', '1234567890'];
334334

335-
const onBody = (buf, start, len) => {
336-
const body = String(buf.slice(start, start + len));
335+
const onBody = (buf) => {
336+
const body = String(buf);
337337
assert.strictEqual(body, body_parts[body_part++]);
338338
};
339339

@@ -371,8 +371,8 @@ function expectBody(expected) {
371371
const body_parts =
372372
['123', '123456', '123456789', '123456789ABC', '123456789ABCDEF'];
373373

374-
const onBody = (buf, start, len) => {
375-
const body = String(buf.slice(start, start + len));
374+
const onBody = (buf) => {
375+
const body = String(buf);
376376
assert.strictEqual(body, body_parts[body_part++]);
377377
};
378378

@@ -428,8 +428,8 @@ function expectBody(expected) {
428428

429429
let expected_body = '123123456123456789123456789ABC123456789ABCDEF';
430430

431-
const onBody = (buf, start, len) => {
432-
const chunk = String(buf.slice(start, start + len));
431+
const onBody = (buf) => {
432+
const chunk = String(buf);
433433
assert.strictEqual(expected_body.indexOf(chunk), 0);
434434
expected_body = expected_body.slice(chunk.length);
435435
};
@@ -445,9 +445,7 @@ function expectBody(expected) {
445445

446446
for (let i = 1; i < request.length - 1; ++i) {
447447
const a = request.slice(0, i);
448-
console.error(`request.slice(0, ${i}) = ${JSON.stringify(a.toString())}`);
449448
const b = request.slice(i);
450-
console.error(`request.slice(${i}) = ${JSON.stringify(b.toString())}`);
451449
test(a, b);
452450
}
453451
}
@@ -488,8 +486,8 @@ function expectBody(expected) {
488486

489487
let expected_body = '123123456123456789123456789ABC123456789ABCDEF';
490488

491-
const onBody = (buf, start, len) => {
492-
const chunk = String(buf.slice(start, start + len));
489+
const onBody = (buf) => {
490+
const chunk = String(buf);
493491
assert.strictEqual(expected_body.indexOf(chunk), 0);
494492
expected_body = expected_body.slice(chunk.length);
495493
};

0 commit comments

Comments
 (0)