Skip to content

Commit bc9f629

Browse files
indutnyFishrock123
authored andcommittedSep 20, 2015
http_parser: do not dealloc during kOnExecute
`freeParser` deallocates `Parser` instances early if they do not fit into the free list. This does not play well with recent socket consumption change, because it will try to deallocate the parser while executing on its stack. Regression was introduced in: 1bc4468 Fix: #2928 PR-URL: #2956 Reviewed-by: Trevor Norris <[email protected]>
1 parent f7edbab commit bc9f629

File tree

2 files changed

+75
-1
lines changed

2 files changed

+75
-1
lines changed
 

‎src/node_http_parser.cc

+24-1
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,9 @@ class Parser : public BaseObject {
366366

367367
static void Close(const FunctionCallbackInfo<Value>& args) {
368368
Parser* parser = Unwrap<Parser>(args.Holder());
369-
delete parser;
369+
370+
if (--parser->refcount_ == 0)
371+
delete parser;
370372
}
371373

372374

@@ -504,6 +506,22 @@ class Parser : public BaseObject {
504506
}
505507

506508
protected:
509+
class ScopedRetainParser {
510+
public:
511+
explicit ScopedRetainParser(Parser* p) : p_(p) {
512+
CHECK_GT(p_->refcount_, 0);
513+
p_->refcount_++;
514+
}
515+
516+
~ScopedRetainParser() {
517+
if (0 == --p_->refcount_)
518+
delete p_;
519+
}
520+
521+
private:
522+
Parser* const p_;
523+
};
524+
507525
static const size_t kAllocBufferSize = 64 * 1024;
508526

509527
static void OnAllocImpl(size_t suggested_size, uv_buf_t* buf, void* ctx) {
@@ -540,6 +558,8 @@ class Parser : public BaseObject {
540558
if (nread == 0)
541559
return;
542560

561+
ScopedRetainParser retain(parser);
562+
543563
parser->current_buffer_.Clear();
544564
Local<Value> ret = parser->Execute(buf->base, nread);
545565

@@ -668,7 +688,10 @@ class Parser : public BaseObject {
668688
char* current_buffer_data_;
669689
StreamResource::Callback<StreamResource::AllocCb> prev_alloc_cb_;
670690
StreamResource::Callback<StreamResource::ReadCb> prev_read_cb_;
691+
int refcount_ = 1;
671692
static const struct http_parser_settings settings;
693+
694+
friend class ScopedRetainParser;
672695
};
673696

674697

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
'use strict';
2+
const common = require('../common');
3+
const assert = require('assert');
4+
const httpCommon = require('_http_common');
5+
const HTTPParser = process.binding('http_parser').HTTPParser;
6+
const net = require('net');
7+
8+
const PARALLEL = 30;
9+
const COUNT = httpCommon.parsers.max + 100;
10+
11+
const parsers = new Array(COUNT);
12+
for (var i = 0; i < parsers.length; i++)
13+
parsers[i] = httpCommon.parsers.alloc();
14+
15+
var gotRequests = 0;
16+
var gotResponses = 0;
17+
18+
function execAndClose() {
19+
process.stdout.write('.');
20+
if (parsers.length === 0)
21+
return;
22+
23+
const parser = parsers.pop();
24+
parser.reinitialize(HTTPParser.RESPONSE);
25+
const socket = net.connect(common.PORT);
26+
parser.consume(socket._handle._externalStream);
27+
28+
parser.onIncoming = function onIncoming() {
29+
process.stdout.write('+');
30+
gotResponses++;
31+
parser.unconsume(socket._handle._externalStream);
32+
httpCommon.freeParser(parser);
33+
socket.destroy();
34+
setImmediate(execAndClose);
35+
};
36+
}
37+
38+
var server = net.createServer(function(c) {
39+
if (++gotRequests === COUNT)
40+
server.close();
41+
c.end('HTTP/1.1 200 OK\r\n\r\n', function() {
42+
c.destroySoon();
43+
});
44+
}).listen(common.PORT, function() {
45+
for (var i = 0; i < PARALLEL; i++)
46+
execAndClose();
47+
});
48+
49+
process.on('exit', function() {
50+
assert.equal(gotResponses, COUNT);
51+
});

0 commit comments

Comments
 (0)
Please sign in to comment.