Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
154 changes: 153 additions & 1 deletion deps/undici/src/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
* [Building for externally shared node builtins](#external-builds)
* [Benchmarks](#benchmarks)
* [Documentation](#documentation)
* [Reproduction](#reproduction)
* [Developer's Certificate of Origin 1.1](#developers-certificate-of-origin)
* [Moderation Policy](#moderation-policy)

Expand Down Expand Up @@ -58,7 +59,7 @@ npm i
> This requires [docker](https://www.docker.com/) installed on your machine.

```bash
npm run build-wasm
npm run build:wasm
```

#### Copy the sources to `undici`
Expand Down Expand Up @@ -198,6 +199,157 @@ cd docs && npm i && npm run serve

The documentation will be available at `http://localhost:3000`.

<a id="reproduction"></a>
### Reproduction

When reporting a bug, a high-quality reproduction helps maintainers diagnose and fix the issue quickly. Follow the guidelines below to create a minimal, standalone reproduction script.

#### What Makes a Good Reproduction

- **Standalone**: The script must be self-contained and run without external dependencies beyond `undici` and Node.js built-in modules.
- **Minimal**: Strip everything unrelated to the bug — no production configs, no frameworks, no unnecessary middleware.
- **Reproducible**: Run the script and confirm the bug occurs before submitting.
- **Specific**: Include the exact `undici` API call that triggers the issue (e.g., `Client`, `Pool`, `Agent`, `fetch`, `request`, `stream`, `pipeline`).
- **Run with Node's test runner**: Use `node --test` (Node 20+) or `node test/your-repro.js`.

#### Standalone Server Pattern

Use `createServer` from `node:http` (or `node:https` for TLS-related bugs) to run a local server inside the same script. This avoids external service dependencies and keeps the reproduction fully self-contained.

```javascript
'use strict'

const { test } = require('node:test')
const { createServer } = require('node:http')
const { once } = require('node:events')
const { Client } = require('undici')

// https://github.com/nodejs/undici/issues/XXXX

test('short description of the bug', { timeout: 60000 }, async (t) => {
t.plan(1)

// 1. Start a local HTTP server that reproduces the scenario
const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {
// Simulate the bug-triggering response
res.statusCode = 200
res.setHeader('content-length', 100)
res.end('hello'.repeat(20))
})
t.after(() => { server.closeAllConnections?.(); server.close() })

server.listen(0)
await once(server, 'listening')

// 2. Create the undici client pointing to the local server
const client = new Client(`http://localhost:${server.address().port}`)
t.after(() => client.close())

// 3. Perform the request that triggers the bug
const { body } = await client.request({ path: '/', method: 'GET' })

let responseBody = ''
for await (const chunk of body) {
responseBody += chunk
}

t.assert.strictEqual(responseBody, 'hello'.repeat(20))
})
```

Save the script as `test/repro-XXXX.js` and run it:

```bash
node --test test/repro-XXXX.js
```

#### Fetch-Based Reproduction

For bugs involving `fetch`, use the same standalone server pattern:

```javascript
'use strict'

const { test } = require('node:test')
const { createServer } = require('node:http')
const { once } = require('node:events')
const { fetch, Agent } = require('undici')

// https://github.com/nodejs/undici/issues/XXXX

test('short description of the fetch bug', { timeout: 60000 }, async (t) => {
t.plan(1)

const server = createServer({ joinDuplicateHeaders: true }, async (req, res) => {
res.statusCode = 200
res.setHeader('content-type', 'application/json')
res.end(JSON.stringify({ ok: true }))
})
t.after(() => server.close())

server.listen(0)
await once(server, 'listening')

const agent = new Agent({ keepAliveTimeout: 1 })
t.after(() => agent.close())

const response = await fetch(`http://localhost:${server.address().port}/`, {
dispatcher: agent
})

await response.body.cancel()

t.assert.strictEqual(response.status, 200)
})
```

#### Using Pool or Agent

For bugs involving connection pooling or agent behavior:

```javascript
'use strict'

const { test } = require('node:test')
const { createServer } = require('node:http')
const { once } = require('node:events')
const { Pool } = require('undici')

// https://github.com/nodejs/undici/issues/XXXX

test('Pool bug reproduction', { timeout: 60000 }, async (t) => {
t.plan(1)

const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {
res.end('ok')
})
t.after(() => server.close())

server.listen(0)
await once(server, 'listening')

const pool = new Pool(`http://localhost:${server.address().port}`)
t.after(() => pool.close())

const data = await pool.request({ path: '/', method: 'GET' })
await data.body.dump()

t.assert.strictEqual(data.statusCode, 200)
})
```

#### Adding the Reproduction to the Repository

If you are submitting a bug fix via a pull request, include a test file `test/issue-XXXX.js` that reproduces the bug and passes with the fix.

#### Checklist for Submitting a Bug Report

1. [ ] Write a standalone reproduction script.
2. [ ] Run it locally to confirm the bug is reproducible.
3. [ ] Attach the script (or a gist link) in the bug report.
4. [ ] Include the exact undici version and Node.js version.
5. [ ] Describe the expected vs. actual behavior.

<a id="developers-certificate-of-origin"></a>
## Developer's Certificate of Origin 1.1

Expand Down
5 changes: 3 additions & 2 deletions deps/undici/src/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -717,8 +717,9 @@ Refs: https://tools.ietf.org/html/rfc7231#section-5.1.1
#### Pipelining

Undici will only use pipelining if configured with a `pipelining` factor
greater than `1`. Also it is important to pass `blocking: false` to the
request options to properly pipeline requests.
greater than `1`. Only enable pipelining when the remote server is trusted.
Also it is important to pass `blocking: false` to the request options to
properly pipeline requests.

Undici always assumes that connections are persistent and will immediately
pipeline requests, without checking whether the connection is persistent.
Expand Down
12 changes: 12 additions & 0 deletions deps/undici/src/SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,18 @@ lead to a loss of confidentiality, integrity, or availability.
calling `body.formData()` on untrusted responses is considered an application
responsibility, not a vulnerability in undici.

#### HTTP/1.1 keep-alive with untrusted servers

* HTTP/1.1 responses on a reused connection are ordered, but they do not carry a
request identifier. Once a request has been written on a reused connection,
a well-formed response sent by the server is the response to that next request
from the client's point of view. Undici avoids immediate reuse of
non-pipelined HTTP/1.1 connections when callers are already waiting to send
more work, but applications that require per-request connection isolation for
untrusted or user-controlled servers should disable reuse by using
`pipelining: 0` or `maxRequestsPerClient: 1`, or use a protocol with response
correlation such as HTTP/2.

#### Application Misconfiguration

* Issues arising from incorrect or insecure use of undici APIs (such as
Expand Down
Loading
Loading