@@ -34,6 +34,9 @@ const clientLocalsSymbol = Symbol.for('astro.locals');
3434
3535const responseSentSymbol = Symbol . for ( 'astro.responseSent' ) ;
3636
37+ const STATUS_CODES = new Set ( [ 404 , 500 ] ) ;
38+ const STATUS_CODE_ROUTES = new Set ( [ '/404' , '/500' ] ) ;
39+
3740export interface MatchOptions {
3841 matchNotFound ?: boolean | undefined ;
3942}
@@ -113,7 +116,7 @@ export class App {
113116 }
114117 return pathname ;
115118 }
116- match ( request : Request , { matchNotFound = false } : MatchOptions = { } ) : RouteData | undefined {
119+ match ( request : Request , _ : MatchOptions = { } ) : RouteData | undefined {
117120 const url = new URL ( request . url ) ;
118121 // ignore requests matching public assets
119122 if ( this . #manifest. assets . has ( url . pathname ) ) {
@@ -125,38 +128,21 @@ export class App {
125128 if ( routeData ) {
126129 if ( routeData . prerender ) return undefined ;
127130 return routeData ;
128- } else if ( matchNotFound ) {
129- const notFoundRouteData = matchRoute ( '/404' , this . #manifestData) ;
130- if ( notFoundRouteData ?. prerender ) return undefined ;
131- return notFoundRouteData ;
132131 } else {
133132 return undefined ;
134133 }
135134 }
136135 async render ( request : Request , routeData ?: RouteData , locals ?: object ) : Promise < Response > {
137- let defaultStatus = 200 ;
138136 if ( ! routeData ) {
139137 routeData = this . match ( request ) ;
140138 if ( ! routeData ) {
141- defaultStatus = 404 ;
142- routeData = this . match ( request , { matchNotFound : true } ) ;
143- }
144- if ( ! routeData ) {
145- return new Response ( null , {
146- status : 404 ,
147- statusText : 'Not found' ,
148- } ) ;
139+ return this . #renderStatusCode( request , routeData , 404 ) ;
149140 }
150141 }
151142
152143 Reflect . set ( request , clientLocalsSymbol , locals ?? { } ) ;
153-
154- // Use the 404 status code for 404.astro components
155- if ( routeData . route === '/404' ) {
156- defaultStatus = 404 ;
157- }
158-
159- let mod = await this . #getModuleForRoute( routeData ) ;
144+ const defaultStatus = this . #getDefaultStatusCode( routeData . route ) ;
145+ const mod = await this . #getModuleForRoute( routeData ) ;
160146
161147 const pageModule = ( await mod . page ( ) ) as any ;
162148 const url = new URL ( request . url ) ;
@@ -179,47 +165,19 @@ export class App {
179165 ) ;
180166 } catch ( err : any ) {
181167 error ( this . #logging, 'ssr' , err . stack || err . message || String ( err ) ) ;
182- response = new Response ( null , {
183- status : 500 ,
184- statusText : 'Internal server error' ,
185- } ) ;
168+ return this . #renderStatusCode( request , routeData , 500 ) ;
186169 }
187170
188171 if ( isResponse ( response , routeData . type ) ) {
189- // If there was a known error code, try sending the according page (e.g. 404.astro / 500.astro).
190- if ( response . status === 500 || response . status === 404 ) {
191- const errorRouteData = matchRoute ( '/' + response . status , this . #manifestData) ;
192- if ( errorRouteData && errorRouteData . route !== routeData . route ) {
193- mod = await this . #getModuleForRoute( errorRouteData ) ;
194- try {
195- const newRenderContext = await this . #createRenderContext(
196- url ,
197- request ,
198- routeData ,
199- mod ,
200- response . status
201- ) ;
202- const page = ( await mod . page ( ) ) as any ;
203- const errorResponse = await tryRenderRoute (
204- routeData . type ,
205- newRenderContext ,
206- this . #env,
207- page
208- ) ;
209- return errorResponse as Response ;
210- } catch { }
211- }
172+ if ( STATUS_CODES . has ( response . status ) ) {
173+ return this . #renderStatusCode( request , routeData , response . status as 404 | 500 ) ;
212174 }
213175 Reflect . set ( response , responseSentSymbol , true ) ;
214176 return response ;
215177 } else {
216178 if ( response . type === 'response' ) {
217179 if ( response . response . headers . get ( 'X-Astro-Response' ) === 'Not-Found' ) {
218- const fourOhFourRequest = new Request ( new URL ( '/404' , request . url ) ) ;
219- const fourOhFourRouteData = this . match ( fourOhFourRequest ) ;
220- if ( fourOhFourRouteData ) {
221- return this . render ( fourOhFourRequest , fourOhFourRouteData ) ;
222- }
180+ return this . #renderStatusCode( request , routeData , 404 ) ;
223181 }
224182 return response . response ;
225183 } else {
@@ -307,6 +265,53 @@ export class App {
307265 }
308266 }
309267
268+ #getDefaultStatusCode( route : string ) {
269+ route = removeTrailingForwardSlash ( route )
270+ if ( route . endsWith ( '/404' ) ) return 404 ;
271+ if ( route . endsWith ( '/500' ) ) return 500 ;
272+ return 200 ;
273+ }
274+
275+ /**
276+ * If is a known error code, try sending the according page (e.g. 404.astro / 500.astro).
277+ * This also handles pre-rendered /404 or /500 routes
278+ */
279+ async #renderStatusCode( request : Request , routeData : RouteData | undefined , status : 404 | 500 ) {
280+ const errorRouteData = matchRoute ( '/' + status , this . #manifestData) ;
281+ const url = new URL ( request . url ) ;
282+ if ( errorRouteData ) {
283+ if ( errorRouteData . prerender ) {
284+ const statusURL = new URL ( `${ this . #baseWithoutTrailingSlash} /${ status } ` , url ) ;
285+ return fetch ( statusURL . toString ( ) ) ;
286+ }
287+
288+ const finalRouteData = routeData ?? errorRouteData ;
289+
290+ let mod = await this . #getModuleForRoute( errorRouteData ) ;
291+ try {
292+ const newRenderContext = await this . #createRenderContext(
293+ url ,
294+ request ,
295+ finalRouteData ,
296+ mod ,
297+ status
298+ ) ;
299+ const page = ( await mod . page ( ) ) as any ;
300+ const errorResponse = await tryRenderRoute (
301+ finalRouteData . type ,
302+ newRenderContext ,
303+ this . #env,
304+ page
305+ ) ;
306+ return errorResponse as Response ;
307+ } catch { }
308+ }
309+
310+ const response = new Response ( null , { status } ) ;
311+ Reflect . set ( response , responseSentSymbol , true ) ;
312+ return response ;
313+ }
314+
310315 async #getModuleForRoute( route : RouteData ) : Promise < SinglePageBuiltModule > {
311316 if ( route . type === 'redirect' ) {
312317 return RedirectSinglePageBuiltModule ;
0 commit comments