@@ -21,14 +21,12 @@ import type { PreviewData } from 'next/types'
2121import type { PagesManifest } from '../build/webpack/plugins/pages-manifest-plugin'
2222import type { BaseNextRequest , BaseNextResponse } from './base-http'
2323
24- import { join , relative , resolve , sep } from 'path'
24+ import { join , resolve } from 'path'
2525import { parse as parseQs , stringify as stringifyQs } from 'querystring'
2626import { format as formatUrl , parse as parseUrl } from 'url'
2727import { getRedirectStatus , modifyRouteRegex } from '../lib/load-custom-routes'
2828import {
2929 CLIENT_PUBLIC_FILES_PATH ,
30- CLIENT_STATIC_FILES_PATH ,
31- CLIENT_STATIC_FILES_RUNTIME ,
3230 PRERENDER_MANIFEST ,
3331 ROUTES_MANIFEST ,
3432 SERVERLESS_DIRECTORY ,
@@ -190,6 +188,8 @@ export default abstract class Server {
190188 protected abstract getBuildId ( ) : string
191189 protected abstract generatePublicRoutes ( ) : Route [ ]
192190 protected abstract generateImageRoutes ( ) : Route [ ]
191+ protected abstract generateStaticRotes ( ) : Route [ ]
192+ protected abstract generateFsStaticRoutes ( ) : Route [ ]
193193 protected abstract generateCatchAllMiddlewareRoute ( ) : Route | undefined
194194 protected abstract getFilesystemPaths ( ) : Set < string >
195195 protected abstract getMiddleware ( ) : {
@@ -227,12 +227,6 @@ export default abstract class Server {
227227 }
228228 ) : Promise < void >
229229
230- protected abstract sendStatic (
231- req : BaseNextRequest ,
232- res : BaseNextResponse ,
233- path : string
234- ) : Promise < void >
235-
236230 protected abstract runApi (
237231 req : BaseNextRequest ,
238232 res : BaseNextResponse ,
@@ -714,64 +708,10 @@ export default abstract class Server {
714708 } {
715709 const publicRoutes = this . generatePublicRoutes ( )
716710 const imageRoutes = this . generateImageRoutes ( )
717-
718- const staticFilesRoute = this . hasStaticDir
719- ? [
720- {
721- // It's very important to keep this route's param optional.
722- // (but it should support as many params as needed, separated by '/')
723- // Otherwise this will lead to a pretty simple DOS attack.
724- // See more: https://github.com/vercel/next.js/issues/2617
725- match : route ( '/static/:path*' ) ,
726- name : 'static catchall' ,
727- fn : async ( req , res , params , parsedUrl ) => {
728- const p = join ( this . dir , 'static' , ...params . path )
729- await this . serveStatic ( req , res , p , parsedUrl )
730- return {
731- finished : true ,
732- }
733- } ,
734- } as Route ,
735- ]
736- : [ ]
711+ const staticFilesRoutes = this . generateStaticRotes ( )
737712
738713 const fsRoutes : Route [ ] = [
739- {
740- match : route ( '/_next/static/:path*' ) ,
741- type : 'route' ,
742- name : '_next/static catchall' ,
743- fn : async ( req , res , params , parsedUrl ) => {
744- // make sure to 404 for /_next/static itself
745- if ( ! params . path ) {
746- await this . render404 ( req , res , parsedUrl )
747- return {
748- finished : true ,
749- }
750- }
751-
752- if (
753- params . path [ 0 ] === CLIENT_STATIC_FILES_RUNTIME ||
754- params . path [ 0 ] === 'chunks' ||
755- params . path [ 0 ] === 'css' ||
756- params . path [ 0 ] === 'image' ||
757- params . path [ 0 ] === 'media' ||
758- params . path [ 0 ] === this . buildId ||
759- params . path [ 0 ] === 'pages' ||
760- params . path [ 1 ] === 'pages'
761- ) {
762- this . setImmutableAssetCacheControl ( res )
763- }
764- const p = join (
765- this . distDir ,
766- CLIENT_STATIC_FILES_PATH ,
767- ...( params . path || [ ] )
768- )
769- await this . serveStatic ( req , res , p , parsedUrl )
770- return {
771- finished : true ,
772- }
773- } ,
774- } ,
714+ ...this . generateFsStaticRoutes ( ) ,
775715 {
776716 match : route ( '/_next/data/:path*' ) ,
777717 type : 'route' ,
@@ -860,7 +800,7 @@ export default abstract class Server {
860800 } ,
861801 } ,
862802 ...publicRoutes ,
863- ...staticFilesRoute ,
803+ ...staticFilesRoutes ,
864804 ]
865805
866806 const restrictedRedirectPaths = [ '/_next' ] . map ( ( p ) =>
@@ -2063,78 +2003,6 @@ export default abstract class Server {
20632003 return this . renderError ( null , req , res , pathname ! , query , setHeaders )
20642004 }
20652005
2066- public async serveStatic (
2067- req : BaseNextRequest ,
2068- res : BaseNextResponse ,
2069- path : string ,
2070- parsedUrl ?: UrlWithParsedQuery
2071- ) : Promise < void > {
2072- if ( ! this . isServeableUrl ( path ) ) {
2073- return this . render404 ( req , res , parsedUrl )
2074- }
2075-
2076- if ( ! ( req . method === 'GET' || req . method === 'HEAD' ) ) {
2077- res . statusCode = 405
2078- res . setHeader ( 'Allow' , [ 'GET' , 'HEAD' ] )
2079- return this . renderError ( null , req , res , path )
2080- }
2081-
2082- try {
2083- await this . sendStatic ( req , res , path )
2084- } catch ( error ) {
2085- if ( ! isError ( error ) ) throw error
2086- const err = error as Error & { code ?: string ; statusCode ?: number }
2087- if ( err . code === 'ENOENT' || err . statusCode === 404 ) {
2088- this . render404 ( req , res , parsedUrl )
2089- } else if ( err . statusCode === 412 ) {
2090- res . statusCode = 412
2091- return this . renderError ( err , req , res , path )
2092- } else {
2093- throw err
2094- }
2095- }
2096- }
2097-
2098- protected isServeableUrl ( untrustedFileUrl : string ) : boolean {
2099- // This method mimics what the version of `send` we use does:
2100- // 1. decodeURIComponent:
2101- // https://github.com/pillarjs/send/blob/0.17.1/index.js#L989
2102- // https://github.com/pillarjs/send/blob/0.17.1/index.js#L518-L522
2103- // 2. resolve:
2104- // https://github.com/pillarjs/send/blob/de073ed3237ade9ff71c61673a34474b30e5d45b/index.js#L561
2105-
2106- let decodedUntrustedFilePath : string
2107- try {
2108- // (1) Decode the URL so we have the proper file name
2109- decodedUntrustedFilePath = decodeURIComponent ( untrustedFileUrl )
2110- } catch {
2111- return false
2112- }
2113-
2114- // (2) Resolve "up paths" to determine real request
2115- const untrustedFilePath = resolve ( decodedUntrustedFilePath )
2116-
2117- // don't allow null bytes anywhere in the file path
2118- if ( untrustedFilePath . indexOf ( '\0' ) !== - 1 ) {
2119- return false
2120- }
2121-
2122- // Check if .next/static, static and public are in the path.
2123- // If not the path is not available.
2124- if (
2125- ( untrustedFilePath . startsWith ( join ( this . distDir , 'static' ) + sep ) ||
2126- untrustedFilePath . startsWith ( join ( this . dir , 'static' ) + sep ) ||
2127- untrustedFilePath . startsWith ( join ( this . dir , 'public' ) + sep ) ) === false
2128- ) {
2129- return false
2130- }
2131-
2132- // Check against the real filesystem paths
2133- const filesystemUrls = this . getFilesystemPaths ( )
2134- const resolved = relative ( this . dir , untrustedFilePath )
2135- return filesystemUrls . has ( resolved )
2136- }
2137-
21382006 protected get _isLikeServerless ( ) : boolean {
21392007 return isTargetLikeServerless ( this . nextConfig . target )
21402008 }
0 commit comments