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
8 changes: 8 additions & 0 deletions lib/handler/cache-handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,14 @@ class CacheHandler {
this.#handler.onRequestStart?.(controller, context)
}

onBodySent (chunk) {
this.#handler.onBodySent?.(chunk)
}

onRequestSent () {
this.#handler.onRequestSent?.()
}

onRequestUpgrade (controller, statusCode, headers, socket) {
this.#handler.onRequestUpgrade?.(controller, statusCode, headers, socket)
}
Expand Down
8 changes: 7 additions & 1 deletion lib/handler/decorator-handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,11 @@ module.exports = class DecoratorHandler {
/**
* @deprecated
*/
onBodySent () {}
onBodySent (...args) {
return this.#handler.onBodySent?.(...args)
}

onRequestSent (...args) {
return this.#handler.onRequestSent?.(...args)
}
}
8 changes: 8 additions & 0 deletions lib/handler/redirect-handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ class RedirectHandler {
this.handler.onRequestStart?.(controller, { ...context, history: this.history })
}

onBodySent (chunk) {
this.handler.onBodySent?.(chunk)
}

onRequestSent () {
this.handler.onRequestSent?.()
}

onRequestUpgrade (controller, statusCode, headers, socket) {
this.handler.onRequestUpgrade?.(controller, statusCode, headers, socket)
}
Expand Down
8 changes: 8 additions & 0 deletions lib/handler/retry-handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,14 @@ class RetryHandler {
}
}

onBodySent (chunk) {
this.handler.onBodySent?.(chunk)
}

onRequestSent () {
this.handler.onRequestSent?.()
}

onRequestUpgrade (_controller, statusCode, headers, socket) {
this.handler.onRequestUpgrade?.(this.controllerProxy, statusCode, headers, socket)
}
Expand Down
36 changes: 36 additions & 0 deletions test/decorator-handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,42 @@ describe('DecoratorHandler', () => {
})
})

describe('#onBodySent', () => {
test('should delegate onBodySent-method', t => {
t = tspl(t, { plan: 1 })
const decorator = new DecoratorHandler({
onBodySent: (chunk) => {
t.equal(chunk, 'chunk')
}
})
decorator.onBodySent('chunk')
})

test('should not throw if onBodySent-method is not defined in the handler', t => {
t = tspl(t, { plan: 1 })
const decorator = new DecoratorHandler({})
t.doesNotThrow(() => decorator.onBodySent('chunk'))
})
})

describe('#onRequestSent', () => {
test('should delegate onRequestSent-method', t => {
t = tspl(t, { plan: 1 })
const decorator = new DecoratorHandler({
onRequestSent: () => {
t.ok(true)
}
})
decorator.onRequestSent()
})

test('should not throw if onRequestSent-method is not defined in the handler', t => {
t = tspl(t, { plan: 1 })
const decorator = new DecoratorHandler({})
t.doesNotThrow(() => decorator.onRequestSent())
})
})

describe('#onResponseError', () => {
test('should delegate onResponseError-method', t => {
t = tspl(t, { plan: 1 })
Expand Down
99 changes: 99 additions & 0 deletions test/interceptors/body-sent-hooks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
'use strict'

// Regression test for https://github.com/nodejs/undici/issues/5695
// DecoratorHandler used to swallow onBodySent (empty method) and omit
// onRequestSent, so composing any interceptor dropped those hooks.

const { test } = require('node:test')
const { createServer } = require('node:http')
const { once } = require('node:events')
const { Client, DecoratorHandler, interceptors } = require('../../')

const BODY = '{"hello":"world"}'

function dispatchAndTrack (dispatcher) {
const seen = { bodySent: [], requestSent: 0 }
return new Promise((resolve, reject) => {
dispatcher.dispatch(
{
method: 'POST',
path: '/',
headers: { 'content-type': 'application/json' },
body: BODY
},
{
onRequestStart () {},
onBodySent (chunk) {
seen.bodySent.push(Buffer.from(chunk).toString())
},
onRequestSent () {
seen.requestSent++
},
onResponseStart () {},
onResponseData () {},
onResponseEnd () {
resolve(seen)
},
onResponseError (_controller, err) {
reject(err)
}
}
)
})
}

async function withClient (t, compose) {
const server = createServer((req, res) => {
req.resume()
req.on('end', () => res.end('ok'))
})
server.listen(0)
await once(server, 'listening')
t.after(() => server.close())

let client = new Client(`http://localhost:${server.address().port}`)
if (compose) {
client = compose(client)
}
t.after(() => client.close())
return client
}

test('onBodySent/onRequestSent fire on a bare Client', async (t) => {
const client = await withClient(t)
const seen = await dispatchAndTrack(client)
t.assert.deepStrictEqual(seen.bodySent, [BODY])
t.assert.strictEqual(seen.requestSent, 1)
})

test('onBodySent/onRequestSent survive DecoratorHandler', async (t) => {
const client = await withClient(t, (c) =>
c.compose((dispatch) => (opts, handler) => dispatch(opts, new DecoratorHandler(handler)))
)
const seen = await dispatchAndTrack(client)
t.assert.deepStrictEqual(seen.bodySent, [BODY])
t.assert.strictEqual(seen.requestSent, 1)
})

test('onBodySent/onRequestSent survive interceptors.retry()', async (t) => {
const client = await withClient(t, (c) => c.compose(interceptors.retry()))
const seen = await dispatchAndTrack(client)
t.assert.deepStrictEqual(seen.bodySent, [BODY])
t.assert.strictEqual(seen.requestSent, 1)
})

test('onBodySent/onRequestSent survive interceptors.cache()', async (t) => {
const client = await withClient(t, (c) => c.compose(interceptors.cache()))
const seen = await dispatchAndTrack(client)
t.assert.deepStrictEqual(seen.bodySent, [BODY])
t.assert.strictEqual(seen.requestSent, 1)
})

test('onBodySent/onRequestSent survive interceptors.redirect()', async (t) => {
const client = await withClient(t, (c) =>
c.compose(interceptors.redirect({ maxRedirections: 0 }))
)
const seen = await dispatchAndTrack(client)
t.assert.deepStrictEqual(seen.bodySent, [BODY])
t.assert.strictEqual(seen.requestSent, 1)
})
Loading