@@ -12,6 +12,7 @@ import { ServerAssets } from './assets';
12
12
import { Hooks } from './hooks' ;
13
13
import { getAngularAppManifest } from './manifest' ;
14
14
import { RenderMode } from './routes/route-config' ;
15
+ import { RouteTreeNodeMetadata } from './routes/route-tree' ;
15
16
import { ServerRouter } from './routes/router' ;
16
17
import { sha256 } from './utils/crypto' ;
17
18
import { InlineCriticalCssProcessor } from './utils/inline-critical-css' ;
@@ -21,9 +22,9 @@ import { joinUrlParts, stripIndexHtmlFromURL, stripLeadingSlash } from './utils/
21
22
22
23
/**
23
24
* 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.
25
26
*/
26
- const DEFAULT_MAX_AGE = 30 * 24 * 60 * 60 ;
27
+ const DEFAULT_MAX_AGE = 365 * 24 * 60 * 60 ;
27
28
28
29
/**
29
30
* Maximum number of critical CSS entries the cache can store.
@@ -107,10 +108,7 @@ export class AngularServerApp {
107
108
* @returns A promise that resolves to the HTTP response object resulting from the rendering, or null if no match is found.
108
109
*/
109
110
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 ) ;
114
112
}
115
113
116
114
/**
@@ -125,10 +123,7 @@ export class AngularServerApp {
125
123
renderStatic ( url : URL , signal ?: AbortSignal ) : Promise < Response | null > {
126
124
const request = new Request ( url , { signal } ) ;
127
125
128
- return Promise . race ( [
129
- this . createAbortPromise ( request ) ,
130
- this . handleRendering ( request , /** isSsrMode */ false ) ,
131
- ] ) ;
126
+ return this . handleAbortableRendering ( request , /** isSsrMode */ false ) ;
132
127
}
133
128
134
129
/**
@@ -143,10 +138,62 @@ export class AngularServerApp {
143
138
* if available, or `null` if the request does not match a prerendered route or asset.
144
139
*/
145
140
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 ) ;
147
158
this . router ??= await ServerRouter . from ( this . manifest , url ) ;
148
159
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 ) ;
150
197
if ( ! matchedRoute ) {
151
198
return null ;
152
199
}
@@ -156,7 +203,8 @@ export class AngularServerApp {
156
203
return null ;
157
204
}
158
205
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' ) ) ;
160
208
if ( ! this . assets . hasServerAsset ( assetPath ) ) {
161
209
return null ;
162
210
}
@@ -176,41 +224,43 @@ export class AngularServerApp {
176
224
}
177
225
178
226
/**
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.
184
230
*
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.
186
237
* @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.
196
238
*
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`.
199
241
*/
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
+ ] ) ;
214
264
}
215
265
216
266
/**
@@ -219,25 +269,26 @@ export class AngularServerApp {
219
269
*
220
270
* @param request - The incoming HTTP request to be processed.
221
271
* @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.
222
274
* @param requestContext - Optional additional context for rendering, such as request metadata.
223
275
*
224
276
* @returns A promise that resolves to the rendered response, or null if no matching route is found.
225
277
*/
226
278
private async handleRendering (
227
279
request : Request ,
228
280
isSsrMode : boolean ,
281
+ matchedRoute ?: RouteTreeNodeMetadata ,
229
282
requestContext ?: unknown ,
230
283
) : 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 ) ;
235
285
if ( ! matchedRoute ) {
236
- // Not a known Angular route.
237
286
return null ;
238
287
}
239
288
240
289
const { redirectTo, status } = matchedRoute ;
290
+ const url = new URL ( request . url ) ;
291
+
241
292
if ( redirectTo !== undefined ) {
242
293
// Note: The status code is validated during route extraction.
243
294
// 302 Found is used by default for redirections
0 commit comments