@@ -182,34 +182,39 @@ export class AppRuntime {
182182 const reader = stream . getReader ( ) ;
183183 return new ReadableStream ( {
184184 async pull ( controller ) {
185- while ( true ) {
186- const { done, value } = await reader . read ( ) ;
187- if ( done ) {
188- controller . close ( ) ;
189- break ;
190- }
191- let chunk = value ;
192- let chunkStart = bytesRead ;
193- let chunkEnd = bytesRead + chunk . length ;
194- if ( chunkEnd <= start ) {
195- // skip entire chunk
196- bytesRead += chunk . length ;
197- continue ;
198- }
199- if ( chunkStart > end ) {
200- // past requested range
201- controller . close ( ) ;
202- break ;
203- }
204- // slice relevant part
205- let sliceStart = Math . max ( start - chunkStart , 0 ) ;
206- let sliceEnd = Math . min ( end - chunkStart + 1 , chunk . length ) ;
207- controller . enqueue ( chunk . subarray ( sliceStart , sliceEnd ) ) ;
208- bytesRead += chunk . length ;
209- if ( chunkEnd > end ) {
210- controller . close ( ) ;
211- break ;
212- }
185+ // Read one chunk at a time
186+ const { done, value } = await reader . read ( ) ;
187+
188+ if ( done ) {
189+ controller . close ( ) ;
190+ return ;
191+ }
192+
193+ let chunk = value ;
194+ let chunkStart = bytesRead ;
195+ let chunkEnd = bytesRead + chunk . length ;
196+ bytesRead += chunk . length ; // Correctly increment for every chunk seen
197+
198+ if ( chunkEnd <= start ) {
199+ // Not at the start yet, just return and wait for next pull
200+ return ;
201+ }
202+ if ( chunkStart > end ) {
203+ // Already past the requested range
204+ controller . close ( ) ;
205+ reader . releaseLock ( ) ;
206+ return ;
207+ }
208+
209+ // Calculate the sub-slice of the current chunk
210+ let sliceStart = Math . max ( start - chunkStart , 0 ) ;
211+ let sliceEnd = Math . min ( end - chunkStart + 1 , chunk . length ) ;
212+
213+ controller . enqueue ( chunk . subarray ( sliceStart , sliceEnd ) ) ;
214+
215+ if ( chunkEnd > end ) {
216+ controller . close ( ) ;
217+ reader . releaseLock ( ) ;
213218 }
214219 } ,
215220 cancel ( reason ) {
@@ -219,51 +224,55 @@ export class AppRuntime {
219224 }
220225
221226 streamJoin ( streams , contentType , totalLength ) {
222- // Single stream?
223227 if ( streams . length === 1 ) {
224228 return streams [ 0 ] ;
225229 }
226- // Multipart!
230+
227231 const boundary = `WEBFLO_BOUNDARY_${ Math . random ( ) . toString ( 36 ) . slice ( 2 ) } ` ;
228232 const encoder = new TextEncoder ( ) ;
229- // Generator for multipart chunks
233+ let gen ; // Declare generator holder
234+
235+ const self = this ;
230236 async function * generateMultipart ( ) {
231237 for ( const { stream, start, end } of streams ) {
232- // Boundary + headers for each part
233238 yield encoder . encode ( `--${ boundary } \r\n` ) ;
234239 yield encoder . encode (
235240 `Content-Type: ${ contentType } \r\n` +
236241 `Content-Range: bytes ${ start } -${ end } /${ totalLength } \r\n\r\n`
237242 ) ;
238- // Stream the sliced body
243+
239244 const reader = stream . getReader ( ) ;
240- while ( true ) {
241- const { done, value } = await reader . read ( ) ;
242- if ( done ) break ;
243- yield value ;
245+ try {
246+ while ( true ) {
247+ const { done, value } = await reader . read ( ) ;
248+ if ( done ) break ;
249+ yield value ;
250+ }
251+ } finally {
252+ reader . releaseLock ( ) ;
244253 }
245254
246255 yield encoder . encode ( '\r\n' ) ;
247256 }
248- // Final boundary
249257 yield encoder . encode ( `--${ boundary } --\r\n` ) ;
250258 }
251- // Create ReadableStream from async generator
259+
252260 return {
253261 stream : new ReadableStream ( {
262+ start ( ) {
263+ // Initialize the generator ONCE when the stream starts
264+ gen = generateMultipart ( ) ;
265+ } ,
254266 async pull ( controller ) {
255- const gen = generateMultipart ( ) ;
256- while ( true ) {
257- const { done, value } = await gen . next ( ) ;
258- if ( done ) {
259- controller . close ( ) ;
260- break ;
261- }
267+ const { done, value } = await gen . next ( ) ;
268+ if ( done ) {
269+ controller . close ( ) ;
270+ } else {
262271 controller . enqueue ( value ) ;
263272 }
264273 } ,
265274 cancel ( reason ) {
266- // Handle cancellation if needed
275+ // Logic to cancel underlying streams if necessary
267276 }
268277 } ) ,
269278 boundary,
@@ -273,19 +282,20 @@ export class AppRuntime {
273282 createStreamingResponse ( httpEvent , readStream , stats ) {
274283 let response ;
275284 const requestRange = httpEvent . request . headers . get ( 'Range' , true ) ; // Parses the Range header
276-
285+
277286 if ( requestRange . length ) {
278287 const streams = requestRange . reduce ( ( streams , range ) => {
279- if ( ! streams ) return ;
280- const currentStart = ( streams [ streams . length - 1 ] ?. end || - 1 ) + 1 ;
288+ if ( ! streams ) return null ;
289+ const currentStart = 0 ; // (streams[streams.length - 1]?.end || -1) + 1;
281290
282- if ( ! range . canResolveAgainst ( currentStart , stats . size ) ) return ; // Only after rendering()
291+ if ( ! range . canResolveAgainst ( currentStart , stats . size ) ) return null ; // Only after rendering()
283292 const [ start , end ] = range . resolveAgainst ( stats . size ) ; // Resolve offsets
284293
285294 try {
286295 return streams . concat ( { start, end, stream : readStream ( { start, end } ) } ) ;
287- } catch ( e ) {
288- console . log ( '_______' , httpEvent . request . headers . get ( 'Range' ) , requestRange , stats . size , [ start , end ] ) ;
296+ } catch ( e ) {
297+ console . error ( 'Failed to create range stream:' , e ) ;
298+ return null
289299 }
290300 } , [ ] ) ;
291301
@@ -299,6 +309,7 @@ export class AppRuntime {
299309
300310 const streamJoin = this . streamJoin ( streams , stats . mime , stats . size ) ;
301311 response = new Response ( streamJoin . stream , { status : 206 , statusText : 'Partial Content' } ) ;
312+
302313 if ( streamJoin . boundary ) {
303314 response . headers . set ( 'Content-Type' , `multipart/byteranges; boundary=${ streamJoin . boundary } ` ) ;
304315 } else {
@@ -311,7 +322,7 @@ export class AppRuntime {
311322 response . headers . set ( 'Content-Type' , stats . mime ) ;
312323 response . headers . set ( 'Content-Length' , stats . size ) ;
313324 }
314-
325+
315326 return response ;
316327 }
317328}
0 commit comments