@@ -15,10 +15,12 @@ import type { Pipeline } from '../base-pipeline.js';
1515import {
1616 clientAddressSymbol ,
1717 DEFAULT_404_COMPONENT ,
18+ NOOP_MIDDLEWARE_HEADER ,
1819 REROUTABLE_STATUS_CODES ,
1920 REROUTE_DIRECTIVE_HEADER ,
2021 responseSentSymbol ,
2122 REWRITE_DIRECTIVE_HEADER_KEY ,
23+ ROUTE_TYPE_HEADER ,
2224} from '../constants.js' ;
2325import { getSetCookiesFromResponse } from '../cookies/index.js' ;
2426import { AstroError , AstroErrorData } from '../errors/index.js' ;
@@ -84,9 +86,17 @@ export interface RenderOptions {
8486 routeData ?: RouteData ;
8587}
8688
87- export interface RenderErrorOptions {
88- locals ?: App . Locals ;
89- routeData ?: RouteData ;
89+ type RequiredRenderOptions = Required < RenderOptions > ;
90+
91+ interface ResolvedRenderOptions {
92+ addCookieHeader : RequiredRenderOptions [ 'addCookieHeader' ] ;
93+ clientAddress : RequiredRenderOptions [ 'clientAddress' ] | undefined ;
94+ prerenderedErrorPageFetch : RequiredRenderOptions [ 'prerenderedErrorPageFetch' ] | undefined ;
95+ locals : RequiredRenderOptions [ 'locals' ] | undefined ;
96+ routeData : RequiredRenderOptions [ 'routeData' ] | undefined ;
97+ }
98+
99+ export interface RenderErrorOptions extends ResolvedRenderOptions {
90100 response ?: Response ;
91101 status : 404 | 500 ;
92102 /**
@@ -97,8 +107,6 @@ export interface RenderErrorOptions {
97107 * Allows passing an error to 500.astro. It will be available through `Astro.props.error`.
98108 */
99109 error ?: unknown ;
100- clientAddress : string | undefined ;
101- prerenderedErrorPageFetch : ( ( url : ErrorPagePath ) => Promise < Response > ) | undefined ;
102110}
103111
104112type ErrorPagePath =
@@ -355,19 +363,23 @@ export abstract class BaseApp<P extends Pipeline = AppPipeline> {
355363 return pathname ;
356364 }
357365
358- public async render ( request : Request , renderOptions ?: RenderOptions ) : Promise < Response > {
366+ public async render (
367+ request : Request ,
368+ {
369+ addCookieHeader = false ,
370+ clientAddress = Reflect . get ( request , clientAddressSymbol ) ,
371+ locals,
372+ prerenderedErrorPageFetch = fetch ,
373+ routeData,
374+ } : RenderOptions = { } ,
375+ ) : Promise < Response > {
359376 const timeStart = performance . now ( ) ;
360- let routeData : RouteData | undefined = renderOptions ?. routeData ;
361- let locals : object | undefined ;
362- let clientAddress : string | undefined ;
363- let addCookieHeader : boolean | undefined ;
364377 const url = new URL ( request . url ) ;
365378 const redirect = this . redirectTrailingSlash ( url . pathname ) ;
366- const prerenderedErrorPageFetch = renderOptions ?. prerenderedErrorPageFetch ?? fetch ;
367379
368380 if ( redirect !== url . pathname ) {
369381 const status = request . method === 'GET' ? 301 : 308 ;
370- return new Response (
382+ const response = new Response (
371383 redirectTemplate ( {
372384 status,
373385 relativeLocation : url . pathname ,
@@ -381,13 +393,10 @@ export abstract class BaseApp<P extends Pipeline = AppPipeline> {
381393 } ,
382394 } ,
383395 ) ;
396+ this . #prepareResponse( response , { addCookieHeader } ) ;
397+ return response ;
384398 }
385399
386- addCookieHeader = renderOptions ?. addCookieHeader ;
387- clientAddress = renderOptions ?. clientAddress ?? Reflect . get ( request , clientAddressSymbol ) ;
388- routeData = renderOptions ?. routeData ;
389- locals = renderOptions ?. locals ;
390-
391400 if ( routeData ) {
392401 this . logger . debug (
393402 'router' ,
@@ -397,15 +406,26 @@ export abstract class BaseApp<P extends Pipeline = AppPipeline> {
397406 this . logger . debug ( 'router' , 'RouteData' ) ;
398407 this . logger . debug ( 'router' , routeData ) ;
399408 }
409+
410+ const resolvedRenderOptions : ResolvedRenderOptions = {
411+ addCookieHeader,
412+ clientAddress,
413+ prerenderedErrorPageFetch,
414+ locals,
415+ routeData,
416+ } ;
417+
400418 if ( locals ) {
401419 if ( typeof locals !== 'object' ) {
402420 const error = new AstroError ( AstroErrorData . LocalsNotAnObject ) ;
403421 this . logger . error ( null , error . stack ! ) ;
404422 return this . renderError ( request , {
423+ ...resolvedRenderOptions ,
424+ // If locals are invalid, we don't want to include them when
425+ // rendering the error page
426+ locals : undefined ,
405427 status : 500 ,
406428 error,
407- clientAddress,
408- prerenderedErrorPageFetch : prerenderedErrorPageFetch ,
409429 } ) ;
410430 }
411431 }
@@ -434,10 +454,8 @@ export abstract class BaseApp<P extends Pipeline = AppPipeline> {
434454 this . logger . debug ( 'router' , "Astro hasn't found routes that match " + request . url ) ;
435455 this . logger . debug ( 'router' , "Here's the available routes:\n" , this . manifestData ) ;
436456 return this . renderError ( request , {
437- locals ,
457+ ... resolvedRenderOptions ,
438458 status : 404 ,
439- clientAddress,
440- prerenderedErrorPageFetch : prerenderedErrorPageFetch ,
441459 } ) ;
442460 }
443461 const pathname = this . getPathnameFromRequest ( request ) ;
@@ -472,11 +490,9 @@ export abstract class BaseApp<P extends Pipeline = AppPipeline> {
472490 } catch ( err : any ) {
473491 this . logger . error ( null , err . stack || err . message || String ( err ) ) ;
474492 return this . renderError ( request , {
475- locals ,
493+ ... resolvedRenderOptions ,
476494 status : 500 ,
477495 error : err ,
478- clientAddress,
479- prerenderedErrorPageFetch : prerenderedErrorPageFetch ,
480496 } ) ;
481497 } finally {
482498 await session ?. [ PERSIST_SYMBOL ] ( ) ;
@@ -490,30 +506,39 @@ export abstract class BaseApp<P extends Pipeline = AppPipeline> {
490506 response . headers . get ( REROUTE_DIRECTIVE_HEADER ) !== 'no'
491507 ) {
492508 return this . renderError ( request , {
493- locals ,
509+ ... resolvedRenderOptions ,
494510 response,
495511 status : response . status as 404 | 500 ,
496512 // We don't have an error to report here. Passing null means we pass nothing intentionally
497513 // while undefined means there's no error
498514 error : response . status === 500 ? null : undefined ,
499- clientAddress,
500- prerenderedErrorPageFetch : prerenderedErrorPageFetch ,
501515 } ) ;
502516 }
503517
518+ this . #prepareResponse( response , { addCookieHeader } ) ;
519+ return response ;
520+ }
521+
522+ #prepareResponse( response : Response , { addCookieHeader } : { addCookieHeader : boolean } ) : void {
504523 // We remove internally-used header before we send the response to the user agent.
505- if ( response . headers . has ( REROUTE_DIRECTIVE_HEADER ) ) {
506- response . headers . delete ( REROUTE_DIRECTIVE_HEADER ) ;
524+ for ( const headerName of [
525+ REROUTE_DIRECTIVE_HEADER ,
526+ REWRITE_DIRECTIVE_HEADER_KEY ,
527+ NOOP_MIDDLEWARE_HEADER ,
528+ ROUTE_TYPE_HEADER ,
529+ ] ) {
530+ if ( response . headers . has ( headerName ) ) {
531+ response . headers . delete ( headerName ) ;
532+ }
507533 }
508534
509535 if ( addCookieHeader ) {
510- for ( const setCookieHeaderValue of BaseApp . getSetCookieFromResponse ( response ) ) {
536+ for ( const setCookieHeaderValue of getSetCookiesFromResponse ( response ) ) {
511537 response . headers . append ( 'set-cookie' , setCookieHeaderValue ) ;
512538 }
513539 }
514540
515541 Reflect . set ( response , responseSentSymbol , true ) ;
516- return response ;
517542 }
518543
519544 setCookieHeaders ( response : Response ) {
@@ -540,13 +565,11 @@ export abstract class BaseApp<P extends Pipeline = AppPipeline> {
540565 public async renderError (
541566 request : Request ,
542567 {
543- locals,
544568 status,
545569 response : originalResponse ,
546570 skipMiddleware = false ,
547571 error,
548- clientAddress,
549- prerenderedErrorPageFetch,
572+ ...resolvedRenderOptions
550573 } : RenderErrorOptions ,
551574 ) : Promise < Response > {
552575 const errorRoutePath = `/${ status } ${ this . manifest . trailingSlash === 'always' ? '/' : '' } ` ;
@@ -556,8 +579,13 @@ export abstract class BaseApp<P extends Pipeline = AppPipeline> {
556579 if ( errorRouteData . prerender ) {
557580 const maybeDotHtml = errorRouteData . route . endsWith ( `/${ status } ` ) ? '.html' : '' ;
558581 const statusURL = new URL ( `${ this . baseWithoutTrailingSlash } /${ status } ${ maybeDotHtml } ` , url ) ;
559- if ( statusURL . toString ( ) !== request . url && prerenderedErrorPageFetch ) {
560- const response = await prerenderedErrorPageFetch ( statusURL . toString ( ) as ErrorPagePath ) ;
582+ if (
583+ statusURL . toString ( ) !== request . url &&
584+ resolvedRenderOptions . prerenderedErrorPageFetch
585+ ) {
586+ const response = await resolvedRenderOptions . prerenderedErrorPageFetch (
587+ statusURL . toString ( ) as ErrorPagePath ,
588+ ) ;
561589
562590 // In order for the response of the remote to be usable as a response
563591 // for this request, it needs to have our status code in the response
@@ -571,36 +599,38 @@ export abstract class BaseApp<P extends Pipeline = AppPipeline> {
571599 // not match the body we provide and need to be removed.
572600 const override = { status, removeContentEncodingHeaders : true } ;
573601
574- return this . mergeResponses ( response , originalResponse , override ) ;
602+ const newResponse = this . mergeResponses ( response , originalResponse , override ) ;
603+ this . #prepareResponse( newResponse , resolvedRenderOptions ) ;
604+ return newResponse ;
575605 }
576606 }
577607 const mod = await this . pipeline . getComponentByRoute ( errorRouteData ) ;
578608 let session : AstroSession | undefined ;
579609 try {
580610 const renderContext = await this . createRenderContext ( {
581- locals,
611+ locals : resolvedRenderOptions . locals ,
582612 pipeline : this . pipeline ,
583613 skipMiddleware,
584614 pathname : this . getPathnameFromRequest ( request ) ,
585615 request,
586616 routeData : errorRouteData ,
587617 status,
588618 props : { error } ,
589- clientAddress,
619+ clientAddress : resolvedRenderOptions . clientAddress ,
590620 } ) ;
591621 session = renderContext . session ;
592622 const response = await renderContext . render ( mod ) ;
593- return this . mergeResponses ( response , originalResponse ) ;
623+ const newResponse = this . mergeResponses ( response , originalResponse ) ;
624+ this . #prepareResponse( newResponse , resolvedRenderOptions ) ;
625+ return newResponse ;
594626 } catch {
595627 // Middleware may be the cause of the error, so we try rendering 404/500.astro without it.
596628 if ( skipMiddleware === false ) {
597629 return this . renderError ( request , {
598- locals ,
630+ ... resolvedRenderOptions ,
599631 status,
600632 response : originalResponse ,
601633 skipMiddleware : true ,
602- clientAddress,
603- prerenderedErrorPageFetch,
604634 } ) ;
605635 }
606636 } finally {
@@ -609,7 +639,7 @@ export abstract class BaseApp<P extends Pipeline = AppPipeline> {
609639 }
610640
611641 const response = this . mergeResponses ( new Response ( null , { status } ) , originalResponse ) ;
612- Reflect . set ( response , responseSentSymbol , true ) ;
642+ this . #prepareResponse ( response , resolvedRenderOptions ) ;
613643 return response ;
614644 }
615645
0 commit comments