Skip to content

Commit 2669966

Browse files
committed
http: speed up callbacks, use array indices
Use array indices rather than named properties to store callbacks on the HTTPParser object. Speeds up the http benchmarks by a few percent.
1 parent d684f50 commit 2669966

4 files changed

Lines changed: 67 additions & 54 deletions

File tree

lib/_http_common.js

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,18 @@ var IncomingMessage = incoming.IncomingMessage;
2727
var readStart = incoming.readStart;
2828
var readStop = incoming.readStop;
2929

30-
3130
var debug = require('util').debuglog('http');
3231
exports.debug = debug;
3332

3433
exports.CRLF = '\r\n';
3534
exports.chunkExpression = /chunk/i;
3635
exports.continueExpression = /100-continue/i;
3736

37+
var kOnHeaders = HTTPParser.kOnHeaders | 0;
38+
var kOnHeadersComplete = HTTPParser.kOnHeadersComplete | 0;
39+
var kOnBody = HTTPParser.kOnBody | 0;
40+
var kOnMessageComplete = HTTPParser.kOnMessageComplete | 0;
41+
3842
// Only called in the slow case where slow means
3943
// that the request headers were either fragmented
4044
// across multiple TCP packets or too large to be
@@ -180,10 +184,10 @@ var parsers = new FreeList('parsers', 1000, function() {
180184
// across multiple TCP packets or too large to be
181185
// processed in a single run. This method is also
182186
// called to process trailing HTTP headers.
183-
parser.onHeaders = parserOnHeaders;
184-
parser.onHeadersComplete = parserOnHeadersComplete;
185-
parser.onBody = parserOnBody;
186-
parser.onMessageComplete = parserOnMessageComplete;
187+
parser[kOnHeaders] = parserOnHeaders;
188+
parser[kOnHeadersComplete] = parserOnHeadersComplete;
189+
parser[kOnBody] = parserOnBody;
190+
parser[kOnMessageComplete] = parserOnMessageComplete;
187191

188192
return parser;
189193
});

src/node_http_parser.cc

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,10 @@ using v8::Object;
6060
using v8::String;
6161
using v8::Value;
6262

63-
static Cached<String> on_headers_sym;
64-
static Cached<String> on_headers_complete_sym;
65-
static Cached<String> on_body_sym;
66-
static Cached<String> on_message_complete_sym;
63+
const uint32_t kOnHeaders = 0;
64+
const uint32_t kOnHeadersComplete = 1;
65+
const uint32_t kOnBody = 2;
66+
const uint32_t kOnMessageComplete = 3;
6767

