@@ -14,6 +14,7 @@ export interface BrowserPreviewNetworkPolicy {
1414 recordExternal : boolean
1515 stats : Map < string , { requests : number ; external : boolean ; blocked : number ; routed : number } >
1616 routedRedirectEscapes : Array < { rawOrigin : string ; effectiveOrigin : string ; reason : string } >
17+ preserveRoutedOrigin : boolean
1718}
1819
1920export interface BrowserPreviewNavigationDecision {
@@ -33,10 +34,20 @@ export interface BrowserPreviewTopology {
3334 preview : BrowserProbePreviewRouting
3435 networkPolicy : BrowserPreviewNetworkPolicy
3536 routedHosts : string [ ]
36- origins : { localPreviewOrigin : string ; requestedPreviewOrigin ?: string ; effectivePreviewOrigin : string }
37+ origins : BrowserPreviewOrigins
3738 navigationScope : BrowserPreviewNavigationScope
3839 resolveUrl ( pathOrUrl : string ) : string
3940 authCookieUrls ( targetUrls : string [ ] ) : string [ ]
41+ contextOptions ( ) : { proxy ?: { server : string } }
42+ }
43+
44+ export interface BrowserPreviewOrigins {
45+ localPreviewOrigin : string
46+ requestedPreviewOrigin ?: string
47+ effectivePreviewOrigin : string
48+ canonicalBrowserOrigin : string
49+ localProxyOrigin : string
50+ upstreamRuntimeOrigin ?: string
4051}
4152
4253export interface BrowserPreviewRouteTracker {
@@ -86,23 +97,27 @@ export function browserPreviewRouting(args: string[], runtimeSpec: RuntimeCreate
8697 }
8798}
8899
89- export function browserPreviewTopology ( args : string [ ] , runtimeSpec : RuntimeCreateSpec | undefined , localPreviewOrigin : string ) : BrowserPreviewTopology {
90- const preview = browserPreviewRouting ( args , runtimeSpec , localPreviewOrigin )
100+ export function browserPreviewTopology ( args : string [ ] , runtimeSpec : RuntimeCreateSpec | undefined , localPreviewOrigin : string , upstreamRuntimeOrigin ?: string ) : BrowserPreviewTopology {
91101 const routedHosts = commaListArg ( args , "route-host" )
102+ const preview = browserPreviewRouting ( args , runtimeSpec , localPreviewOrigin )
103+ applyCanonicalRoutedPreviewOrigin ( preview , runtimeSpec ?. preview ?. siteUrl , routedHosts )
92104 const networkPolicy = browserPreviewNetworkPolicy ( args , routedHosts , preview )
93105
94106 return {
95107 preview,
96108 networkPolicy,
97109 routedHosts,
98- origins : browserPreviewOrigins ( preview ) ,
110+ origins : browserPreviewOrigins ( preview , upstreamRuntimeOrigin ) ,
99111 navigationScope : browserPreviewNavigationScope ( preview . effectiveOrigin , networkPolicy ) ,
100112 resolveUrl ( pathOrUrl ) {
101113 return resolveBrowserPreviewUrl ( pathOrUrl , preview . effectiveOrigin )
102114 } ,
103115 authCookieUrls ( targetUrls ) {
104116 return browserPreviewAuthCookieUrls ( localPreviewOrigin , routedHosts , targetUrls )
105117 } ,
118+ contextOptions ( ) {
119+ return networkPolicy . preserveRoutedOrigin ? { proxy : { server : new URL ( localPreviewOrigin ) . origin } } : { }
120+ } ,
106121 }
107122}
108123
@@ -135,12 +150,50 @@ export function browserPreviewNavigationScope(effectivePreviewOrigin: string, po
135150 }
136151}
137152
138- export function browserPreviewOrigins ( preview : BrowserProbePreviewRouting ) : { localPreviewOrigin : string ; requestedPreviewOrigin ?: string ; effectivePreviewOrigin : string } {
153+ export function browserPreviewOrigins ( preview : BrowserProbePreviewRouting , upstreamRuntimeOrigin ?: string ) : BrowserPreviewOrigins {
139154 return {
140155 localPreviewOrigin : preview . localOrigin ,
141- requestedPreviewOrigin : preview . publicOrigin ,
156+ ... ( preview . publicOrigin ? { requestedPreviewOrigin : preview . publicOrigin } : { } ) ,
142157 effectivePreviewOrigin : preview . effectiveOrigin ,
158+ canonicalBrowserOrigin : new URL ( preview . effectiveOrigin ) . origin ,
159+ localProxyOrigin : new URL ( preview . localOrigin ) . origin ,
160+ ...( upstreamRuntimeOrigin ? { upstreamRuntimeOrigin : new URL ( upstreamRuntimeOrigin ) . origin } : { } ) ,
161+ }
162+ }
163+
164+ function applyCanonicalRoutedPreviewOrigin ( preview : BrowserProbePreviewRouting , siteUrl : string | undefined , routedHosts : string [ ] ) : void {
165+ if ( preview . effectiveMode !== "local" || ! siteUrl ) {
166+ return
167+ }
168+
169+ let canonical : URL
170+ try {
171+ canonical = new URL ( siteUrl )
172+ } catch {
173+ return
174+ }
175+ const host = normalizeBrowserPreviewHost ( canonical . hostname )
176+ if ( ! routedHosts . map ( normalizeBrowserPreviewHost ) . includes ( host ) ) {
177+ return
178+ }
179+
180+ if ( canonical . protocol !== "http:" ) {
181+ preview . diagnostics . push ( {
182+ code : "preview-canonical-origin-preservation-inconclusive" ,
183+ severity : "error" ,
184+ message : "The local Playground provider cannot preserve this declared canonical preview protocol." ,
185+ details : { status : "inconclusive" , canonicalOrigin : canonical . origin , supportedProtocols : [ "http:" ] } ,
186+ } )
187+ return
143188 }
189+
190+ preview . effectiveOrigin = canonical . toString ( )
191+ preview . diagnostics . push ( {
192+ code : "preview-canonical-routed-origin" ,
193+ severity : "info" ,
194+ message : "The declared routed preview alias is the browser-visible origin." ,
195+ details : { canonicalOrigin : canonical . origin , localProxyOrigin : new URL ( preview . localOrigin ) . origin } ,
196+ } )
144197}
145198
146199export function browserPreviewReadinessError ( preview : BrowserProbePreviewRouting ) : Error | undefined {
@@ -207,6 +260,7 @@ export function browserPreviewNetworkPolicy(args: string[], routeHosts: string[]
207260 recordExternal : strictBooleanArg ( args , "record-external" , false ) ,
208261 stats : new Map ( ) ,
209262 routedRedirectEscapes : [ ] ,
263+ preserveRoutedOrigin : new URL ( preview . effectiveOrigin ) . origin !== new URL ( preview . localOrigin ) . origin && preview . effectiveMode === "local" ,
210264 }
211265}
212266
@@ -293,28 +347,38 @@ async function routeBrowserPreviewNetwork(routePattern: (url: string, handler: (
293347 stat . requests += 1
294348 stat . external = ! policy . firstPartyHosts . has ( host )
295349
296- if ( policy . blockHosts . has ( host ) || ( policy . mode === "block" && stat . external && ! policy . allowHosts . has ( host ) ) ) {
350+ if ( policy . blockHosts . has ( host ) ) {
297351 stat . blocked += 1
298352 await route . abort ( "blockedbyclient" )
299353 return
300354 }
301355
302- if ( ! policy . routeHosts . has ( host ) ) {
303- await route . continue ( )
356+ if ( policy . routeHosts . has ( host ) ) {
357+ stat . routed += 1
358+ if ( policy . preserveRoutedOrigin ) {
359+ await route . continue ( )
360+ return
361+ }
362+ const task = fulfillBrowserPreviewRoutedHost ( route , requestUrl , policy , origin )
363+ tracker ?. pending . add ( task )
364+ try {
365+ await task
366+ } catch ( error ) {
367+ tracker ?. errors . push ( error )
368+ throw error
369+ } finally {
370+ tracker ?. pending . delete ( task )
371+ }
304372 return
305373 }
306374
307- stat . routed += 1
308- const task = fulfillBrowserPreviewRoutedHost ( route , requestUrl , policy , origin )
309- tracker ?. pending . add ( task )
310- try {
311- await task
312- } catch ( error ) {
313- tracker ?. errors . push ( error )
314- throw error
315- } finally {
316- tracker ?. pending . delete ( task )
375+ if ( policy . preserveRoutedOrigin || ( policy . mode === "block" && stat . external && ! policy . allowHosts . has ( host ) ) || ( request . resourceType ( ) === "document" && stat . external ) ) {
376+ stat . blocked += 1
377+ await route . abort ( "blockedbyclient" )
378+ return
317379 }
380+
381+ await route . continue ( )
318382 } )
319383}
320384
0 commit comments