From 554a1939d60fbedba297200235f9491f4e662d0d Mon Sep 17 00:00:00 2001 From: Jimb Esser Date: Sat, 2 Nov 2019 13:48:39 -0700 Subject: [PATCH 01/38] Use whatever parser is currently bound --- tests/parallel/test-http-client-max-header-size_increased.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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'); From c8a3b30dcad13788a70c787952e3657ebf7201cc Mon Sep 17 00:00:00 2001 From: Jimb Esser Date: Sat, 2 Nov 2019 13:49:07 -0700 Subject: [PATCH 02/38] Add description of state of tests on Node v13 --- tests/README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 tests/README.md diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..bc1fc68 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,20 @@ +## Tests that currently fail on Node v13.0.1: + +['chunked with (arguably wrong) content length'](https://github.com/creationix/http-parser-js/blob/master/tests/iojs/test-http-parser-durability.js#L1279-L1318) +* Real case from proxy servers: Should ignore `content-length` if both it and `encoding: chunked` are specified + +[parallel/test-http-client-max-header-size_increased](https://github.com/creationix/http-parser-js/tree/master/tests/parallel/test-http-client-max-header-size_increased) +* Test from actual user issue: Extra large headers (configurable max setable in `http-parser-js`) + +[parallel/test-http-max-headers-count](https://github.com/creationix/http-parser-js/tree/master/tests/parallel/test-http-max-headers-count) +* Test from old Node version: Exceeding Node v12's new 8KB header size limit + +['200 malformed header'](https://github.com/creationix/http-parser-js/blob/master/tests/iojs/test-http-parser-durability.js#L1249-L1278) +* Getting blank headers on malformed headers (probably fine, used to crash Node) + + +Whitespace/trimming differences (probably fine): all in https://github.com/creationix/http-parser-js/blob/master/tests/iojs/test-http-parser-durability.js +* 'line folding in header value with CRLF' +* 'line folding in header value with LF' +* 'multiple connection header values with folding and lws and CRLF' +* 'google 301' From 8a81c92201e49fda4668386263fdce284789ed73 Mon Sep 17 00:00:00 2001 From: Jimb Esser Date: Sun, 3 Nov 2019 11:28:40 -0800 Subject: [PATCH 03/38] Add test for duplicate content-length header --- tests/README.md | 7 ++++-- tests/iojs/test-http-parser-durability.js | 26 +++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/tests/README.md b/tests/README.md index bc1fc68..b1acd04 100644 --- a/tests/README.md +++ b/tests/README.md @@ -1,15 +1,18 @@ ## Tests that currently fail on Node v13.0.1: -['chunked with (arguably wrong) content length'](https://github.com/creationix/http-parser-js/blob/master/tests/iojs/test-http-parser-durability.js#L1279-L1318) +['chunked with (arguably wrong) content length'](https://github.com/creationix/http-parser-js/blob/master/tests/iojs/test-http-parser-durability.js#L1306-L1345) * Real case from proxy servers: Should ignore `content-length` if both it and `encoding: chunked` are specified +['duplicate content-length'](https://github.com/creationix/http-parser-js/blob/master/tests/iojs/test-http-parser-durability.js#L220-L245) +* Real case: Allow duplicate (but identical) `content-length` headers + [parallel/test-http-client-max-header-size_increased](https://github.com/creationix/http-parser-js/tree/master/tests/parallel/test-http-client-max-header-size_increased) * Test from actual user issue: Extra large headers (configurable max setable in `http-parser-js`) [parallel/test-http-max-headers-count](https://github.com/creationix/http-parser-js/tree/master/tests/parallel/test-http-max-headers-count) * Test from old Node version: Exceeding Node v12's new 8KB header size limit -['200 malformed header'](https://github.com/creationix/http-parser-js/blob/master/tests/iojs/test-http-parser-durability.js#L1249-L1278) +['200 malformed header'](https://github.com/creationix/http-parser-js/blob/master/tests/iojs/test-http-parser-durability.js#L1276-L1305) * Getting blank headers on malformed headers (probably fine, used to crash Node) diff --git a/tests/iojs/test-http-parser-durability.js b/tests/iojs/test-http-parser-durability.js index 06e7c55..a0749d5 100644 --- a/tests/iojs/test-http-parser-durability.js +++ b/tests/iojs/test-http-parser-durability.js @@ -217,6 +217,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, From a3b19177c10a1cbfe022bb8b7ef4c4152226d8f1 Mon Sep 17 00:00:00 2001 From: Jimb Esser Date: Fri, 1 Jan 2021 09:53:39 -0800 Subject: [PATCH 04/38] Fix for Node v12.19.0: do not use '0' as an exported constant, it conflicts with unknown constants --- http-parser.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/http-parser.js b/http-parser.js index 8e78028..2ce829d 100644 --- a/http-parser.js +++ b/http-parser.js @@ -33,10 +33,13 @@ HTTPParser.encoding = 'ascii'; HTTPParser.maxHeaderSize = 80 * 1024; // maxHeaderSize (in bytes) is configurable, but 80kb by default; HTTPParser.REQUEST = 'REQUEST'; HTTPParser.RESPONSE = 'RESPONSE'; -var kOnHeaders = HTTPParser.kOnHeaders = 0; -var kOnHeadersComplete = HTTPParser.kOnHeadersComplete = 1; -var kOnBody = HTTPParser.kOnBody = 2; -var kOnMessageComplete = HTTPParser.kOnMessageComplete = 3; + +// Note: *not* starting with kOnHeaders=0 line the Node parser, because any +// newly added constants (kOnTimeout in Node v12.19.0) will overwrite 0! +var kOnHeaders = HTTPParser.kOnHeaders = 1; +var kOnHeadersComplete = HTTPParser.kOnHeadersComplete = 2; +var kOnBody = HTTPParser.kOnBody = 3; +var kOnMessageComplete = HTTPParser.kOnMessageComplete = 4; // Some handler stubs, needed for compatibility HTTPParser.prototype[kOnHeaders] = @@ -49,7 +52,7 @@ Object.defineProperty(HTTPParser, 'kOnExecute', { get: function () { // hack for backward compatibility compatMode0_12 = false; - return 4; + return 99; } }); From 6538c7c2740da4c3dde297af9e6ea5f64b19ef76 Mon Sep 17 00:00:00 2001 From: Jimb Esser Date: Fri, 1 Jan 2021 12:13:34 -0800 Subject: [PATCH 05/38] Fix tests on Node v14.9.0 --- .../test-http-agent-destroyed-socket.js | 22 +++++++++---------- tests/parallel/test-http-flush.js | 16 -------------- 2 files changed, 11 insertions(+), 27 deletions(-) delete mode 100644 tests/parallel/test-http-flush.js diff --git a/tests/parallel/test-http-agent-destroyed-socket.js b/tests/parallel/test-http-agent-destroyed-socket.js index 34772f7..b6a255f 100644 --- a/tests/parallel/test-http-agent-destroyed-socket.js +++ b/tests/parallel/test-http-agent-destroyed-socket.js @@ -29,6 +29,17 @@ var server = http.createServer(function(req, res) { console.log('request1 socket closed'); }); response.pipe(process.stdout); + response.socket.once('close', function() { + // assert request2 was removed from the queue + assert(!agent.requests[key]); + console.log("waiting for request2.onSocket's nextTick"); + process.nextTick(function() { + // assert that the same socket was not assigned to request2, + // since it was destroyed. + assert(request1.socket !== request2.socket); + assert(!request2.socket.destroyed, 'the socket is destroyed'); + }); + }); response.on('end', function() { console.log('response1 done'); ///////////////////////////////// @@ -44,17 +55,6 @@ var server = http.createServer(function(req, res) { // is triggered. request1.socket.destroy(); - response.once('close', function() { - // assert request2 was removed from the queue - assert(!agent.requests[key]); - console.log("waiting for request2.onSocket's nextTick"); - process.nextTick(function() { - // assert that the same socket was not assigned to request2, - // since it was destroyed. - assert(request1.socket !== request2.socket); - assert(!request2.socket.destroyed, 'the socket is destroyed'); - }); - }); }); }); diff --git a/tests/parallel/test-http-flush.js b/tests/parallel/test-http-flush.js deleted file mode 100644 index 8b3dacb..0000000 --- a/tests/parallel/test-http-flush.js +++ /dev/null @@ -1,16 +0,0 @@ -'use strict'; -require('../common'); -var http = require('http'); - -http.createServer(function(req, res) { - res.end('ok'); - this.close(); -}).listen(0, '127.0.0.1', function() { - var req = http.request({ - method: 'POST', - host: '127.0.0.1', - port: this.address().port, - }); - req.flush(); // Flush the request headers. - req.flush(); // Should be idempotent. -}); From bc05e8b6805cef352dc318855a6cb0b5e5158fcf Mon Sep 17 00:00:00 2001 From: Jimb Esser Date: Fri, 1 Jan 2021 12:20:36 -0800 Subject: [PATCH 06/38] Add test that ensures we're actually testing against the monkeypatched parser --- tests/parallel/test-is-monkeypatched.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 tests/parallel/test-is-monkeypatched.js diff --git a/tests/parallel/test-is-monkeypatched.js b/tests/parallel/test-is-monkeypatched.js new file mode 100644 index 0000000..f34ad99 --- /dev/null +++ b/tests/parallel/test-is-monkeypatched.js @@ -0,0 +1,14 @@ +'use strict'; +require('../common'); + +var assert = require('assert'); +var http_common = require('_http_common'); +var http_parser_js = require('../../http-parser.js') + +if (parseInt(process.versions.node) <= 10) { + // not exported, no easy way to check, but we know it works there +} else { + // on newer Node versions, this is exported + assert(http_common.HTTPParser); + assert.equal(http_common.HTTPParser, http_parser_js.HTTPParser); +} From a9096ff27d6179cae0a43493dc51af6201f38435 Mon Sep 17 00:00:00 2001 From: Jimb Esser Date: Fri, 1 Jan 2021 12:33:36 -0800 Subject: [PATCH 07/38] Disable part of test that is no longer valid on Node v15 --- tests/parallel/test-http-res-write-after-end.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/parallel/test-http-res-write-after-end.js b/tests/parallel/test-http-res-write-after-end.js index 8dca7ad..1a64f73 100644 --- a/tests/parallel/test-http-res-write-after-end.js +++ b/tests/parallel/test-http-res-write-after-end.js @@ -14,7 +14,9 @@ var server = http.Server(function(req, res) { res.end(); var r = res.write('This should raise an error.'); - assert.equal(r, true, 'write after end should return true'); + // JE: Disabling this, node v15 changed this to return false, not true, + // cannot reasonably test (must not be important!) + // assert.equal(r, true, 'write after end should return true'); }); server.listen(0, function() { From 677aba5a1c639553103e627b5a7070e21bec2ab9 Mon Sep 17 00:00:00 2001 From: HonkingGoose <34918129+HonkingGoose@users.noreply.github.com> Date: Fri, 1 Jan 2021 13:46:07 +0100 Subject: [PATCH 08/38] Create NodeJS GitHub Actions --- .github/workflows/node-v12.yaml | 31 +++++++++++++++++++++++++++++++ .github/workflows/node.yaml | 31 +++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 .github/workflows/node-v12.yaml create mode 100644 .github/workflows/node.yaml diff --git a/.github/workflows/node-v12.yaml b/.github/workflows/node-v12.yaml new file mode 100644 index 0000000..b975a2e --- /dev/null +++ b/.github/workflows/node-v12.yaml @@ -0,0 +1,31 @@ +# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions + +# NOTE: This action runs the testv12 test suite. + +name: Node-v12 + +on: + push: + branches: [master] + pull_request: + branches: [master] + +jobs: + build: + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + node-version: [12.x] + + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - run: npm install + - run: npm run build --if-present + - run: npm run testv12 diff --git a/.github/workflows/node.yaml b/.github/workflows/node.yaml new file mode 100644 index 0000000..4bc19a7 --- /dev/null +++ b/.github/workflows/node.yaml @@ -0,0 +1,31 @@ +# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions + +# NOTE: This action runs the normal test suite. + +name: Node + +on: + push: + branches: [master] + pull_request: + branches: [master] + +jobs: + build: + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + node-version: [6.x, 8.x, 10.x, 14.x] + + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - run: npm install + - run: npm run build --if-present + - run: npm test From 73fd6ad5fdeb99aeece72833f1b594a2431763d7 Mon Sep 17 00:00:00 2001 From: HonkingGoose <34918129+HonkingGoose@users.noreply.github.com> Date: Fri, 1 Jan 2021 22:53:14 +0100 Subject: [PATCH 09/38] use setup-node@v2 (newer stable release) --- .github/workflows/node-v12.yaml | 2 +- .github/workflows/node.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/node-v12.yaml b/.github/workflows/node-v12.yaml index b975a2e..3aaf756 100644 --- a/.github/workflows/node-v12.yaml +++ b/.github/workflows/node-v12.yaml @@ -23,7 +23,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 + uses: actions/setup-node@v2 with: node-version: ${{ matrix.node-version }} - run: npm install diff --git a/.github/workflows/node.yaml b/.github/workflows/node.yaml index 4bc19a7..39bca49 100644 --- a/.github/workflows/node.yaml +++ b/.github/workflows/node.yaml @@ -23,7 +23,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 + uses: actions/setup-node@v2 with: node-version: ${{ matrix.node-version }} - run: npm install From 64abe88fc5460b5edca6cad22368bf383f30fd62 Mon Sep 17 00:00:00 2001 From: Jimb Esser Date: Fri, 1 Jan 2021 14:44:22 -0800 Subject: [PATCH 10/38] Remove .travis.yml --- .travis.yml | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 2ae9d62..0000000 --- a/.travis.yml +++ /dev/null @@ -1,5 +0,0 @@ -language: node_js -node_js: - - '10' - - '8' - - '6' From 6bbab2ce3f0e387c3c8727a68cdc4b7ee383afa0 Mon Sep 17 00:00:00 2001 From: Jimb Esser Date: Fri, 1 Jan 2021 14:51:32 -0800 Subject: [PATCH 11/38] Publish to NPM as v0.5.3 - fixes trailers not parsing on Node v14+ --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index eb187d7..05636c0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "http-parser-js", - "version": "0.5.2", + "version": "0.5.3", "description": "A pure JS HTTP parser for node.", "main": "http-parser.js", "scripts": { From 84dabfe44d40e7a39cbda524d2558a4b1b6d68c3 Mon Sep 17 00:00:00 2001 From: HonkingGoose <34918129+HonkingGoose@users.noreply.github.com> Date: Sat, 2 Jan 2021 13:28:06 +0100 Subject: [PATCH 12/38] Add CI status badges to README --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 534fc67..d371b1d 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +![Node](https://github.com/creationix/http-parser-js/workflows/Node/badge.svg) +![Node-v12](https://github.com/creationix/http-parser-js/workflows/Node-v12/badge.svg) + # 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. 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. From 52c37dba1225d4e34ff8723a5e95938d50ae0cc8 Mon Sep 17 00:00:00 2001 From: Jimb Esser Date: Sat, 2 Jan 2021 06:10:26 -0800 Subject: [PATCH 13/38] Update README.md to add v14 as supported --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d371b1d..0dadd10 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ This should now be usable in any node application, it now supports (nearly) ever ### Node Versions -`http-parser-js` should work via monkey-patching on Node v6-v11, and v13. +`http-parser-js` should work via monkey-patching on Node v6-v11, and v13-14. 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. From cd327d6569c6ede95e9ac7293ae66549f0c3acd1 Mon Sep 17 00:00:00 2001 From: Jimb Esser Date: Sun, 2 May 2021 10:33:20 -0700 Subject: [PATCH 14/38] Fix 'unknown globals' error on Node v16 --- tests/common.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/common.js b/tests/common.js index a610c29..0af3e1a 100644 --- a/tests/common.js +++ b/tests/common.js @@ -293,6 +293,10 @@ if (global.gc) { knownGlobals.push(global.gc); } +if (global.performance) { + knownGlobals.push(global.performance); +} + if (global.DTRACE_HTTP_SERVER_RESPONSE) { knownGlobals.push(DTRACE_HTTP_SERVER_RESPONSE); knownGlobals.push(DTRACE_HTTP_SERVER_REQUEST); From 5735f7d75b069c5337259de8801b9759bde3bd63 Mon Sep 17 00:00:00 2001 From: Jimb Esser Date: Sun, 2 May 2021 10:37:08 -0700 Subject: [PATCH 15/38] Fix tests to not fail on Node v16 --- tests/parallel/test-http-client-agent.js | 4 ++-- tests/parallel/test-http-connect-req-res.js | 4 ++-- tests/parallel/test-http-connect.js | 4 ++-- tests/parallel/test-http-keep-alive.js | 6 +++--- tests/parallel/test-http-upgrade-agent.js | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/parallel/test-http-client-agent.js b/tests/parallel/test-http-client-agent.js index 36ed24e..3434a0c 100644 --- a/tests/parallel/test-http-client-agent.js +++ b/tests/parallel/test-http-client-agent.js @@ -36,8 +36,8 @@ function request(i) { if (count < max) { assert.equal(http.globalAgent.sockets[name].indexOf(socket), -1); } else { - assert(!http.globalAgent.sockets.hasOwnProperty(name)); - assert(!http.globalAgent.requests.hasOwnProperty(name)); + assert(!http.globalAgent.sockets[name]); + assert(!http.globalAgent.requests[name]); server.close(); } }); diff --git a/tests/parallel/test-http-connect-req-res.js b/tests/parallel/test-http-connect-req-res.js index c6b545b..ab43b2b 100644 --- a/tests/parallel/test-http-connect-req-res.js +++ b/tests/parallel/test-http-connect-req-res.js @@ -44,8 +44,8 @@ server.listen(0, common.mustCall(function() { // Make sure this request got removed from the pool. const name = 'localhost:' + server.address().port; - assert(!http.globalAgent.sockets.hasOwnProperty(name)); - assert(!http.globalAgent.requests.hasOwnProperty(name)); + assert(!http.globalAgent.sockets[name]); + assert(!http.globalAgent.requests[name]); // Make sure this socket has detached. assert(!socket.ondata); diff --git a/tests/parallel/test-http-connect.js b/tests/parallel/test-http-connect.js index b0efca6..54551ea 100644 --- a/tests/parallel/test-http-connect.js +++ b/tests/parallel/test-http-connect.js @@ -45,8 +45,8 @@ server.listen(0, function() { // Make sure this request got removed from the pool. var name = 'localhost:' + server.address().port; - assert(!http.globalAgent.sockets.hasOwnProperty(name)); - assert(!http.globalAgent.requests.hasOwnProperty(name)); + assert(!http.globalAgent.sockets[name]); + assert(!http.globalAgent.requests[name]); // Make sure this socket has detached. assert(!socket.ondata); diff --git a/tests/parallel/test-http-keep-alive.js b/tests/parallel/test-http-keep-alive.js index d48732e..356c691 100644 --- a/tests/parallel/test-http-keep-alive.js +++ b/tests/parallel/test-http-keep-alive.js @@ -38,7 +38,7 @@ server.listen(0, function() { }, function(response) { response.on('end', function() { assert.equal(agent.sockets[name].length, 1); - assert(!agent.requests.hasOwnProperty(name)); + assert(!agent.requests[name]); server.close(); }); response.resume(); @@ -46,6 +46,6 @@ server.listen(0, function() { }); process.on('exit', function() { - assert(!agent.sockets.hasOwnProperty(name)); - assert(!agent.requests.hasOwnProperty(name)); + assert(!agent.sockets[name]); + assert(!agent.requests[name]); }); diff --git a/tests/parallel/test-http-upgrade-agent.js b/tests/parallel/test-http-upgrade-agent.js index 01074d7..00d57cd 100644 --- a/tests/parallel/test-http-upgrade-agent.js +++ b/tests/parallel/test-http-upgrade-agent.js @@ -62,7 +62,7 @@ srv.listen(0, '127.0.0.1', function() { assert.deepStrictEqual(expectedHeaders, res.headers); // Make sure this request got removed from the pool. - assert(!http.globalAgent.sockets.hasOwnProperty(name)); + assert(!http.globalAgent.sockets[name]); req.on('close', function() { socket.end(); From b93f548e0f992ca8fe5df7dd732994d97f6ad473 Mon Sep 17 00:00:00 2001 From: HonkingGoose <34918129+HonkingGoose@users.noreply.github.com> Date: Sun, 2 May 2021 14:11:13 +0200 Subject: [PATCH 16/38] run on ubuntu-18.04, test node 16 --- .github/workflows/node-v12.yaml | 2 +- .github/workflows/node.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/node-v12.yaml b/.github/workflows/node-v12.yaml index 3aaf756..72743f6 100644 --- a/.github/workflows/node-v12.yaml +++ b/.github/workflows/node-v12.yaml @@ -13,7 +13,7 @@ on: jobs: build: - runs-on: ubuntu-latest + runs-on: ubuntu-18.04 strategy: fail-fast: false diff --git a/.github/workflows/node.yaml b/.github/workflows/node.yaml index 39bca49..3be146a 100644 --- a/.github/workflows/node.yaml +++ b/.github/workflows/node.yaml @@ -13,12 +13,12 @@ on: jobs: build: - runs-on: ubuntu-latest + runs-on: ubuntu-18.04 strategy: fail-fast: false matrix: - node-version: [6.x, 8.x, 10.x, 14.x] + node-version: [6.x, 8.x, 10.x, 14.x, 16.x] steps: - uses: actions/checkout@v2 From 889410d88d98d46ea9a4fd6d82f0d766df6a552b Mon Sep 17 00:00:00 2001 From: HonkingGoose <34918129+HonkingGoose@users.noreply.github.com> Date: Mon, 3 May 2021 09:54:29 +0200 Subject: [PATCH 17/38] improve readme --- README.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 0dadd10..d6ba3be 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,12 @@ # 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. 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 library parses HTTP protocol for requests and responses. +It was created to replace `http_parser.c` since calling C++ functions 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. +This is packaged as a standalone npm module. +To use in node, monkeypatch HTTPParser. ```js // Monkey patch before you require http for the first time. @@ -17,13 +20,14 @@ var http = require('http'); ## Testing -Simply do `npm test`. The tests are copied from node and mscedex/io.js, with some modifcations. +Simply run `npm test`. +The tests are copied from node and mscedex/io.js, with some modifcations. ## 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, and other kinds of malformed data. -### Node Versions +### Node versions `http-parser-js` should work via monkey-patching on Node v6-v11, and v13-14. @@ -31,4 +35,5 @@ Node v12.x renamed the internal http parser, and did not expose it for monkey-pa ## License -MIT. See LICENSE.md +MIT. +See [LICENSE.md](LICENSE.md) From db7f74a96e98e75f5204731e27e8fe727f7c5f04 Mon Sep 17 00:00:00 2001 From: 0x0a0d Date: Fri, 26 Nov 2021 19:41:47 +0700 Subject: [PATCH 18/38] support typescript --- http-parser.d.ts | 175 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 http-parser.d.ts diff --git a/http-parser.d.ts b/http-parser.d.ts new file mode 100644 index 0000000..4d94b77 --- /dev/null +++ b/http-parser.d.ts @@ -0,0 +1,175 @@ +type ParserType = + | 'REQUEST' + | 'RESPONSE' + +type RequestMethod = + | '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' + | string + +type StateHeaderKey = + | 'REQUEST_LINE' + | 'RESPONSE_LINE' + | 'HEADER' + +type StateFinishAllowedKey = + | 'REQUEST_LINE' + | 'RESPONSE_LINE' + | 'BODY_RAW' + +type HeaderObject = Record +type noop = ()=> T + +type HeaderInfo
= { + versionMajor: number + versionMinor: number + headers: HEADER + method: RequestMethod + url: string + statusCode: number + statusMessage: string + upgrade: boolean + shouldKeepAlive: boolean +} +export type OnHeadersCompleteParser = Mode_0_12 extends true + ? (info: HeaderInfo)=> number + : ( + versionMajor: number, + versionMinor: number, + headers: HEADER, + method: RequestMethod, + url: string, + statusCode: number, + statusMessage: string, + upgrade: boolean, + shouldKeepAlive: boolean, + )=> number +export type OnBodyParser = (chunk: Buffer, offset: number, length: number)=> void +// Only called in the slow case where slow means +// that the request headers were either fragmented +// across multiple TCP packets or too large to be +// processed in a single run. This method is also +// called to process trailing HTTP headers. +export type OnHeadersParser = (headers: string[], url: string)=> void + +declare class HTTPParserJS { + initialize(type: ParserType, async_resource?: unknown): void + + // Some handler stubs, needed for compatibility + [HTTPParserConstructor.kOnHeaders]: OnHeadersParser + [HTTPParserConstructor.kOnHeadersComplete]: OnHeadersCompleteParser + [HTTPParserConstructor.kOnBody]: OnBodyParser + [HTTPParserConstructor.kOnMessageComplete]: noop + + reinitialize: HTTPParserConstructor + close: noop + pause: noop + resume: noop + free: noop + private _compatMode0_11: false | boolean + getAsyncId: noop<0> + + execute(chunk: Buffer, start?: number, length?: number): number | Error + finish(): void | Error + + // These three methods are used for an internal speed optimization, and it also + // works if theses are noops. Basically consume() asks us to read the bytes + // ourselves, but if we don't do it we get them through execute(). + consume: noop + unconsume: noop + getCurrentBuffer: noop + + /** + * For correct error handling - see HTTPParser#execute + * @example this.userCall()(userFunction('arg')); + */ + userCall(): (ret?: T)=> T + private nextRequest: noop + private consumeLine: noop + parseHeader(line: string, headers: string[]): void + private REQUEST_LINE: noop + private RESPONSE_LINE: noop + shouldKeepAlive(): boolean + /** + * For older versions of node (v6.x and older?), that return `skipBody=1` or `skipBody=true`, need this `return true;` if it's an upgrade request. + */ + private HEADER(): void | boolean + private BODY_CHUNKHEAD(): void + private BODY_CHUNK(): void + private BODY_CHUNKEMPTYLINE(): void + private BODY_CHUNKTRAILERS(): void + private BODY_RAW(): void + private BODY_SIZED(): void + + get onHeaders(): HTTPParser[HTTPParserConstructor.kOnHeaders] + set onHeaders(to: OnHeadersParser): void + + get onHeadersComplete(): HTTPParser[HTTPParserConstructor.kOnHeadersComplete] + set onHeadersComplete(to: OnHeadersCompleteParser): void + + get onBody(): HTTPParser[HTTPParserConstructor.kOnBody] + set onBody(to: OnBodyParser): void + + get onMessageComplete(): HTTPParser[HTTPParserConstructor.kOnMessageComplete] + set onMessageComplete(to: noop): void +} + +interface HTTPParserConstructor extends Function { + new(type?: ParserType): HTTPParserJS + (type?: ParserType): void + + readonly prototype: HTTPParserJS + + readonly REQUEST: 'REQUEST' + readonly RESPONSE: 'RESPONSE' + readonly methods: RequestMethod[] + + encoding: 'ascii'|string + /** + * maxHeaderSize (in bytes) is configurable, but 80kb by default; + * @default 80 * 1024 = 80kb + */ + maxHeaderSize: 81920|number + + // Note: *not* starting with kOnHeaders=0 line the Node parser, because any + // newly added constants (kOnTimeout in Node v12.19.0) will overwrite 0! + readonly kOnHeaders: 1 + readonly kOnHeadersComplete: 2 + readonly kOnBody: 3 + readonly kOnMessageComplete: 4 + + kOnExecute(): void +} +export const HTTPParser: HTTPParserConstructor +export const methods: RequestMethod[] From 4f4ee0c16e3934fc2fe10ca6d7094471e2e5fca6 Mon Sep 17 00:00:00 2001 From: Jimb Esser Date: Fri, 26 Nov 2021 08:34:29 -0800 Subject: [PATCH 19/38] Add typescript file to npm package --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 05636c0..cac3a3f 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ "url": "git://github.com/creationix/http-parser-js.git" }, "files": [ - "http-parser.js" + "http-parser.js", + "http-parser.d.ts" ], "keywords": [ "http" From f37d4e4926ab9309aabe7fa35fef88df0d5290e6 Mon Sep 17 00:00:00 2001 From: Jimb Esser Date: Fri, 26 Nov 2021 08:32:28 -0800 Subject: [PATCH 20/38] Publish to NPM as v0.5.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cac3a3f..0fb4552 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "http-parser-js", - "version": "0.5.3", + "version": "0.5.4", "description": "A pure JS HTTP parser for node.", "main": "http-parser.js", "scripts": { From dc06aab4b399e0c44c50daa3fe369b3eea5af59b Mon Sep 17 00:00:00 2001 From: 0x0a0d Date: Sat, 27 Nov 2021 08:33:21 +0700 Subject: [PATCH 21/38] ignore ide config, node_modules --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index c97c649..b48af5b 100644 --- a/.gitignore +++ b/.gitignore @@ -216,3 +216,5 @@ pip-log.txt #Mr Developer .mr.developer.cfg +.idea +node_modules From 9886c4fe7f1683003bda09b213fd14b4bc02414e Mon Sep 17 00:00:00 2001 From: 0x0a0d Date: Sat, 27 Nov 2021 08:36:02 +0700 Subject: [PATCH 22/38] setter/getter correct setter should not return anything getter return pre-defined type --- http-parser.d.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/http-parser.d.ts b/http-parser.d.ts index 4d94b77..e309151 100644 --- a/http-parser.d.ts +++ b/http-parser.d.ts @@ -132,17 +132,17 @@ declare class HTTPParserJS { private BODY_RAW(): void private BODY_SIZED(): void - get onHeaders(): HTTPParser[HTTPParserConstructor.kOnHeaders] - set onHeaders(to: OnHeadersParser): void + get onHeaders(): OnHeadersParser + set onHeaders(to: OnHeadersParser) - get onHeadersComplete(): HTTPParser[HTTPParserConstructor.kOnHeadersComplete] - set onHeadersComplete(to: OnHeadersCompleteParser): void + get onHeadersComplete(): OnHeadersCompleteParser + set onHeadersComplete(to: OnHeadersCompleteParser) - get onBody(): HTTPParser[HTTPParserConstructor.kOnBody] - set onBody(to: OnBodyParser): void + get onBody(): OnBodyParser + set onBody(to: OnBodyParser) - get onMessageComplete(): HTTPParser[HTTPParserConstructor.kOnMessageComplete] - set onMessageComplete(to: noop): void + get onMessageComplete(): noop + set onMessageComplete(to: noop) } interface HTTPParserConstructor extends Function { From 347f96ac6d0e3f0b6b70f77e3e2d37e108de6dda Mon Sep 17 00:00:00 2001 From: 0x0a0d Date: Sat, 27 Nov 2021 08:36:53 +0700 Subject: [PATCH 23/38] mistake fix --- http-parser.d.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/http-parser.d.ts b/http-parser.d.ts index e309151..0ca14dd 100644 --- a/http-parser.d.ts +++ b/http-parser.d.ts @@ -87,10 +87,10 @@ declare class HTTPParserJS { initialize(type: ParserType, async_resource?: unknown): void // Some handler stubs, needed for compatibility - [HTTPParserConstructor.kOnHeaders]: OnHeadersParser - [HTTPParserConstructor.kOnHeadersComplete]: OnHeadersCompleteParser - [HTTPParserConstructor.kOnBody]: OnBodyParser - [HTTPParserConstructor.kOnMessageComplete]: noop + [HTTPParser.kOnHeaders]: OnHeadersParser + [HTTPParser.kOnHeadersComplete]: OnHeadersCompleteParser + [HTTPParser.kOnBody]: OnBodyParser + [HTTPParser.kOnMessageComplete]: noop reinitialize: HTTPParserConstructor close: noop From 92bbfc0be8f1dea6ccf86a31bceb43a99fa4ad4f Mon Sep 17 00:00:00 2001 From: 0x0a0d Date: Sat, 27 Nov 2021 08:37:17 +0700 Subject: [PATCH 24/38] add missing HEADER generic --- http-parser.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/http-parser.d.ts b/http-parser.d.ts index 0ca14dd..33ee108 100644 --- a/http-parser.d.ts +++ b/http-parser.d.ts @@ -62,8 +62,8 @@ type HeaderInfo
= { upgrade: boolean shouldKeepAlive: boolean } -export type OnHeadersCompleteParser = Mode_0_12 extends true - ? (info: HeaderInfo)=> number +export type OnHeadersCompleteParser
= Mode_0_12 extends true + ? (info: HeaderInfo
)=> number : ( versionMajor: number, versionMinor: number, From 31158c1361cfa62fc3b2dccd4bbcc56f65e89d94 Mon Sep 17 00:00:00 2001 From: Jimb Esser Date: Sat, 27 Nov 2021 06:13:04 -0800 Subject: [PATCH 25/38] Publish to NPM as v0.5.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0fb4552..0e94037 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "http-parser-js", - "version": "0.5.4", + "version": "0.5.5", "description": "A pure JS HTTP parser for node.", "main": "http-parser.js", "scripts": { From 654ee50a31f86891ad9f18ec0d82e364fc55bd28 Mon Sep 17 00:00:00 2001 From: 0x0a0d Date: Sun, 28 Nov 2021 09:50:55 +0700 Subject: [PATCH 26/38] OnHeadersCompleteParser should accept void return --- http-parser.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/http-parser.d.ts b/http-parser.d.ts index 33ee108..8e9ee33 100644 --- a/http-parser.d.ts +++ b/http-parser.d.ts @@ -63,7 +63,7 @@ type HeaderInfo
= { shouldKeepAlive: boolean } export type OnHeadersCompleteParser
= Mode_0_12 extends true - ? (info: HeaderInfo
)=> number + ? (info: HeaderInfo
)=> number | void : ( versionMajor: number, versionMinor: number, @@ -74,7 +74,7 @@ export type OnHeadersCompleteParser
number + )=> number | void export type OnBodyParser = (chunk: Buffer, offset: number, length: number)=> void // Only called in the slow case where slow means // that the request headers were either fragmented From 0fa5a8a35d68a5bdf7d418220af4394d0baa3137 Mon Sep 17 00:00:00 2001 From: Alexander Prinzhorn Date: Mon, 31 Jan 2022 15:14:37 +0100 Subject: [PATCH 27/38] Added standalone usage example (#89) * Added standalone usage example * Integrate standalone-example.js into tests --- README.md | 4 + standalone-example.js | 244 ++++++++++++++++++++++ tests/parallel/test-standalone-example.js | 1 + 3 files changed, 249 insertions(+) create mode 100644 standalone-example.js create mode 100644 tests/parallel/test-standalone-example.js diff --git a/README.md b/README.md index d6ba3be..6c7fce0 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,10 @@ This should now be usable in any node application, it now supports (nearly) ever 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. +## Standalone usage + +While this module is intended to be used as a replacement for the internal Node.js parser, it can be used as a standalone parser. The [`standalone-example.js`](standalone-example.js) demonstrates how to use the somewhat awkward API (coming from compatibility with the Node.js internals) to parse HTTP from raw Buffers. + ## License MIT. diff --git a/standalone-example.js b/standalone-example.js new file mode 100644 index 0000000..9f5c652 --- /dev/null +++ b/standalone-example.js @@ -0,0 +1,244 @@ +/* + The following is an example of how to use the parser as a standalone module. + Both parseRequest and parseResponse can be used to parse a raw HTTP request/response from a Buffer. +*/ + +const { deepStrictEqual, throws } = require('assert'); +// Replace the require when using the module from npm. +//const { HTTPParser } = require('http-parser-js'); +const { HTTPParser } = require('./http-parser.js'); + +function parseRequest(input) { + const parser = new HTTPParser(HTTPParser.REQUEST); + let complete = false; + let shouldKeepAlive; + let upgrade; + let method; + let url; + let versionMajor; + let versionMinor; + let headers = []; + let trailers = []; + let bodyChunks = []; + + parser[HTTPParser.kOnHeadersComplete] = function (req) { + shouldKeepAlive = req.shouldKeepAlive; + upgrade = req.upgrade; + method = HTTPParser.methods[req.method]; + url = req.url; + versionMajor = req.versionMajor; + versionMinor = req.versionMinor; + headers = req.headers; + }; + + parser[HTTPParser.kOnBody] = function (chunk, offset, length) { + bodyChunks.push(chunk.slice(offset, offset + length)); + }; + + // This is actually the event for trailers, go figure. + parser[HTTPParser.kOnHeaders] = function (t) { + trailers = t; + }; + + parser[HTTPParser.kOnMessageComplete] = function () { + complete = true; + }; + + // Since we are sending the entire Buffer at once here all callbacks above happen synchronously. + // The parser does not do _anything_ asynchronous. + // However, you can of course call execute() multiple times with multiple chunks, e.g. from a stream. + // But then you have to refactor the entire logic to be async (e.g. resolve a Promise in kOnMessageComplete and add timeout logic). + parser.execute(input); + parser.finish(); + + if (!complete) { + throw new Error('Could not parse request'); + } + + let body = Buffer.concat(bodyChunks); + + return { + shouldKeepAlive, + upgrade, + method, + url, + versionMajor, + versionMinor, + headers, + body, + trailers, + }; +} + +function parseResponse(input) { + const parser = new HTTPParser(HTTPParser.RESPONSE); + let complete = false; + let shouldKeepAlive; + let upgrade; + let statusCode; + let statusMessage; + let versionMajor; + let versionMinor; + let headers = []; + let trailers = []; + let bodyChunks = []; + + parser[HTTPParser.kOnHeadersComplete] = function (res) { + shouldKeepAlive = res.shouldKeepAlive; + upgrade = res.upgrade; + statusCode = res.statusCode; + statusMessage = res.statusMessage; + versionMajor = res.versionMajor; + versionMinor = res.versionMinor; + headers = res.headers; + }; + + parser[HTTPParser.kOnBody] = function (chunk, offset, length) { + bodyChunks.push(chunk.slice(offset, offset + length)); + }; + + // This is actually the event for trailers, go figure. + parser[HTTPParser.kOnHeaders] = function (t) { + trailers = t; + }; + + parser[HTTPParser.kOnMessageComplete] = function () { + complete = true; + }; + + // Since we are sending the entire Buffer at once here all callbacks above happen synchronously. + // The parser does not do _anything_ asynchronous. + // However, you can of course call execute() multiple times with multiple chunks, e.g. from a stream. + // But then you have to refactor the entire logic to be async (e.g. resolve a Promise in kOnMessageComplete and add timeout logic). + parser.execute(input); + parser.finish(); + + if (!complete) { + throw new Error('Could not parse'); + } + + let body = Buffer.concat(bodyChunks); + + return { + shouldKeepAlive, + upgrade, + statusCode, + statusMessage, + versionMajor, + versionMinor, + headers, + body, + trailers, + }; +} + +let parsed; + +console.log('Example: basic GET request:'); + +parsed = parseRequest( + Buffer.from(`GET / HTTP/1.1 +Host: www.example.com + +`) +); + +console.log(parsed); + +deepStrictEqual(parsed.shouldKeepAlive, true); +deepStrictEqual(parsed.upgrade, false); +deepStrictEqual(parsed.method, 'GET'); +deepStrictEqual(parsed.url, '/'); +deepStrictEqual(parsed.versionMajor, 1); +deepStrictEqual(parsed.versionMinor, 1); +deepStrictEqual(parsed.headers, ['Host', 'www.example.com']); +deepStrictEqual(parsed.body.toString(), ''); +deepStrictEqual(parsed.trailers, []); + +console.log('Example: POST request with body:'); + +parsed = parseRequest( + Buffer.from(`POST /memes HTTP/1.1 +Host: www.example.com +Content-Length: 7 +Content-Type: text/plain + +foo bar +`) +); + +console.log(parsed); + +deepStrictEqual(parsed.shouldKeepAlive, true); +deepStrictEqual(parsed.upgrade, false); +deepStrictEqual(parsed.method, 'POST'); +deepStrictEqual(parsed.url, '/memes'); +deepStrictEqual(parsed.versionMajor, 1); +deepStrictEqual(parsed.versionMinor, 1); +deepStrictEqual(parsed.headers, ['Host', 'www.example.com', 'Content-Length', '7', 'Content-Type', 'text/plain']); +deepStrictEqual(parsed.body.toString(), 'foo bar'); +deepStrictEqual(parsed.trailers, []); + +console.log('Example: basic HTML response'); + +parsed = parseResponse( + Buffer.from(`HTTP/1.1 404 Not Found +Content-Type: text/html +Content-Length: 33 + +Computer says no +`) +); + +console.log(parsed); + +deepStrictEqual(parsed.shouldKeepAlive, true); +deepStrictEqual(parsed.upgrade, false); +deepStrictEqual(parsed.statusCode, 404); +deepStrictEqual(parsed.statusMessage, 'Not Found'); +deepStrictEqual(parsed.versionMajor, 1); +deepStrictEqual(parsed.versionMinor, 1); +deepStrictEqual(parsed.headers, ['Content-Type', 'text/html', 'Content-Length', '33']); +deepStrictEqual(parsed.body.toString(), 'Computer says no'); +deepStrictEqual(parsed.trailers, []); + +console.log('Example: chunked response with trailers'); + +parsed = parseResponse( + Buffer.from(`HTTP/1.1 200 OK +Content-Type: text/plain +Transfer-Encoding: chunked +Trailer: Expires + +7 +Mozilla +9 +Developer +7 +Network +0 +Expires: Wed, 21 Oct 2015 07:28:00 GMT + +`) +); + +console.log(parsed); + +deepStrictEqual(parsed.shouldKeepAlive, true); +deepStrictEqual(parsed.upgrade, false); +deepStrictEqual(parsed.statusCode, 200); +deepStrictEqual(parsed.statusMessage, 'OK'); +deepStrictEqual(parsed.versionMajor, 1); +deepStrictEqual(parsed.versionMinor, 1); +deepStrictEqual(parsed.headers, ['Content-Type', 'text/plain', 'Transfer-Encoding', 'chunked', 'Trailer', 'Expires']); +deepStrictEqual(parsed.body.toString(), 'MozillaDeveloperNetwork'); +deepStrictEqual(parsed.trailers, ['Expires', 'Wed, 21 Oct 2015 07:28:00 GMT']); + +throws(function () { + parseResponse( + Buffer.from(`HTTP/1.1 200 OK +Content-Length: 1 + +`) + ); +}); diff --git a/tests/parallel/test-standalone-example.js b/tests/parallel/test-standalone-example.js new file mode 100644 index 0000000..cb8d49f --- /dev/null +++ b/tests/parallel/test-standalone-example.js @@ -0,0 +1 @@ +require('../../standalone-example.js'); \ No newline at end of file From 8a13b79e0d9ddadbb0997eaf3d375aa5bc89e0f1 Mon Sep 17 00:00:00 2001 From: Jimb Esser Date: Sun, 6 Mar 2022 08:19:15 -0800 Subject: [PATCH 28/38] Publish to NPM as v0.5.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0e94037..1aaa908 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "http-parser-js", - "version": "0.5.5", + "version": "0.5.6", "description": "A pure JS HTTP parser for node.", "main": "http-parser.js", "scripts": { From ad2ac404ebc574609ce102f0d2946578aa67868e Mon Sep 17 00:00:00 2001 From: masx200 <34191203+masx200@users.noreply.github.com> Date: Tue, 21 Jun 2022 21:05:53 +0800 Subject: [PATCH 29/38] types declare in package.json (#91) --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 1aaa908..49d12ba 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "0.5.6", "description": "A pure JS HTTP parser for node.", "main": "http-parser.js", + "types": "http-parser.d.ts", "scripts": { "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" From c8f3c7b3e4f607bb3f2abbc251e075e322f3940e Mon Sep 17 00:00:00 2001 From: masx200 <34191203+masx200@users.noreply.github.com> Date: Wed, 22 Jun 2022 01:09:41 +0800 Subject: [PATCH 30/38] Add new http method `SOURCE` (#90) --- http-parser.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/http-parser.js b/http-parser.js index 2ce829d..6c687b8 100644 --- a/http-parser.js +++ b/http-parser.js @@ -89,7 +89,8 @@ var methods = exports.methods = HTTPParser.methods = [ 'PURGE', 'MKCALENDAR', 'LINK', - 'UNLINK' + 'UNLINK', + 'SOURCE', ]; var method_connect = methods.indexOf('CONNECT'); HTTPParser.prototype.reinitialize = HTTPParser; From 278ebfe402c4054eca1e0b19cb632bab8bae6b1a Mon Sep 17 00:00:00 2001 From: masx200 <34191203+masx200@users.noreply.github.com> Date: Thu, 23 Jun 2022 09:12:24 +0800 Subject: [PATCH 31/38] Update http-parser.d.ts --- http-parser.d.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/http-parser.d.ts b/http-parser.d.ts index 8e9ee33..6a50c97 100644 --- a/http-parser.d.ts +++ b/http-parser.d.ts @@ -48,14 +48,14 @@ type StateFinishAllowedKey = | 'RESPONSE_LINE' | 'BODY_RAW' -type HeaderObject = Record +type HeaderObject = Array type noop = ()=> T type HeaderInfo
= { versionMajor: number versionMinor: number headers: HEADER - method: RequestMethod + method: number url: string statusCode: number statusMessage: string @@ -68,7 +68,7 @@ export type OnHeadersCompleteParser
Date: Thu, 23 Jun 2022 05:35:44 -0700 Subject: [PATCH 32/38] Publish to NPM as v0.5.7 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 49d12ba..080e444 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "http-parser-js", - "version": "0.5.6", + "version": "0.5.7", "description": "A pure JS HTTP parser for node.", "main": "http-parser.js", "types": "http-parser.d.ts", From 160b8956621f6f827c451d62bd2060d473d23fcd Mon Sep 17 00:00:00 2001 From: masx200 <34191203+masx200@users.noreply.github.com> Date: Mon, 27 Jun 2022 10:28:29 +0800 Subject: [PATCH 33/38] Update http-parser.js --- http-parser.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/http-parser.js b/http-parser.js index 6c687b8..8f9e669 100644 --- a/http-parser.js +++ b/http-parser.js @@ -10,6 +10,7 @@ function HTTPParser(type) { } else { this.initialize(type); } + this.maxHeaderSize=HTTPParser.maxHeaderSize } HTTPParser.prototype.initialize = function (type, async_resource) { assert.ok(type === HTTPParser.REQUEST || type === HTTPParser.RESPONSE); @@ -136,7 +137,7 @@ HTTPParser.prototype.execute = function (chunk, start, length) { length = this.offset - start; if (headerState[this.state]) { this.headerSize += length; - if (this.headerSize > HTTPParser.maxHeaderSize) { + if (this.headerSize > (this.maxHeaderSize||HTTPParser.maxHeaderSize)) { return new Error('max header size exceeded'); } } From 88c665381470e27cd428a728447a13dce198f782 Mon Sep 17 00:00:00 2001 From: Jimb Esser Date: Mon, 27 Jun 2022 05:52:08 -0700 Subject: [PATCH 34/38] Publish to NPM as v0.5.8 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 080e444..d90bb63 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "http-parser-js", - "version": "0.5.7", + "version": "0.5.8", "description": "A pure JS HTTP parser for node.", "main": "http-parser.js", "types": "http-parser.d.ts", From 48c3de14280d86633b3ef1fe2b939602a4433a0a Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Fri, 3 Jan 2025 13:29:11 -0500 Subject: [PATCH 35/38] Remove assert --- http-parser.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/http-parser.js b/http-parser.js index 8f9e669..bfe9bad 100644 --- a/http-parser.js +++ b/http-parser.js @@ -1,10 +1,10 @@ /*jshint node:true */ -var assert = require('assert'); - exports.HTTPParser = HTTPParser; function HTTPParser(type) { - assert.ok(type === HTTPParser.REQUEST || type === HTTPParser.RESPONSE || type === undefined); + if (type !== undefined && type !== HTTPParser.REQUEST && type !== HTTPParser.RESPONSE) { + throw new Error('type must be REQUEST or RESPONSE'); + } if (type === undefined) { // Node v12+ } else { @@ -13,7 +13,9 @@ function HTTPParser(type) { this.maxHeaderSize=HTTPParser.maxHeaderSize } HTTPParser.prototype.initialize = function (type, async_resource) { - assert.ok(type === HTTPParser.REQUEST || type === HTTPParser.RESPONSE); + if (type !== HTTPParser.REQUEST && type !== HTTPParser.RESPONSE) { + throw new Error('type must be REQUEST or RESPONSE'); + } this.type = type; this.state = type + '_LINE'; this.info = { @@ -405,7 +407,9 @@ HTTPParser.prototype.BODY_CHUNKEMPTYLINE = function () { if (line === undefined) { return; } - assert.equal(line, ''); + if (line !== '') { + throw new Error('Expected empty line'); + } this.state = 'BODY_CHUNKHEAD'; }; From 9e5daea5edf0236dc3d1ac5e7f0760711f061b7f Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Fri, 3 Jan 2025 01:45:26 -0500 Subject: [PATCH 36/38] Fix body parsing This was broken by: https://github.com/nodejs/node/commit/b970634cfe42df09206846a438f17476c57f36f1 --- http-parser.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/http-parser.js b/http-parser.js index bfe9bad..3e98ab9 100644 --- a/http-parser.js +++ b/http-parser.js @@ -100,6 +100,7 @@ HTTPParser.prototype.reinitialize = HTTPParser; HTTPParser.prototype.close = HTTPParser.prototype.pause = HTTPParser.prototype.resume = +HTTPParser.prototype.remove = HTTPParser.prototype.free = function () {}; HTTPParser.prototype._compatMode0_11 = false; HTTPParser.prototype.getAsyncId = function() { return 0; }; @@ -394,7 +395,8 @@ HTTPParser.prototype.BODY_CHUNKHEAD = function () { HTTPParser.prototype.BODY_CHUNK = function () { var length = Math.min(this.end - this.offset, this.body_bytes); - this.userCall()(this[kOnBody](this.chunk, this.offset, length)); + // 0, length are for backwards compatibility. See: https://github.com/creationix/http-parser-js/pull/98 + this.userCall()(this[kOnBody](this.chunk.slice(this.offset, this.offset + length), 0, length)); this.offset += length; this.body_bytes -= length; if (!this.body_bytes) { @@ -429,14 +431,15 @@ HTTPParser.prototype.BODY_CHUNKTRAILERS = function () { }; HTTPParser.prototype.BODY_RAW = function () { - var length = this.end - this.offset; - this.userCall()(this[kOnBody](this.chunk, this.offset, length)); + // 0, length are for backwards compatibility. See: https://github.com/creationix/http-parser-js/pull/98 + this.userCall()(this[kOnBody](this.chunk.slice(this.offset, this.end), 0, this.end - this.offset)); this.offset = this.end; }; HTTPParser.prototype.BODY_SIZED = function () { var length = Math.min(this.end - this.offset, this.body_bytes); - this.userCall()(this[kOnBody](this.chunk, this.offset, length)); + // 0, length are for backwards compatibility. See: https://github.com/creationix/http-parser-js/pull/98 + this.userCall()(this[kOnBody](this.chunk.slice(this.offset, this.offset + length), 0, length)); this.offset += length; this.body_bytes -= length; if (!this.body_bytes) { From ebbeba4838c8939a6e17f984f326498156c68313 Mon Sep 17 00:00:00 2001 From: Alex Potsides Date: Tue, 8 Apr 2025 07:05:56 +0100 Subject: [PATCH 37/38] Add maxHeaderSize to types Adds the `.maxHeaderSize` field to the types of the `HTTPParserJS` class, otherwise tsc complains that the field doesn't exist. Fixes this sort of thing: ```ts const parser = new HTTPParser('RESPONSE') parser.maxHeaderSize = 1024 * 1024 // error TS2339: Property 'maxHeaderSize' does not exist on type 'HTTPParserJS' ``` --- http-parser.d.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/http-parser.d.ts b/http-parser.d.ts index 6a50c97..9a8ac57 100644 --- a/http-parser.d.ts +++ b/http-parser.d.ts @@ -92,6 +92,12 @@ declare class HTTPParserJS { [HTTPParser.kOnBody]: OnBodyParser [HTTPParser.kOnMessageComplete]: noop + /** + * Max number of bytes that will be parsed as headers, 80kb by default + * @default 81920 + */ + maxHeaderSize: number + reinitialize: HTTPParserConstructor close: noop pause: noop From ed2a08c59f221e6d1d0fec664df28e8adac5e0bd Mon Sep 17 00:00:00 2001 From: Jimb Esser Date: Thu, 9 Jan 2025 12:42:58 -0800 Subject: [PATCH 38/38] Publish to NPM as v0.5.10 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d90bb63..f1a1c24 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "http-parser-js", - "version": "0.5.8", + "version": "0.5.10", "description": "A pure JS HTTP parser for node.", "main": "http-parser.js", "types": "http-parser.d.ts",