diff --git a/tests/common.js b/tests/common.js index a610c29..1ea756d 100644 --- a/tests/common.js +++ b/tests/common.js @@ -1,6 +1,6 @@ /* eslint-disable required-modules */ 'use strict'; -process.binding('http_parser').HTTPParser = require('../http-parser.js').HTTPParser; +//process.binding('http_parser').HTTPParser = require('../http-parser.js').HTTPParser; var path = require('path'); var fs = require('fs'); diff --git a/tests/iojs/test-http-parser-durability.js b/tests/iojs/test-http-parser-durability.js index 06e7c55..d0ec0ff 100644 --- a/tests/iojs/test-http-parser-durability.js +++ b/tests/iojs/test-http-parser-durability.js @@ -2,12 +2,20 @@ var assert = require('assert'); var inspect = require('util').inspect; var format = require('util').format; -var HTTPParser = require('../../http-parser').HTTPParser; +//var HTTPParser = require('../../http-parser').HTTPParser; +var HTTPParser = process.binding('http_parser').HTTPParser; HTTPParser.prototype[0] = HTTPParser.prototype[1] = HTTPParser.prototype[2] = HTTPParser.prototype[3] = function(){}; +const kIncomingMessage = Symbol('IncomingMessage'); +const kOnHeaders = HTTPParser.kOnHeaders | 0; +const kOnHeadersComplete = HTTPParser.kOnHeadersComplete | 0; +const kOnBody = HTTPParser.kOnBody | 0; +const kOnMessageComplete = HTTPParser.kOnMessageComplete | 0; +const kOnExecute = HTTPParser.kOnExecute | 0; + var CRLF = '\r\n'; var LF = '\n'; var REQUEST = HTTPParser.REQUEST; @@ -191,6 +199,7 @@ var cases = [ headers: [ 'Accept', '*/*', + '', '', ], body: undefined }, @@ -217,6 +226,32 @@ var cases = [ ], body: 'HELLO' }, + { + name: 'duplicate content-length', + type: REQUEST, + raw: [ + 'GET /get_duplicate_content_length HTTP/1.0', + 'Content-Length: 5', + 'Content-Length: 5', + '', + 'HELLO' + ].join(CRLF), + shouldKeepAlive: false, + msgCompleteOnEOF: false, + httpMajor: 1, + httpMinor: 0, + method: 'GET', + url: '/get_duplicate_content_length', + statusCode: null, + statusText: null, + headers: [ + 'Content-Length', + '5', + 'Content-Length', + '5', + ], + body: 'HELLO' + }, { name: 'post identity body world', type: REQUEST, @@ -542,9 +577,8 @@ var cases = [ ].join(CRLF), shouldKeepAlive: false, msgCompleteOnEOF: false, - error: true, - httpMajor: 1, - httpMinor: null, + httpMajor: 0, + httpMinor: 9, method: 'GET', url: '/', statusCode: null, @@ -611,9 +645,9 @@ var cases = [ headers: [ 'Line1', //'abc def ghi jkl mno qrs', - 'abc def ghi jkl mno qrs', + 'abc\tdef ghi\t\tjkl mno \t \tqrs', 'Line2', - 'line2', + 'line2\t', 'Line3', 'line3', 'Line4', @@ -940,9 +974,9 @@ var cases = [ headers: [ 'Line1', //'abc def ghi jkl mno qrs', - 'abc def ghi jkl mno qrs', + 'abc\tdef ghi\t\tjkl mno \t \tqrs', 'Line2', - 'line2', + 'line2\t', 'Line3', 'line3', 'Line4', @@ -1044,7 +1078,7 @@ var cases = [ headers: [ 'Connection', //'keep-alive, upgrade', - 'keep-alive, upgrade', + 'keep-alive, upgrade', 'Upgrade', 'WebSocket', ], @@ -1052,89 +1086,6 @@ var cases = [ body: undefined }, // RESPONSES ================================================================= - { - name: 'chunked upgrade response', - type: RESPONSE, - raw: [ - 'HTTP/1.1 101 Switching Protocols', - 'Connection: upgrade', - 'Upgrade: websocket', - 'Sec-WebSocket-Accept: QV3I5XUXU2CdhtjixE7QCkCcMZM=', - 'Transfer-Encoding: chunked', - '', - 'hello' - ].join(CRLF), - shouldKeepAlive: true, - msgCompleteOnEOF: false, - httpMajor: 1, - httpMinor: 1, - method: null, - url: null, - statusCode: 101, - statusText: 'Switching Protocols', - headers: [ - 'Connection', - 'upgrade', - 'Upgrade', - 'websocket', - 'Sec-WebSocket-Accept', - 'QV3I5XUXU2CdhtjixE7QCkCcMZM=', - 'Transfer-Encoding', - 'chunked', - ], - upgrade: 'hello', - body: undefined - }, - { - name: 'google 301', - type: RESPONSE, - raw: [ - 'HTTP/1.1 301 Moved Permanently', - 'Location: http://www.google.com/', - 'Content-Type: text/html; charset=UTF-8', - 'Date: Sun, 26 Apr 2009 11:11:49 GMT', - 'Expires: Tue, 26 May 2009 11:11:49 GMT', - 'X-$PrototypeBI-Version: 1.6.0.3', - 'Cache-Control: public, max-age=2592000', - 'Server: gws', - 'Content-Length: 219 ', - '', - '\n301 Moved\n

