msw icon indicating copy to clipboard operation
msw copied to clipboard

Error: Premature close

Open molvqingtai opened this issue 2 years ago • 6 comments

Prerequisites

Environment check

  • [X] I'm using the latest msw version
  • [X] I'm using Node.js version 14 or higher

Node.js version

v16.14.1

Reproduction repository

https://github.com/molvqingtai/resreq/blob/35d7e2e19d3bbbca6d4c55f5fd6119db7afdc30d/tests/main.spec.ts

Reproduction steps

I use Vitest test and use some Polyfill:

  • node-fetch v3.2.3
  • abort-controller v3.0.0
  • formdata-node v4.3.2

Current behavior

When I run the test, there is a high probability that this error will occur:

 ❯ __tests__/main.spec.ts (2)
   ❯ Test methods (2)
     √ GET request
     × POST request

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯

 FAIL  __tests__/main.spec.ts > Test methods > POST request
Error: Premature close
 ❯ NodeClientRequest.emit node:events:402:35
 ❯ NodeClientRequest.emit node_modules/.pnpm/@[email protected]/node_modules/@mswjs/interceptors/lib/interceptors/ClientRequest/NodeClientRequest.js:309:38
 ❯ Socket.socketCloseListener node:_http_client:423:9
 ❯ Socket.emit node:events:402:35
 ❯ TCP.<anonymous> node:net:687:12

Expected behavior

No errors

molvqingtai avatar Mar 16 '22 09:03 molvqingtai

Hey, @molvqingtai. Thanks for reporting this!

I confirm I can reproduce the issue, although only at times. This seems to be the Node.js error thrown when the socket is closed by the readable stream has not ended:

Error [ERR_STREAM_PREMATURE_CLOSE]: Premature close
    at new NodeError (node:internal/errors:371:5)
    at NodeClientRequest.onclose (node:internal/streams/end-of-stream:140:30)
    at NodeClientRequest.emit (node:events:532:35)
    at NodeClientRequest.emit (/Users/kettanaito/Projects/contrib/msw-1169-premature-close/node_modules/@mswjs/interceptors/lib/interceptors/ClientRequest/NodeClientRequest.js:309:38)
    at Socket.socketCloseListener (node:_http_client:418:9)
    at Socket.emit (node:events:532:35)
    at TCP.<anonymous> (node:net:687:12) {
  code: 'ERR_STREAM_PREMATURE_CLOSE'
}

https://github.com/nodejs/node/blob/418ff70b810f0e7112d48baaa72932a56cfa213b/lib/internal/streams/end-of-stream.js#L140

And this is how it checks if the stream is finished:

https://github.com/nodejs/node/blob/418ff70b810f0e7112d48baaa72932a56cfa213b/lib/internal/streams/end-of-stream.js#L46-L51

We may need to look into the state of the stream in the interceptors and make sure it's properly marked as finished. But I suspect that the issue is in socket closing the connection before the mocked response is written (I think it will close it immediately once connecting to 127.0.0.1 rejects).

kettanaito avatar Mar 16 '22 16:03 kettanaito

I'm getting the same error with jest when upgrading from v0.36.3.

In my case:

  • v0.36.3 didn't trigger the issue (my initial version)
  • v0.36.8 didn't trigger the issue
  • v0.38.0 trigger the issue
  • v0.38.1 trigger the issue
  • v0.38.2 trigger the issue
  • v0.39.2 trigger the issue

By looking into the changelog of the v0.38.0, it could linked to the interceptor upgrade.

armandabric avatar Mar 22 '22 10:03 armandabric

Thanks for checking the version compatibility, @armandabric!

I'd expect the issue to originate from @mswjs/[email protected] as it's related to the request interceptor algorithm changes we've introduced in that version. I wrote about this change here.

The root cause here is the socket terminates the connection when it cannot make one, terminating the request while its readable stream is still handling data, resulting in premature close. I think it's okay that me make the connection (see my reasoning in the article above) but we need to make sure that socket errors do not propagate to the ClientRequest internals before we're sure that those errors are relevant and not just happen because we're mocking endpoints.

I do welcome anybody with the will and time to look into Node.js internals I've posted above and figure out the correct way for us to prevent socket errors from bubbling to the ClientRequest unless we have to replay them. I wanted to focus on other things at the moment so I can't put this at the top of my priorities list.

kettanaito avatar Mar 22 '22 11:03 kettanaito

I wonder if this is caused by node-fetch listening to the Socket errors instead of ClientRequest errors. While we're silencing the latter, Socket errors still occur and may halt the request client if it reacts to them.

We should look into this in the interceptors library.

kettanaito avatar May 02 '22 10:05 kettanaito

I wonder if this is caused by node-fetch listening to the Socket errors instead of ClientRequest errors. While we're silencing the latter, Socket errors still occur and may halt the request client if it reacts to them.

We should look into this in the interceptors library.

I am using cross-fetch for mocking fetch api. "cross-fetch": "^3.1.5",

josuevalrob avatar May 24 '22 11:05 josuevalrob

I'm seeing a weird error that seems unrelated: when doing an axios.put, the request hangs forever unless I set a timeout. msw doesn't properly track this request: it neither responds to it nor does it trigger the onUnhandledRequest.

Other http verbs seem to work fine.

I'm mentioning it here though because of @armandabric's comment regarding versions: the issue I'm facing doesn't happen in v0.36.8 but it happens consistently in 0.38.0 and newer versions.

lautarodragan avatar May 31 '22 07:05 lautarodragan

This should be fixed in msw@latest. Please upgrade and let me know if you still experience this. There's been a lot of work done on the Node.js interception over the past year.

kettanaito avatar Nov 16 '23 18:11 kettanaito