@@ -18,6 +18,7 @@ lazy: Lazy = .{ .none = {} },
18
18
buffered : std .ArrayListUnmanaged (u8 ) = .{},
19
19
read_inside_on_pull : ReadDuringJSOnPullResult = .{ .none = {} },
20
20
highwater_mark : usize = 16384 ,
21
+ flowing : bool = true ,
21
22
22
23
pub const IOReader = bun .io .BufferedReader ;
23
24
pub const Poll = IOReader ;
@@ -487,6 +488,14 @@ pub fn onPull(this: *FileReader, buffer: []u8, array: jsc.JSValue) streams.Resul
487
488
}
488
489
489
490
if (! this .reader .hasPendingRead ()) {
491
+ // If not flowing (paused), don't initiate new reads
492
+ if (! this .flowing ) {
493
+ log ("onPull({d}) = pending (not flowing)" , .{buffer .len });
494
+ this .pending_value .set (this .parent ().globalThis , array );
495
+ this .pending_view = buffer ;
496
+ return .{ .pending = & this .pending };
497
+ }
498
+
490
499
this .read_inside_on_pull = .{ .js = buffer };
491
500
this .reader .read ();
492
501
@@ -581,32 +590,15 @@ pub fn onReaderDone(this: *FileReader) void {
581
590
}
582
591
this .buffered = .{};
583
592
this .pending .run ();
584
- } else if (this .buffered .items .len > 0 ) {
585
- const this_value = this .parent ().this_jsvalue ;
586
- const globalThis = this .parent ().globalThis ;
587
- if (this_value != .zero ) {
588
- if (Source .js .onDrainCallbackGetCached (this_value )) | cb | {
589
- const buffered = this .buffered ;
590
- this .buffered = .{};
591
- this .parent ().incrementCount ();
592
- defer _ = this .parent ().decrementCount ();
593
- this .eventLoop ().js .runCallback (
594
- cb ,
595
- globalThis ,
596
- .js_undefined ,
597
- &.{
598
- jsc .ArrayBuffer .fromBytes (buffered .items , .Uint8Array ).toJS (globalThis ) catch | err | {
599
- this .pending .result = .{ .err = .{ .WeakJSValue = globalThis .takeException (err ) } };
600
- return ;
601
- },
602
- },
603
- );
604
- }
605
- }
606
593
}
594
+ // Don't handle buffered data here - it will be returned on the next onPull
595
+ // This ensures proper ordering of chunks
607
596
}
608
597
609
- this .parent ().onClose ();
598
+ // Only close the stream if there's no buffered data left to deliver
599
+ if (this .buffered .items .len == 0 ) {
600
+ this .parent ().onClose ();
601
+ }
610
602
if (this .waiting_for_onReaderDone ) {
611
603
this .waiting_for_onReaderDone = false ;
612
604
_ = this .parent ().decrementCount ();
@@ -631,6 +623,26 @@ pub fn setRawMode(this: *FileReader, flag: bool) bun.sys.Maybe(void) {
631
623
return this .reader .setRawMode (flag );
632
624
}
633
625
626
+ pub fn setFlowing (this : * FileReader , flag : bool ) void {
627
+ log ("setFlowing({}) was={}" , .{ flag , this .flowing });
628
+
629
+ if (this .flowing == flag ) {
630
+ return ;
631
+ }
632
+
633
+ this .flowing = flag ;
634
+
635
+ if (flag ) {
636
+ this .reader .unpause ();
637
+ if (! this .reader .isDone () and ! this .reader .hasPendingRead ()) {
638
+ // Kick off a new read if needed
639
+ this .reader .read ();
640
+ }
641
+ } else {
642
+ this .reader .pause ();
643
+ }
644
+ }
645
+
634
646
pub fn memoryCost (this : * const FileReader ) usize {
635
647
// ReadableStreamSource covers @sizeOf(FileReader)
636
648
return this .reader .memoryCost () + this .buffered .capacity ;
0 commit comments