301 ' - + 'Moved

\nThe document has moved\nhere.\r\n\r\n' - ].join(CRLF), - shouldKeepAlive: true, - msgCompleteOnEOF: false, - httpMajor: 1, - httpMinor: 1, - method: null, - url: null, - statusCode: 301, - statusText: 'Moved Permanently', - headers: [ - 'Location', - 'http://www.google.com/', - 'Content-Type', - 'text/html; charset=UTF-8', - 'Date', - 'Sun, 26 Apr 2009 11:11:49 GMT', - 'Expires', - 'Tue, 26 May 2009 11:11:49 GMT', - 'X-$PrototypeBI-Version', - '1.6.0.3', - 'Cache-Control', - 'public, max-age=2592000', - 'Server', - 'gws', - 'Content-Length', - '219', - ], - body: '\n301 Moved\n

301 ' - + 'Moved

\nThe document has moved\nhere.\r\n\r\n' - }, { name: 'no content-length response', type: RESPONSE, @@ -1271,51 +1222,12 @@ var cases = [ headers: [ 'Content-Type', 'text/plain', + '','', 'Transfer-Encoding', 'chunked', ], body: 'This is the data in the first chunk\r\n' }, - { - name: 'chunked with (arguably wrong) content length', - type: RESPONSE, - raw: [ - 'HTTP/1.1 200 OK', - 'Transfer-Encoding: chunked', - 'Content-Length: 13', - 'Content-Type: application/download', - 'Content-Disposition: attachment;filename=test.txt', - 'Connection: keep-alive', - '', - '7', - 'thunk1\n', - '6', - 'thunk2', - '0', - '', '' - ].join(CRLF), - shouldKeepAlive: true, - msgCompleteOnEOF: false, - httpMajor: 1, - httpMinor: 1, - method: null, - url: null, - statusCode: 200, - statusText: 'OK', - headers: [ - 'Transfer-Encoding', - 'chunked', - 'Content-Length', - '13', - 'Content-Type', - 'application/download', - 'Content-Disposition', - 'attachment;filename=test.txt', - 'Connection', - 'keep-alive', - ], - body: 'thunk1\nthunk2' - }, // Test below does not work, not in Chrome either, but could be detected, // not sure if it ever actually happens though. // { @@ -1751,9 +1663,56 @@ for (var i = 0; i < cases.length; ++i) { // handlers process.setMaxListeners(0); +var methods = [ + 'DELETE', + 'GET', + 'HEAD', + 'POST', + 'PUT', + 'CONNECT', + 'OPTIONS', + 'TRACE', + 'COPY', + 'LOCK', + 'MKCOL', + 'MOVE', + 'PROPFIND', + 'PROPPATCH', + 'SEARCH', + 'UNLOCK', + 'BIND', + 'REBIND', + 'UNBIND', + 'ACL', + 'REPORT', + 'MKACTIVITY', + 'CHECKOUT', + 'MERGE', + 'M-SEARCH', + 'NOTIFY', + 'SUBSCRIBE', + 'UNSUBSCRIBE', + 'PATCH', + 'PURGE', + 'MKCALENDAR', + 'LINK', + 'UNLINK' +]; + +function newParser(type) { + var parser; + if (parseInt(process.versions.node) >= 12) { + parser = new HTTPParser(); + parser.initialize(type, {}); + } else { + parser = new HTTPParser(type); + } + return parser; +} + // Test predefined requests/responses cases.forEach(function(testCase) { - var parser = new HTTPParser(testCase.type); + var parser = newParser(testCase.type); var input = Buffer.from(testCase.raw, 'binary'); var reqEvents = ['onHeaders']; var completed = false; @@ -1768,6 +1727,7 @@ cases.forEach(function(testCase) { 'onHeaders', 'Expected onHeaders to be the next event for: ' + testCase.name); + method = methods[method] || null; reqEvents.shift(); message = { type: (method === null && url === null ? RESPONSE : REQUEST), @@ -1804,22 +1764,25 @@ cases.forEach(function(testCase) { completed = true; } - parser.onHeadersComplete = function(info) { - onHeaders(info.versionMajor, - info.versionMinor, - info.headers, - info.method || null, - info.url || null, - info.statusCode || null, - info.statusMessage === undefined ? null : info.statusMessage, - info.upgrade, - info.shouldKeepAlive); + parser[kOnHeadersComplete] = function(versionMajor, + versionMinor, headers, method, url, statusCode, + statusMessage, upgrade, shouldKeepAlive + ) { + onHeaders(versionMajor, + versionMinor, + headers, + method || null, + url || null, + statusCode || null, + statusMessage === undefined ? null : statusMessage, + upgrade, + shouldKeepAlive); }; - parser.onHeaders = function(headers) { + parser[kOnHeaders] = function(headers) { message.headers = message.headers.concat(headers); }; - parser.onBody = onBody; - parser.onMessageComplete = onMessageComplete; + parser[kOnBody] = onBody; + parser[kOnMessageComplete] = onMessageComplete; process.on('exit', function() { assert.strictEqual(completed, @@ -1835,9 +1798,10 @@ cases.forEach(function(testCase) { throw new Error('Unexpected error thrown for: ' + testCase.name + ':\n\n' + ex.stack + '\n'); } - if (testCase.error !== undefined && typeof ret === 'number') + if (testCase.error !== undefined && typeof ret === 'number') { + console.log(message); throw new Error('Expected error for: ' + testCase.name); - else if (testCase.error === undefined && typeof ret !== 'number') { + } else if (testCase.error === undefined && typeof ret !== 'number') { throw new Error('Unexpected error for: ' + testCase.name + ':\n\n' + ret.stack + '\n'); } @@ -1854,22 +1818,23 @@ cases.forEach(function(testCase) { // Test execute() return value (function() { - var parser = new HTTPParser(REQUEST); + var parser = newParser(REQUEST); var input = 'GET / HTTP/1.1\r\nheader: value\r\nhdr: value\r\n'; var ret; - parser.onHeaders = parser.onBody = parser.onMessageComplete = function() {}; + parser[kOnHeaders] = parser[kOnBody] = parser[kOnMessageComplete] = function() {}; ret = parser.execute(Buffer.from(input)); assert.strictEqual(ret, Buffer.byteLength(input)); })(); +if (1) { // Test for header overflow [REQUEST, RESPONSE].forEach(function(type) { - var parser = new HTTPParser(type); + var parser = newParser(type); var input = (type === REQUEST ? 'GET / HTTP/1.1\r\n' : 'HTTP/1.0 200 OK\r\n'); var ret; - parser.onHeaders = parser.onBody = parser.onMessageComplete = function() {}; + parser[kOnHeaders] = parser[kOnBody] = parser[kOnMessageComplete] = function() {}; ret = parser.execute(Buffer.from(input)); assert.strictEqual(ret, Buffer.byteLength(input)); @@ -1888,7 +1853,7 @@ cases.forEach(function(testCase) { // Test for no overflow with long body [REQUEST, RESPONSE].forEach(function(type) { [1000, 100000].forEach(function(length) { - var parser = new HTTPParser(type); + var parser = newParser(type); var input = format( '%s\r\nConnection: Keep-Alive\r\nContent-Length: %d\r\n\r\n', type === REQUEST ? 'POST / HTTP/1.0' : 'HTTP/1.0 200 OK', @@ -1897,7 +1862,7 @@ cases.forEach(function(testCase) { var input2 = Buffer.from('a'); var ret; - parser.onHeaders = parser.onBody = parser.onMessageComplete = function() {}; + parser[kOnHeaders] = parser[kOnBody] = parser[kOnMessageComplete] = function() {}; ret = parser.execute(Buffer.from(input)); assert.strictEqual(ret, Buffer.byteLength(input)); @@ -1914,11 +1879,11 @@ cases.forEach(function(testCase) { // Test for content length overflow /*['9007199254740991', '9007199254740992', '9007199254740993'].forEach( function(length, i) { - var parser = new HTTPParser(RESPONSE); + var parser = newParser(RESPONSE); var input = format('HTTP/1.1 200 OK\r\nContent-Length: %s\r\n\r\n', length); var ret; - parser.onHeaders = parser.onBody = parser.onMessageComplete = function() {}; + parser[kOnHeaders] = parser[kOnBody] = parser[kOnMessageComplete] = function() {}; ret = parser.execute(Buffer.from(input)); if (i === 0) assert.strictEqual(ret, Buffer.byteLength(input)); @@ -1932,12 +1897,12 @@ cases.forEach(function(testCase) { // Test for chunk length overflow /*['1fffffffffffff', '20000000000000', '20000000000001'].forEach( function(length, i) { - var parser = new HTTPParser(RESPONSE); + var parser = newParser(RESPONSE); var input = format('HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n' + '%s\r\n...', length); var ret; - parser.onHeaders = parser.onBody = parser.onMessageComplete = function() {}; + parser[kOnHeaders] = parser[kOnBody] = parser[kOnMessageComplete] = function() {}; ret = parser.execute(Buffer.from(input)); if (i === 0) assert.strictEqual(ret, Buffer.byteLength(input)); @@ -1996,7 +1961,7 @@ cases.forEach(function(testCase) { bodySize: 31337 * 1024 } ].forEach(function(expected) { - var parser = new HTTPParser(expected.type); + var parser = newParser(expected.type); var expectedBodySize = (expected.bodySize !== undefined ? expected.bodySize : (expected.body && expected.body.length) || 0); @@ -2021,21 +1986,25 @@ cases.forEach(function(testCase) { statusText: statusText }; }; - parser.onHeadersComplete = function(info) { - onHeaders(info.versionMajor, - info.versionMinor, - info.headers, - info.method || null, - info.url || null, - info.statusCode || null, - info.statusMessage === undefined ? null : info.statusMessage, - info.upgrade, - info.shouldKeepAlive); + parser[kOnHeadersComplete] = function(versionMajor, + versionMinor, headers, method, url, statusCode, + statusMessage, upgrade, shouldKeepAlive + ) { + method = methods[method] || null; + onHeaders(versionMajor, + versionMinor, + headers, + method || null, + url || null, + statusCode || null, + statusMessage === undefined ? null : statusMessage, + upgrade, + shouldKeepAlive); }; - parser.onHeaders = function(headers) { + parser[kOnHeaders] = function(headers) { message.headers = message.headers.concat(headers); }; - parser.onBody = function(data, offset, len) { + parser[kOnBody] = function(data, offset, len) { if (message.bodySize === undefined) { message.bodySize = len; body = data.toString('binary', offset, offset + len); @@ -2044,7 +2013,7 @@ cases.forEach(function(testCase) { body += data.toString('binary', offset, offset + len); } }; - parser.onMessageComplete = function() { + parser[kOnMessageComplete] = function() { messages.push(message); message = {}; }; @@ -2108,11 +2077,11 @@ console.log('responses okay'); // Test malformed HTTP version in request (function() { - var parser = new HTTPParser(REQUEST); + var parser = newParser(REQUEST); var input = 'GET / HTP/1.1\r\n\r\n'; var ret; - parser.onHeaders = parser.onBody = parser.onMessageComplete = function() {}; + parser[kOnHeaders] = parser[kOnBody] = parser[kOnMessageComplete] = function() {}; ret = parser.execute(Buffer.from(input)); assert.strictEqual(typeof ret !== 'number', true); //assert.strictEqual(/Malformed request line/i.test(ret.message), true); @@ -2120,23 +2089,23 @@ console.log('responses okay'); // Test well-formed but incomplete request (function() { - var parser = new HTTPParser(REQUEST); + var parser = newParser(REQUEST); var input = 'GET / HTTP/1.1\r\nContent-Type: text/plain\r\n' + 'Content-Length: 6\r\n\r\nfooba'; var ret; - parser.onHeaders = parser.onBody = parser.onMessageComplete = function() {}; + parser[kOnHeaders] = parser[kOnBody] = parser[kOnMessageComplete] = function() {}; ret = parser.execute(Buffer.from(input)); assert.strictEqual(ret, input.length); })(); // Test illegal header field name line folding in request /*(function() { - var parser = new HTTPParser(REQUEST); + var parser = newParser(REQUEST); var input = 'GET / HTTP/1.1\r\nname\r\n : value\r\n\r\n'; var ret; - parser.onHeaders = parser.onBody = parser.onMessageComplete = function() {}; + parser[kOnHeaders] = parser[kOnBody] = parser[kOnMessageComplete] = function() {}; ret = parser.execute(Buffer.from(input)); assert.strictEqual(typeof ret !== 'number', true); //assert.strictEqual(/Malformed header line/i.test(ret.message), true); @@ -2144,7 +2113,7 @@ console.log('responses okay'); // Test large SSL certificate header value in request (function() { - var parser = new HTTPParser(REQUEST); + var parser = newParser(REQUEST); var input = 'GET / HTTP/1.1\r\n' + 'X-SSL-Bullshit: -----BEGIN CERTIFICATE-----\r\n' + @@ -2183,7 +2152,7 @@ console.log('responses okay'); '\r\n'; var ret; - parser.onHeaders = parser.onBody = parser.onMessageComplete = function() {}; + parser[kOnHeaders] = parser[kOnBody] = parser[kOnMessageComplete] = function() {}; ret = parser.execute(Buffer.from(input)); assert.strictEqual(ret, input.length); })(); @@ -2228,7 +2197,7 @@ testScan(getMessageByName('query url with question mark'), getMessageByName('connect request')); console.log('requests okay'); - +} // HELPER FUNCTIONS ============================================================ @@ -2303,11 +2272,11 @@ function testScan(case1, case2, case3) { } ++ops; - var parser = new HTTPParser(case1.type); - parser.onHeaders = onHeaders; - parser.onHeadersComplete = onHeadersComplete; - parser.onBody = onBody; - parser.onMessageComplete = onMessageComplete; + var parser = newParser(case1.type); + parser[kOnHeaders] = onHeaders; + parser[kOnHeadersComplete] = onHeadersComplete; + parser[kOnBody] = onBody; + parser[kOnMessageComplete] = onMessageComplete; messages = []; hasUpgrade = false; @@ -2383,7 +2352,7 @@ function testScan(case1, case2, case3) { function testMultiple3(case1, case2, case3) { var messageCount = countParsedMessages(case1, case2, case3); var total = case1.raw + case2.raw + case3.raw; - var parser = new HTTPParser(case1.type); + var parser = newParser(case1.type); var messages = []; var message = {}; var ret; @@ -2406,7 +2375,7 @@ function testMultiple3(case1, case2, case3) { }; }; - parser.onHeadersComplete = function(info) { + parser[kOnHeadersComplete] = function(info) { onHeaders(info.versionMajor, info.versionMinor, info.headers, @@ -2417,17 +2386,17 @@ function testMultiple3(case1, case2, case3) { info.upgrade, info.shouldKeepAlive); }; - parser.onHeaders = function(headers) { + parser[kOnHeaders] = function(headers) { message.headers = message.headers.concat(headers); }; - parser.onBody = function(data, offset, len) { + parser[kOnBody] = function(data, offset, len) { if (!message.body) message.body = data.toString('binary', offset, offset + len); else message.body += data.toString('binary', offset, offset + len); }; - parser.onMessageComplete = function() { + parser[kOnMessageComplete] = function() { messages.push(message); message = {}; }; diff --git a/tests/parallel/test-http-client-max-header-size_increased.js b/tests/parallel/test-http-client-max-header-size_increased.js index a60d423..537316a 100644 --- a/tests/parallel/test-http-client-max-header-size_increased.js +++ b/tests/parallel/test-http-client-max-header-size_increased.js @@ -2,7 +2,7 @@ require('../common'); // set max header size -require('../../../http-parser-js').HTTPParser.maxHeaderSize = 1024 * 1024; // 1MB instead of 80kb +process.binding('http_parser').HTTPParser.maxHeaderSize = 1024 * 1024; // 1MB instead of 80kb var assert = require('assert'); var http = require('http');