Skip to content

Commit 87a4811

Browse files
authored
Merge pull request from GHSA-9f24-jqhm-jfcw
* fetch: pull don't push Signed-off-by: Matteo Collina <[email protected]> * added tests Signed-off-by: Matteo Collina <[email protected]> --------- Signed-off-by: Matteo Collina <[email protected]>
1 parent b9da3e4 commit 87a4811

File tree

2 files changed

+59
-3
lines changed

2 files changed

+59
-3
lines changed

lib/fetch/index.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1099,10 +1099,10 @@ function fetchFinale (fetchParams, response) {
10991099

11001100
const byteStream = new ReadableStream({
11011101
readableStream: transformStream.readable,
1102-
async start (controller) {
1102+
async pull (controller) {
11031103
const reader = this.readableStream.getReader()
11041104

1105-
while (true) {
1105+
while (controller.desiredSize >= 0) {
11061106
const { done, value } = await reader.read()
11071107

11081108
if (done) {
@@ -1113,6 +1113,7 @@ function fetchFinale (fetchParams, response) {
11131113
controller.enqueue(value)
11141114
}
11151115
},
1116+
queuingStrategy: new ByteLengthQueuingStrategy({ highWaterMark: 16384 }),
11161117
type: 'bytes'
11171118
})
11181119

@@ -1927,6 +1928,7 @@ async function httpNetworkFetch (
19271928
// cancelAlgorithm set to cancelAlgorithm.
19281929
const stream = new ReadableStream(
19291930
{
1931+
highWaterMark: 16384,
19301932
async start (controller) {
19311933
fetchParams.controller.controller = controller
19321934
},
@@ -1936,7 +1938,8 @@ async function httpNetworkFetch (
19361938
async cancel (reason) {
19371939
await cancelAlgorithm(reason)
19381940
},
1939-
type: 'bytes'
1941+
type: 'bytes',
1942+
queuingStrategy: new ByteLengthQueuingStrategy({ highWaterMark: 16384 })
19401943
}
19411944
)
19421945

test/fetch/pull-dont-push.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
'use strict'
2+
3+
const { test } = require('node:test')
4+
const assert = require('node:assert')
5+
const { fetch } = require('../..')
6+
const { createServer } = require('http')
7+
const { once } = require('events')
8+
const { Readable, pipeline } = require('stream')
9+
const { setTimeout: sleep } = require('timers/promises')
10+
11+
const { closeServerAsPromise } = require('../utils/node-http')
12+
13+
test('Allow the usage of custom implementation of AbortController', async (t) => {
14+
let count = 0
15+
let socket
16+
const server = createServer((req, res) => {
17+
res.statusCode = 200
18+
socket = res.socket
19+
20+
// infinite stream
21+
const stream = new Readable({
22+
read () {
23+
this.push('a')
24+
if (count++ > 1000000) {
25+
this.push(null)
26+
}
27+
}
28+
})
29+
30+
pipeline(stream, res, () => {})
31+
})
32+
33+
t.after(closeServerAsPromise(server))
34+
35+
server.listen(0)
36+
await once(server, 'listening')
37+
38+
t.diagnostic('server listening on port %d', server.address().port)
39+
const res = await fetch(`http://localhost:${server.address().port}`)
40+
t.diagnostic('fetched')
41+
42+
// Some time is needed to fill the buffer
43+
await sleep(1000)
44+
45+
assert.strictEqual(socket.bytesWritten < 1024 * 1024, true) // 1 MB
46+
socket.destroy()
47+
48+
// consume the stream
49+
try {
50+
/* eslint-disable-next-line no-empty, no-unused-vars */
51+
for await (const chunk of res.body) {}
52+
} catch {}
53+
})

0 commit comments

Comments
 (0)