@@ -311,182 +311,192 @@ export default class Server {
311311 res : ServerResponse ,
312312 parsedUrl ?: UrlWithParsedQuery
313313 ) : Promise < void > {
314- const urlParts = ( req . url || '' ) . split ( '?' )
315- const urlNoQuery = urlParts [ 0 ]
316-
317- if ( urlNoQuery ?. match ( / ( \\ | \/ \/ ) / ) ) {
318- const cleanUrl = normalizeRepeatedSlashes ( req . url ! )
319- res . setHeader ( 'Location' , cleanUrl )
320- res . setHeader ( 'Refresh' , `0;url=${ cleanUrl } ` )
321- res . statusCode = 308
322- res . end ( cleanUrl )
323- return
324- }
314+ try {
315+ const urlParts = ( req . url || '' ) . split ( '?' )
316+ const urlNoQuery = urlParts [ 0 ]
317+
318+ if ( urlNoQuery ?. match ( / ( \\ | \/ \/ ) / ) ) {
319+ const cleanUrl = normalizeRepeatedSlashes ( req . url ! )
320+ res . setHeader ( 'Location' , cleanUrl )
321+ res . setHeader ( 'Refresh' , `0;url=${ cleanUrl } ` )
322+ res . statusCode = 308
323+ res . end ( cleanUrl )
324+ return
325+ }
325326
326- setLazyProp ( { req : req as any } , 'cookies' , getCookieParser ( req . headers ) )
327+ setLazyProp ( { req : req as any } , 'cookies' , getCookieParser ( req . headers ) )
327328
328- // Parse url if parsedUrl not provided
329- if ( ! parsedUrl || typeof parsedUrl !== 'object' ) {
330- const url : any = req . url
331- parsedUrl = parseUrl ( url , true )
332- }
333- const { basePath, i18n } = this . nextConfig
329+ // Parse url if parsedUrl not provided
330+ if ( ! parsedUrl || typeof parsedUrl !== 'object' ) {
331+ const url : any = req . url
332+ parsedUrl = parseUrl ( url , true )
333+ }
334+ const { basePath, i18n } = this . nextConfig
334335
335- // Parse the querystring ourselves if the user doesn't handle querystring parsing
336- if ( typeof parsedUrl . query === 'string' ) {
337- parsedUrl . query = parseQs ( parsedUrl . query )
338- }
339- ; ( req as any ) . __NEXT_INIT_QUERY = Object . assign ( { } , parsedUrl . query )
336+ // Parse the querystring ourselves if the user doesn't handle querystring parsing
337+ if ( typeof parsedUrl . query === 'string' ) {
338+ parsedUrl . query = parseQs ( parsedUrl . query )
339+ }
340+ ; ( req as any ) . __NEXT_INIT_QUERY = Object . assign ( { } , parsedUrl . query )
340341
341- const url = parseNextUrl ( {
342- headers : req . headers ,
343- nextConfig : this . nextConfig ,
344- url : req . url ?. replace ( / ^ \/ + / , '/' ) ,
345- } )
342+ const url = parseNextUrl ( {
343+ headers : req . headers ,
344+ nextConfig : this . nextConfig ,
345+ url : req . url ?. replace ( / ^ \/ + / , '/' ) ,
346+ } )
346347
347- if ( url . basePath ) {
348- ; ( req as any ) . _nextHadBasePath = true
349- req . url = req . url ! . replace ( basePath , '' ) || '/'
350- }
348+ if ( url . basePath ) {
349+ ; ( req as any ) . _nextHadBasePath = true
350+ req . url = req . url ! . replace ( basePath , '' ) || '/'
351+ }
351352
352- if (
353- this . minimalMode &&
354- req . headers [ 'x-matched-path' ] &&
355- typeof req . headers [ 'x-matched-path' ] === 'string'
356- ) {
357- const reqUrlIsDataUrl = req . url ?. includes ( '/_next/data' )
358- const matchedPathIsDataUrl =
359- req . headers [ 'x-matched-path' ] ?. includes ( '/_next/data' )
360- const isDataUrl = reqUrlIsDataUrl || matchedPathIsDataUrl
361-
362- let parsedPath = parseUrl (
363- isDataUrl ? req . url ! : ( req . headers [ 'x-matched-path' ] as string ) ,
364- true
365- )
366- const { pathname, query } = parsedPath
367- let matchedPathname = pathname as string
353+ if (
354+ this . minimalMode &&
355+ req . headers [ 'x-matched-path' ] &&
356+ typeof req . headers [ 'x-matched-path' ] === 'string'
357+ ) {
358+ const reqUrlIsDataUrl = req . url ?. includes ( '/_next/data' )
359+ const matchedPathIsDataUrl =
360+ req . headers [ 'x-matched-path' ] ?. includes ( '/_next/data' )
361+ const isDataUrl = reqUrlIsDataUrl || matchedPathIsDataUrl
362+
363+ let parsedPath = parseUrl (
364+ isDataUrl ? req . url ! : ( req . headers [ 'x-matched-path' ] as string ) ,
365+ true
366+ )
367+ const { pathname, query } = parsedPath
368+ let matchedPathname = pathname as string
368369
369- let matchedPathnameNoExt = isDataUrl
370- ? matchedPathname . replace ( / \. j s o n $ / , '' )
371- : matchedPathname
370+ let matchedPathnameNoExt = isDataUrl
371+ ? matchedPathname . replace ( / \. j s o n $ / , '' )
372+ : matchedPathname
372373
373- if ( i18n ) {
374- const localePathResult = normalizeLocalePath (
375- matchedPathname || '/' ,
376- i18n . locales
377- )
374+ if ( i18n ) {
375+ const localePathResult = normalizeLocalePath (
376+ matchedPathname || '/' ,
377+ i18n . locales
378+ )
378379
379- if ( localePathResult . detectedLocale ) {
380- parsedUrl . query . __nextLocale = localePathResult . detectedLocale
380+ if ( localePathResult . detectedLocale ) {
381+ parsedUrl . query . __nextLocale = localePathResult . detectedLocale
382+ }
381383 }
382- }
383384
384- if ( isDataUrl ) {
385- matchedPathname = denormalizePagePath ( matchedPathname )
386- matchedPathnameNoExt = denormalizePagePath ( matchedPathnameNoExt )
387- }
388-
389- const pageIsDynamic = isDynamicRoute ( matchedPathnameNoExt )
390- const combinedRewrites : Rewrite [ ] = [ ]
385+ if ( isDataUrl ) {
386+ matchedPathname = denormalizePagePath ( matchedPathname )
387+ matchedPathnameNoExt = denormalizePagePath ( matchedPathnameNoExt )
388+ }
391389
392- combinedRewrites . push ( ...this . customRoutes . rewrites . beforeFiles )
393- combinedRewrites . push ( ...this . customRoutes . rewrites . afterFiles )
394- combinedRewrites . push ( ...this . customRoutes . rewrites . fallback )
390+ const pageIsDynamic = isDynamicRoute ( matchedPathnameNoExt )
391+ const combinedRewrites : Rewrite [ ] = [ ]
395392
396- const utils = getUtils ( {
397- pageIsDynamic,
398- page : matchedPathnameNoExt ,
399- i18n : this . nextConfig . i18n ,
400- basePath : this . nextConfig . basePath ,
401- rewrites : combinedRewrites ,
402- } )
393+ combinedRewrites . push ( ...this . customRoutes . rewrites . beforeFiles )
394+ combinedRewrites . push ( ...this . customRoutes . rewrites . afterFiles )
395+ combinedRewrites . push ( ...this . customRoutes . rewrites . fallback )
403396
404- utils . handleRewrites ( req , parsedUrl )
397+ const utils = getUtils ( {
398+ pageIsDynamic,
399+ page : matchedPathnameNoExt ,
400+ i18n : this . nextConfig . i18n ,
401+ basePath : this . nextConfig . basePath ,
402+ rewrites : combinedRewrites ,
403+ } )
405404
406- // interpolate dynamic params and normalize URL if needed
407- if ( pageIsDynamic ) {
408- let params : ParsedUrlQuery | false = { }
405+ utils . handleRewrites ( req , parsedUrl )
409406
410- Object . assign ( parsedUrl . query , query )
411- const paramsResult = utils . normalizeDynamicRouteParams ( parsedUrl . query )
407+ // interpolate dynamic params and normalize URL if needed
408+ if ( pageIsDynamic ) {
409+ let params : ParsedUrlQuery | false = { }
412410
413- if ( paramsResult . hasValidParams ) {
414- params = paramsResult . params
415- } else if ( req . headers [ 'x-now-route-matches' ] ) {
416- const opts : Record < string , string > = { }
417- params = utils . getParamsFromRouteMatches (
418- req ,
419- opts ,
420- ( parsedUrl . query . __nextLocale as string | undefined ) || ''
411+ Object . assign ( parsedUrl . query , query )
412+ const paramsResult = utils . normalizeDynamicRouteParams (
413+ parsedUrl . query
421414 )
422415
423- if ( opts . locale ) {
424- parsedUrl . query . __nextLocale = opts . locale
416+ if ( paramsResult . hasValidParams ) {
417+ params = paramsResult . params
418+ } else if ( req . headers [ 'x-now-route-matches' ] ) {
419+ const opts : Record < string , string > = { }
420+ params = utils . getParamsFromRouteMatches (
421+ req ,
422+ opts ,
423+ ( parsedUrl . query . __nextLocale as string | undefined ) || ''
424+ )
425+
426+ if ( opts . locale ) {
427+ parsedUrl . query . __nextLocale = opts . locale
428+ }
429+ } else {
430+ params = utils . dynamicRouteMatcher ! ( matchedPathnameNoExt )
425431 }
426- } else {
427- params = utils . dynamicRouteMatcher ! ( matchedPathnameNoExt )
428- }
429432
430- if ( params ) {
431- params = utils . normalizeDynamicRouteParams ( params ) . params
433+ if ( params ) {
434+ params = utils . normalizeDynamicRouteParams ( params ) . params
432435
433- matchedPathname = utils . interpolateDynamicPath (
434- matchedPathname ,
435- params
436- )
437- req . url = utils . interpolateDynamicPath ( req . url ! , params )
438- }
436+ matchedPathname = utils . interpolateDynamicPath (
437+ matchedPathname ,
438+ params
439+ )
440+ req . url = utils . interpolateDynamicPath ( req . url ! , params )
441+ }
439442
440- if ( reqUrlIsDataUrl && matchedPathIsDataUrl ) {
441- req . url = formatUrl ( {
442- ...parsedPath ,
443- pathname : matchedPathname ,
444- } )
443+ if ( reqUrlIsDataUrl && matchedPathIsDataUrl ) {
444+ req . url = formatUrl ( {
445+ ...parsedPath ,
446+ pathname : matchedPathname ,
447+ } )
448+ }
449+
450+ Object . assign ( parsedUrl . query , params )
451+ utils . normalizeVercelUrl ( req , true )
445452 }
446453
447- Object . assign ( parsedUrl . query , params )
448- utils . normalizeVercelUrl ( req , true )
454+ parsedUrl . pathname = `${ basePath || '' } ${
455+ matchedPathname === '/' && basePath ? '' : matchedPathname
456+ } `
449457 }
450458
451- parsedUrl . pathname = `${ basePath || '' } ${
452- matchedPathname === '/' && basePath ? '' : matchedPathname
453- } `
454- }
455-
456- ; ( req as any ) . __nextHadTrailingSlash = url . locale ?. trailingSlash
457- if ( url . locale ?. domain ) {
458- ; ( req as any ) . __nextIsLocaleDomain = true
459- }
459+ ; ( req as any ) . __nextHadTrailingSlash = url . locale ?. trailingSlash
460+ if ( url . locale ?. domain ) {
461+ ; ( req as any ) . __nextIsLocaleDomain = true
462+ }
460463
461- if ( url . locale ?. path . detectedLocale ) {
462- req . url = formatUrl ( url )
463- ; ( req as any ) . __nextStrippedLocale = true
464- if ( url . pathname === '/api' || url . pathname . startsWith ( '/api/' ) ) {
465- return this . render404 ( req , res , parsedUrl )
464+ if ( url . locale ?. path . detectedLocale ) {
465+ req . url = formatUrl ( url )
466+ ; ( req as any ) . __nextStrippedLocale = true
467+ if ( url . pathname === '/api' || url . pathname . startsWith ( '/api/' ) ) {
468+ return this . render404 ( req , res , parsedUrl )
469+ }
466470 }
467- }
468471
469- if ( ! this . minimalMode || ! parsedUrl . query . __nextLocale ) {
470- if ( url ?. locale ?. locale ) {
471- parsedUrl . query . __nextLocale = url . locale . locale
472+ if ( ! this . minimalMode || ! parsedUrl . query . __nextLocale ) {
473+ if ( url ?. locale ?. locale ) {
474+ parsedUrl . query . __nextLocale = url . locale . locale
475+ }
472476 }
473- }
474477
475- if ( url ?. locale ?. defaultLocale ) {
476- parsedUrl . query . __nextDefaultLocale = url . locale . defaultLocale
477- }
478+ if ( url ?. locale ?. defaultLocale ) {
479+ parsedUrl . query . __nextDefaultLocale = url . locale . defaultLocale
480+ }
478481
479- if ( url . locale ?. redirect ) {
480- res . setHeader ( 'Location' , url . locale . redirect )
481- res . statusCode = TEMPORARY_REDIRECT_STATUS
482- res . end ( )
483- return
484- }
482+ if ( url . locale ?. redirect ) {
483+ res . setHeader ( 'Location' , url . locale . redirect )
484+ res . statusCode = TEMPORARY_REDIRECT_STATUS
485+ res . end ( )
486+ return
487+ }
485488
486- res . statusCode = 200
487- try {
489+ res . statusCode = 200
488490 return await this . run ( req , res , parsedUrl )
489491 } catch ( err ) {
492+ if (
493+ ( err && typeof err === 'object' && err . code === 'ERR_INVALID_URL' ) ||
494+ err instanceof DecodeError
495+ ) {
496+ res . statusCode = 400
497+ return this . renderError ( null , req , res , '/_error' , { } )
498+ }
499+
490500 if ( this . minimalMode || this . renderOpts . dev ) {
491501 throw err
492502 }
0 commit comments