Skip to content

Commit 0bf1d12

Browse files
committed
http: optimize on_headers_complete
Use an array instead of an object to pass a parsed header chunk from c++ to javascript. This offers a 5-10% speedup on the http_simple benchmark, as evidenced by running: ab -k -t 100 -c 100 http://127.0.0.1:8000/bytes/100 PR: nodejs#292 Reviewed-by: Ben Noordhuis <info@bnoordhuis.nl>
1 parent f468e5f commit 0bf1d12

4 files changed

Lines changed: 220 additions & 189 deletions

File tree

lib/_http_common.js

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,14 @@ function parserOnHeaders(headers, url) {
3636
this._url += url;
3737
}
3838

39-
// info.headers and info.url are set only if .onHeaders()
40-
// has not been called for this request.
41-
//
42-
// info.url is not set for response parsers but that's not
43-
// applicable here since all our parsers are request parsers.
44-
function parserOnHeadersComplete(info) {
45-
debug('parserOnHeadersComplete', info);
39+
// `headers` and `url` are set only if .onHeaders() has not been called for
40+
// this request.
41+
// `url` is not set for response parsers but that's not applicable here since
42+
// all our parsers are request parsers.
43+
function parserOnHeadersComplete(versionMajor, versionMinor, headers, method,
44+
url, statusCode, statusMessage, upgrade,
45+
shouldKeepAlive) {
4646
var parser = this;
47-
var headers = info.headers;
48-
var url = info.url;
4947

5048
if (!headers) {
5149
headers = parser._headers;
@@ -58,38 +56,37 @@ function parserOnHeadersComplete(info) {
5856
}
5957

6058
parser.incoming = new IncomingMessage(parser.socket);
61-
parser.incoming.httpVersionMajor = info.versionMajor;
62-
parser.incoming.httpVersionMinor = info.versionMinor;
63-
parser.incoming.httpVersion = info.versionMajor + '.' + info.versionMinor;
59+
parser.incoming.httpVersionMajor = versionMajor;
60+
parser.incoming.httpVersionMinor = versionMinor;
61+
parser.incoming.httpVersion = versionMajor + '.' + versionMinor;
6462
parser.incoming.url = url;
6563

6664
var n = headers.length;
6765

68-
// If parser.maxHeaderPairs <= 0 - assume that there're no limit
69-
if (parser.maxHeaderPairs > 0) {
66+
// If parser.maxHeaderPairs <= 0 assume that there's no limit.
67+
if (parser.maxHeaderPairs > 0)
7068
n = Math.min(n, parser.maxHeaderPairs);
71-
}
7269

7370
parser.incoming._addHeaderLines(headers, n);
7471

75-
if (isNumber(info.method)) {
72+
if (isNumber(method)) {
7673
// server only
77-
parser.incoming.method = HTTPParser.methods[info.method];
74+
parser.incoming.method = HTTPParser.methods[method];
7875
} else {
7976
// client only
80-
parser.incoming.statusCode = info.statusCode;
81-
parser.incoming.statusMessage = info.statusMessage;
77+
parser.incoming.statusCode = statusCode;
78+
parser.incoming.statusMessage = statusMessage;
8279
}
8380

84-
parser.incoming.upgrade = info.upgrade;
81+
parser.incoming.upgrade = upgrade;
8582

8683
var skipBody = false; // response to HEAD or CONNECT
8784

88-
if (!info.upgrade) {
89-
// For upgraded connections and CONNECT method request,
90-
// we'll emit this after parser.execute
91-
// so that we can capture the first part of the new protocol
92-
skipBody = parser.onIncoming(parser.incoming, info.shouldKeepAlive);
85+
if (!upgrade) {
86+
// For upgraded connections and CONNECT method request, we'll emit this
87+
// after parser.execute so that we can capture the first part of the new
88+
// protocol.
89+
skipBody = parser.onIncoming(parser.incoming, shouldKeepAlive);
9390
}
9491

9592
return skipBody;

src/env.h

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,6 @@ namespace node {
9292
V(fsevent_string, "FSEvent") \
9393
V(gid_string, "gid") \
9494
V(handle_string, "handle") \
95-
V(headers_string, "headers") \
9695
V(heap_size_limit_string, "heap_size_limit") \
9796
V(heap_total_string, "heapTotal") \
9897
V(heap_used_string, "heapUsed") \
@@ -114,7 +113,6 @@ namespace node {
114113
V(mark_sweep_compact_string, "mark-sweep-compact") \
115114
V(max_buffer_string, "maxBuffer") \
116115
V(message_string, "message") \
117-
V(method_string, "method") \
118116
V(minttl_string, "minttl") \
119117
V(mode_string, "mode") \
120118
V(model_string, "model") \
@@ -176,16 +174,13 @@ namespace node {
176174
V(service_string, "service") \
177175
V(servername_string, "servername") \
178176
V(session_id_string, "sessionId") \
179-
V(should_keep_alive_string, "shouldKeepAlive") \
180177
V(signal_string, "signal") \
181178
V(size_string, "size") \
182179
V(smalloc_p_string, "_smalloc_p") \
183180
V(sni_context_err_string, "Invalid SNI context") \
184181
V(sni_context_string, "sni_context") \
185182
V(speed_string, "speed") \
186183
V(stack_string, "stack") \
187-
V(status_code_string, "statusCode") \
188-
V(status_message_string, "statusMessage") \
189184
V(status_string, "status") \
190185
V(stdio_string, "stdio") \
191186
V(subject_string, "subject") \
@@ -209,16 +204,12 @@ namespace node {
209204
V(type_string, "type") \
210205
V(uid_string, "uid") \
211206
V(unknown_string, "<unknown>") \
212-
V(upgrade_string, "upgrade") \
213-
V(url_string, "url") \
214207
V(used_heap_size_string, "used_heap_size") \
215208
V(user_string, "user") \
216209
V(uv_string, "uv") \
217210
V(valid_from_string, "valid_from") \
218211
V(valid_to_string, "valid_to") \
219212
V(verify_error_string, "verifyError") \
220-
V(version_major_string, "versionMajor") \
221-
V(version_minor_string, "versionMinor") \
222213
V(version_string, "version") \
223214
V(weight_string, "weight") \
224215
V(windows_verbatim_arguments_string, "windowsVerbatimArguments") \

src/node_http_parser.cc

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
namespace node {
3535

3636
using v8::Array;
37+
using v8::Boolean;
3738
using v8::Context;
3839
using v8::Exception;
3940
using v8::Function;
@@ -46,6 +47,7 @@ using v8::Local;
4647
using v8::Object;
4748
using v8::String;
4849
using v8::Uint32;
50+
using v8::Undefined;
4951
using v8::Value;
5052

5153
const uint32_t kOnHeaders = 0;
@@ -218,55 +220,68 @@ class Parser : public BaseObject {
218220

219221

220222
HTTP_CB(on_headers_complete) {
223+
// Arguments for the on-headers-complete javascript callback. This
224+
// list needs to be kept in sync with the actual argument list for
225+
// `parserOnHeadersComplete` in lib/_http_common.js.
226+
enum on_headers_complete_arg_index {
227+
A_VERSION_MAJOR = 0,
228+
A_VERSION_MINOR,
229+
A_HEADERS,
230+
A_METHOD,
231+
A_URL,
232+
A_STATUS_CODE,
233+
A_STATUS_MESSAGE,
234+
A_UPGRADE,
235+
A_SHOULD_KEEP_ALIVE,
236+
A_MAX
237+
};
238+
239+
Local<Value> argv[A_MAX];
221240
Local<Object> obj = object();
222241
Local<Value> cb = obj->Get(kOnHeadersComplete);
223242

224243
if (!cb->IsFunction())
225244
return 0;
226245

227-
Local<Object> message_info = Object::New(env()->isolate());
246+
Local<Value> undefined = Undefined(env()->isolate());
247+
for (size_t i = 0; i < ARRAY_SIZE(argv); i++)
248+
argv[i] = undefined;
228249

229250
if (have_flushed_) {
230251
// Slow case, flush remaining headers.
231252
Flush();
232253
} else {
233254
// Fast case, pass headers and URL to JS land.
234-
message_info->Set(env()->headers_string(), CreateHeaders());
255+
argv[A_HEADERS] = CreateHeaders();
235256
if (parser_.type == HTTP_REQUEST)
236-
message_info->Set(env()->url_string(), url_.ToString(env()));
257+
argv[A_URL] = url_.ToString(env());
237258
}
238-
num_fields_ = num_values_ = 0;
259+
260+
num_fields_ = 0;
261+
num_values_ = 0;
239262

240263
// METHOD
241264
if (parser_.type == HTTP_REQUEST) {
242-
message_info->Set(env()->method_string(),
243-
Uint32::NewFromUnsigned(env()->isolate(),
244-
parser_.method));
265+
argv[A_METHOD] =
266+
Uint32::NewFromUnsigned(env()->isolate(), parser_.method);
245267
}
246268

247269
// STATUS
248270
if (parser_.type == HTTP_RESPONSE) {
249-
message_info->Set(env()->status_code_string(),
250-
Integer::New(env()->isolate(), parser_.status_code));
251-
message_info->Set(env()->status_message_string(),
252-
status_message_.ToString(env()));
271+
argv[A_STATUS_CODE] =
272+
Integer::New(env()->isolate(), parser_.status_code);
273+
argv[A_STATUS_MESSAGE] = status_message_.ToString(env());
253274
}
254275

255276
// VERSION
256-
message_info->Set(env()->version_major_string(),
257-
Integer::New(env()->isolate(), parser_.http_major));
258-
message_info->Set(env()->version_minor_string(),
259-
Integer::New(env()->isolate(), parser_.http_minor));
277+
argv[A_VERSION_MAJOR] = Integer::New(env()->isolate(), parser_.http_major);
278+
argv[A_VERSION_MINOR] = Integer::New(env()->isolate(), parser_.http_minor);
260279

261-
message_info->Set(env()->should_keep_alive_string(),
262-
http_should_keep_alive(&parser_) ?
263-
True(env()->isolate()) : False(env()->isolate()));
280+
argv[A_SHOULD_KEEP_ALIVE] =
281+
Boolean::New(env()->isolate(), http_should_keep_alive(&parser_));
264282

265-
message_info->Set(env()->upgrade_string(),
266-
parser_.upgrade ? True(env()->isolate())
267-
: False(env()->isolate()));
283+
argv[A_UPGRADE] = Boolean::New(env()->isolate(), parser_.upgrade);
268284

269-
Local<Value> argv[1] = { message_info };
270285
Local<Value> head_response =
271286
cb.As<Function>()->Call(obj, ARRAY_SIZE(argv), argv);
272287

0 commit comments

Comments
 (0)