Repository navigation
onBodySent is not called in mockDispatch #3843
Copy link
Copy link
Closed
Labels
bugSomething isn't workingSomething isn't working
Description
Bug Description
onBodySent is not called when using the MockAgent
Reproducible By
import { DecoratorHandler, Dispatcher, fetch, MockAgent } from 'undici';
class LoggingHandler extends DecoratorHandler implements Dispatcher.DispatchHandlers {
private requestBody = '';
constructor(
protected requestOptions: Dispatcher.DispatchOptions,
protected handler: Dispatcher.DispatchHandlers,
) {
super(handler);
}
onConnect(abort: (err?: Error) => void): void {
if (this.handler.onConnect) {
return this.handler.onConnect(abort);
}
}
onBodySent(chunk: any, totalBytesSent: number) {
try {
this.requestBody += Buffer.from(chunk).toString();
} catch (err) {
console.debug('Failed to parse request body:', err);
}
if (this.handler.onBodySent) {
this.handler.onBodySent(chunk, totalBytesSent);
}
}
onComplete(trailers: string[] | null) {
console.log('onComplete', this.requestBody); // In reality this would log to something like GCP with headers/response/status etc
return this.handler.onComplete!(trailers);
}
}
function createLoggingInterceptor() {
return (dispatch: Dispatcher['dispatch']) => {
return function Cache(requestOptions: Dispatcher.DispatchOptions, handler: Dispatcher.DispatchHandlers) {
return dispatch(requestOptions, new LoggingHandler(requestOptions, handler));
};
};
}
describe('onBodySent', () => {
it('onBodySent should be called when using MockAgent', async () => {
const mockAgent = new MockAgent();
mockAgent.disableNetConnect();
const mockPool = mockAgent.get('http://mock.localhost');
const onConnectSpy = jest.spyOn(LoggingHandler.prototype, 'onConnect');
const onBodySentSpy = jest.spyOn(LoggingHandler.prototype, 'onBodySent');
const dispatcher = mockAgent.compose(createLoggingInterceptor());
const bodyString = JSON.stringify({
amount: '100',
});
mockPool
.intercept({
path: '/test',
method: 'POST',
body: () => {
return true;
},
})
.reply(200, {
test: 1,
});
const res = await fetch('http://mock.localhost/test', {
dispatcher,
method: 'POST',
body: bodyString,
});
expect(await res.json()).toEqual({ test: 1 });
expect(onConnectSpy).toHaveBeenCalledTimes(1);
expect(onBodySentSpy).toHaveBeenCalledTimes(1); // fails
});
});Expected Behavior
onBodySent would be called when making a POST request
Additional context
Looks like it should be called here:
Line 317 in a73fd09
| handler.onHeaders?.(statusCode, responseHeaders, resume, getStatusText(statusCode)) |
onBodySent future?
Concerned about these future changes which remove onBodySent. Not sure how I'd easily get the request body in an interceptor in the next branch. Mostly creating this issue to figure out what I should be doing instead going forward.
#2723 (comment)
Reacted by Toni Villena
Metadata
Metadata
Assignees
Labels
bugSomething isn't workingSomething isn't working
Activity
mcollina commentedon Nov 19, 2024
Thanks for reporting! Would you like to send a Pull Request to address this issue? Remember to add unit tests.
metcoder95 commentedon Nov 19, 2024
FWIW, This can be achieved in the same way with the current interceptor setup. e.g.
tjhiggins commentedon Nov 19, 2024
@metcoder95 I appreciate the quick response. I don't think this works for fetch since it's requestOptions.body is an AsyncGenerator:
undici/lib/web/fetch/index.js
Line 1845 in 444a0af
I tried wrapping in a Readable.from, but ran into other issues with content-length being set in the headers.
tjhiggins commentedon Nov 19, 2024
This feels pretty bad, but it seems to work:
metcoder95 commentedon Nov 19, 2024
Yeah, AsyncGenerators cannot be re-used/consumed in parallel.
Can you elaborate why bad? The interceptors API has been made for these kind of purposes
tjhiggins commentedon Nov 26, 2024
The onBodySent callback is more straightforward than having to tee the body that is passed into dispatch/handler. Advocating for keeping the event since request/fetch have different request bodies.
metcoder95 commentedon Nov 27, 2024
cc: @ronag
mcollina commentedon Nov 27, 2024
I think we have dropped
onBodySentin v7.tjhiggins commentedon Jan 17, 2025
@metcoder95 @mcollina could we add onBodySent back? What was the rationale for removing in the first place?
metcoder95 commentedon Jan 19, 2025
In pro of an improved lifecycle hooks API.
I believe something like
onRequestSent(that is triggered only after the full Request is sent) could be beneficial (I'll addonRequestErroron top for situations where the transmission of the request failed due to other side ended or networking issue).Contributions are always welcomed
tjhiggins commentedon Jan 19, 2025
@metcoder95 both of those hooks would be great and make it a lot easier to build interceptors. I'm going to hold off on upgrading to v7 until they are added.