Skip to content

Commit f9c0d5a

Browse files
debadree25RafaelGSS
authored andcommitted
lib: fix blob.stream() causing hanging promises
Refs: #47993 (comment) PR-URL: #48232 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]>
1 parent 0162a0f commit f9c0d5a

File tree

2 files changed

+85
-29
lines changed

2 files changed

+85
-29
lines changed

‎lib/internal/blob.js‎

Lines changed: 39 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -329,34 +329,45 @@ class Blob{
329329
pull(c){
330330
const{ promise, resolve, reject }=createDeferredPromise();
331331
this.pendingPulls.push({ resolve, reject });
332-
reader.pull((status,buffer)=>{
333-
// If pendingPulls is empty here, the stream had to have
334-
// been canceled, and we don't really care about the result.
335-
// we can simply exit.
336-
if(this.pendingPulls.length===0){
337-
return;
338-
}
339-
constpending=this.pendingPulls.shift();
340-
if(status===0){
341-
// EOS
342-
c.close();
343-
pending.resolve();
344-
return;
345-
}elseif(status<0){
346-
// The read could fail for many different reasons when reading
347-
// from a non-memory resident blob part (e.g. file-backed blob).
348-
// The error details the system error code.
349-
consterror=lazyDOMException('The blob could not be read','NotReadableError');
350-
351-
c.error(error);
352-
pending.reject(error);
353-
return;
354-
}
355-
if(buffer!==undefined){
356-
c.enqueue(newUint8Array(buffer));
357-
}
358-
pending.resolve();
359-
});
332+
constreadNext=()=>{
333+
reader.pull((status,buffer)=>{
334+
// If pendingPulls is empty here, the stream had to have
335+
// been canceled, and we don't really care about the result.
336+
// We can simply exit.
337+
if(this.pendingPulls.length===0){
338+
return;
339+
}
340+
if(status===0){
341+
// EOS
342+
c.close();
343+
constpending=this.pendingPulls.shift();
344+
pending.resolve();
345+
return;
346+
}elseif(status<0){
347+
// The read could fail for many different reasons when reading
348+
// from a non-memory resident blob part (e.g. file-backed blob).
349+
// The error details the system error code.
350+
consterror=lazyDOMException('The blob could not be read','NotReadableError');
351+
constpending=this.pendingPulls.shift();
352+
c.error(error);
353+
pending.reject(error);
354+
return;
355+
}
356+
if(buffer!==undefined){
357+
c.enqueue(newUint8Array(buffer));
358+
}
359+
// We keep reading until we either reach EOS, some error, or we
360+
// hit the flow rate of the stream (c.desiredSize).
361+
queueMicrotask(()=>{
362+
if(c.desiredSize<=0){
363+
// A manual backpressure check.
364+
return;
365+
}
366+
readNext();
367+
});
368+
});
369+
};
370+
readNext();
360371
returnpromise;
361372
},
362373
cancel(reason){

‎test/parallel/test-blob.js‎

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
// Flags: --no-warnings
1+
// Flags: --no-warnings --expose-internals
22
'use strict';
33

44
constcommon=require('../common');
55
constassert=require('assert');
66
const{ Blob }=require('buffer');
77
const{ inspect }=require('util');
88
const{EOL}=require('os');
9+
const{ kState }=require('internal/webstreams/util');
910

1011
{
1112
constb=newBlob();
@@ -237,6 +238,50 @@ assert.throws(() => new Blob({}),{
237238
assert(res.done);
238239
})().then(common.mustCall());
239240

241+
(async()=>{
242+
constb=newBlob(Array(10).fill('hello'));
243+
constreader=b.stream().getReader();
244+
constchunks=[];
245+
while(true){
246+
constres=awaitreader.read();
247+
if(res.done)break;
248+
assert.strictEqual(res.value.byteLength,5);
249+
chunks.push(res.value);
250+
}
251+
assert.strictEqual(chunks.length,10);
252+
})().then(common.mustCall());
253+
254+
(async()=>{
255+
constb=newBlob(Array(10).fill('hello'));
256+
constreader=b.stream().getReader();
257+
constchunks=[];
258+
while(true){
259+
constres=awaitreader.read();
260+
if(chunks.length===5){
261+
reader.cancel('boom');
262+
break;
263+
}
264+
if(res.done)break;
265+
assert.strictEqual(res.value.byteLength,5);
266+
chunks.push(res.value);
267+
}
268+
assert.strictEqual(chunks.length,5);
269+
reader.closed.then(common.mustCall());
270+
})().then(common.mustCall());
271+
272+
(async()=>{
273+
constb=newBlob(Array(10).fill('hello'));
274+
conststream=b.stream();
275+
constreader=stream.getReader();
276+
assert.strictEqual(stream[kState].controller.desiredSize,1);
277+
const{ value, done }=awaitreader.read();
278+
assert.strictEqual(value.byteLength,5);
279+
assert(!done);
280+
setTimeout(()=>{
281+
assert.strictEqual(stream[kState].controller.desiredSize,0);
282+
},0);
283+
})().then(common.mustCall());
284+
240285
{
241286
constb=newBlob(['hello\n'],{endings: 'native'});
242287
assert.strictEqual(b.size,EOL.length+5);

0 commit comments

Comments
(0)