@@ -48,24 +48,31 @@ const geoHeaderToNextHeader = {
4848 "x-open-next-longitude" : "x-vercel-ip-longitude" ,
4949} ;
5050
51+ /**
52+ * Adds the middleware headers to an event or result.
53+ *
54+ * @param eventOrResult
55+ * @param middlewareHeaders
56+ */
5157function applyMiddlewareHeaders (
52- eventHeaders : Record < string , string | string [ ] > ,
58+ eventOrResult : InternalEvent | InternalResult ,
5359 middlewareHeaders : Record < string , string | string [ ] | undefined > ,
54- setPrefix = true ,
5560) {
56- const keyPrefix = setPrefix ? MIDDLEWARE_HEADER_PREFIX : "" ;
61+ // Use the `MIDDLEWARE_HEADER_PREFIX` prefix for events, they will be processed by the request handler later.
62+ // Results do not go through the request handler and should not be prefixed.
63+ const isResult = isInternalResult ( eventOrResult ) ;
64+ const headers = eventOrResult . headers ;
65+ const keyPrefix = isResult ? "" : MIDDLEWARE_HEADER_PREFIX ;
5766 Object . entries ( middlewareHeaders ) . forEach ( ( [ key , value ] ) => {
5867 if ( value ) {
59- eventHeaders [ keyPrefix + key ] = Array . isArray ( value )
60- ? value . join ( "," )
61- : value ;
68+ headers [ keyPrefix + key ] = Array . isArray ( value ) ? value . join ( "," ) : value ;
6269 }
6370 } ) ;
6471}
6572
6673export default async function routingHandler (
6774 event : InternalEvent ,
68- { assetResolver } : { assetResolver ?: AssetResolver } = { } ,
75+ { assetResolver } : { assetResolver ?: AssetResolver } ,
6976) : Promise < InternalResult | RoutingResult > {
7077 try {
7178 // Add Next geo headers
@@ -91,14 +98,13 @@ export default async function routingHandler(
9198
9299 const nextHeaders = getNextConfigHeaders ( event , ConfigHeaders ) ;
93100
94- const internalEventOrResult = fixDataPage ( event , BuildId ) ;
95- if ( "statusCode" in internalEventOrResult ) {
96- return internalEventOrResult ;
97- }
101+ let eventOrResult = fixDataPage ( event , BuildId ) ;
98102
99- let internalEvent : InternalEvent | InternalResult = internalEventOrResult ;
103+ if ( isInternalResult ( eventOrResult ) ) {
104+ return eventOrResult ;
105+ }
100106
101- const redirect = handleRedirects ( internalEvent , RoutesManifest . redirects ) ;
107+ const redirect = handleRedirects ( eventOrResult , RoutesManifest . redirects ) ;
102108 if ( redirect ) {
103109 // We need to encode the value in the Location header to make sure it is valid according to RFC
104110 // https://stackoverflow.com/a/7654605/16587222
@@ -110,68 +116,89 @@ export default async function routingHandler(
110116 }
111117
112118 const middlewareEventOrResult = await handleMiddleware (
113- internalEvent ,
119+ eventOrResult ,
114120 // We need to pass the initial search without any decoding
115121 // TODO: we'd need to refactor InternalEvent to include the initial querystring directly
116122 // Should be done in another PR because it is a breaking change
117123 new URL ( event . url ) . search ,
118124 ) ;
119- if ( "statusCode" in middlewareEventOrResult ) {
125+ if ( isInternalResult ( middlewareEventOrResult ) ) {
120126 return middlewareEventOrResult ;
121127 }
122128
123- const middlewareResponseHeaders = middlewareEventOrResult . responseHeaders ;
129+ const middlewareResponseHeaders = {
130+ ...middlewareEventOrResult . responseHeaders ,
131+ ...nextHeaders ,
132+ } ;
124133 let isExternalRewrite = middlewareEventOrResult . isExternalRewrite ?? false ;
125- internalEvent = middlewareEventOrResult ;
134+ eventOrResult = middlewareEventOrResult ;
126135
127136 if ( ! isExternalRewrite ) {
128137 // First rewrite to be applied
129- const beforeRewrites = handleRewrites (
130- internalEvent ,
138+ const beforeRewrite = handleRewrites (
139+ eventOrResult ,
131140 RoutesManifest . rewrites . beforeFiles ,
132141 ) ;
133- internalEvent = beforeRewrites . internalEvent ;
134- isExternalRewrite = beforeRewrites . isExternalRewrite ;
142+ eventOrResult = beforeRewrite . internalEvent ;
143+ isExternalRewrite = beforeRewrite . isExternalRewrite ;
144+ // Check for matching public files after `beforeFiles` rewrites
145+ if ( ! isExternalRewrite ) {
146+ const assetResult =
147+ await assetResolver ?. getMaybeAssetResult ?.( eventOrResult ) ;
148+ if ( assetResult ) {
149+ applyMiddlewareHeaders ( assetResult , middlewareResponseHeaders ) ;
150+ return assetResult ;
151+ }
152+ }
135153 }
136- const foundStaticRoute = staticRouteMatcher ( internalEvent . rawPath ) ;
154+ const foundStaticRoute = staticRouteMatcher ( eventOrResult . rawPath ) ;
137155 const isStaticRoute = ! isExternalRewrite && foundStaticRoute . length > 0 ;
138156
139157 if ( ! ( isStaticRoute || isExternalRewrite ) ) {
140158 // Second rewrite to be applied
141- const afterRewrites = handleRewrites (
142- internalEvent ,
159+ const afterRewrite = handleRewrites (
160+ eventOrResult ,
143161 RoutesManifest . rewrites . afterFiles ,
144162 ) ;
145- internalEvent = afterRewrites . internalEvent ;
146- isExternalRewrite = afterRewrites . isExternalRewrite ;
163+ eventOrResult = afterRewrite . internalEvent ;
164+ isExternalRewrite = afterRewrite . isExternalRewrite ;
165+ // Check for matching public files after `afterFiles` rewrites
166+ if ( ! isExternalRewrite ) {
167+ const assetResult =
168+ await assetResolver ?. getMaybeAssetResult ?.( eventOrResult ) ;
169+ if ( assetResult ) {
170+ applyMiddlewareHeaders ( assetResult , middlewareResponseHeaders ) ;
171+ return assetResult ;
172+ }
173+ }
147174 }
148175
149176 let isISR = false ;
150177 // We want to run this just before the dynamic route check
151178 // We can skip it if its an external rewrite
152179 if ( ! isExternalRewrite ) {
153180 const fallbackResult = handleFallbackFalse (
154- internalEvent ,
181+ eventOrResult ,
155182 PrerenderManifest ,
156183 ) ;
157- internalEvent = fallbackResult . event ;
184+ eventOrResult = fallbackResult . event ;
158185 isISR = fallbackResult . isISR ;
159186 }
160187
161- const foundDynamicRoute = dynamicRouteMatcher ( internalEvent . rawPath ) ;
188+ const foundDynamicRoute = dynamicRouteMatcher ( eventOrResult . rawPath ) ;
162189 const isDynamicRoute = ! isExternalRewrite && foundDynamicRoute . length > 0 ;
163190
164191 if ( ! ( isDynamicRoute || isStaticRoute || isExternalRewrite ) ) {
165192 // Fallback rewrite to be applied
166193 const fallbackRewrites = handleRewrites (
167- internalEvent ,
194+ eventOrResult ,
168195 RoutesManifest . rewrites . fallback ,
169196 ) ;
170- internalEvent = fallbackRewrites . internalEvent ;
197+ eventOrResult = fallbackRewrites . internalEvent ;
171198 isExternalRewrite = fallbackRewrites . isExternalRewrite ;
172199 }
173200
174- const isNextImageRoute = internalEvent . rawPath . startsWith ( "/_next/image" ) ;
201+ const isNextImageRoute = eventOrResult . rawPath . startsWith ( "/_next/image" ) ;
175202
176203 const isRouteFoundBeforeAllRewrites =
177204 isStaticRoute || isDynamicRoute || isExternalRewrite ;
@@ -183,31 +210,24 @@ export default async function routingHandler(
183210 isRouteFoundBeforeAllRewrites ||
184211 isNextImageRoute ||
185212 // We need to check again once all rewrites have been applied
186- staticRouteMatcher ( internalEvent . rawPath ) . length > 0 ||
187- dynamicRouteMatcher ( internalEvent . rawPath ) . length > 0
213+ staticRouteMatcher ( eventOrResult . rawPath ) . length > 0 ||
214+ dynamicRouteMatcher ( eventOrResult . rawPath ) . length > 0
188215 )
189216 ) {
190- const assetEventOrResult =
191- await assetResolver ?. onRouteNotFound ( internalEvent ) ;
192-
193- if ( assetEventOrResult && "statusCode" in assetEventOrResult ) {
194- applyMiddlewareHeaders (
195- assetEventOrResult . headers ,
196- {
197- ...middlewareResponseHeaders ,
198- ...nextHeaders ,
199- } ,
200- false ,
201- ) ;
202- return assetEventOrResult ;
217+ // Check for matching public file when no routes are matched
218+ const assetResult =
219+ await assetResolver ?. getMaybeAssetResult ?.( eventOrResult ) ;
220+ if ( assetResult ) {
221+ applyMiddlewareHeaders ( assetResult , middlewareResponseHeaders ) ;
222+ return assetResult ;
203223 }
204224
205- internalEvent = assetEventOrResult ?? {
206- ...internalEvent ,
225+ eventOrResult = {
226+ ...eventOrResult ,
207227 rawPath : "/404" ,
208- url : constructNextUrl ( internalEvent . url , "/404" ) ,
228+ url : constructNextUrl ( eventOrResult . url , "/404" ) ,
209229 headers : {
210- ...internalEvent . headers ,
230+ ...eventOrResult . headers ,
211231 "x-middleware-response-cache-control" :
212232 "private, no-cache, no-store, max-age=0, must-revalidate" ,
213233 } ,
@@ -216,28 +236,27 @@ export default async function routingHandler(
216236
217237 if (
218238 globalThis . openNextConfig . dangerous ?. enableCacheInterception &&
219- ! ( "statusCode" in internalEvent )
239+ ! isInternalResult ( eventOrResult )
220240 ) {
221241 debug ( "Cache interception enabled" ) ;
222- internalEvent = await cacheInterceptor ( internalEvent ) ;
223- if ( "statusCode" in internalEvent ) {
224- applyMiddlewareHeaders (
225- internalEvent . headers ,
226- {
227- ...middlewareResponseHeaders ,
228- ...nextHeaders ,
229- } ,
230- false ,
231- ) ;
232- return internalEvent ;
242+ eventOrResult = await cacheInterceptor ( eventOrResult ) ;
243+ if ( isInternalResult ( eventOrResult ) ) {
244+ applyMiddlewareHeaders ( eventOrResult , middlewareResponseHeaders ) ;
245+ return eventOrResult ;
233246 }
234247 }
235248
249+ // Check for matching public file when some routes are matched
250+ // An asset could override a dynamic route.
251+ const assetResult =
252+ await assetResolver ?. getMaybeAssetResult ?.( eventOrResult ) ;
253+ if ( assetResult ) {
254+ applyMiddlewareHeaders ( assetResult , middlewareResponseHeaders ) ;
255+ return assetResult ;
256+ }
257+
236258 // We apply the headers from the middleware response last
237- applyMiddlewareHeaders ( internalEvent . headers , {
238- ...middlewareResponseHeaders ,
239- ...nextHeaders ,
240- } ) ;
259+ applyMiddlewareHeaders ( eventOrResult , middlewareResponseHeaders ) ;
241260
242261 const resolvedRoutes : ResolvedRoute [ ] = [
243262 ...foundStaticRoute ,
@@ -247,14 +266,14 @@ export default async function routingHandler(
247266 debug ( "resolvedRoutes" , resolvedRoutes ) ;
248267
249268 return {
250- internalEvent,
269+ internalEvent : eventOrResult ,
251270 isExternalRewrite,
252271 origin : false ,
253272 isISR,
254273 resolvedRoutes,
255274 initialURL : event . url ,
256275 locale : NextConfig . i18n
257- ? detectLocale ( internalEvent , NextConfig . i18n )
276+ ? detectLocale ( eventOrResult , NextConfig . i18n )
258277 : undefined ,
259278 } ;
260279 } catch ( e ) {
@@ -284,3 +303,13 @@ export default async function routingHandler(
284303 } ;
285304 }
286305}
306+
307+ /**
308+ * @param eventOrResult
309+ * @returns Whether the event is an instance of `InternalResult`
310+ */
311+ function isInternalResult (
312+ eventOrResult : InternalEvent | InternalResult ,
313+ ) : eventOrResult is InternalResult {
314+ return eventOrResult != null && "statusCode" in eventOrResult ;
315+ }
0 commit comments