6868
static Cached<String> method_sym;
6969
static Cached<String> status_code_sym;
@@ -255,7 +255,7 @@ class Parser : public ObjectWrap {
255255

256256
HTTP_CB(on_headers_complete) {
257257
Local<Object> obj = handle(node_isolate);
258-
Local<Value> cb = obj->Get(on_headers_complete_sym);
258+
Local<Value> cb = obj->Get(kOnHeadersComplete);
259259

260260
if (!cb->IsFunction())
261261
return 0;
@@ -315,7 +315,7 @@ class Parser : public ObjectWrap {
315315
HandleScope scope(node_isolate);
316316

317317
Local<Object> obj = handle(node_isolate);
318-
Local<Value> cb = obj->Get(on_body_sym);
318+
Local<Value> cb = obj->Get(kOnBody);
319319

320320
if (!cb->IsFunction())
321321
return 0;
@@ -344,7 +344,7 @@ class Parser : public ObjectWrap {
344344
Flush(); // Flush trailing HTTP headers.
345345

346346
Local<Object> obj = handle(node_isolate);
347-
Local<Value> cb = obj->Get(on_message_complete_sym);
347+
Local<Value> cb = obj->Get(kOnMessageComplete);
348348

349349
if (!cb->IsFunction())
350350
return 0;
@@ -506,7 +506,7 @@ class Parser : public ObjectWrap {
506506
HandleScope scope(node_isolate);
507507

508508
Local<Object> obj = handle(node_isolate);
509-
Local<Value> cb = obj->Get(on_headers_sym);
509+
Local<Value> cb = obj->Get(kOnHeaders);
510510

511511
if (!cb->IsFunction())
512512
return;
@@ -558,6 +558,14 @@ void InitHttpParser(Handle<Object> target) {
558558
Integer::New(HTTP_REQUEST, node_isolate));
559559
t->Set(FIXED_ONE_BYTE_STRING(node_isolate, "RESPONSE"),
560560
Integer::New(HTTP_RESPONSE, node_isolate));
561+
t->Set(FIXED_ONE_BYTE_STRING(node_isolate, "kOnHeaders"),
562+
Integer::NewFromUnsigned(kOnHeaders, node_isolate));
563+
t->Set(FIXED_ONE_BYTE_STRING(node_isolate, "kOnHeadersComplete"),
564+
Integer::NewFromUnsigned(kOnHeadersComplete, node_isolate));
565+
t->Set(FIXED_ONE_BYTE_STRING(node_isolate, "kOnBody"),
566+
Integer::NewFromUnsigned(kOnBody, node_isolate));
567+
t->Set(FIXED_ONE_BYTE_STRING(node_isolate, "kOnMessageComplete"),
568+
Integer::NewFromUnsigned(kOnMessageComplete, node_isolate));
561569

562570
NODE_SET_PROTOTYPE_METHOD(t, "execute", Parser::Execute);
563571
NODE_SET_PROTOTYPE_METHOD(t, "finish", Parser::Finish);
@@ -566,15 +574,6 @@ void InitHttpParser(Handle<Object> target) {
566574
target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "HTTPParser"),
567575
t->GetFunction());
568576

569-
on_headers_sym =
570-
FIXED_ONE_BYTE_STRING(node_isolate, "onHeaders");
571-
on_headers_complete_sym =
572-
FIXED_ONE_BYTE_STRING(node_isolate, "onHeadersComplete");
573-
on_body_sym =
574-
FIXED_ONE_BYTE_STRING(node_isolate, "onBody");
575-
on_message_complete_sym =
576-
FIXED_ONE_BYTE_STRING(node_isolate, "onMessageComplete");
577-
578577
#define X(num, name, string) \
579578
name ## _sym = OneByteString(node_isolate, #string);
580579
HTTP_METHOD_MAP(X)

test/simple/test-http-parser-bad-ref.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ var common = require('../common');
77
var assert = require('assert');
88
var HTTPParser = process.binding('http_parser').HTTPParser;
99

10+
var kOnHeaders = HTTPParser.kOnHeaders | 0;
11+
var kOnHeadersComplete = HTTPParser.kOnHeadersComplete | 0;
12+
var kOnBody = HTTPParser.kOnBody | 0;
13+
var kOnMessageComplete = HTTPParser.kOnMessageComplete | 0;
14+
1015
var headersComplete = 0;
1116
var messagesComplete = 0;
1217

@@ -23,19 +28,19 @@ function demoBug(part1, part2) {
2328
parser.headers = [];
2429
parser.url = '';
2530

26-
parser.onHeaders = function(headers, url) {
31+
parser[kOnHeaders] = function(headers, url) {
2732
parser.headers = parser.headers.concat(headers);
2833
parser.url += url;
2934
};
3035

31-
parser.onHeadersComplete = function(info) {
36+
parser[kOnHeadersComplete] = function(info) {
3237
headersComplete++;
3338
console.log('url', info.url);
3439
};
3540

36-
parser.onBody = function(b, start, len) { };
41+
parser[kOnBody] = function(b, start, len) { };
3742

38-
parser.onMessageComplete = function() {
43+
parser[kOnMessageComplete] = function() {
3944
messagesComplete++;
4045
};
4146

test/simple/test-http-parser.js

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ var CRLF = '\r\n';
2828
var REQUEST = HTTPParser.REQUEST;
2929
var RESPONSE = HTTPParser.RESPONSE;
3030

31+
var kOnHeaders = HTTPParser.kOnHeaders | 0;
32+
var kOnHeadersComplete = HTTPParser.kOnHeadersComplete | 0;
33+
var kOnBody = HTTPParser.kOnBody | 0;
34+
var kOnMessageComplete = HTTPParser.kOnMessageComplete | 0;
35+
3136
// The purpose of this test is not to check HTTP compliance but to test the
3237
// binding. Tests for pathological http messages should be submitted
3338
// upstream to https://github.com/joyent/http-parser for inclusion into
@@ -40,19 +45,19 @@ function newParser(type) {
4045
parser.headers = [];
4146
parser.url = '';
4247

43-
parser.onHeaders = function(headers, url) {
48+
parser[kOnHeaders] = function(headers, url) {
4449
parser.headers = parser.headers.concat(headers);
4550
parser.url += url;
4651
};
4752

48-
parser.onHeadersComplete = function(info) {
53+
parser[kOnHeadersComplete] = function(info) {
4954
};
5055

51-
parser.onBody = function(b, start, len) {
56+
parser[kOnBody] = function(b, start, len) {
5257
assert.ok(false, 'Function should not be called.');
5358
};
5459

55-
parser.onMessageComplete = function() {
60+
parser[kOnMessageComplete] = function() {
5661
};
5762

5863
return parser;
@@ -92,7 +97,7 @@ function expectBody(expected) {
9297

9398
var parser = newParser(REQUEST);
9499

95-
parser.onHeadersComplete = mustCall(function(info) {
100+
parser[kOnHeadersComplete] = mustCall(function(info) {
96101
assert.equal(info.method, 'GET');
97102
assert.equal(info.url || parser.url, '/hello');
98103
assert.equal(info.versionMajor, 1);
@@ -106,7 +111,7 @@ function expectBody(expected) {
106111
// thrown from parser.execute()
107112
//
108113

109-
parser.onHeadersComplete = function(info) {
114+
parser[kOnHeadersComplete] = function(info) {
110115
throw new Error('hello world');
111116
};
112117

@@ -131,14 +136,14 @@ function expectBody(expected) {
131136

132137
var parser = newParser(RESPONSE);
133138

134-
parser.onHeadersComplete = mustCall(function(info) {
139+
parser[kOnHeadersComplete] = mustCall(function(info) {
135140
assert.equal(info.method, undefined);
136141
assert.equal(info.versionMajor, 1);
137142
assert.equal(info.versionMinor, 1);
138143
assert.equal(info.statusCode, 200);
139144
});
140145

141-
parser.onBody = mustCall(function(buf, start, len) {
146+
parser[kOnBody] = mustCall(function(buf, start, len) {
142147
var body = '' + buf.slice(start, start + len);
143148
assert.equal(body, 'pong');
144149
});
@@ -157,7 +162,7 @@ function expectBody(expected) {
157162

158163
var parser = newParser(RESPONSE);
159164

160-
parser.onHeadersComplete = mustCall(function(info) {
165+
parser[kOnHeadersComplete] = mustCall(function(info) {
161166
assert.equal(info.method, undefined);
162167
assert.equal(info.versionMajor, 1);
163168
assert.equal(info.versionMinor, 0);
@@ -194,16 +199,16 @@ function expectBody(expected) {
194199

195200
var parser = newParser(REQUEST);
196201

197-
parser.onHeadersComplete = mustCall(function(info) {
202+
parser[kOnHeadersComplete] = mustCall(function(info) {
198203
assert.equal(info.method, 'POST');
199204
assert.equal(info.url || parser.url, '/it');
200205
assert.equal(info.versionMajor, 1);
201206
assert.equal(info.versionMinor, 1);
202207
// expect to see trailing headers now
203-
parser.onHeaders = mustCall(onHeaders);
208+
parser[kOnHeaders] = mustCall(onHeaders);
204209
});
205210

206-
parser.onBody = mustCall(function(buf, start, len) {
211+
parser[kOnBody] = mustCall(function(buf, start, len) {
207212
var body = '' + buf.slice(start, start + len);
208213
assert.equal(body, 'ping');
209214
seen_body = true;
@@ -226,7 +231,7 @@ function expectBody(expected) {
226231

227232
var parser = newParser(REQUEST);
228233

229-
parser.onHeadersComplete = mustCall(function(info) {
234+
parser[kOnHeadersComplete] = mustCall(function(info) {
230235
assert.equal(info.method, 'GET');
231236
assert.equal(info.versionMajor, 1);
232237
assert.equal(info.versionMinor, 0);
@@ -255,7 +260,7 @@ function expectBody(expected) {
255260

256261
var parser = newParser(REQUEST);
257262

258-
parser.onHeadersComplete = mustCall(function(info) {
263+
parser[kOnHeadersComplete] = mustCall(function(info) {
259264
assert.equal(info.method, 'GET');
260265
assert.equal(info.url || parser.url, '/foo/bar/baz?quux=42#1337');
261266
assert.equal(info.versionMajor, 1);
@@ -287,14 +292,14 @@ function expectBody(expected) {
287292

288293
var parser = newParser(REQUEST);
289294

290-
parser.onHeadersComplete = mustCall(function(info) {
295+
parser[kOnHeadersComplete] = mustCall(function(info) {
291296
assert.equal(info.method, 'POST');
292297
assert.equal(info.url || parser.url, '/it');
293298
assert.equal(info.versionMajor, 1);
294299
assert.equal(info.versionMinor, 1);
295300
});
296301

297-
parser.onBody = mustCall(function(buf, start, len) {
302+
parser[kOnBody] = mustCall(function(buf, start, len) {
298303
var body = '' + buf.slice(start, start + len);
299304
assert.equal(body, 'foo=42&bar=1337');
300305
});
@@ -322,7 +327,7 @@ function expectBody(expected) {
322327

323328
var parser = newParser(REQUEST);
324329

325-
parser.onHeadersComplete = mustCall(function(info) {
330+
parser[kOnHeadersComplete] = mustCall(function(info) {
326331
assert.equal(info.method, 'POST');
327332
assert.equal(info.url || parser.url, '/it');
328333
assert.equal(info.versionMajor, 1);
@@ -337,7 +342,7 @@ function expectBody(expected) {
337342
assert.equal(body, body_parts[body_part++]);
338343
}
339344

340-
parser.onBody = mustCall(onBody, body_parts.length);
345+
parser[kOnBody] = mustCall(onBody, body_parts.length);
341346
parser.execute(request, 0, request.length);
342347
})();
343348

@@ -358,7 +363,7 @@ function expectBody(expected) {
358363

359364
var parser = newParser(REQUEST);
360365

361-
parser.onHeadersComplete = mustCall(function(info) {
366+
parser[kOnHeadersComplete] = mustCall(function(info) {
362367
assert.equal(info.method, 'POST');
363368
assert.equal(info.url || parser.url, '/it');
364369
assert.equal(info.versionMajor, 1);
@@ -375,7 +380,7 @@ function expectBody(expected) {
375380
assert.equal(body, body_parts[body_part++]);
376381
}
377382

378-
parser.onBody = mustCall(onBody, body_parts.length);
383+
parser[kOnBody] = mustCall(onBody, body_parts.length);
379384
parser.execute(request, 0, request.length);
380385

381386
request = Buffer(
@@ -415,7 +420,7 @@ function expectBody(expected) {
415420
function test(a, b) {
416421
var parser = newParser(REQUEST);
417422

418-
parser.onHeadersComplete = mustCall(function(info) {
423+
parser[kOnHeadersComplete] = mustCall(function(info) {
419424
assert.equal(info.method, 'POST');
420425
assert.equal(info.url || parser.url, '/helpme');
421426
assert.equal(info.versionMajor, 1);
@@ -424,7 +429,7 @@ function expectBody(expected) {
424429

425430
var expected_body = '123123456123456789123456789ABC123456789ABCDEF';
426431

427-
parser.onBody = function(buf, start, len) {
432+
parser[kOnBody] = function(buf, start, len) {
428433
var chunk = '' + buf.slice(start, start + len);
429434
assert.equal(expected_body.indexOf(chunk), 0);
430435
expected_body = expected_body.slice(chunk.length);
@@ -471,7 +476,7 @@ function expectBody(expected) {
471476

472477
var parser = newParser(REQUEST);
473478

474-
parser.onHeadersComplete = mustCall(function(info) {
479+
parser[kOnHeadersComplete] = mustCall(function(info) {
475480
assert.equal(info.method, 'POST');
476481
assert.equal(info.url || parser.url, '/it');
477482
assert.equal(info.versionMajor, 1);
@@ -483,7 +488,7 @@ function expectBody(expected) {
483488

484489
var expected_body = '123123456123456789123456789ABC123456789ABCDEF';
485490

486-
parser.onBody = function(buf, start, len) {
491+
parser[kOnBody] = function(buf, start, len) {
487492
var chunk = '' + buf.slice(start, start + len);
488493
assert.equal(expected_body.indexOf(chunk), 0);
489494
expected_body = expected_body.slice(chunk.length);
@@ -538,12 +543,12 @@ function expectBody(expected) {
538543
};
539544

540545
var parser = newParser(REQUEST);
541-
parser.onHeadersComplete = onHeadersComplete1;
542-
parser.onBody = expectBody('ping');
546+
parser[kOnHeadersComplete] = onHeadersComplete1;
547+
parser[kOnBody] = expectBody('ping');
543548
parser.execute(req1, 0, req1.length);
544549

545550
parser.reinitialize(REQUEST);
546-
parser.onBody = expectBody('pong');
547-
parser.onHeadersComplete = onHeadersComplete2;
551+
parser[kOnBody] = expectBody('pong');
552+
parser[kOnHeadersComplete] = onHeadersComplete2;
548553
parser.execute(req2, 0, req2.length);
549554
})();

0 commit comments

Comments
 (0)