From 5d1ad244748f36e5a51730e27f50c18edb12953a Mon Sep 17 00:00:00 2001 From: Jimb Esser Date: Sat, 2 Nov 2019 09:53:12 -0700 Subject: [PATCH 1/2] Add simple test of chunked body --- tests/parallel/test-http-chunked-2.js | 45 +++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 tests/parallel/test-http-chunked-2.js diff --git a/tests/parallel/test-http-chunked-2.js b/tests/parallel/test-http-chunked-2.js new file mode 100644 index 0000000..2175870 --- /dev/null +++ b/tests/parallel/test-http-chunked-2.js @@ -0,0 +1,45 @@ +'use strict'; + +require('../common'); +var assert = require('assert'); +var http = require('http'); +var url = require('url'); + +var responses_sent = 0; +var responses_recvd = 0; +var body0 = ''; +var body1 = ''; + +var server = http.Server(function(req, res) { + this.close(); + req.on('end', function() { + res.writeHead(200, {'Content-Type': 'text/plain', 'Transfer-Encoding': 'chunked'}); + res.write('some'); + res.write('chunked'); + res.write('data'); + res.end(); + }); + req.resume(); +}); +server.listen(0); + +server.on('listening', function() { + var agent = new http.Agent({ port: this.address().port, maxSockets: 1 }); + http.get({ + port: this.address().port, + path: '/hello', + headers: {'Accept': '*/*', 'Foo': 'bar'}, + agent: agent + }, function(res) { + assert.equal(200, res.statusCode); + res.setEncoding('utf8'); + res.on('data', function(chunk) { body0 += chunk; }); + res.on('end', function () { + console.error('Got /hello response', body0); + }); + }); +}); + +process.on('exit', function() { + assert.equal('somechunkeddata', body0); +}); From c633c207f58275b6b83575298f0272a93165b969 Mon Sep 17 00:00:00 2001 From: Jimb Esser Date: Sat, 2 Nov 2019 09:53:32 -0700 Subject: [PATCH 2/2] Update to work on Node v12 and v13 --- README.md | 14 ++++++++------ http-parser.js | 11 ++++++++++- package.json | 5 +++-- tests/common.js | 4 ++++ .../parallel/test-http-destroyed-socket-write2.js | 8 ++++++-- tests/parallel/test-http-max-headers-count.js | 2 ++ 6 files changed, 33 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 8b9abef..534fc67 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,6 @@ # HTTP Parser -This library parses HTTP protocol for requests and responses. It was created to replace `http_parser.c` since calling C++ function from JS is really slow in V8. - -This was further modified by Jimbly to be useable in parsing responses, specifically tested with the "request" module, and addresses issues such as corrupt HTTP headers, which would otherwise cause Node's parser to throw a fatal error (HPE_INVALID_HEADER_TOKEN). - -Jan Schär (jscissr) made some bigger changes and added tests. This fixed some bugs and added many missing features. +This library parses HTTP protocol for requests and responses. It was created to replace `http_parser.c` since calling C++ function from JS is really slow in V8. However, it is now primarily useful in having a more flexible/tolerant HTTP parser when dealing with legacy services that do not meet the strict HTTP parsing rules Node's parser follows. This is packaged as a standalone npm module. To use in node, monkeypatch HTTPParser. @@ -22,7 +18,13 @@ Simply do `npm test`. The tests are copied from node and mscedex/io.js, with som ## Status -This should now be usable in any node application, it now supports (nearly) everything `http_parser.c` does while still being tolerant with corrupted headers. +This should now be usable in any node application, it now supports (nearly) everything `http_parser.c` does while still being tolerant with corrupted headers, and other kinds of malformed data. + +### Node Versions + +`http-parser-js` should work via monkey-patching on Node v6-v11, and v13. + +Node v12.x renamed the internal http parser, and did not expose it for monkey-patching, so to be able to monkey-patch on Node v12, you must run `node --http-parser=legacy file.js` to opt in to the old, monkey-patchable http_parser binding. ## License diff --git a/http-parser.js b/http-parser.js index de8a3ac..8e78028 100644 --- a/http-parser.js +++ b/http-parser.js @@ -4,6 +4,14 @@ var assert = require('assert'); exports.HTTPParser = HTTPParser; function HTTPParser(type) { + assert.ok(type === HTTPParser.REQUEST || type === HTTPParser.RESPONSE || type === undefined); + if (type === undefined) { + // Node v12+ + } else { + this.initialize(type); + } +} +HTTPParser.prototype.initialize = function (type, async_resource) { assert.ok(type === HTTPParser.REQUEST || type === HTTPParser.RESPONSE); this.type = type; this.state = type + '_LINE'; @@ -19,7 +27,8 @@ function HTTPParser(type) { this.body_bytes = null; this.isUserCall = false; this.hadError = false; -} +}; + HTTPParser.encoding = 'ascii'; HTTPParser.maxHeaderSize = 80 * 1024; // maxHeaderSize (in bytes) is configurable, but 80kb by default; HTTPParser.REQUEST = 'REQUEST'; diff --git a/package.json b/package.json index 58e0f27..eb187d7 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,11 @@ { "name": "http-parser-js", - "version": "0.5.1", + "version": "0.5.2", "description": "A pure JS HTTP parser for node.", "main": "http-parser.js", "scripts": { - "test": "python tests/test.py && node tests/iojs/test-http-parser-durability.js" + "test": "python tests/test.py && node tests/iojs/test-http-parser-durability.js", + "testv12": "python tests/test.py --node-args=\"--http-parser=legacy\" && node --http-parser=legacy tests/iojs/test-http-parser-durability.js" }, "repository": { "type": "git", diff --git a/tests/common.js b/tests/common.js index c6fcad9..a610c29 100644 --- a/tests/common.js +++ b/tests/common.js @@ -343,6 +343,10 @@ if (global.Symbol) { knownGlobals.push(Symbol); } +if (global.queueMicrotask) { + knownGlobals.push(queueMicrotask); +} + function leakedGlobals() { var leaked = []; diff --git a/tests/parallel/test-http-destroyed-socket-write2.js b/tests/parallel/test-http-destroyed-socket-write2.js index 7d2f235..df03a32 100644 --- a/tests/parallel/test-http-destroyed-socket-write2.js +++ b/tests/parallel/test-http-destroyed-socket-write2.js @@ -47,8 +47,12 @@ server.listen(0, function() { break; } - assert.equal(req.output.length, 0); - assert.equal(req.outputEncodings.length, 0); + if (req.outputData) { // Node v12+ + assert.strictEqual(req.outputData.length, 0); + } else { + assert.equal(req.output.length, 0); + assert.equal(req.outputEncodings.length, 0); + } server.close(); })); diff --git a/tests/parallel/test-http-max-headers-count.js b/tests/parallel/test-http-max-headers-count.js index 2d68ead..d86a008 100644 --- a/tests/parallel/test-http-max-headers-count.js +++ b/tests/parallel/test-http-max-headers-count.js @@ -1,3 +1,5 @@ +// Note: If this test fails, monkey-patching probably failed and you're using +// the default Node parser, which now has a limit of 8KB for header data 'use strict'; require('../common'); var assert = require('assert');