@@ -140,11 +140,109 @@ function populateProcessEnv(url: URL, env: CloudflareEnv) {
140140 process . env . __NEXT_PRIVATE_ORIGIN = url . origin ;
141141}
142142
143+ export type RemotePattern = {
144+ protocol ?: "http" | "https" ;
145+ hostname : string ;
146+ port ?: string ;
147+ pathname : string ;
148+ search ?: string ;
149+ } ;
150+
151+ const imgRemotePatterns = __IMAGES_REMOTE_PATTERNS__ ;
152+
153+ /**
154+ * Fetches an images.
155+ *
156+ * Local images (starting with a '/' as fetched using the passed fetcher).
157+ * Remote images should match the configured remote patterns or a 404 response is returned.
158+ */
159+ export function fetchImage ( fetcher : Fetcher | undefined , url : string ) {
160+ // https://github.com/vercel/next.js/blob/d76f0b1/packages/next/src/server/image-optimizer.ts#L208
161+ if ( ! url || url . length > 3072 || url . startsWith ( "//" ) ) {
162+ return new Response ( "Not Found" , { status : 404 } ) ;
163+ }
164+
165+ // Local
166+ if ( url . startsWith ( "/" ) ) {
167+ if ( / \/ _ n e x t \/ i m a g e ( $ | \/ ) / . test ( decodeURIComponent ( parseUrl ( url ) ?. pathname ?? "" ) ) ) {
168+ return new Response ( "Not Found" , { status : 404 } ) ;
169+ }
170+
171+ return fetcher ?. fetch ( `http://assets.local${ url } ` ) ;
172+ }
173+
174+ // Remote
175+ let hrefParsed : URL ;
176+ try {
177+ hrefParsed = new URL ( url ) ;
178+ } catch {
179+ return new Response ( "Not Found" , { status : 404 } ) ;
180+ }
181+
182+ if ( ! [ "http:" , "https:" ] . includes ( hrefParsed . protocol ) ) {
183+ return new Response ( "Not Found" , { status : 404 } ) ;
184+ }
185+
186+ if ( ! imgRemotePatterns . some ( ( p : RemotePattern ) => matchRemotePattern ( p , hrefParsed ) ) ) {
187+ return new Response ( "Not Found" , { status : 404 } ) ;
188+ }
189+
190+ return fetch ( url , { cf : { cacheEverything : true } } ) ;
191+ }
192+
193+ export function matchRemotePattern ( pattern : RemotePattern , url : URL ) : boolean {
194+ // https://github.com/vercel/next.js/blob/d76f0b1/packages/next/src/shared/lib/match-remote-pattern.ts
195+ if ( pattern . protocol !== undefined ) {
196+ if ( pattern . protocol . replace ( / : $ / , "" ) !== url . protocol . replace ( / : $ / , "" ) ) {
197+ return false ;
198+ }
199+ }
200+ if ( pattern . port !== undefined ) {
201+ if ( pattern . port !== url . port ) {
202+ return false ;
203+ }
204+ }
205+
206+ if ( pattern . hostname === undefined ) {
207+ throw new Error ( `Pattern should define hostname but found\n${ JSON . stringify ( pattern ) } ` ) ;
208+ } else {
209+ if ( ! new RegExp ( pattern . hostname ) . test ( url . hostname ) ) {
210+ return false ;
211+ }
212+ }
213+
214+ if ( pattern . search !== undefined ) {
215+ if ( pattern . search !== url . search ) {
216+ return false ;
217+ }
218+ }
219+
220+ // Should be the same as writeImagesManifest()
221+ if ( ! new RegExp ( pattern . pathname ) . test ( url . pathname ) ) {
222+ return false ;
223+ }
224+
225+ return true ;
226+ }
227+
228+ function parseUrl ( url : string ) : URL | undefined {
229+ let parsed : URL | undefined = undefined ;
230+ try {
231+ parsed = new URL ( url , "http://n" ) ;
232+ } catch {
233+ // empty
234+ }
235+ return parsed ;
236+ }
237+
143238/* eslint-disable no-var */
144239declare global {
145240 // Build timestamp
146241 var __BUILD_TIMESTAMP_MS__ : number ;
147242 // Next basePath
148243 var __NEXT_BASE_PATH__ : string ;
244+ // Images patterns
245+ var __IMAGES_REMOTE_PATTERNS__ : RemotePattern [ ] ;
246+ var __IMAGES_LOCAL_PATTERNS__ : unknown [ ] ;
149247}
150248/* eslint-enable no-var */
0 commit comments