@@ -12,6 +12,7 @@ import type {
1212 RoutingResult ,
1313} from "types/open-next" ;
1414
15+ import type { AssetResolver } from "types/overrides" ;
1516import { debug , error } from "../adapters/logger" ;
1617import { cacheInterceptor } from "./routing/cacheInterceptor" ;
1718import { detectLocale } from "./routing/i18n" ;
@@ -47,23 +48,31 @@ const geoHeaderToNextHeader = {
4748 "x-open-next-longitude" : "x-vercel-ip-longitude" ,
4849} ;
4950
51+ /**
52+ * Adds the middleware headers to an event or result.
53+ *
54+ * @param eventOrResult
55+ * @param middlewareHeaders
56+ */
5057function applyMiddlewareHeaders (
51- eventHeaders : Record < string , string | string [ ] > ,
58+ eventOrResult : InternalEvent | InternalResult ,
5259 middlewareHeaders : Record < string , string | string [ ] | undefined > ,
53- setPrefix = true ,
5460) {
55- 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 ;
5666 Object . entries ( middlewareHeaders ) . forEach ( ( [ key , value ] ) => {
5767 if ( value ) {
58- eventHeaders [ keyPrefix + key ] = Array . isArray ( value )
59- ? value . join ( "," )
60- : value ;
68+ headers [ keyPrefix + key ] = Array . isArray ( value ) ? value . join ( "," ) : value ;
6169 }
6270 } ) ;
6371}
6472
6573export default async function routingHandler (
6674 event : InternalEvent ,
75+ { assetResolver } : { assetResolver ?: AssetResolver } ,
6776) : Promise < InternalResult | RoutingResult > {
6877 try {
6978 // Add Next geo headers
@@ -87,14 +96,17 @@ export default async function routingHandler(
8796 }
8897 }
8998
90- const nextHeaders = getNextConfigHeaders ( event , ConfigHeaders ) ;
99+ // Headers from the Next config and middleware (the later are applied further down).
100+ let headers : Record < string , string | string [ ] | undefined > =
101+ getNextConfigHeaders ( event , ConfigHeaders ) ;
91102
92- let internalEvent = fixDataPage ( event , BuildId ) ;
93- if ( "statusCode" in internalEvent ) {
94- return internalEvent ;
103+ let eventOrResult = fixDataPage ( event , BuildId ) ;
104+
105+ if ( isInternalResult ( eventOrResult ) ) {
106+ return eventOrResult ;
95107 }
96108
97- const redirect = handleRedirects ( internalEvent , RoutesManifest . redirects ) ;
109+ const redirect = handleRedirects ( eventOrResult , RoutesManifest . redirects ) ;
98110 if ( redirect ) {
99111 // We need to encode the value in the Location header to make sure it is valid according to RFC
100112 // https://stackoverflow.com/a/7654605/16587222
@@ -105,70 +117,84 @@ export default async function routingHandler(
105117 return redirect ;
106118 }
107119
108- const eventOrResult = await handleMiddleware (
109- internalEvent ,
120+ const middlewareEventOrResult = await handleMiddleware (
121+ eventOrResult ,
110122 // We need to pass the initial search without any decoding
111123 // TODO: we'd need to refactor InternalEvent to include the initial querystring directly
112124 // Should be done in another PR because it is a breaking change
113125 new URL ( event . url ) . search ,
114126 ) ;
115- const isResult = "statusCode" in eventOrResult ;
116- if ( isResult ) {
117- return eventOrResult ;
127+ if ( isInternalResult ( middlewareEventOrResult ) ) {
128+ return middlewareEventOrResult ;
118129 }
119- const middlewareResponseHeaders = eventOrResult . responseHeaders ;
120- let isExternalRewrite = eventOrResult . isExternalRewrite ?? false ;
121- // internalEvent is `InternalEvent | MiddlewareEvent`
122- internalEvent = eventOrResult ;
130+
131+ headers = {
132+ ...middlewareEventOrResult . responseHeaders ,
133+ ...headers ,
134+ } ;
135+ let isExternalRewrite = middlewareEventOrResult . isExternalRewrite ?? false ;
136+ eventOrResult = middlewareEventOrResult ;
123137
124138 if ( ! isExternalRewrite ) {
125139 // First rewrite to be applied
126- const beforeRewrites = handleRewrites (
127- internalEvent ,
140+ const beforeRewrite = handleRewrites (
141+ eventOrResult ,
128142 RoutesManifest . rewrites . beforeFiles ,
129143 ) ;
130- internalEvent = beforeRewrites . internalEvent ;
131- isExternalRewrite = beforeRewrites . isExternalRewrite ;
144+ eventOrResult = beforeRewrite . internalEvent ;
145+ isExternalRewrite = beforeRewrite . isExternalRewrite ;
146+ // Check for matching public files after `beforeFiles` rewrites
147+ // See:
148+ // - https://nextjs.org/docs/app/api-reference/file-conventions/middleware#execution-order
149+ // - https://nextjs.org/docs/app/api-reference/config/next-config-js/rewrites
150+ if ( ! isExternalRewrite ) {
151+ const assetResult =
152+ await assetResolver ?. maybeGetAssetResult ?.( eventOrResult ) ;
153+ if ( assetResult ) {
154+ applyMiddlewareHeaders ( assetResult , headers ) ;
155+ return assetResult ;
156+ }
157+ }
132158 }
133- const foundStaticRoute = staticRouteMatcher ( internalEvent . rawPath ) ;
159+ const foundStaticRoute = staticRouteMatcher ( eventOrResult . rawPath ) ;
134160 const isStaticRoute = ! isExternalRewrite && foundStaticRoute . length > 0 ;
135161
136162 if ( ! ( isStaticRoute || isExternalRewrite ) ) {
137163 // Second rewrite to be applied
138- const afterRewrites = handleRewrites (
139- internalEvent ,
164+ const afterRewrite = handleRewrites (
165+ eventOrResult ,
140166 RoutesManifest . rewrites . afterFiles ,
141167 ) ;
142- internalEvent = afterRewrites . internalEvent ;
143- isExternalRewrite = afterRewrites . isExternalRewrite ;
168+ eventOrResult = afterRewrite . internalEvent ;
169+ isExternalRewrite = afterRewrite . isExternalRewrite ;
144170 }
145171
146172 let isISR = false ;
147173 // We want to run this just before the dynamic route check
148174 // We can skip it if its an external rewrite
149175 if ( ! isExternalRewrite ) {
150176 const fallbackResult = handleFallbackFalse (
151- internalEvent ,
177+ eventOrResult ,
152178 PrerenderManifest ,
153179 ) ;
154- internalEvent = fallbackResult . event ;
180+ eventOrResult = fallbackResult . event ;
155181 isISR = fallbackResult . isISR ;
156182 }
157183
158- const foundDynamicRoute = dynamicRouteMatcher ( internalEvent . rawPath ) ;
184+ const foundDynamicRoute = dynamicRouteMatcher ( eventOrResult . rawPath ) ;
159185 const isDynamicRoute = ! isExternalRewrite && foundDynamicRoute . length > 0 ;
160186
161187 if ( ! ( isDynamicRoute || isStaticRoute || isExternalRewrite ) ) {
162188 // Fallback rewrite to be applied
163189 const fallbackRewrites = handleRewrites (
164- internalEvent ,
190+ eventOrResult ,
165191 RoutesManifest . rewrites . fallback ,
166192 ) ;
167- internalEvent = fallbackRewrites . internalEvent ;
193+ eventOrResult = fallbackRewrites . internalEvent ;
168194 isExternalRewrite = fallbackRewrites . isExternalRewrite ;
169195 }
170196
171- const isNextImageRoute = internalEvent . rawPath . startsWith ( "/_next/image" ) ;
197+ const isNextImageRoute = eventOrResult . rawPath . startsWith ( "/_next/image" ) ;
172198
173199 const isRouteFoundBeforeAllRewrites =
174200 isStaticRoute || isDynamicRoute || isExternalRewrite ;
@@ -180,16 +206,16 @@ export default async function routingHandler(
180206 isRouteFoundBeforeAllRewrites ||
181207 isNextImageRoute ||
182208 // We need to check again once all rewrites have been applied
183- staticRouteMatcher ( internalEvent . rawPath ) . length > 0 ||
184- dynamicRouteMatcher ( internalEvent . rawPath ) . length > 0
209+ staticRouteMatcher ( eventOrResult . rawPath ) . length > 0 ||
210+ dynamicRouteMatcher ( eventOrResult . rawPath ) . length > 0
185211 )
186212 ) {
187- internalEvent = {
188- ...internalEvent ,
213+ eventOrResult = {
214+ ...eventOrResult ,
189215 rawPath : "/404" ,
190- url : constructNextUrl ( internalEvent . url , "/404" ) ,
216+ url : constructNextUrl ( eventOrResult . url , "/404" ) ,
191217 headers : {
192- ...internalEvent . headers ,
218+ ...eventOrResult . headers ,
193219 "x-middleware-response-cache-control" :
194220 "private, no-cache, no-store, max-age=0, must-revalidate" ,
195221 } ,
@@ -198,28 +224,18 @@ export default async function routingHandler(
198224
199225 if (
200226 globalThis . openNextConfig . dangerous ?. enableCacheInterception &&
201- ! ( "statusCode" in internalEvent )
227+ ! isInternalResult ( eventOrResult )
202228 ) {
203229 debug ( "Cache interception enabled" ) ;
204- internalEvent = await cacheInterceptor ( internalEvent ) ;
205- if ( "statusCode" in internalEvent ) {
206- applyMiddlewareHeaders (
207- internalEvent . headers ,
208- {
209- ...middlewareResponseHeaders ,
210- ...nextHeaders ,
211- } ,
212- false ,
213- ) ;
214- return internalEvent ;
230+ eventOrResult = await cacheInterceptor ( eventOrResult ) ;
231+ if ( isInternalResult ( eventOrResult ) ) {
232+ applyMiddlewareHeaders ( eventOrResult , headers ) ;
233+ return eventOrResult ;
215234 }
216235 }
217236
218237 // We apply the headers from the middleware response last
219- applyMiddlewareHeaders ( internalEvent . headers , {
220- ...middlewareResponseHeaders ,
221- ...nextHeaders ,
222- } ) ;
238+ applyMiddlewareHeaders ( eventOrResult , headers ) ;
223239
224240 const resolvedRoutes : ResolvedRoute [ ] = [
225241 ...foundStaticRoute ,
@@ -229,14 +245,14 @@ export default async function routingHandler(
229245 debug ( "resolvedRoutes" , resolvedRoutes ) ;
230246
231247 return {
232- internalEvent,
248+ internalEvent : eventOrResult ,
233249 isExternalRewrite,
234250 origin : false ,
235251 isISR,
236252 resolvedRoutes,
237253 initialURL : event . url ,
238254 locale : NextConfig . i18n
239- ? detectLocale ( internalEvent , NextConfig . i18n )
255+ ? detectLocale ( eventOrResult , NextConfig . i18n )
240256 : undefined ,
241257 } ;
242258 } catch ( e ) {
@@ -266,3 +282,13 @@ export default async function routingHandler(
266282 } ;
267283 }
268284}
285+
286+ /**
287+ * @param eventOrResult
288+ * @returns Whether the event is an instance of `InternalResult`
289+ */
290+ function isInternalResult (
291+ eventOrResult : InternalEvent | InternalResult ,
292+ ) : eventOrResult is InternalResult {
293+ return eventOrResult != null && "statusCode" in eventOrResult ;
294+ }
0 commit comments