Do not call ".end()" in the callback of ".write"
See original GitHub issueHey š
Iāve got a question about the way this library calls the original .end()
method on the ClientRequest
. It does so in the callback of .write()
(given there is data to send so that the callback gets called):
I think I understand the intention here: send this chunk, when done (regardless of whether itās successful or notābecause youāre not checking the error
callback argument) mark the request as ready.
Question
Can we execute .write
and .end
in parallel?
this.write(data, encoding, function () {
self._ended = true;
});
// This call is moved out of the write's callback.
currentRequest.end(null, null, callback);
this._ending = true;
Context
Iām a maintainer of the API mocking library called Mock Service Worker. We intercept requests in Node.js using the @mswjs/interceptors library, which augments the native ClientRequest
class. We use class augmentation instead of stubbing it to allow as much of the Node.js default behavior to execute without having to polyfill it. Since we are an API mocking tool, we cannot really write request body chunks until we know thereās (or isnāt) a request handler for this particular request. Thatās why we only collect body chunks in the .write()
method:
We then write those chunks only for passthrough requests when the request handler is known. That is possible to check only in the .end
method:
Now since RedirectableRequest
only calls .end
in the callback of .write
, when our library is applied the .end
is never called. That happens because we do not call the .write
callbacks immediately (not semantic, no chunks are being written just yet so it feels wrong to confirm the result by calling a callback), but instead are referred to the .end
. Note that this difference is purely an implementation detail which should have no effect on the end-consumer. For them, the expected result is intercepted request and mocked response.
- The original reported issue: https://github.com/mswjs/msw/issues/1125
Common usage of .end
Iāve also noticed that commonly the .write
and .end
are called in parallel:
const req = http.get()
req.write('hello world')
req.end()
That is the way to send request body chunk and end a request in about any example I can find. Iāve never seen the write(chunk, () => this.end())
usage before but I understand it must have reasons behind it.
What Iād like to know
- What are the reasons to call
.end
only in the callback of.write
? - What are the implications of calling these two methods in parallel?
- Do you have any recommendations on how to handle your flow in our āinterceptorsā library?
Thank you for looking into this.
Issue Analytics
- State:
- Created 2 years ago
- Reactions:16
- Comments:11
Top GitHub Comments
Youāve fulfilled the contract; the
write
callbacks have been called.There will be no errors here, because none occur at that stage. Any errors will be emitted as an
error
event on the stream or through theend
callback, and thatās fine.No; from the perspective of the direct consumer, it is written: itās just that your writing is a memory buffer. Itās success, so the callback should be invoked.
I think both work, but I think changing the interceptor is the conceptually correct and safest one.