Fetch doesn't work with ReadableStream bodies

Issue Description: fetch does not accept ReadableStream properly in the body property.

Can you reproduce it (exact steps to reproduce):

(async function() {
  console.log(
    await (await fetch('https://httpbin.org/put', {
      method: 'PUT',
      body: new ReadableStream({
        start(controller) {
          controller.enqueue(Uint8Array.from('hello world', (c) => c.charCodeAt(0)));
          controller.close();
        },
      }),
    })).text(),
  );
})();

or

(async function() {
  console.log(
    await (await fetch(new Request('https://httpbin.org/put', {
      method: 'PUT',
      body: new ReadableStream({
        start(controller) {
          controller.enqueue(Uint8Array.from('hello world', (c) => c.charCodeAt(0)));
          controller.close();
        },
      }),
    }))).text(),
  );
})();

Expected result:

{
  "args": {}, 
  "data": "hello world", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate, br", 
    "Accept-Language": "en-US,en;q=0.9", 
    "Content-Length": "11", 
    "Content-Type": "text/plain;charset=UTF-8", 
    "Host": "httpbin.org", 
    "Origin": "overwolf-extension://redacted", 
    "Sec-Fetch-Dest": "empty", 
    "Sec-Fetch-Mode": "cors", 
    "Sec-Fetch-Site": "cross-site", 
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36 OverwolfClient/0.204.0.1"
  }, 
  "json": null, 
  "origin": "x.x.x.x", 
  "url": "https://httpbin.org/put"
}

Actual result:

{
  "args": {}, 
  "data": "[object ReadableStream]", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate, br", 
    "Accept-Language": "en-US,en;q=0.9", 
    "Content-Length": "23", 
    "Content-Type": "text/plain;charset=UTF-8", 
    "Host": "httpbin.org", 
    "Origin": "overwolf-extension://redacted", 
    "Sec-Fetch-Dest": "empty", 
    "Sec-Fetch-Mode": "cors", 
    "Sec-Fetch-Site": "cross-site", 
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36 OverwolfClient/0.204.0.1"
  }, 
  "json": null, 
  "origin": "x.x.x.x", 
  "url": "https://httpbin.org/put"
}

Impact for my app: We cannot upload files without loading them entirely into memory. This is show stopping for longer video recordings and files that are otherwise larger than the amount of memory that a user has.

Do you currently have a workaround? We have a plugin for this purpose, but it is not scalable for additional upload targets and web-only developers are unable to implement features for the plugin.

Hi!
It seems like the issue was with the way to interact with the stream. I had managed to reproduce it in an online code playground.

After some fiddling, it seems like this should work:

  let r = (
    await (await fetch('https://httpbin.org/put', {
      method: 'PUT',
      body: new ReadableStream({
        start(controller) {
          controller.enqueue(Uint8Array.from('hello world', (c) => c.charCodeAt(0)));
          controller.close();
        },
      }),
    })))
    let b = r.body
  console.log(await b.getReader().read());
})();

Hope this helps!

The same problem still occurs for me on chrome 104.

According to this ticket, the implementation was just merged in july and may not be released yet: 688906 - chromium - An open-source project to help move the web forward. - Monorail

For anyone reading in the future, only HTTP2 may to be allowed for reasons mentioned in this issue: https://github.com/whatwg/fetch/issues/966

1 Like