ardatan/whatwg-node

`bodyUsed` and `response.clone()` not working as expected

FGoessler opened this issue · 1 comments

Describe the bug

A response body - as it is based on streams/buffers - can only be used once.

bodyUsed should return false once a body has been used.
Calling .clone() on a response should effectively clone the response, enabling a repeat consumption of the body.

Source: MDN Docs here and here.

Both is not the case for the fetch/response implementation in this repository.

To Reproduce

Setup a simple node.js project with dependencies for node-fetch and @whatwg-node/fetch to compare results.

package.json:

...
  "dependencies": {
    "@whatwg-node/fetch": "^0.9.16",
    "node-fetch": "^3.3.2"
  },
...

main.mjs:

import { fetch as whatwgFetch } from '@whatwg-node/fetch'
import fetch from 'node-fetch'

async function test(fetchFunc) {
  const res = await fetchFunc('https://example.com')
  const res2 = res.clone()
  console.log("first res bodyUsed before: " + res.bodyUsed)
  console.log("first res: " + (await res.text()).length)
  console.log("first res bodyUsed after: " + res.bodyUsed)
  console.log("second res: " + (await res2.text()).length)
}

// use with whatwg-fetch:
test(whatwgFetch).then(() => console.log('done')).catch(e => console.error(e))
// Prints:
// first res bodyUsed before: false                                                                                                                                                                                   ─╯
// first res: 1256
// first res bodyUsed after: false


// or use with node-fetch:
// test(fetch).then(() => console.log('done')).catch(e => console.error(e))
// Prints:
// first res bodyUsed before: false                                                                                                                                                                                   ─╯
// first res: 1256
// first res bodyUsed after: true
// second res: 1256
// done
  • As you can see the bodyUsed flag gives no indication that the body has been used.
  • For the consumption of the cloned response no data is available.
  • Note how no done is printed because the promise actually hangs indefinitely, trying to create a buffer or so.

Expected behavior

  • Once a response's body is used the response.bodyUsed returns true.
  • Calling réponse.clone() creates a proper clone of the response that enables rereading the response body on the copy.
  • Trying to reread an already consumed body causes a TypeError: body used already for: <url>.

Environment:

  • OS: macOS 13.2
  • "@whatwg-node/fetch": "^0.9.16"
  • NodeJS: v20.11.0 and v18.19.0

Additional context

Found during debugging ardatan/graphql-mesh#6593 . Proper response body consumption guards would have helped finding the source of that issue as the system would blow up properly with a TypeError: body used already for: <url> and not run into a hanging promise.