Skip to content
Merged
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
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ jobs:
fail-fast: false
max-parallel: 0
matrix:
node-version: ['22', '24', '25']
Comment thread
trivikr marked this conversation as resolved.
node-version: ['22', '24', '25', '26']
runs-on: ['ubuntu-latest', 'windows-latest', 'macos-latest']
uses: ./.github/workflows/nodejs.yml
with:
Expand All @@ -74,7 +74,7 @@ jobs:
fail-fast: false
max-parallel: 0
matrix:
node-version: ['22', '24', '25']
node-version: ['22', '24', '25', '26']
runs-on: ['ubuntu-latest']
uses: ./.github/workflows/nodejs.yml
with:
Expand Down Expand Up @@ -273,7 +273,7 @@ jobs:
# --shared-builtin-undici/undici-path still hits upstream Node.js issues there.
# Keep validating supported/current majors, and start exercising 26
# automatically once a release is available.
node-version: ['24', '25', '26']
node-version: ['24', '26']
runs-on: ['ubuntu-latest']
with:
node-version: ${{ matrix.node-version }}
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/nodejs-shared.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ jobs:
rm -rf deps/undici
./configure --shared-builtin-undici/undici-path ${{ github.workspace }}/undici/loader.js --ninja --prefix=./final
make
if grep -q '^build-ffi-tests:' Makefile; then
make build-ffi-tests
fi
make install
if make -qp | grep -q '^build-ffi-tests:'; then
make build-ffi-tests
Expand Down
106 changes: 63 additions & 43 deletions test/node-test/client-errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,33 +122,71 @@ test('GET errors and reconnect with pipelining 3', async (t) => {
await p.completed
})

function errorAndPipelining (type) {
test(`POST with a ${type} that errors and pipelining 1 should reconnect`, async (t) => {
const p = tspl(t, { plan: 12 })
function installErrorAndReconnectServer (server, p, { contentLength, trackPostWithPlan }) {
let sawPost = false
let sawGet = false

const server = createServer({ joinDuplicateHeaders: true })
server.once('request', (req, res) => {
server.on('request', (req, res) => {
if (req.method === 'GET') {
if (sawGet) {
req.socket?.destroy()
return
}

sawGet = true
p.strictEqual('/', req.url)
p.strictEqual('GET', req.method)
res.setHeader('content-type', 'text/plain')
res.end('hello')
return
}

if (sawPost) {
// Node.js 26 can surface additional POST attempts around the queued GET.
// Tear them down and keep the test focused on the reconnect behavior.
req.resume()
req.socket?.destroy()
return
}

sawPost = true

if (trackPostWithPlan) {
p.strictEqual('/', req.url)
p.strictEqual('POST', req.method)
p.strictEqual('42', req.headers['content-length'])
p.strictEqual(req.headers['content-length'], contentLength)
} else {
assert.strictEqual('/', req.url)
assert.strictEqual('POST', req.method)
assert.strictEqual(req.headers['content-length'], contentLength)
}

const bufs = []
req.on('data', (buf) => {
bufs.push(buf)
})
const bufs = []
req.on('data', (buf) => {
bufs.push(buf)
})

req.on('aborted', () => {
// we will abruptly close the connection here
// but this will still end
req.on('aborted', () => {
// we will abruptly close the connection here
// but this will still end
if (trackPostWithPlan) {
p.strictEqual('a string', Buffer.concat(bufs).toString('utf8'))
})
} else {
assert.strictEqual('a string', Buffer.concat(bufs).toString('utf8'))
}
})
})
}

server.once('request', (req, res) => {
p.strictEqual('/', req.url)
p.strictEqual('GET', req.method)
res.setHeader('content-type', 'text/plain')
res.end('hello')
})
function errorAndPipelining (type) {
test(`POST with a ${type} that errors and pipelining 1 should reconnect`, async (t) => {
const trackPostWithPlan = type !== consts.STREAM
const p = tspl(t, { plan: trackPostWithPlan ? 12 : 8 })

const server = createServer({ joinDuplicateHeaders: true })
installErrorAndReconnectServer(server, p, {
contentLength: '42',
trackPostWithPlan
})
t.after(closeServerAsPromise(server))

Expand Down Expand Up @@ -199,31 +237,13 @@ errorAndPipelining(consts.ASYNC_ITERATOR)

function errorAndChunkedEncodingPipelining (type) {
test(`POST with chunked encoding, ${type} body that errors and pipelining 1 should reconnect`, async (t) => {
const p = tspl(t, { plan: 12 })
const trackPostWithPlan = type !== consts.STREAM
const p = tspl(t, { plan: trackPostWithPlan ? 12 : 8 })

const server = createServer({ joinDuplicateHeaders: true })
server.once('request', (req, res) => {
p.strictEqual('/', req.url)
p.strictEqual('POST', req.method)
p.strictEqual(req.headers['content-length'], undefined)

const bufs = []
req.on('data', (buf) => {
bufs.push(buf)
})

req.on('aborted', () => {
// we will abruptly close the connection here
// but this will still end
p.strictEqual('a string', Buffer.concat(bufs).toString('utf8'))
})

server.once('request', (req, res) => {
p.strictEqual('/', req.url)
p.strictEqual('GET', req.method)
res.setHeader('content-type', 'text/plain')
res.end('hello')
})
installErrorAndReconnectServer(server, p, {
contentLength: undefined,
trackPostWithPlan
})
t.after(closeServerAsPromise(server))

Expand Down
Loading