@@ -12,6 +12,7 @@ import { ServerAssets } from './assets';
1212import { Hooks } from './hooks' ;
1313import { getAngularAppManifest } from './manifest' ;
1414import { RenderMode } from './routes/route-config' ;
15+ import { RouteTreeNodeMetadata } from './routes/route-tree' ;
1516import { ServerRouter } from './routes/router' ;
1617import { sha256 } from './utils/crypto' ;
1718import { InlineCriticalCssProcessor } from './utils/inline-critical-css' ;
@@ -21,9 +22,9 @@ import { joinUrlParts, stripIndexHtmlFromURL, stripLeadingSlash } from './utils/
2122
2223/**
2324 * The default maximum age in seconds.
24- * Represents the total number of seconds in a 30 -day period.
25+ * Represents the total number of seconds in a 365 -day period.
2526 */
26- const DEFAULT_MAX_AGE = 30 * 24 * 60 * 60 ;
27+ const DEFAULT_MAX_AGE = 365 * 24 * 60 * 60 ;
2728
2829/**
2930 * Maximum number of critical CSS entries the cache can store.
@@ -107,10 +108,7 @@ export class AngularServerApp {
107108 * @returns A promise that resolves to the HTTP response object resulting from the rendering, or null if no match is found.
108109 */
109110 render ( request : Request , requestContext ?: unknown ) : Promise < Response | null > {
110- return Promise . race ( [
111- this . createAbortPromise ( request ) ,
112- this . handleRendering ( request , /** isSsrMode */ true , requestContext ) ,
113- ] ) ;
111+ return this . handleAbortableRendering ( request , /** isSsrMode */ true , undefined , requestContext ) ;
114112 }
115113
116114 /**
@@ -125,10 +123,7 @@ export class AngularServerApp {
125123 renderStatic ( url : URL , signal ?: AbortSignal ) : Promise < Response | null > {
126124 const request = new Request ( url , { signal } ) ;
127125
128- return Promise . race ( [
129- this . createAbortPromise ( request ) ,
130- this . handleRendering ( request , /** isSsrMode */ false ) ,
131- ] ) ;
126+ return this . handleAbortableRendering ( request , /** isSsrMode */ false ) ;
132127 }
133128
134129 /**
@@ -143,10 +138,62 @@ export class AngularServerApp {
143138 * if available, or `null` if the request does not match a prerendered route or asset.
144139 */
145140 async serve ( request : Request ) : Promise < Response | null > {
146- const url = stripIndexHtmlFromURL ( new URL ( request . url ) ) ;
141+ return this . handleServe ( request ) ;
142+ }
143+
144+ /**
145+ * Handles incoming HTTP requests by serving prerendered content or rendering the page.
146+ *
147+ * This method first attempts to serve a prerendered page. If the prerendered page is not available,
148+ * it falls back to rendering the requested page using server-side rendering. The function returns
149+ * a promise that resolves to the appropriate HTTP response.
150+ *
151+ * @param request - The incoming HTTP request to be processed.
152+ * @param requestContext - Optional additional context for rendering, such as request metadata.
153+ * @returns A promise that resolves to the HTTP response object resulting from the request handling,
154+ * or null if no matching content is found.
155+ */
156+ async process ( request : Request , requestContext ?: unknown ) : Promise < Response | null > {
157+ const url = new URL ( request . url ) ;
147158 this . router ??= await ServerRouter . from ( this . manifest , url ) ;
148159
149- const matchedRoute = this . router . match ( new URL ( request . url ) ) ;
160+ const matchedRoute = this . router . match ( url ) ;
161+ if ( ! matchedRoute ) {
162+ // Not a known Angular route.
163+ return null ;
164+ }
165+
166+ return (
167+ ( await this . handleServe ( request , matchedRoute ) ) ??
168+ this . handleAbortableRendering ( request , /** isSsrMode */ true , matchedRoute , requestContext )
169+ ) ;
170+ }
171+
172+ /**
173+ * Retrieves the matched route for the incoming request based on the request URL.
174+ *
175+ * @param request - The incoming HTTP request to match against routes.
176+ * @returns A promise that resolves to the matched route metadata or `undefined` if no route matches.
177+ */
178+ private async getMatchedRoute ( request : Request ) : Promise < RouteTreeNodeMetadata | undefined > {
179+ this . router ??= await ServerRouter . from ( this . manifest , new URL ( request . url ) ) ;
180+
181+ return this . router . match ( new URL ( request . url ) ) ;
182+ }
183+
184+ /**
185+ * Handles serving a prerendered static asset if available for the matched route.
186+ *
187+ * @param request - The incoming HTTP request for serving a static page.
188+ * @param matchedRoute - Optional parameter representing the metadata of the matched route for rendering.
189+ * If not provided, the method attempts to find a matching route based on the request URL.
190+ * @returns A promise that resolves to a `Response` object if the prerendered page is found, or `null`.
191+ */
192+ private async handleServe (
193+ request : Request ,
194+ matchedRoute ?: RouteTreeNodeMetadata ,
195+ ) : Promise < Response | null > {
196+ matchedRoute ??= await this . getMatchedRoute ( request ) ;
150197 if ( ! matchedRoute ) {
151198 return null ;
152199 }
@@ -156,7 +203,8 @@ export class AngularServerApp {
156203 return null ;
157204 }
158205
159- const assetPath = stripLeadingSlash ( joinUrlParts ( url . pathname , 'index.html' ) ) ;
206+ const { pathname } = stripIndexHtmlFromURL ( new URL ( request . url ) ) ;
207+ const assetPath = stripLeadingSlash ( joinUrlParts ( pathname , 'index.html' ) ) ;
160208 if ( ! this . assets . hasServerAsset ( assetPath ) ) {
161209 return null ;
162210 }
@@ -176,41 +224,43 @@ export class AngularServerApp {
176224 }
177225
178226 /**
179- * Handles incoming HTTP requests by serving prerendered content or rendering the page.
180- *
181- * This method first attempts to serve a prerendered page. If the prerendered page is not available,
182- * it falls back to rendering the requested page using server-side rendering. The function returns
183- * a promise that resolves to the appropriate HTTP response.
227+ * Handles the server-side rendering process for the given HTTP request, allowing for abortion
228+ * of the rendering if the request is aborted. This method matches the request URL to a route
229+ * and performs rendering if a matching route is found.
184230 *
185- * @param request - The incoming HTTP request to be processed.
231+ * @param request - The incoming HTTP request to be processed. It includes a signal to monitor
232+ * for abortion events.
233+ * @param isSsrMode - A boolean indicating whether the rendering is performed in server-side
234+ * rendering (SSR) mode.
235+ * @param matchedRoute - Optional parameter representing the metadata of the matched route for
236+ * rendering. If not provided, the method attempts to find a matching route based on the request URL.
186237 * @param requestContext - Optional additional context for rendering, such as request metadata.
187- * @returns A promise that resolves to the HTTP response object resulting from the request handling,
188- * or null if no matching content is found.
189- */
190- async process ( request : Request , requestContext ?: unknown ) : Promise < Response | null > {
191- return ( await this . serve ( request ) ) ?? ( await this . render ( request , requestContext ) ) ;
192- }
193-
194- /**
195- * Creates a promise that rejects when the request is aborted.
196238 *
197- * @param request - The HTTP request to monitor for abortion .
198- * @returns A promise that never resolves but rejects with an `AbortError` if the request is aborted .
239+ * @returns A promise that resolves to the rendered response, or null if no matching route is found .
240+ * If the request is aborted, the promise will reject with an `AbortError`.
199241 */
200- private createAbortPromise ( request : Request ) : Promise < never > {
201- return new Promise < never > ( ( _ , reject ) => {
202- request . signal . addEventListener (
203- 'abort' ,
204- ( ) => {
205- const abortError = new Error (
206- `Request for: ${ request . url } was aborted.\n${ request . signal . reason } ` ,
207- ) ;
208- abortError . name = 'AbortError' ;
209- reject ( abortError ) ;
210- } ,
211- { once : true } ,
212- ) ;
213- } ) ;
242+ private async handleAbortableRendering (
243+ request : Request ,
244+ isSsrMode : boolean ,
245+ matchedRoute ?: RouteTreeNodeMetadata ,
246+ requestContext ?: unknown ,
247+ ) : Promise < Response | null > {
248+ return Promise . race ( [
249+ new Promise < never > ( ( _ , reject ) => {
250+ request . signal . addEventListener (
251+ 'abort' ,
252+ ( ) => {
253+ const abortError = new Error (
254+ `Request for: ${ request . url } was aborted.\n${ request . signal . reason } ` ,
255+ ) ;
256+ abortError . name = 'AbortError' ;
257+ reject ( abortError ) ;
258+ } ,
259+ { once : true } ,
260+ ) ;
261+ } ) ,
262+ this . handleRendering ( request , isSsrMode , matchedRoute , requestContext ) ,
263+ ] ) ;
214264 }
215265
216266 /**
@@ -219,25 +269,26 @@ export class AngularServerApp {
219269 *
220270 * @param request - The incoming HTTP request to be processed.
221271 * @param isSsrMode - A boolean indicating whether the rendering is performed in server-side rendering (SSR) mode.
272+ * @param matchedRoute - Optional parameter representing the metadata of the matched route for rendering.
273+ * If not provided, the method attempts to find a matching route based on the request URL.
222274 * @param requestContext - Optional additional context for rendering, such as request metadata.
223275 *
224276 * @returns A promise that resolves to the rendered response, or null if no matching route is found.
225277 */
226278 private async handleRendering (
227279 request : Request ,
228280 isSsrMode : boolean ,
281+ matchedRoute ?: RouteTreeNodeMetadata ,
229282 requestContext ?: unknown ,
230283 ) : Promise < Response | null > {
231- const url = new URL ( request . url ) ;
232- this . router ??= await ServerRouter . from ( this . manifest , url ) ;
233-
234- const matchedRoute = this . router . match ( url ) ;
284+ matchedRoute ??= await this . getMatchedRoute ( request ) ;
235285 if ( ! matchedRoute ) {
236- // Not a known Angular route.
237286 return null ;
238287 }
239288
240289 const { redirectTo, status } = matchedRoute ;
290+ const url = new URL ( request . url ) ;
291+
241292 if ( redirectTo !== undefined ) {
242293 // Note: The status code is validated during route extraction.
243294 // 302 Found is used by default for redirections
0 commit comments