@@ -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
@@ -89,16 +96,17 @@ export default async function routingHandler(
8996 }
9097 }
9198
92- const nextHeaders = getNextConfigHeaders ( event , ConfigHeaders ) ;
99+ // Headers from the Next config and middleware (the latest are applied further down).
100+ let headers : Record < string , string | string [ ] | undefined > =
101+ getNextConfigHeaders ( event , ConfigHeaders ) ;
93102
94- const internalEventOrResult = fixDataPage ( event , BuildId ) ;
95- if ( "statusCode" in internalEventOrResult ) {
96- return internalEventOrResult ;
97- }
103+ let eventOrResult = fixDataPage ( event , BuildId ) ;
98104
99- let internalEvent : InternalEvent | InternalResult = internalEventOrResult ;
105+ if ( isInternalResult ( eventOrResult ) ) {
106+ return eventOrResult ;
107+ }
100108
101- const redirect = handleRedirects ( internalEvent , RoutesManifest . redirects ) ;
109+ const redirect = handleRedirects ( eventOrResult , RoutesManifest . redirects ) ;
102110 if ( redirect ) {
103111 // We need to encode the value in the Location header to make sure it is valid according to RFC
104112 // https://stackoverflow.com/a/7654605/16587222
@@ -110,68 +118,89 @@ export default async function routingHandler(
110118 }
111119
112120 const middlewareEventOrResult = await handleMiddleware (
113- internalEvent ,
121+ eventOrResult ,
114122 // We need to pass the initial search without any decoding
115123 // TODO: we'd need to refactor InternalEvent to include the initial querystring directly
116124 // Should be done in another PR because it is a breaking change
117125 new URL ( event . url ) . search ,
118126 ) ;
119- if ( "statusCode" in middlewareEventOrResult ) {
127+ if ( isInternalResult ( middlewareEventOrResult ) ) {
120128 return middlewareEventOrResult ;
121129 }
122130
123- const middlewareResponseHeaders = middlewareEventOrResult . responseHeaders ;
131+ headers = {
132+ ...middlewareEventOrResult . responseHeaders ,
133+ ...headers ,
134+ } ;
124135 let isExternalRewrite = middlewareEventOrResult . isExternalRewrite ?? false ;
125- internalEvent = middlewareEventOrResult ;
136+ eventOrResult = middlewareEventOrResult ;
126137
127138 if ( ! isExternalRewrite ) {
128139 // First rewrite to be applied
129- const beforeRewrites = handleRewrites (
130- internalEvent ,
140+ const beforeRewrite = handleRewrites (
141+ eventOrResult ,
131142 RoutesManifest . rewrites . beforeFiles ,
132143 ) ;
133- internalEvent = beforeRewrites . internalEvent ;
134- isExternalRewrite = beforeRewrites . isExternalRewrite ;
144+ eventOrResult = beforeRewrite . internalEvent ;
145+ isExternalRewrite = beforeRewrite . isExternalRewrite ;
146+ // Check for matching public files after `beforeFiles` rewrites
147+ if ( ! isExternalRewrite ) {
148+ const assetResult =
149+ await assetResolver ?. getMaybeAssetResult ?.( eventOrResult ) ;
150+ if ( assetResult ) {
151+ applyMiddlewareHeaders ( assetResult , headers ) ;
152+ return assetResult ;
153+ }
154+ }
135155 }
136- const foundStaticRoute = staticRouteMatcher ( internalEvent . rawPath ) ;
156+ const foundStaticRoute = staticRouteMatcher ( eventOrResult . rawPath ) ;
137157 const isStaticRoute = ! isExternalRewrite && foundStaticRoute . length > 0 ;
138158
139159 if ( ! ( isStaticRoute || isExternalRewrite ) ) {
140160 // Second rewrite to be applied
141- const afterRewrites = handleRewrites (
142- internalEvent ,
161+ const afterRewrite = handleRewrites (
162+ eventOrResult ,
143163 RoutesManifest . rewrites . afterFiles ,
144164 ) ;
145- internalEvent = afterRewrites . internalEvent ;
146- isExternalRewrite = afterRewrites . isExternalRewrite ;
165+ eventOrResult = afterRewrite . internalEvent ;
166+ isExternalRewrite = afterRewrite . isExternalRewrite ;
167+ // Check for matching public files after `afterFiles` rewrites
168+ if ( ! isExternalRewrite ) {
169+ const assetResult =
170+ await assetResolver ?. getMaybeAssetResult ?.( eventOrResult ) ;
171+ if ( assetResult ) {
172+ applyMiddlewareHeaders ( assetResult , headers ) ;
173+ return assetResult ;
174+ }
175+ }
147176 }
148177
149178 let isISR = false ;
150179 // We want to run this just before the dynamic route check
151180 // We can skip it if its an external rewrite
152181 if ( ! isExternalRewrite ) {
153182 const fallbackResult = handleFallbackFalse (
154- internalEvent ,
183+ eventOrResult ,
155184 PrerenderManifest ,
156185 ) ;
157- internalEvent = fallbackResult . event ;
186+ eventOrResult = fallbackResult . event ;
158187 isISR = fallbackResult . isISR ;
159188 }
160189
161- const foundDynamicRoute = dynamicRouteMatcher ( internalEvent . rawPath ) ;
190+ const foundDynamicRoute = dynamicRouteMatcher ( eventOrResult . rawPath ) ;
162191 const isDynamicRoute = ! isExternalRewrite && foundDynamicRoute . length > 0 ;
163192
164193 if ( ! ( isDynamicRoute || isStaticRoute || isExternalRewrite ) ) {
165194 // Fallback rewrite to be applied
166195 const fallbackRewrites = handleRewrites (
167- internalEvent ,
196+ eventOrResult ,
168197 RoutesManifest . rewrites . fallback ,
169198 ) ;
170- internalEvent = fallbackRewrites . internalEvent ;
199+ eventOrResult = fallbackRewrites . internalEvent ;
171200 isExternalRewrite = fallbackRewrites . isExternalRewrite ;
172201 }
173202
174- const isNextImageRoute = internalEvent . rawPath . startsWith ( "/_next/image" ) ;
203+ const isNextImageRoute = eventOrResult . rawPath . startsWith ( "/_next/image" ) ;
175204
176205 const isRouteFoundBeforeAllRewrites =
177206 isStaticRoute || isDynamicRoute || isExternalRewrite ;
@@ -183,31 +212,24 @@ export default async function routingHandler(
183212 isRouteFoundBeforeAllRewrites ||
184213 isNextImageRoute ||
185214 // We need to check again once all rewrites have been applied
186- staticRouteMatcher ( internalEvent . rawPath ) . length > 0 ||
187- dynamicRouteMatcher ( internalEvent . rawPath ) . length > 0
215+ staticRouteMatcher ( eventOrResult . rawPath ) . length > 0 ||
216+ dynamicRouteMatcher ( eventOrResult . rawPath ) . length > 0
188217 )
189218 ) {
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 ;
219+ // Check for matching public file when no routes are matched
220+ const assetResult =
221+ await assetResolver ?. getMaybeAssetResult ?.( eventOrResult ) ;
222+ if ( assetResult ) {
223+ applyMiddlewareHeaders ( assetResult , headers ) ;
224+ return assetResult ;
203225 }
204226
205- internalEvent = assetEventOrResult ?? {
206- ...internalEvent ,
227+ eventOrResult = {
228+ ...eventOrResult ,
207229 rawPath : "/404" ,
208- url : constructNextUrl ( internalEvent . url , "/404" ) ,
230+ url : constructNextUrl ( eventOrResult . url , "/404" ) ,
209231 headers : {
210- ...internalEvent . headers ,
232+ ...eventOrResult . headers ,
211233 "x-middleware-response-cache-control" :
212234 "private, no-cache, no-store, max-age=0, must-revalidate" ,
213235 } ,
@@ -216,28 +238,27 @@ export default async function routingHandler(
216238
217239 if (
218240 globalThis . openNextConfig . dangerous ?. enableCacheInterception &&
219- ! ( "statusCode" in internalEvent )
241+ ! isInternalResult ( eventOrResult )
220242 ) {
221243 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 ;
244+ eventOrResult = await cacheInterceptor ( eventOrResult ) ;
245+ if ( isInternalResult ( eventOrResult ) ) {
246+ applyMiddlewareHeaders ( eventOrResult , headers ) ;
247+ return eventOrResult ;
233248 }
234249 }
235250
251+ // Check for matching public file when some routes are matched
252+ // An asset could override a dynamic route.
253+ const assetResult =
254+ await assetResolver ?. getMaybeAssetResult ?.( eventOrResult ) ;
255+ if ( assetResult ) {
256+ applyMiddlewareHeaders ( assetResult , headers ) ;
257+ return assetResult ;
258+ }
259+
236260 // We apply the headers from the middleware response last
237- applyMiddlewareHeaders ( internalEvent . headers , {
238- ...middlewareResponseHeaders ,
239- ...nextHeaders ,
240- } ) ;
261+ applyMiddlewareHeaders ( eventOrResult , headers ) ;
241262
242263 const resolvedRoutes : ResolvedRoute [ ] = [
243264 ...foundStaticRoute ,
@@ -247,14 +268,14 @@ export default async function routingHandler(
247268 debug ( "resolvedRoutes" , resolvedRoutes ) ;
248269
249270 return {
250- internalEvent,
271+ internalEvent : eventOrResult ,
251272 isExternalRewrite,
252273 origin : false ,
253274 isISR,
254275 resolvedRoutes,
255276 initialURL : event . url ,
256277 locale : NextConfig . i18n
257- ? detectLocale ( internalEvent , NextConfig . i18n )
278+ ? detectLocale ( eventOrResult , NextConfig . i18n )
258279 : undefined ,
259280 } ;
260281 } catch ( e ) {
@@ -284,3 +305,13 @@ export default async function routingHandler(
284305 } ;
285306 }
286307}
308+
309+ /**
310+ * @param eventOrResult
311+ * @returns Whether the event is an instance of `InternalResult`
312+ */
313+ function isInternalResult (
314+ eventOrResult : InternalEvent | InternalResult ,
315+ ) : eventOrResult is InternalResult {
316+ return eventOrResult != null && "statusCode" in eventOrResult ;
317+ }
0 commit comments