diff --git a/lib/_http_common.js b/lib/_http_common.js index 9413843ded..d7bbcf47cd 100644 --- a/lib/_http_common.js +++ b/lib/_http_common.js @@ -36,16 +36,14 @@ function parserOnHeaders(headers, url) { this._url += url; } -// info.headers and info.url are set only if .onHeaders() -// has not been called for this request. -// -// info.url is not set for response parsers but that's not -// applicable here since all our parsers are request parsers. -function parserOnHeadersComplete(info) { - debug('parserOnHeadersComplete', info); +// `headers` and `url` are set only if .onHeaders() has not been called for +// this request. +// `url` is not set for response parsers but that's not applicable here since +// all our parsers are request parsers. +function parserOnHeadersComplete(versionMajor, versionMinor, headers, method, + url, statusCode, statusMessage, upgrade, + shouldKeepAlive) { var parser = this; - var headers = info.headers; - var url = info.url; if (!headers) { headers = parser._headers; @@ -58,38 +56,37 @@ function parserOnHeadersComplete(info) { } parser.incoming = new IncomingMessage(parser.socket); - parser.incoming.httpVersionMajor = info.versionMajor; - parser.incoming.httpVersionMinor = info.versionMinor; - parser.incoming.httpVersion = info.versionMajor + '.' + info.versionMinor; + parser.incoming.httpVersionMajor = versionMajor; + parser.incoming.httpVersionMinor = versionMinor; + parser.incoming.httpVersion = versionMajor + '.' + versionMinor; parser.incoming.url = url; var n = headers.length; - // If parser.maxHeaderPairs <= 0 - assume that there're no limit - if (parser.maxHeaderPairs > 0) { + // If parser.maxHeaderPairs <= 0 assume that there's no limit. + if (parser.maxHeaderPairs > 0) n = Math.min(n, parser.maxHeaderPairs); - } parser.incoming._addHeaderLines(headers, n); - if (isNumber(info.method)) { + if (isNumber(method)) { // server only - parser.incoming.method = HTTPParser.methods[info.method]; + parser.incoming.method = HTTPParser.methods[method]; } else { // client only - parser.incoming.statusCode = info.statusCode; - parser.incoming.statusMessage = info.statusMessage; + parser.incoming.statusCode = statusCode; + parser.incoming.statusMessage = statusMessage; } - parser.incoming.upgrade = info.upgrade; + parser.incoming.upgrade = upgrade; var skipBody = false; // response to HEAD or CONNECT - if (!info.upgrade) { - // For upgraded connections and CONNECT method request, - // we'll emit this after parser.execute - // so that we can capture the first part of the new protocol - skipBody = parser.onIncoming(parser.incoming, info.shouldKeepAlive); + if (!upgrade) { + // For upgraded connections and CONNECT method request, we'll emit this + // after parser.execute so that we can capture the first part of the new + // protocol. + skipBody = parser.onIncoming(parser.incoming, shouldKeepAlive); } return skipBody; diff --git a/src/env.h b/src/env.h index 7e28fb3ac1..e2002ee92f 100644 --- a/src/env.h +++ b/src/env.h @@ -92,7 +92,6 @@ namespace node { V(fsevent_string, "FSEvent") \ V(gid_string, "gid") \ V(handle_string, "handle") \ - V(headers_string, "headers") \ V(heap_size_limit_string, "heap_size_limit") \ V(heap_total_string, "heapTotal") \ V(heap_used_string, "heapUsed") \ @@ -114,7 +113,6 @@ namespace node { V(mark_sweep_compact_string, "mark-sweep-compact") \ V(max_buffer_string, "maxBuffer") \ V(message_string, "message") \ - V(method_string, "method") \ V(minttl_string, "minttl") \ V(mode_string, "mode") \ V(model_string, "model") \ @@ -176,7 +174,6 @@ namespace node { V(service_string, "service") \ V(servername_string, "servername") \ V(session_id_string, "sessionId") \ - V(should_keep_alive_string, "shouldKeepAlive") \ V(signal_string, "signal") \ V(size_string, "size") \ V(smalloc_p_string, "_smalloc_p") \ @@ -184,8 +181,6 @@ namespace node { V(sni_context_string, "sni_context") \ V(speed_string, "speed") \ V(stack_string, "stack") \ - V(status_code_string, "statusCode") \ - V(status_message_string, "statusMessage") \ V(status_string, "status") \ V(stdio_string, "stdio") \ V(subject_string, "subject") \ @@ -209,16 +204,12 @@ namespace node { V(type_string, "type") \ V(uid_string, "uid") \ V(unknown_string, "") \ - V(upgrade_string, "upgrade") \ - V(url_string, "url") \ V(used_heap_size_string, "used_heap_size") \ V(user_string, "user") \ V(uv_string, "uv") \ V(valid_from_string, "valid_from") \ V(valid_to_string, "valid_to") \ V(verify_error_string, "verifyError") \ - V(version_major_string, "versionMajor") \ - V(version_minor_string, "versionMinor") \ V(version_string, "version") \ V(weight_string, "weight") \ V(windows_verbatim_arguments_string, "windowsVerbatimArguments") \ diff --git a/src/node_http_parser.cc b/src/node_http_parser.cc index 284e786917..f71302d313 100644 --- a/src/node_http_parser.cc +++ b/src/node_http_parser.cc @@ -34,6 +34,7 @@ namespace node { using v8::Array; +using v8::Boolean; using v8::Context; using v8::Exception; using v8::Function; @@ -46,6 +47,7 @@ using v8::Local; using v8::Object; using v8::String; using v8::Uint32; +using v8::Undefined; using v8::Value; const uint32_t kOnHeaders = 0; @@ -218,55 +220,68 @@ class Parser : public BaseObject { HTTP_CB(on_headers_complete) { + // Arguments for the on-headers-complete javascript callback. This + // list needs to be kept in sync with the actual argument list for + // `parserOnHeadersComplete` in lib/_http_common.js. + enum on_headers_complete_arg_index { + A_VERSION_MAJOR = 0, + A_VERSION_MINOR, + A_HEADERS, + A_METHOD, + A_URL, + A_STATUS_CODE, + A_STATUS_MESSAGE, + A_UPGRADE, + A_SHOULD_KEEP_ALIVE, + A_MAX + }; + + Local argv[A_MAX]; Local obj = object(); Local cb = obj->Get(kOnHeadersComplete); if (!cb->IsFunction()) return 0; - Local message_info = Object::New(env()->isolate()); + Local undefined = Undefined(env()->isolate()); + for (size_t i = 0; i < ARRAY_SIZE(argv); i++) + argv[i] = undefined; if (have_flushed_) { // Slow case, flush remaining headers. Flush(); } else { // Fast case, pass headers and URL to JS land. - message_info->Set(env()->headers_string(), CreateHeaders()); + argv[A_HEADERS] = CreateHeaders(); if (parser_.type == HTTP_REQUEST) - message_info->Set(env()->url_string(), url_.ToString(env())); + argv[A_URL] = url_.ToString(env()); } - num_fields_ = num_values_ = 0; + + num_fields_ = 0; + num_values_ = 0; // METHOD if (parser_.type == HTTP_REQUEST) { - message_info->Set(env()->method_string(), - Uint32::NewFromUnsigned(env()->isolate(), - parser_.method)); + argv[A_METHOD] = + Uint32::NewFromUnsigned(env()->isolate(), parser_.method); } // STATUS if (parser_.type == HTTP_RESPONSE) { - message_info->Set(env()->status_code_string(), - Integer::New(env()->isolate(), parser_.status_code)); - message_info->Set(env()->status_message_string(), - status_message_.ToString(env())); + argv[A_STATUS_CODE] = + Integer::New(env()->isolate(), parser_.status_code); + argv[A_STATUS_MESSAGE] = status_message_.ToString(env()); } // VERSION - message_info->Set(env()->version_major_string(), - Integer::New(env()->isolate(), parser_.http_major)); - message_info->Set(env()->version_minor_string(), - Integer::New(env()->isolate(), parser_.http_minor)); + argv[A_VERSION_MAJOR] = Integer::New(env()->isolate(), parser_.http_major); + argv[A_VERSION_MINOR] = Integer::New(env()->isolate(), parser_.http_minor); - message_info->Set(env()->should_keep_alive_string(), - http_should_keep_alive(&parser_) ? - True(env()->isolate()) : False(env()->isolate())); + argv[A_SHOULD_KEEP_ALIVE] = + Boolean::New(env()->isolate(), http_should_keep_alive(&parser_)); - message_info->Set(env()->upgrade_string(), - parser_.upgrade ? True(env()->isolate()) - : False(env()->isolate())); + argv[A_UPGRADE] = Boolean::New(env()->isolate(), parser_.upgrade); - Local argv[1] = { message_info }; Local head_response = cb.As()->Call(obj, ARRAY_SIZE(argv), argv); diff --git a/test/parallel/test-http-parser.js b/test/parallel/test-http-parser.js index e860e91a0b..d8c181bf60 100644 --- a/test/parallel/test-http-parser.js +++ b/test/parallel/test-http-parser.js @@ -76,15 +76,17 @@ function expectBody(expected) { 'GET /hello HTTP/1.1' + CRLF + CRLF); - var parser = newParser(REQUEST); - - parser[kOnHeadersComplete] = mustCall(function(info) { - assert.equal(info.method, methods.indexOf('GET')); - assert.equal(info.url || parser.url, '/hello'); - assert.equal(info.versionMajor, 1); - assert.equal(info.versionMinor, 1); - }); + var onHeadersComplete = function(versionMajor, versionMinor, headers, method, + url, statusCode, statusMessage, upgrade, + shouldKeepAlive) { + assert.equal(versionMajor, 1); + assert.equal(versionMinor, 1); + assert.equal(method, methods.indexOf('GET')); + assert.equal(url || parser.url, '/hello'); + }; + var parser = newParser(REQUEST); + parser[kOnHeadersComplete] = mustCall(onHeadersComplete); parser.execute(request, 0, request.length); // @@ -115,21 +117,24 @@ function expectBody(expected) { CRLF + 'pong'); - var parser = newParser(RESPONSE); - - parser[kOnHeadersComplete] = mustCall(function(info) { - assert.equal(info.method, undefined); - assert.equal(info.versionMajor, 1); - assert.equal(info.versionMinor, 1); - assert.equal(info.statusCode, 200); - assert.equal(info.statusMessage, "OK"); - }); + var onHeadersComplete = function(versionMajor, versionMinor, headers, method, + url, statusCode, statusMessage, upgrade, + shouldKeepAlive) { + assert.equal(method, undefined); + assert.equal(versionMajor, 1); + assert.equal(versionMinor, 1); + assert.equal(statusCode, 200); + assert.equal(statusMessage, 'OK'); + }; - parser[kOnBody] = mustCall(function(buf, start, len) { + var onBody = function(buf, start, len) { var body = '' + buf.slice(start, start + len); assert.equal(body, 'pong'); - }); + }; + var parser = newParser(RESPONSE); + parser[kOnHeadersComplete] = mustCall(onHeadersComplete); + parser[kOnBody] = mustCall(onBody); parser.execute(request, 0, request.length); })(); @@ -142,17 +147,19 @@ function expectBody(expected) { 'HTTP/1.0 200 Connection established' + CRLF + CRLF); - var parser = newParser(RESPONSE); - - parser[kOnHeadersComplete] = mustCall(function(info) { - assert.equal(info.method, undefined); - assert.equal(info.versionMajor, 1); - assert.equal(info.versionMinor, 0); - assert.equal(info.statusCode, 200); - assert.equal(info.statusMessage, "Connection established"); - assert.deepEqual(info.headers || parser.headers, []); - }); + var onHeadersComplete = function(versionMajor, versionMinor, headers, method, + url, statusCode, statusMessage, upgrade, + shouldKeepAlive) { + assert.equal(versionMajor, 1); + assert.equal(versionMinor, 0); + assert.equal(method, undefined); + assert.equal(statusCode, 200); + assert.equal(statusMessage, 'Connection established'); + assert.deepEqual(headers || parser.headers, []); + }; + var parser = newParser(RESPONSE); + parser[kOnHeadersComplete] = mustCall(onHeadersComplete); parser.execute(request, 0, request.length); })(); @@ -174,29 +181,31 @@ function expectBody(expected) { var seen_body = false; - function onHeaders(headers, url) { + var onHeaders = function(headers, url) { assert.ok(seen_body); // trailers should come after the body - assert.deepEqual(headers, - ['Vary', '*', 'Content-Type', 'text/plain']); - } - - var parser = newParser(REQUEST); + assert.deepEqual(headers, ['Vary', '*', 'Content-Type', 'text/plain']); + }; - parser[kOnHeadersComplete] = mustCall(function(info) { - assert.equal(info.method, methods.indexOf('POST')); - assert.equal(info.url || parser.url, '/it'); - assert.equal(info.versionMajor, 1); - assert.equal(info.versionMinor, 1); + var onHeadersComplete = function(versionMajor, versionMinor, headers, method, + url, statusCode, statusMessage, upgrade, + shouldKeepAlive) { + assert.equal(method, methods.indexOf('POST')); + assert.equal(url || parser.url, '/it'); + assert.equal(versionMajor, 1); + assert.equal(versionMinor, 1); // expect to see trailing headers now parser[kOnHeaders] = mustCall(onHeaders); - }); + }; - parser[kOnBody] = mustCall(function(buf, start, len) { + var onBody = function(buf, start, len) { var body = '' + buf.slice(start, start + len); assert.equal(body, 'ping'); seen_body = true; - }); + }; + var parser = newParser(REQUEST); + parser[kOnHeadersComplete] = mustCall(onHeadersComplete); + parser[kOnBody] = mustCall(onBody); parser.execute(request, 0, request.length); })(); @@ -212,18 +221,19 @@ function expectBody(expected) { 'X-Filler2: 42' + CRLF + CRLF); - var parser = newParser(REQUEST); - - parser[kOnHeadersComplete] = mustCall(function(info) { - assert.equal(info.method, methods.indexOf('GET')); - assert.equal(info.versionMajor, 1); - assert.equal(info.versionMinor, 0); - assert.deepEqual(info.headers || parser.headers, - ['X-Filler', '1337', - 'X-Filler', '42', - 'X-Filler2', '42']); - }); + var onHeadersComplete = function(versionMajor, versionMinor, headers, method, + url, statusCode, statusMessage, upgrade, + shouldKeepAlive) { + assert.equal(method, methods.indexOf('GET')); + assert.equal(versionMajor, 1); + assert.equal(versionMinor, 0); + assert.deepEqual( + headers || parser.headers, + ['X-Filler', '1337', 'X-Filler', '42', 'X-Filler2', '42']); + }; + var parser = newParser(REQUEST); + parser[kOnHeadersComplete] = mustCall(onHeadersComplete); parser.execute(request, 0, request.length); })(); @@ -241,23 +251,25 @@ function expectBody(expected) { lots_of_headers + CRLF); - var parser = newParser(REQUEST); - - parser[kOnHeadersComplete] = mustCall(function(info) { - assert.equal(info.method, methods.indexOf('GET')); - assert.equal(info.url || parser.url, '/foo/bar/baz?quux=42#1337'); - assert.equal(info.versionMajor, 1); - assert.equal(info.versionMinor, 0); + var onHeadersComplete = function(versionMajor, versionMinor, headers, method, + url, statusCode, statusMessage, upgrade, + shouldKeepAlive) { + assert.equal(method, methods.indexOf('GET')); + assert.equal(url || parser.url, '/foo/bar/baz?quux=42#1337'); + assert.equal(versionMajor, 1); + assert.equal(versionMinor, 0); - var headers = info.headers || parser.headers; + var headers = headers || parser.headers; assert.equal(headers.length, 2 * 256); // 256 key/value pairs for (var i = 0; i < headers.length; i += 2) { assert.equal(headers[i], 'X-Filler'); assert.equal(headers[i + 1], '42'); } - }); + }; + var parser = newParser(REQUEST); + parser[kOnHeadersComplete] = mustCall(onHeadersComplete); parser.execute(request, 0, request.length); })(); @@ -273,20 +285,23 @@ function expectBody(expected) { CRLF + 'foo=42&bar=1337'); - var parser = newParser(REQUEST); - - parser[kOnHeadersComplete] = mustCall(function(info) { - assert.equal(info.method, methods.indexOf('POST')); - assert.equal(info.url || parser.url, '/it'); - assert.equal(info.versionMajor, 1); - assert.equal(info.versionMinor, 1); - }); + var onHeadersComplete = function(versionMajor, versionMinor, headers, method, + url, statusCode, statusMessage, upgrade, + shouldKeepAlive) { + assert.equal(method, methods.indexOf('POST')); + assert.equal(url || parser.url, '/it'); + assert.equal(versionMajor, 1); + assert.equal(versionMinor, 1); + }; - parser[kOnBody] = mustCall(function(buf, start, len) { + var onBody = function(buf, start, len) { var body = '' + buf.slice(start, start + len); assert.equal(body, 'foo=42&bar=1337'); - }); + }; + var parser = newParser(REQUEST); + parser[kOnHeadersComplete] = mustCall(onHeadersComplete); + parser[kOnBody] = mustCall(onBody); parser.execute(request, 0, request.length); })(); @@ -308,23 +323,25 @@ function expectBody(expected) { '1234567890' + CRLF + '0' + CRLF); - var parser = newParser(REQUEST); - - parser[kOnHeadersComplete] = mustCall(function(info) { - assert.equal(info.method, methods.indexOf('POST')); - assert.equal(info.url || parser.url, '/it'); - assert.equal(info.versionMajor, 1); - assert.equal(info.versionMinor, 1); - }); + var onHeadersComplete = function(versionMajor, versionMinor, headers, method, + url, statusCode, statusMessage, upgrade, + shouldKeepAlive) { + assert.equal(method, methods.indexOf('POST')); + assert.equal(url || parser.url, '/it'); + assert.equal(versionMajor, 1); + assert.equal(versionMinor, 1); + }; var body_part = 0, body_parts = ['123', '123456', '1234567890']; - function onBody(buf, start, len) { + var onBody = function(buf, start, len) { var body = '' + buf.slice(start, start + len); assert.equal(body, body_parts[body_part++]); - } + }; + var parser = newParser(REQUEST); + parser[kOnHeadersComplete] = mustCall(onHeadersComplete); parser[kOnBody] = mustCall(onBody, body_parts.length); parser.execute(request, 0, request.length); })(); @@ -344,25 +361,26 @@ function expectBody(expected) { '6' + CRLF + '123456' + CRLF); - var parser = newParser(REQUEST); - - parser[kOnHeadersComplete] = mustCall(function(info) { - assert.equal(info.method, methods.indexOf('POST')); - assert.equal(info.url || parser.url, '/it'); - assert.equal(info.versionMajor, 1); - assert.equal(info.versionMinor, 1); - }); + var onHeadersComplete = function(versionMajor, versionMinor, headers, method, + url, statusCode, statusMessage, upgrade, + shouldKeepAlive) { + assert.equal(method, methods.indexOf('POST')); + assert.equal(url || parser.url, '/it'); + assert.equal(versionMajor, 1); + assert.equal(versionMinor, 1); + }; var body_part = 0, - body_parts = [ - '123', '123456', '123456789', - '123456789ABC', '123456789ABCDEF']; + body_parts = + ['123', '123456', '123456789', '123456789ABC', '123456789ABCDEF']; - function onBody(buf, start, len) { + var onBody = function(buf, start, len) { var body = '' + buf.slice(start, start + len); assert.equal(body, body_parts[body_part++]); - } + }; + var parser = newParser(REQUEST); + parser[kOnHeadersComplete] = mustCall(onHeadersComplete); parser[kOnBody] = mustCall(onBody, body_parts.length); parser.execute(request, 0, request.length); @@ -401,23 +419,26 @@ function expectBody(expected) { '0' + CRLF); function test(a, b) { - var parser = newParser(REQUEST); - - parser[kOnHeadersComplete] = mustCall(function(info) { - assert.equal(info.method, methods.indexOf('POST')); - assert.equal(info.url || parser.url, '/helpme'); - assert.equal(info.versionMajor, 1); - assert.equal(info.versionMinor, 1); - }); + var onHeadersComplete = function(versionMajor, versionMinor, headers, + method, url, statusCode, statusMessage, + upgrade, shouldKeepAlive) { + assert.equal(method, methods.indexOf('POST')); + assert.equal(url || parser.url, '/helpme'); + assert.equal(versionMajor, 1); + assert.equal(versionMinor, 1); + }; var expected_body = '123123456123456789123456789ABC123456789ABCDEF'; - parser[kOnBody] = function(buf, start, len) { + var onBody = function(buf, start, len) { var chunk = '' + buf.slice(start, start + len); assert.equal(expected_body.indexOf(chunk), 0); expected_body = expected_body.slice(chunk.length); }; + var parser = newParser(REQUEST); + parser[kOnHeadersComplete] = mustCall(onHeadersComplete); + parser[kOnBody] = onBody; parser.execute(a, 0, a.length); parser.execute(b, 0, b.length); @@ -457,26 +478,30 @@ function expectBody(expected) { '123456789ABCDEF' + CRLF + '0' + CRLF); - var parser = newParser(REQUEST); - - parser[kOnHeadersComplete] = mustCall(function(info) { - assert.equal(info.method, methods.indexOf('POST')); - assert.equal(info.url || parser.url, '/it'); - assert.equal(info.versionMajor, 1); - assert.equal(info.versionMinor, 1); - assert.deepEqual(info.headers || parser.headers, - ['Content-Type', 'text/plain', - 'Transfer-Encoding', 'chunked']); - }); + var onHeadersComplete = function(versionMajor, versionMinor, headers, method, + url, statusCode, statusMessage, upgrade, + shouldKeepAlive) { + assert.equal(method, methods.indexOf('POST')); + assert.equal(url || parser.url, '/it'); + assert.equal(versionMajor, 1); + assert.equal(versionMinor, 1); + assert.deepEqual( + headers || parser.headers, + ['Content-Type', 'text/plain', 'Transfer-Encoding', 'chunked']); + }; var expected_body = '123123456123456789123456789ABC123456789ABCDEF'; - parser[kOnBody] = function(buf, start, len) { + var onBody = function(buf, start, len) { var chunk = '' + buf.slice(start, start + len); assert.equal(expected_body.indexOf(chunk), 0); expected_body = expected_body.slice(chunk.length); }; + var parser = newParser(REQUEST); + parser[kOnHeadersComplete] = mustCall(onHeadersComplete); + parser[kOnBody] = onBody; + for (var i = 0; i < request.length; ++i) { parser.execute(request, i, 1); } @@ -505,24 +530,27 @@ function expectBody(expected) { CRLF + 'pong'); - function onHeadersComplete1(info) { - assert.equal(info.method, methods.indexOf('PUT')); - assert.equal(info.url, '/this'); - assert.equal(info.versionMajor, 1); - assert.equal(info.versionMinor, 1); - assert.deepEqual(info.headers, - ['Content-Type', 'text/plain', - 'Transfer-Encoding', 'chunked']); + var onHeadersComplete1 = function(versionMajor, versionMinor, headers, method, + url, statusCode, statusMessage, upgrade, + shouldKeepAlive) { + assert.equal(method, methods.indexOf('PUT')); + assert.equal(url, '/this'); + assert.equal(versionMajor, 1); + assert.equal(versionMinor, 1); + assert.deepEqual( + headers, + ['Content-Type', 'text/plain', 'Transfer-Encoding', 'chunked']); }; - function onHeadersComplete2(info) { - assert.equal(info.method, methods.indexOf('POST')); - assert.equal(info.url, '/that'); - assert.equal(info.versionMajor, 1); - assert.equal(info.versionMinor, 0); - assert.deepEqual(info.headers, - ['Content-Type', 'text/plain', - 'Content-Length', '4']); + var onHeadersComplete2 = function(versionMajor, versionMinor, headers, method, + url, statusCode, statusMessage, upgrade, + shouldKeepAlive) { + assert.equal(method, methods.indexOf('POST')); + assert.equal(url, '/that'); + assert.equal(versionMajor, 1); + assert.equal(versionMinor, 0); + assert.deepEqual(headers, + ['Content-Type', 'text/plain', 'Content-Length', '4']); }; var parser = newParser(REQUEST);