Skip to content

Commit 1c71f36

Browse files
committed
remove anti-pattern dispatcher hooks
onBodySent on onRequestSent are footguns that easily cause bugs when implementing logic will send multiple requests, e.g. redirect and retry. To achieve similar functionality wrap body into a stream and listen to 'data' and 'end' events. Refs: #2722
1 parent 0a069ab commit 1c71f36

20 files changed

+11
-369
lines changed

docs/api/DiagnosticsChannel.md

-11
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,6 @@ diagnosticsChannel.channel('undici:request:create').subscribe(({ request }) => {
2727

2828
Note: a request is only loosely completed to a given socket.
2929

30-
31-
## `undici:request:bodySent`
32-
33-
```js
34-
import diagnosticsChannel from 'diagnostics_channel'
35-
36-
diagnosticsChannel.channel('undici:request:bodySent').subscribe(({ request }) => {
37-
// request is the same object undici:request:create
38-
})
39-
```
40-
4130
## `undici:request:headers`
4231

4332
This message is published after the response headers have been received, i.e. the response has been completed.

docs/api/Dispatcher.md

-2
Original file line numberDiff line numberDiff line change
@@ -209,11 +209,9 @@ Returns: `Boolean` - `false` if dispatcher is busy and further dispatch calls wo
209209
* **onConnect** `(abort: () => void, context: object) => void` - Invoked before request is dispatched on socket. May be invoked multiple times when a request is retried when the request at the head of the pipeline fails.
210210
* **onError** `(error: Error) => void` - Invoked when an error has occurred. May not throw.
211211
* **onUpgrade** `(statusCode: number, headers: Buffer[], socket: Duplex) => void` (optional) - Invoked when request is upgraded. Required if `DispatchOptions.upgrade` is defined or `DispatchOptions.method === 'CONNECT'`.
212-
* **onResponseStarted** `() => void` (optional) - Invoked when response is received, before headers have been read.
213212
* **onHeaders** `(statusCode: number, headers: Buffer[], resume: () => void, statusText: string) => boolean` - Invoked when statusCode and headers have been received. May be invoked multiple times due to 1xx informational headers. Not required for `upgrade` requests.
214213
* **onData** `(chunk: Buffer) => boolean` - Invoked when response payload data is received. Not required for `upgrade` requests.
215214
* **onComplete** `(trailers: Buffer[]) => void` - Invoked when response payload and trailers have been received and the request has completed. Not required for `upgrade` requests.
216-
* **onBodySent** `(chunk: string | Buffer | Uint8Array) => void` - Invoked when a body chunk is sent to the server. Not required. For a stream or iterable body this will be invoked for every chunk. For other body types, it will be invoked once after the body is sent.
217215

218216
#### Example 1 - Dispatch GET request
219217

docs/api/RedirectHandler.md

-8
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,3 @@ Called when the request is complete.
8686
Parameters:
8787

8888
- **trailers** `object` - The trailers received.
89-
90-
#### `onBodySent(chunk)`
91-
92-
Called when the request body is sent.
93-
94-
Parameters:
95-
96-
- **chunk** `Buffer` - The chunk of the request body sent.

docs/api/RetryHandler.md

-2
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ const handler = new RetryHandler(
7373
},
7474
handler: {
7575
onConnect() {},
76-
onBodySent() {},
7776
onHeaders(status, _rawHeaders, resume, _statusMessage) {
7877
// do something with headers
7978
},
@@ -98,7 +97,6 @@ const handler = new RetryHandler(dispatchOptions, {
9897
dispatch: client.dispatch.bind(client),
9998
handler: {
10099
onConnect() {},
101-
onBodySent() {},
102100
onHeaders(status, _rawHeaders, resume, _statusMessage) {},
103101
onData(chunk) {},
104102
onComplete() {},

lib/client.js

+3-26
Original file line numberDiff line numberDiff line change
@@ -726,7 +726,6 @@ class Parser {
726726
if (!request) {
727727
return -1
728728
}
729-
request.onResponseStarted()
730729
}
731730

732731
onHeaderField (buf) {
@@ -1606,16 +1605,13 @@ function write (client, request) {
16061605
assert(contentLength === null, 'no body must not have content length')
16071606
socket.write(`${header}\r\n`, 'latin1')
16081607
}
1609-
request.onRequestSent()
16101608
} else if (util.isBuffer(body)) {
16111609
assert(contentLength === body.byteLength, 'buffer body must have content length')
16121610

16131611
socket.cork()
16141612
socket.write(`${header}content-length: ${contentLength}\r\n\r\n`, 'latin1')
16151613
socket.write(body)
16161614
socket.uncork()
1617-
request.onBodySent(body)
1618-
request.onRequestSent()
16191615
if (!expectsPayload) {
16201616
socket[kReset] = true
16211617
}
@@ -1789,7 +1785,6 @@ function writeH2 (client, session, request) {
17891785

17901786
stream.once('response', headers => {
17911787
const { [HTTP2_HEADER_STATUS]: statusCode, ...realHeaders } = headers
1792-
request.onResponseStarted()
17931788

17941789
if (request.onHeaders(Number(statusCode), realHeaders, stream.resume.bind(stream), '') === false) {
17951790
stream.pause()
@@ -1870,15 +1865,13 @@ function writeH2 (client, session, request) {
18701865
function writeBodyH2 () {
18711866
/* istanbul ignore else: assertion */
18721867
if (!body) {
1873-
request.onRequestSent()
1868+
// Do nothing...
18741869
} else if (util.isBuffer(body)) {
18751870
assert(contentLength === body.byteLength, 'buffer body must have content length')
18761871
stream.cork()
18771872
stream.write(body)
18781873
stream.uncork()
18791874
stream.end()
1880-
request.onBodySent(body)
1881-
request.onRequestSent()
18821875
} else if (util.isBlobLike(body)) {
18831876
if (typeof body.stream === 'function') {
18841877
writeIterable({
@@ -1943,22 +1936,14 @@ function writeStream ({ h2stream, body, client, request, socket, contentLength,
19431936
if (err) {
19441937
util.destroy(body, err)
19451938
util.destroy(h2stream, err)
1946-
} else {
1947-
request.onRequestSent()
19481939
}
19491940
}
19501941
)
19511942

1952-
pipe.on('data', onPipeData)
19531943
pipe.once('end', () => {
1954-
pipe.removeListener('data', onPipeData)
19551944
util.destroy(pipe)
19561945
})
19571946

1958-
function onPipeData (chunk) {
1959-
request.onBodySent(chunk)
1960-
}
1961-
19621947
return
19631948
}
19641949

@@ -2074,9 +2059,6 @@ async function writeBlob ({ h2stream, body, client, request, socket, contentLeng
20742059
socket.uncork()
20752060
}
20762061

2077-
request.onBodySent(buffer)
2078-
request.onRequestSent()
2079-
20802062
if (!expectsPayload) {
20812063
socket[kReset] = true
20822064
}
@@ -2122,15 +2104,13 @@ async function writeIterable ({ h2stream, body, client, request, socket, content
21222104
}
21232105

21242106
const res = h2stream.write(chunk)
2125-
request.onBodySent(chunk)
21262107
if (!res) {
21272108
await waitForDrain()
21282109
}
21292110
}
21302111
} catch (err) {
21312112
h2stream.destroy(err)
21322113
} finally {
2133-
request.onRequestSent()
21342114
h2stream.end()
21352115
h2stream
21362116
.off('close', onDrain)
@@ -2181,7 +2161,7 @@ class AsyncWriter {
21812161
}
21822162

21832163
write (chunk) {
2184-
const { socket, request, contentLength, client, bytesWritten, expectsPayload, header } = this
2164+
const { socket, contentLength, client, bytesWritten, expectsPayload, header } = this
21852165

21862166
if (socket[kError]) {
21872167
throw socket[kError]
@@ -2229,8 +2209,6 @@ class AsyncWriter {
22292209

22302210
socket.uncork()
22312211

2232-
request.onBodySent(chunk)
2233-
22342212
if (!ret) {
22352213
if (socket[kParser].timeout && socket[kParser].timeoutType === TIMEOUT_HEADERS) {
22362214
// istanbul ignore else: only for jest
@@ -2244,8 +2222,7 @@ class AsyncWriter {
22442222
}
22452223

22462224
end () {
2247-
const { socket, contentLength, client, bytesWritten, expectsPayload, header, request } = this
2248-
request.onRequestSent()
2225+
const { socket, contentLength, client, bytesWritten, expectsPayload, header } = this
22492226

22502227
socket[kWriting] = false
22512228

lib/core/diagnostics.js

-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ const channels = {
1414
sendHeaders: diagnosticsChannel.channel('undici:client:sendHeaders'),
1515
// Request
1616
create: diagnosticsChannel.channel('undici:request:create'),
17-
bodySent: diagnosticsChannel.channel('undici:request:bodySent'),
1817
headers: diagnosticsChannel.channel('undici:request:headers'),
1918
trailers: diagnosticsChannel.channel('undici:request:trailers'),
2019
error: diagnosticsChannel.channel('undici:request:error'),

lib/core/request.js

-28
Original file line numberDiff line numberDiff line change
@@ -201,30 +201,6 @@ class Request {
201201
}
202202
}
203203

204-
onBodySent (chunk) {
205-
if (this[kHandler].onBodySent) {
206-
try {
207-
return this[kHandler].onBodySent(chunk)
208-
} catch (err) {
209-
this.abort(err)
210-
}
211-
}
212-
}
213-
214-
onRequestSent () {
215-
if (channels.bodySent.hasSubscribers) {
216-
channels.bodySent.publish({ request: this })
217-
}
218-
219-
if (this[kHandler].onRequestSent) {
220-
try {
221-
return this[kHandler].onRequestSent()
222-
} catch (err) {
223-
this.abort(err)
224-
}
225-
}
226-
}
227-
228204
onConnect (abort) {
229205
assert(!this.aborted)
230206
assert(!this.completed)
@@ -237,10 +213,6 @@ class Request {
237213
}
238214
}
239215

240-
onResponseStarted () {
241-
return this[kHandler].onResponseStarted?.()
242-
}
243-
244216
onHeaders (statusCode, headers, resume, statusText) {
245217
assert(!this.aborted)
246218
assert(!this.completed)

lib/core/util.js

-4
Original file line numberDiff line numberDiff line change
@@ -323,10 +323,6 @@ function validateHandler (handler, method, upgrade) {
323323
throw new InvalidArgumentError('invalid onError method')
324324
}
325325

326-
if (typeof handler.onBodySent !== 'function' && handler.onBodySent !== undefined) {
327-
throw new InvalidArgumentError('invalid onBodySent method')
328-
}
329-
330326
if (upgrade || method === 'CONNECT') {
331327
if (typeof handler.onUpgrade !== 'function') {
332328
throw new InvalidArgumentError('invalid onUpgrade method')

lib/fetch/index.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -2113,15 +2113,13 @@ async function httpNetworkFetch (
21132113
timingInfo.finalNetworkRequestStartTime = coarsenedSharedCurrentTime(fetchParams.crossOriginIsolatedCapability)
21142114
},
21152115

2116-
onResponseStarted () {
2116+
onHeaders (status, rawHeaders, resume, statusText) {
21172117
// Set timingInfo’s final network-response start time to the coarsened shared current
21182118
// time given fetchParams’s cross-origin isolated capability, immediately after the
21192119
// user agent’s HTTP parser receives the first byte of the response (e.g., frame header
21202120
// bytes for HTTP/2 or response status line for HTTP/1.x).
21212121
timingInfo.finalNetworkResponseStartTime = coarsenedSharedCurrentTime(fetchParams.crossOriginIsolatedCapability)
2122-
},
21232122

2124-
onHeaders (status, rawHeaders, resume, statusText) {
21252123
if (status < 200) {
21262124
return
21272125
}

lib/handler/DecoratorHandler.js

-4
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,4 @@ module.exports = class DecoratorHandler {
2828
onComplete (...args) {
2929
return this.handler.onComplete(...args)
3030
}
31-
32-
onBodySent (...args) {
33-
return this.handler.onBodySent(...args)
34-
}
3531
}

lib/handler/RedirectHandler.js

-6
Original file line numberDiff line numberDiff line change
@@ -173,12 +173,6 @@ class RedirectHandler {
173173
this.handler.onComplete(trailers)
174174
}
175175
}
176-
177-
onBodySent (chunk) {
178-
if (this.handler.onBodySent) {
179-
this.handler.onBodySent(chunk)
180-
}
181-
}
182176
}
183177

184178
function parseLocation (statusCode, headers) {

lib/handler/RetryHandler.js

-10
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,6 @@ class RetryHandler {
7474
})
7575
}
7676

77-
onRequestSent () {
78-
if (this.handler.onRequestSent) {
79-
this.handler.onRequestSent()
80-
}
81-
}
82-
8377
onUpgrade (statusCode, headers, socket) {
8478
if (this.handler.onUpgrade) {
8579
this.handler.onUpgrade(statusCode, headers, socket)
@@ -94,10 +88,6 @@ class RetryHandler {
9488
}
9589
}
9690

97-
onBodySent (chunk) {
98-
if (this.handler.onBodySent) return this.handler.onBodySent(chunk)
99-
}
100-
10191
static [kRetryHandlerDefaultRetry] (err, { state, opts }, cb) {
10292
const { statusCode, code, headers } = err
10393
const { method, retryOptions } = opts

test/http2.js

+1-4
Original file line numberDiff line numberDiff line change
@@ -804,7 +804,7 @@ test('Should handle h2 request with body (string or buffer) - dispatch', t => {
804804
stream.end('hello h2!')
805805
})
806806

807-
t.plan(7)
807+
t.plan(6)
808808

809809
server.listen(0, () => {
810810
const client = new Client(`https://localhost:${server.address().port}`, {
@@ -842,9 +842,6 @@ test('Should handle h2 request with body (string or buffer) - dispatch', t => {
842842
onData (chunk) {
843843
response.push(chunk)
844844
},
845-
onBodySent (body) {
846-
t.equal(body.toString('utf-8'), expectedBody)
847-
},
848845
onComplete () {
849846
t.equal(Buffer.concat(response).toString('utf-8'), 'hello h2!')
850847
t.equal(

test/jest/interceptor.test.js

-8
Original file line numberDiff line numberDiff line change
@@ -143,14 +143,6 @@ describe('interceptors with NtlmRequestHandler', () => {
143143
return this.handler.onComplete(...args)
144144
}
145145
}
146-
147-
onBodySent (...args) {
148-
if (this.requestCount < 2) {
149-
// Do nothing
150-
} else {
151-
return this.handler.onBodySent(...args)
152-
}
153-
}
154146
}
155147
let server
156148

0 commit comments

Comments
 (0)