@@ -47,6 +47,8 @@ const DEFAULT_BROWSER_ROUTING_SUBRESOURCES = [
4747 'computer' ,
4848 'playwright' ,
4949 'process' ,
50+ 'fs' ,
51+ 'logs/stream' ,
5052] ;
5153const BROWSER_ROUTE_CACHEABLE_PATH = / ^ \/ (?: v \d + \/ ) ? b r o w s e r s (?: \/ [ ^ / ] + ) ? \/ ? $ / ;
5254const BROWSER_POOL_ACQUIRE_PATH = / ^ \/ (?: v \d + \/ ) ? b r o w s e r _ p o o l s \/ [ ^ / ] + \/ a c q u i r e \/ ? $ / ;
@@ -278,9 +280,13 @@ async function routeRequest(
278280
279281 const headers = new Headers ( request . headers ) ;
280282 headers . delete ( 'authorization' ) ;
283+ const body = requestBodyForFetch ( request , init ) ;
281284 const routed = await innerFetch ( target . toString ( ) , buildRoutedInit ( input , request , init , headers ) ) ;
282285 if ( ( routed . status === 401 || routed . status === 403 ) && target . searchParams . get ( 'jwt' ) ) {
283286 cache . deleteIfJwt ( sessionId , target . searchParams . get ( 'jwt' ) ?? '' ) ;
287+ if ( isStreamingBody ( body ) ) {
288+ return routed ;
289+ }
284290 await CancelReadableStream ( routed . body ) ;
285291 return innerFetch ( input , init ) ;
286292 }
@@ -294,6 +300,14 @@ function buildRoutedInit(
294300 headers : Headers ,
295301) : RequestInit {
296302 const method = request . method . toUpperCase ( ) ;
303+ const body = method === 'GET' || method === 'HEAD' ? undefined : requestBodyForFetch ( request , originalInit ) ;
304+ if ( derivesOwnContentType ( body ) && ! new Headers ( originalInit ?. headers ) . get ( 'content-type' ) ) {
305+ // `request` was constructed from the same body, so its content-type carries
306+ // that construction's multipart boundary. The routed fetch re-encodes the
307+ // body with a new boundary, so let it derive the header again.
308+ headers . delete ( 'content-type' ) ;
309+ }
310+
297311 const routedInit = {
298312 ...( ( originalInit ?? { } ) as Record < string , unknown > ) ,
299313 method,
@@ -305,14 +319,13 @@ function buildRoutedInit(
305319 delete routedInit [ 'body' ] ;
306320 delete routedInit [ 'duplex' ] ;
307321
322+ if ( body !== undefined ) {
323+ routedInit . body = body ;
324+ }
308325 if ( method !== 'GET' && method !== 'HEAD' ) {
309- const body = requestBodyForFetch ( request , originalInit ) ;
310- if ( body !== undefined ) {
311- routedInit . body = body ;
312- }
313326 if ( originalInit ?. duplex !== undefined ) {
314327 routedInit . duplex = originalInit . duplex ;
315- } else if ( requiresHalfDuplex ( body ) ) {
328+ } else if ( isStreamingBody ( body ) ) {
316329 routedInit . duplex = 'half' ;
317330 }
318331 }
@@ -331,7 +344,15 @@ function requestBodyForFetch(
331344 return request . body ?? undefined ;
332345}
333346
334- function requiresHalfDuplex ( body : RequestInit [ 'body' ] | undefined ) : boolean {
347+ function derivesOwnContentType ( body : RequestInit [ 'body' ] | undefined ) : boolean {
348+ return (
349+ ( ( globalThis as any ) . FormData && body instanceof ( globalThis as any ) . FormData ) ||
350+ ( ( globalThis as any ) . URLSearchParams && body instanceof ( globalThis as any ) . URLSearchParams ) ||
351+ ( ( globalThis as any ) . Blob && body instanceof ( globalThis as any ) . Blob )
352+ ) ;
353+ }
354+
355+ function isStreamingBody ( body : RequestInit [ 'body' ] | undefined ) : boolean {
335356 return (
336357 ( ( globalThis as any ) . ReadableStream && body instanceof ( globalThis as any ) . ReadableStream ) ||
337358 ( typeof body === 'object' && body !== null && Symbol . asyncIterator in body )
0 commit comments