Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
ddabe91
remove anti-pattern dispatcher hooks (#2723)
ronag Feb 10, 2024
56b410f
chore: upgrade llhttp to 9.2.0 (#2705)
Uzlopak Feb 9, 2024
6c40513
refactor(#2722)!: Drop Interceptors support (#2754)
metcoder95 Feb 18, 2024
d1c7ff0
fix: remove more interceptor stuff (#2794)
ronag Feb 21, 2024
31fb862
chore: remove sinon dev dependency (#2767)
Uzlopak Feb 15, 2024
740d473
tests: skip test/node-test/debug on node 21.6.2 and windows (#2765)
Uzlopak Feb 17, 2024
bd8f735
chore: improve usage of skip in tests (#2761)
Uzlopak Feb 17, 2024
6824d5b
feat: improve mock error breadcrumbs (#2774)
rossilor95 Feb 17, 2024
4eeae9e
expose MessageEvent in fetch bundle (#2770)
KhafraDev Feb 17, 2024
4a38549
test: always exit with 0 when running in Node's Daily WPT Report CI j…
panva Feb 18, 2024
12c22ad
fix: add node prefix for util to fix issue in env with min version no…
riderx Feb 18, 2024
917ace0
perf: improve perf of parseRawHeaders (#2781)
Uzlopak Feb 19, 2024
ec405e4
fix: make mock-agent.js test more resilient (#2780)
Uzlopak Feb 19, 2024
d2d3630
chore: make some test run even without internet connection (#2786)
Uzlopak Feb 20, 2024
11b67df
mock: improve validateReplyParameters (#2783)
Uzlopak Feb 20, 2024
b798f30
perf: improve TernarySearchTree (#2782)
Uzlopak Feb 20, 2024
c4b22ef
fix: convert HeadersInit to sequence/dictionary correctly (#2784)
KhafraDev Feb 20, 2024
5594a4c
chore: improve getFieldValue (#2785)
Uzlopak Feb 20, 2024
3f08197
Add RetryHandler to sidebar (#2797)
mcollina Feb 21, 2024
ae2b232
Add RetryAgent (#2798)
mcollina Feb 21, 2024
8709f6c
build(deps): bump step-security/harden-runner from 2.6.0 to 2.7.0 (#2…
dependabot[bot] Feb 21, 2024
01edc6e
build(deps): bump actions/checkout from 4.1.0 to 4.1.1 (#2393)
dependabot[bot] Feb 21, 2024
a43ad89
build(deps): bump actions/upload-artifact from 3.1.3 to 4.3.1 (#2799)
dependabot[bot] Feb 21, 2024
7550212
build(deps): bump node from 20-alpine to 21-alpine in /build (#2803)
dependabot[bot] Feb 21, 2024
8f3ef5a
perf: improve sort algorithm (#2756)
tsctx Feb 21, 2024
9668e5e
refactor: move web stuff into their own folder (#2793)
ronag Feb 21, 2024
f5ddb17
`s/ dispactgher/dispatcher/` (#2807)
steveluscher Feb 22, 2024
cba8e0f
Use paralellelRequests instead of connections to calculate req/sec in…
mcollina Feb 22, 2024
649185a
Dispatch compose (#2795)
ronag Feb 23, 2024
8a940ec
Revert "Dispatch compose (#2795)"
ronag Feb 23, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/bench.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
persist-credentials: false
ref: ${{ github.base_ref }}
Expand All @@ -30,7 +30,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
persist-credentials: false
- name: Setup Node
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ jobs:

steps:
- name: Harden Runner
uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0
uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
with:
egress-policy: audit

- name: Checkout repository
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/dependency-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0
uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
with:
egress-policy: audit

- name: 'Checkout Repository'
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: 'Dependency Review'
uses: actions/dependency-review-action@4901385134134e04cec5fbe5ddfe3b2c5bd5d976 # v4.0.0
4 changes: 2 additions & 2 deletions .github/workflows/fuzz.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
persist-credentials: false

Expand All @@ -29,7 +29,7 @@ jobs:
run: |
npm run fuzz

- uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
- uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
if: ${{ failure() }}
with:
name: undici-fuzz-results-${{ github.sha }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
persist-credentials: false
- uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish-undici-types.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1
with:
node-version: '16.x'
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/scorecard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:

steps:
- name: "Checkout code"
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
persist-credentials: false

Expand All @@ -43,7 +43,7 @@ jobs:
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: SARIF file
path: results.sarif
Expand Down
38 changes: 23 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,31 @@ npm i undici

## Benchmarks

The benchmark is a simple `hello world` [example](benchmarks/benchmark.js) using a
50 TCP connections with a pipelining depth of 10 running on Node 20.10.0.
The benchmark is a simple `hello world` [example](benchmarks/benchmark.js) using:

* 50 TCP connections
* A pipelining factor of 10 for undici
* 200 parallel requests issued per iteration (sample)

The benchmark was run on Linux on top of Node 20.10.0.

```
│ Tests │ Samples │ Result │ Tolerance │ Difference with slowest │
|─────────────────────|─────────|─────────────────|───────────|─────────────────────────|
│ got │ 45 │ 1661.71 req/sec │ ± 2.93 % │ - │
│ node-fetch │ 20 │ 2164.81 req/sec │ ± 2.63 % │ + 30.28 % │
│ undici - fetch │ 35 │ 2274.27 req/sec │ ± 2.70 % │ + 36.86 % │
│ http - no keepalive │ 15 │ 2376.04 req/sec │ ± 2.99 % │ + 42.99 % │
│ axios │ 25 │ 2612.93 req/sec │ ± 2.89 % │ + 57.24 % │
│ request │ 40 │ 2712.19 req/sec │ ± 2.92 % │ + 63.22 % │
│ http - keepalive │ 45 │ 4393.25 req/sec │ ± 2.86 % │ + 164.38 % │
│ undici - pipeline │ 45 │ 5484.69 req/sec │ ± 2.87 % │ + 230.06 % │
│ undici - request │ 55 │ 7773.98 req/sec │ ± 2.93 % │ + 367.83 % │
│ undici - stream │ 70 │ 8425.96 req/sec │ ± 2.91 % │ + 407.07 % │
│ undici - dispatch │ 50 │ 9488.99 req/sec │ ± 2.85 % │ + 471.04 % │
┌─────────┬───────────────────────┬─────────┬────────────────────┬────────────┬─────────────────────────┐
│ (index) │ Tests │ Samples │ Result │ Tolerance │ Difference with slowest │
├─────────┼───────────────────────┼─────────┼────────────────────┼────────────┼─────────────────────────┤
│ 0 │ 'got' │ 25 │ '3444.59 req/sec' │ '± 2.88 %' │ '-' │
│ 1 │ 'node-fetch' │ 20 │ '4927.30 req/sec' │ '± 2.46 %' │ '+ 43.04 %' │
│ 2 │ 'undici - fetch' │ 10 │ '5043.80 req/sec' │ '± 1.87 %' │ '+ 46.43 %' │
│ 3 │ 'request' │ 35 │ '6389.13 req/sec' │ '± 2.93 %' │ '+ 85.48 %' │
│ 4 │ 'axios' │ 25 │ '6920.61 req/sec' │ '± 2.76 %' │ '+ 100.91 %' │
│ 5 │ 'http - no keepalive' │ 10 │ '9357.37 req/sec' │ '± 2.24 %' │ '+ 171.65 %' │
│ 6 │ 'http - keepalive' │ 30 │ '9921.36 req/sec' │ '± 2.83 %' │ '+ 188.03 %' │
│ 7 │ 'superagent' │ 10 │ '10118.35 req/sec' │ '± 2.18 %' │ '+ 193.75 %' │
│ 8 │ 'undici - pipeline' │ 10 │ '17106.69 req/sec' │ '± 1.46 %' │ '+ 396.62 %' │
│ 9 │ 'undici - request' │ 20 │ '21611.80 req/sec' │ '± 2.50 %' │ '+ 527.41 %' │
│ 10 │ 'undici - stream' │ 10 │ '24282.13 req/sec' │ '± 1.94 %' │ '+ 604.94 %' │
│ 11 │ 'undici - dispatch' │ 20 │ '24441.95 req/sec' │ '± 2.68 %' │ '+ 609.58 %' │
└─────────┴───────────────────────┴─────────┴────────────────────┴────────────┴─────────────────────────┘
```

## Quick Start
Expand Down
20 changes: 20 additions & 0 deletions benchmarks/TernarySearchTree.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { bench, group, run } from 'mitata'
import { tree } from '../lib/core/tree.js'

const contentLength = Buffer.from('Content-Length')
const contentLengthUpperCase = Buffer.from('Content-Length'.toUpperCase())
const contentLengthLowerCase = Buffer.from('Content-Length'.toLowerCase())

group('tree.search', () => {
bench('content-length', () => {
tree.lookup(contentLengthLowerCase)
})
bench('CONTENT-LENGTH', () => {
tree.lookup(contentLengthUpperCase)
})
bench('Content-Length', () => {
tree.lookup(contentLength)
})
})

await run()
37 changes: 6 additions & 31 deletions benchmarks/benchmark-http2.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
'use strict'

const { connect } = require('node:http2')
const { createSecureContext } = require('node:tls')
const os = require('node:os')
const path = require('node:path')
const { readFileSync } = require('node:fs')
Expand Down Expand Up @@ -48,11 +46,6 @@ const httpsBaseOptions = {
...dest
}

const http2ClientOptions = {
secureContext: createSecureContext({ ca }),
servername
}

const undiciOptions = {
path: '/',
method: 'GET',
Expand Down Expand Up @@ -113,7 +106,9 @@ class SimpleRequest {
}

function makeParallelRequests (cb) {
return Promise.all(Array.from(Array(parallelRequests)).map(() => new Promise(cb)))
const res = Promise.all(Array.from(Array(parallelRequests)).map(() => new Promise(cb)))
res.catch(console.error)
return res
}

function printResults (results) {
Expand Down Expand Up @@ -143,10 +138,12 @@ function printResults (results) {
last = mean
}

console.log(mean)

return {
Tests: name,
Samples: size,
Result: `${((connections * 1e9) / mean).toFixed(2)} req/sec`,
Result: `${((1e9 * parallelRequests) / mean).toFixed(2)} req/sec`,
Tolerance: `± ${((standardError / mean) * 100).toFixed(2)} %`,
'Difference with slowest': relative > 0 ? `+ ${relative.toFixed(2)} %` : '-'
}
Expand All @@ -156,28 +153,6 @@ function printResults (results) {
}

const experiments = {
'http2 - request' () {
return makeParallelRequests(resolve => {
connect(dest.url, http2ClientOptions, (session) => {
const headers = {
':path': '/',
':method': 'GET',
':scheme': 'https',
':authority': `localhost:${dest.port}`
}

const request = session.request(headers)

request.pipe(
new Writable({
write (chunk, encoding, callback) {
callback()
}
})
).on('finish', resolve)
})
})
},
'undici - pipeline' () {
return makeParallelRequests(resolve => {
dispatcher
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/benchmark-https.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ function printResults (results) {
return {
Tests: name,
Samples: size,
Result: `${((connections * 1e9) / mean).toFixed(2)} req/sec`,
Result: `${((parallelRequests * 1e9) / mean).toFixed(2)} req/sec`,
Tolerance: `± ${((standardError / mean) * 100).toFixed(2)} %`,
'Difference with slowest': relative > 0 ? `+ ${relative.toFixed(2)} %` : '-'
}
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/benchmark.js
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ function printResults (results) {
return {
Tests: name,
Samples: size,
Result: `${((connections * 1e9) / mean).toFixed(2)} req/sec`,
Result: `${((parallelRequests * 1e9) / mean).toFixed(2)} req/sec`,
Tolerance: `± ${((standardError / mean) * 100).toFixed(2)} %`,
'Difference with slowest': relative > 0 ? `+ ${relative.toFixed(2)} %` : '-'
}
Expand Down
23 changes: 23 additions & 0 deletions benchmarks/cacheGetFieldValues.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { bench, group, run } from 'mitata'
import { getFieldValues } from '../lib/web/cache/util.js'

const values = [
'',
'foo',
'invälid',
'foo, ',
'foo, bar',
'foo, bar, baz',
'foo, bar, baz, ',
'foo, bar, baz, , '
]

group('getFieldValues', () => {
bench('getFieldValues', () => {
for (let i = 0; i < values.length; ++i) {
getFieldValues(values[i])
}
})
})

await run()
56 changes: 56 additions & 0 deletions benchmarks/headers-length32.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { bench, run } from 'mitata'
import { Headers } from '../lib/web/fetch/headers.js'

const headers = new Headers(
[
'Origin-Agent-Cluster',
'RTT',
'Accept-CH-Lifetime',
'X-Frame-Options',
'Sec-CH-UA-Platform-Version',
'Digest',
'Cache-Control',
'Sec-CH-UA-Platform',
'If-Range',
'SourceMap',
'Strict-Transport-Security',
'Want-Digest',
'Cross-Origin-Resource-Policy',
'Width',
'Accept-CH',
'Via',
'Refresh',
'Server',
'Sec-Fetch-Dest',
'Sec-CH-UA-Model',
'Access-Control-Request-Method',
'Access-Control-Request-Headers',
'Date',
'Expires',
'DNT',
'Proxy-Authorization',
'Alt-Svc',
'Alt-Used',
'ETag',
'Sec-Fetch-User',
'Sec-CH-UA-Full-Version-List',
'Referrer-Policy'
].map((v) => [v, ''])
)

const kHeadersList = Reflect.ownKeys(headers).find(
(c) => String(c) === 'Symbol(headers list)'
)

const headersList = headers[kHeadersList]

const kHeadersSortedMap = Reflect.ownKeys(headersList).find(
(c) => String(c) === 'Symbol(headers map sorted)'
)

bench('Headers@@iterator', () => {
headersList[kHeadersSortedMap] = null
return [...headers]
})

await run()
57 changes: 57 additions & 0 deletions benchmarks/headers.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { bench, group, run } from 'mitata'
import { Headers } from '../lib/web/fetch/headers.js'

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 settings = {
'fast-path (tiny array)': 4,
'fast-path (small array)': 8,
'fast-path (middle array)': 16,
'fast-path': 32,
'slow-path': 64
}

for (const [name, length] of Object.entries(settings)) {
const headers = new Headers(
Array.from(Array(length), () => [generateAsciiString(12), ''])
)

const headersSorted = new Headers(headers)

const kHeadersList = Reflect.ownKeys(headers).find(
(c) => String(c) === 'Symbol(headers list)'
)

const headersList = headers[kHeadersList]

const headersListSorted = headersSorted[kHeadersList]

const kHeadersSortedMap = Reflect.ownKeys(headersList).find(
(c) => String(c) === 'Symbol(headers map sorted)'
)

group(`length ${length} #${name}`, () => {
bench('Headers@@iterator', () => {
// prevention of memoization of results
headersList[kHeadersSortedMap] = null
return [...headers]
})

bench('Headers@@iterator (sorted)', () => {
// prevention of memoization of results
headersListSorted[kHeadersSortedMap] = null
return [...headersSorted]
})
})
}

await run()
Loading