mirror of
https://github.com/denoland/std.git
synced 2024-11-22 04:59:05 +00:00
fix(streams): prevent earlyZipReadableStreams()
from possibly using excessive memory (#5082)
* refactor(streams): `earlyZipReadableStreams` * add(streams): test for cancelling stream * chore(streams): fmt * nit(streams): Make one line into two Co-authored-by: Asher Gomez <ashersaupingomez@gmail.com> * improve(streams): test to assert all streams were actually cancelled * adjust(streams): reason for cancelling streams * tweak cancel reason * tweak --------- Co-authored-by: Asher Gomez <ashersaupingomez@gmail.com>
This commit is contained in:
parent
b75d42a329
commit
5bcbb8f7f1
@ -84,26 +84,25 @@
|
|||||||
export function earlyZipReadableStreams<T>(
|
export function earlyZipReadableStreams<T>(
|
||||||
...streams: ReadableStream<T>[]
|
...streams: ReadableStream<T>[]
|
||||||
): ReadableStream<T> {
|
): ReadableStream<T> {
|
||||||
const readers = streams.map((s) => s.getReader());
|
const readers = streams.map((stream) => stream.getReader());
|
||||||
return new ReadableStream<T>({
|
return new ReadableStream<T>({
|
||||||
async start(controller) {
|
async pull(controller) {
|
||||||
try {
|
for (let i = 0; i < readers.length; ++i) {
|
||||||
loop:
|
const { done, value } = await readers[i]!.read();
|
||||||
while (true) {
|
if (done) {
|
||||||
for (const reader of readers) {
|
await Promise.all(
|
||||||
const { value, done } = await reader.read();
|
readers.map((reader) =>
|
||||||
if (!done) {
|
reader.cancel(`Stream at index ${i} ended`)
|
||||||
controller.enqueue(value!);
|
),
|
||||||
} else {
|
);
|
||||||
await Promise.all(readers.map((reader) => reader.cancel()));
|
|
||||||
break loop;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
controller.close();
|
controller.close();
|
||||||
} catch (e) {
|
return;
|
||||||
controller.error(e);
|
|
||||||
}
|
}
|
||||||
|
controller.enqueue(value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async cancel(reason) {
|
||||||
|
await Promise.all(readers.map((reader) => reader.cancel(reason)));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -61,6 +61,27 @@ Deno.test("earlyZipReadableStreams() can zip three streams", async () => {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Deno.test("earlyZipReadableStreams() forwards cancel()", async () => {
|
||||||
|
const num = 10;
|
||||||
|
let cancelled = 0;
|
||||||
|
const streams = new Array(num).fill(false).map(() =>
|
||||||
|
new ReadableStream(
|
||||||
|
{
|
||||||
|
pull(controller) {
|
||||||
|
controller.enqueue("chunk");
|
||||||
|
},
|
||||||
|
cancel(reason) {
|
||||||
|
cancelled++;
|
||||||
|
assertEquals(reason, "I was cancelled!");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
await earlyZipReadableStreams(...streams).cancel("I was cancelled!");
|
||||||
|
assertEquals(cancelled, num);
|
||||||
|
});
|
||||||
|
|
||||||
Deno.test("earlyZipReadableStreams() controller error", async () => {
|
Deno.test("earlyZipReadableStreams() controller error", async () => {
|
||||||
const errorMsg = "Test error";
|
const errorMsg = "Test error";
|
||||||
const stream = new ReadableStream({
|
const stream = new ReadableStream({
|
||||||
|
Loading…
Reference in New Issue
Block a user