-
-
Notifications
You must be signed in to change notification settings - Fork 674
perf(util/parseHeaders): If the header name is buffer #2501
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
34 commits
Select commit
Hold shift + click to select a range
a8728c4
initial implementation
tsctx 8abb9c9
test: fix
tsctx 8324d21
compatible API
tsctx 0db0b0f
Merge remote-tracking branch 'origin' into perf/improve-parseheaders-…
tsctx b9fc805
fix: tree
tsctx 42b0a46
add benchmark
tsctx 559e6db
fix: lint
tsctx 007e7de
fix: benchmark
tsctx 82c198d
perf
tsctx 061cd39
use number key
tsctx 7c5f84e
remove unsafe
tsctx a6a37a7
format & add comment
tsctx 07efe2e
fix: benchmark import path
tsctx f7dbb98
better benchmark
tsctx 0e7cba8
better benchmark
tsctx 41c0e46
Merge remote-tracking branch 'origin' into perf/improve-parseheaders-…
tsctx feb97c2
perf: rewrite tree
tsctx e3dc3f1
test: fuzz test
tsctx b303023
fix test
tsctx 25f2ad0
test
tsctx ddeb169
test: remove tree
tsctx 26817f4
Merge remote-tracking branch 'origin' into perf/improve-parseheaders-…
tsctx 292db1d
refactor
tsctx f5fdfc2
refactor
tsctx bd17e95
suggested change
tsctx 1d4a848
test: refactor
tsctx b2b00fe
add use strict
tsctx 5bda182
test: refactor
tsctx f7a3b8a
add type comment
tsctx 249bc9c
check length
tsctx 7ed1720
test: perf
tsctx 9179784
improve type
tsctx e68b73d
Merge remote-tracking branch 'origin' into perf/improve-parseheaders-…
tsctx 6efff14
fix: type
tsctx File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,105 @@ | ||
| import { bench, group, run } from 'mitata' | ||
| import { parseHeaders } from '../lib/core/util.js' | ||
|
|
||
| const target = [ | ||
| { | ||
| 'Content-Type': 'application/json', | ||
| Date: 'Wed, 01 Nov 2023 00:00:00 GMT', | ||
| 'Powered-By': 'NodeJS', | ||
| 'Content-Encoding': 'gzip', | ||
| 'Set-Cookie': '__Secure-ID=123; Secure; Domain=example.com', | ||
| 'Content-Length': '150', | ||
| Vary: 'Accept-Encoding, Accept, X-Requested-With' | ||
| }, | ||
| { | ||
| 'Content-Type': 'text/html; charset=UTF-8', | ||
| 'Content-Length': '1234', | ||
| Date: 'Wed, 06 Dec 2023 12:47:57 GMT', | ||
| Server: 'Bing' | ||
| }, | ||
| { | ||
| 'Content-Type': 'image/jpeg', | ||
| 'Content-Length': '56789', | ||
| Date: 'Wed, 06 Dec 2023 12:48:12 GMT', | ||
| Server: 'Bing', | ||
| ETag: '"a1b2c3d4e5f6g7h8i9j0"' | ||
| }, | ||
| { | ||
| Cookie: 'session_id=1234567890abcdef', | ||
| 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)', | ||
| Host: 'www.bing.com', | ||
| Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', | ||
| 'Accept-Language': 'en-US,en;q=0.5', | ||
| 'Accept-Encoding': 'gzip, deflate, br' | ||
| }, | ||
| { | ||
| Location: 'https://www.bing.com/search?q=bing', | ||
| Status: '302 Found', | ||
| Date: 'Wed, 06 Dec 2023 12:48:27 GMT', | ||
| Server: 'Bing', | ||
| 'Content-Type': 'text/html; charset=UTF-8', | ||
| 'Content-Length': '0' | ||
| }, | ||
| { | ||
| 'Content-Type': | ||
| 'multipart/form-data; boundary=----WebKitFormBoundary1234567890', | ||
| 'Content-Length': '98765', | ||
| 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)', | ||
| Host: 'www.bing.com', | ||
| Accept: '*/*', | ||
| 'Accept-Language': 'en-US,en;q=0.5', | ||
| 'Accept-Encoding': 'gzip, deflate, br' | ||
| }, | ||
| { | ||
| 'Content-Type': 'application/json; charset=UTF-8', | ||
| 'Content-Length': '2345', | ||
| Date: 'Wed, 06 Dec 2023 12:48:42 GMT', | ||
| Server: 'Bing', | ||
| Status: '200 OK', | ||
| 'Cache-Control': 'no-cache, no-store, must-revalidate' | ||
| }, | ||
| { | ||
| Host: 'www.example.com', | ||
| Connection: 'keep-alive', | ||
| Accept: 'text/html, application/xhtml+xml, application/xml;q=0.9,;q=0.8', | ||
| 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)' | ||
| } | ||
| ] | ||
|
|
||
| const headers = Array.from(target, (x) => | ||
| Object.entries(x) | ||
| .flat() | ||
| .map((c) => Buffer.from(c)) | ||
| ) | ||
|
|
||
| const headersIrregular = Array.from( | ||
| target, | ||
| (x) => Object.entries(x) | ||
| .flat() | ||
| .map((c) => Buffer.from(c.toUpperCase())) | ||
| ) | ||
|
|
||
| // avoid JIT bias | ||
| bench('noop', () => {}) | ||
| bench('noop', () => {}) | ||
| bench('noop', () => {}) | ||
| bench('noop', () => {}) | ||
| bench('noop', () => {}) | ||
| bench('noop', () => {}) | ||
|
|
||
| group('parseHeaders', () => { | ||
| bench('parseHeaders', () => { | ||
| for (let i = 0; i < headers.length; ++i) { | ||
| parseHeaders(headers[i]) | ||
| } | ||
| }) | ||
| bench('parseHeaders (irregular)', () => { | ||
| for (let i = 0; i < headersIrregular.length; ++i) { | ||
| parseHeaders(headersIrregular[i]) | ||
| } | ||
| }) | ||
| }) | ||
|
|
||
| await new Promise((resolve) => setTimeout(resolve, 7000)) | ||
|
|
||
| await run() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,5 @@ | ||
| 'use strict' | ||
|
|
||
| /** @type {Record<string, string | undefined>} */ | ||
| const headerNameLowerCasedRecord = {} | ||
|
|
||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,129 @@ | ||
| 'use strict' | ||
|
|
||
| const { wellknownHeaderNames } = require('./constants') | ||
|
|
||
| class TstNode { | ||
| /** @type {any} */ | ||
| value = null | ||
| /** @type {null | TstNode} */ | ||
| left = null | ||
| /** @type {null | TstNode} */ | ||
| middle = null | ||
| /** @type {null | TstNode} */ | ||
| right = null | ||
| /** @type {number} */ | ||
| code | ||
| /** | ||
| * @param {Uint8Array} key | ||
| * @param {any} value | ||
| */ | ||
| constructor (key, value) { | ||
| if (key.length === 0) { | ||
| throw new TypeError('Unreachable') | ||
| } | ||
| this.code = key[0] | ||
| if (key.length > 1) { | ||
| this.middle = new TstNode(key.subarray(1), value) | ||
| } else { | ||
| this.value = value | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * @param {Uint8Array} key | ||
| * @param {any} value | ||
| */ | ||
| add (key, value) { | ||
| if (key.length === 0) { | ||
| throw new TypeError('Unreachable') | ||
| } | ||
| const code = key[0] | ||
| if (this.code === code) { | ||
| if (key.length === 1) { | ||
| this.value = value | ||
| } else if (this.middle !== null) { | ||
| this.middle.add(key.subarray(1), value) | ||
| } else { | ||
| this.middle = new TstNode(key.subarray(1), value) | ||
| } | ||
| } else if (this.code < code) { | ||
| if (this.left !== null) { | ||
| this.left.add(key, value) | ||
| } else { | ||
| this.left = new TstNode(key, value) | ||
| } | ||
| } else { | ||
| if (this.right !== null) { | ||
| this.right.add(key, value) | ||
| } else { | ||
| this.right = new TstNode(key, value) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * @param {Uint8Array} key | ||
| * @return {TstNode | null} | ||
| */ | ||
| search (key) { | ||
| const keylength = key.length | ||
| let index = 0 | ||
| let node = this | ||
| while (node !== null && index < keylength) { | ||
| let code = key[index] | ||
| // A-Z | ||
| if (code >= 0x41 && code <= 0x5a) { | ||
| // Lowercase for uppercase. | ||
| code |= 32 | ||
| } | ||
| while (node !== null) { | ||
| if (code === node.code) { | ||
| if (keylength === ++index) { | ||
| // Returns Node since it is the last key. | ||
| return node | ||
| } | ||
| node = node.middle | ||
| break | ||
| } | ||
| node = node.code < code ? node.left : node.right | ||
| } | ||
| } | ||
| return null | ||
| } | ||
| } | ||
|
|
||
| class TernarySearchTree { | ||
| /** @type {TstNode | null} */ | ||
| node = null | ||
|
|
||
| /** | ||
| * @param {Uint8Array} key | ||
| * @param {any} value | ||
| * */ | ||
| insert (key, value) { | ||
| if (this.node === null) { | ||
| this.node = new TstNode(key, value) | ||
| } else { | ||
| this.node.add(key, value) | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * @param {Uint8Array} key | ||
| */ | ||
| lookup (key) { | ||
| return this.node?.search(key)?.value ?? null | ||
| } | ||
| } | ||
|
|
||
| const tree = new TernarySearchTree() | ||
|
|
||
| for (let i = 0; i < wellknownHeaderNames.length; ++i) { | ||
| const key = wellknownHeaderNames[i].toLowerCase() | ||
| tree.insert(Buffer.from(key), key) | ||
| } | ||
|
|
||
| module.exports = { | ||
| TernarySearchTree, | ||
| tree | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| 'use strict' | ||
|
|
||
| const { TernarySearchTree } = require('../lib/core/tree') | ||
tsctx marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| const { test } = require('tap') | ||
|
|
||
| test('Ternary Search Tree', (t) => { | ||
| t.plan(1) | ||
| const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' | ||
| const charactersLength = characters.length | ||
|
|
||
| function generateAsciiString (length) { | ||
| let result = '' | ||
| for (let i = 0; i < length; ++i) { | ||
| result += characters[Math.floor(Math.random() * charactersLength)] | ||
| } | ||
| return result | ||
| } | ||
| const tst = new TernarySearchTree() | ||
|
|
||
| const LENGTH = 5000 | ||
|
|
||
| /** @type {string[]} */ | ||
| const random = new Array(LENGTH) | ||
| /** @type {Buffer[]} */ | ||
| const randomBuffer = new Array(LENGTH) | ||
|
|
||
| for (let i = 0; i < LENGTH; ++i) { | ||
| const key = generateAsciiString((Math.random() * 100 + 5) | 0) | ||
| const lowerCasedKey = random[i] = key.toLowerCase() | ||
| randomBuffer[i] = Buffer.from(key) | ||
| tst.insert(Buffer.from(lowerCasedKey), lowerCasedKey) | ||
| } | ||
|
|
||
| t.test('all', (t) => { | ||
| t.plan(LENGTH) | ||
| for (let i = 0; i < LENGTH; ++i) { | ||
| t.equal(tst.lookup(randomBuffer[i]), random[i]) | ||
| } | ||
| }) | ||
| }) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.