Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Execute middleware on Next.js internal requests #37121

Merged
merged 9 commits into from
May 27, 2022
4 changes: 2 additions & 2 deletions packages/next/build/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import escapePathDelimiters from '../shared/lib/router/utils/escape-path-delimit
import { findPageFile } from '../server/lib/find-page-file'
import { GetStaticPaths, PageConfig } from 'next/types'
import { BuildManifest } from '../server/get-page-files'
import { removePathTrailingSlash } from '../client/normalize-trailing-slash'
import { removeTrailingSlash } from '../shared/lib/router/utils/remove-trailing-slash'
import { UnwrapPromise } from '../lib/coalesced-function'
import { normalizeLocalePath } from '../shared/lib/i18n/normalize-locale-path'
import * as Log from './output/log'
Expand Down Expand Up @@ -719,7 +719,7 @@ export async function buildStaticPaths(
// For a string-provided path, we must make sure it matches the dynamic
// route.
if (typeof entry === 'string') {
entry = removePathTrailingSlash(entry)
entry = removeTrailingSlash(entry)

const localePathResult = normalizeLocalePath(entry, locales)
let cleanedEntry = entry
Expand Down
11 changes: 3 additions & 8 deletions packages/next/client/normalize-trailing-slash.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
/**
* Removes the trailing slash of a path if there is one. Preserves the root path `/`.
*/
export function removePathTrailingSlash(path: string): string {
return path.endsWith('/') && path !== '/' ? path.slice(0, -1) : path
}
import { removeTrailingSlash } from '../shared/lib/router/utils/remove-trailing-slash'

/**
* Normalizes the trailing slash of a path according to the `trailingSlash` option
Expand All @@ -12,11 +7,11 @@ export function removePathTrailingSlash(path: string): string {
export const normalizePathTrailingSlash = process.env.__NEXT_TRAILING_SLASH
? (path: string): string => {
if (/\.[^/]+\/?$/.test(path)) {
return removePathTrailingSlash(path)
return removeTrailingSlash(path)
} else if (path.endsWith('/')) {
return path
} else {
return path + '/'
}
}
: removePathTrailingSlash
: removeTrailingSlash
4 changes: 2 additions & 2 deletions packages/next/client/page-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
import getAssetPathFromRoute from '../shared/lib/router/utils/get-asset-path-from-route'
import { isDynamicRoute } from '../shared/lib/router/utils/is-dynamic'
import { parseRelativeUrl } from '../shared/lib/router/utils/parse-relative-url'
import { removePathTrailingSlash } from './normalize-trailing-slash'
import { removeTrailingSlash } from '../shared/lib/router/utils/remove-trailing-slash'
import {
createRouteLoader,
getClientBuildManifest,
Expand Down Expand Up @@ -152,7 +152,7 @@ export default class PageLoader {
}

const dataRoute = getAssetPathFromRoute(
removePathTrailingSlash(addLocale(path, locale)),
removeTrailingSlash(addLocale(path, locale)),
'.json'
)
return addBasePath(
Expand Down
80 changes: 51 additions & 29 deletions packages/next/server/base-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ import { IncrementalCache } from './incremental-cache'
import { execOnce } from '../shared/lib/utils'
import { isBlockedPage, isBot } from './utils'
import RenderResult from './render-result'
import { removePathTrailingSlash } from '../client/normalize-trailing-slash'
import { removeTrailingSlash } from '../shared/lib/router/utils/remove-trailing-slash'
import getRouteFromAssetPath from '../shared/lib/router/utils/get-route-from-asset-path'
import { denormalizePagePath } from '../shared/lib/page-path/denormalize-page-path'
import { normalizeLocalePath } from '../shared/lib/i18n/normalize-locale-path'
Expand All @@ -58,16 +58,19 @@ import { detectDomainLocale } from '../shared/lib/i18n/detect-domain-locale'
import escapePathDelimiters from '../shared/lib/router/utils/escape-path-delimiters'
import { getUtils } from '../build/webpack/loaders/next-serverless-loader/utils'
import ResponseCache from './response-cache'
import { parseNextUrl } from '../shared/lib/router/utils/parse-next-url'
import isError, { getProperError } from '../lib/is-error'
import { addRequestMeta, getRequestMeta } from './request-meta'
import { createHeaderRoute, createRedirectRoute } from './server-route-utils'
import { PrerenderManifest } from '../build'
import { ImageConfigComplete } from '../shared/lib/image-config'
import { replaceBasePath } from './router-utils'
import { removePathPrefix } from '../shared/lib/router/utils/remove-path-prefix'
import { normalizeAppPath } from '../shared/lib/router/utils/app-paths'
import { getRouteMatcher } from '../shared/lib/router/utils/route-matcher'
import { getRouteRegex } from '../shared/lib/router/utils/route-regex'
import { getLocaleRedirect } from '../shared/lib/i18n/get-locale-redirect'
import { getHostname } from '../shared/lib/get-hostname'
import { parseUrl as parseUrlUtil } from '../shared/lib/router/utils/parse-url'
import { getNextPathnameInfo } from '../shared/lib/router/utils/get-next-pathname-info'

export type FindComponentsResult = {
components: LoadComponentsReturnType
Expand Down Expand Up @@ -413,14 +416,23 @@ export default abstract class Server<ServerOptions extends Options = Options> {
addRequestMeta(req, '__NEXT_INIT_URL', initUrl)
addRequestMeta(req, '__NEXT_INIT_QUERY', { ...parsedUrl.query })

const url = parseNextUrl({
headers: req.headers,
const domainLocale = detectDomainLocale(
this.nextConfig.i18n?.domains,
getHostname(parsedUrl, req.headers)
)

const defaultLocale =
domainLocale?.defaultLocale || this.nextConfig.i18n?.defaultLocale

const url = parseUrlUtil(req.url.replace(/^\/+/, '/'))
const pathnameInfo = getNextPathnameInfo(url.pathname, {
nextConfig: this.nextConfig,
url: req.url?.replace(/^\/+/, '/'),
})

if (url.basePath) {
req.url = replaceBasePath(req.url!, this.nextConfig.basePath)
url.pathname = pathnameInfo.pathname

if (pathnameInfo.basePath) {
req.url = removePathPrefix(req.url!, this.nextConfig.basePath)
addRequestMeta(req, '_nextHadBasePath', true)
}

Expand Down Expand Up @@ -493,8 +505,8 @@ export default abstract class Server<ServerOptions extends Options = Options> {
try {
// ensure parsedUrl.pathname includes URL before processing
// rewrites or they won't match correctly
if (this.nextConfig.i18n && !url.locale?.path.detectedLocale) {
parsedUrl.pathname = `/${url.locale?.locale}${parsedUrl.pathname}`
if (defaultLocale && !pathnameInfo.locale) {
parsedUrl.pathname = `/${defaultLocale}${parsedUrl.pathname}`
}
const pathnameBeforeRewrite = parsedUrl.pathname
const rewriteParams = utils.handleRewrites(req, parsedUrl)
Expand Down Expand Up @@ -575,32 +587,42 @@ export default abstract class Server<ServerOptions extends Options = Options> {
url.pathname = parsedUrl.pathname
}

addRequestMeta(req, '__nextHadTrailingSlash', url.locale?.trailingSlash)
if (url.locale?.domain) {
addRequestMeta(req, '__nextIsLocaleDomain', true)
}
addRequestMeta(req, '__nextHadTrailingSlash', pathnameInfo.trailingSlash)
addRequestMeta(req, '__nextIsLocaleDomain', Boolean(domainLocale))
parsedUrl.query.__nextDefaultLocale = defaultLocale

if (url.locale?.path.detectedLocale) {
if (pathnameInfo.locale) {
req.url = formatUrl(url)
addRequestMeta(req, '__nextStrippedLocale', true)
}

if (!this.minimalMode || !parsedUrl.query.__nextLocale) {
if (url?.locale?.locale) {
parsedUrl.query.__nextLocale = url.locale.locale
if (pathnameInfo.locale || defaultLocale) {
parsedUrl.query.__nextLocale = pathnameInfo.locale || defaultLocale
}
}

if (url?.locale?.defaultLocale) {
parsedUrl.query.__nextDefaultLocale = url.locale.defaultLocale
}
if (defaultLocale) {
const redirect = getLocaleRedirect({
defaultLocale,
domainLocale,
headers: req.headers,
nextConfig: this.nextConfig,
pathLocale: pathnameInfo.locale,
urlParsed: {
...url,
pathname: pathnameInfo.locale
? `/${pathnameInfo.locale}${url.pathname}`
: url.pathname,
},
})

if (url.locale?.redirect) {
res
.redirect(url.locale.redirect, TEMPORARY_REDIRECT_STATUS)
.body(url.locale.redirect)
.send()
return
if (redirect) {
return res
.redirect(redirect, TEMPORARY_REDIRECT_STATUS)
.body(redirect)
.send()
}
}

res.statusCode = 200
Expand Down Expand Up @@ -807,7 +829,7 @@ export default abstract class Server<ServerOptions extends Options = Options> {
}

// next.js core assumes page path without trailing slash
pathname = removePathTrailingSlash(pathname)
pathname = removeTrailingSlash(pathname)

if (this.nextConfig.i18n) {
const localePathResult = normalizeLocalePath(
Expand Down Expand Up @@ -1284,9 +1306,9 @@ export default abstract class Server<ServerOptions extends Options = Options> {
let resolvedUrlPathname =
getRequestMeta(req, '_nextRewroteUrl') || urlPathname

urlPathname = removePathTrailingSlash(urlPathname)
urlPathname = removeTrailingSlash(urlPathname)
resolvedUrlPathname = normalizeLocalePath(
removePathTrailingSlash(resolvedUrlPathname),
removeTrailingSlash(resolvedUrlPathname),
this.nextConfig.i18n?.locales
).pathname

Expand Down
11 changes: 6 additions & 5 deletions packages/next/server/dev/next-dev-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { FindComponentsResult } from '../next-server'
import type { LoadComponentsReturnType } from '../load-components'
import type { Options as ServerOptions } from '../next-server'
import type { Params } from '../../shared/lib/router/utils/route-matcher'
import type { ParsedNextUrl } from '../../shared/lib/router/utils/parse-next-url'
import type { ParsedUrl } from '../../shared/lib/router/utils/parse-url'
import type { ParsedUrlQuery } from 'querystring'
import type { Server as HTTPServer } from 'http'
import type { UrlWithParsedQuery } from 'url'
Expand Down Expand Up @@ -41,7 +41,8 @@ import { normalizePagePath } from '../../shared/lib/page-path/normalize-page-pat
import { absolutePathToPage } from '../../shared/lib/page-path/absolute-path-to-page'
import Router from '../router'
import { getPathMatch } from '../../shared/lib/router/utils/path-match'
import { hasBasePath, replaceBasePath } from '../router-utils'
import { pathHasPrefix } from '../../shared/lib/router/utils/path-has-prefix'
import { removePathPrefix } from '../../shared/lib/router/utils/remove-path-prefix'
import { eventCliSession } from '../../telemetry/events'
import { Telemetry } from '../../telemetry/storage'
import { setGlobal } from '../../trace'
Expand Down Expand Up @@ -613,7 +614,7 @@ export default class DevServer extends Server {
async runMiddleware(params: {
request: BaseNextRequest
response: BaseNextResponse
parsedUrl: ParsedNextUrl
parsedUrl: ParsedUrl
parsed: UrlWithParsedQuery
}): Promise<FetchEventResult | null> {
try {
Expand Down Expand Up @@ -663,11 +664,11 @@ export default class DevServer extends Server {
const { basePath } = this.nextConfig
let originalPathname: string | null = null

if (basePath && hasBasePath(parsedUrl.pathname || '/', basePath)) {
if (basePath && pathHasPrefix(parsedUrl.pathname || '/', basePath)) {
// strip basePath before handling dev bundles
// If replace ends up replacing the full url it'll be `undefined`, meaning we have to default it to `/`
originalPathname = parsedUrl.pathname
parsedUrl.pathname = replaceBasePath(parsedUrl.pathname || '/', basePath)
parsedUrl.pathname = removePathPrefix(parsedUrl.pathname || '/', basePath)
}

const { pathname } = parsedUrl
Expand Down
25 changes: 9 additions & 16 deletions packages/next/server/next-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { CacheFs, DecodeError, execOnce } from '../shared/lib/utils'
import type { MiddlewareManifest } from '../build/webpack/plugins/middleware-plugin'
import type RenderResult from './render-result'
import type { FetchEventResult } from './web/types'
import type { ParsedNextUrl } from '../shared/lib/router/utils/parse-next-url'
import type { PrerenderManifest } from '../build'
import type { Rewrite } from '../lib/load-custom-routes'
import type { BaseNextRequest, BaseNextResponse } from './base-http'
Expand Down Expand Up @@ -64,7 +63,6 @@ import isError, { getProperError } from '../lib/is-error'
import { FontManifest } from './font-utils'
import { toNodeHeaders } from './web/utils'
import { relativizeURL } from '../shared/lib/router/utils/relativize-url'
import { parseNextUrl } from '../shared/lib/router/utils/parse-next-url'
import { prepareDestination } from '../shared/lib/router/utils/prepare-destination'
import { normalizeLocalePath } from '../shared/lib/i18n/normalize-locale-path'
import { getRouteMatcher } from '../shared/lib/router/utils/route-matcher'
Expand All @@ -73,9 +71,10 @@ import { loadEnvConfig } from '@next/env'
import { getCustomRoute } from './server-route-utils'
import { urlQueryToSearchParams } from '../shared/lib/router/utils/querystring'
import ResponseCache from '../server/response-cache'
import { removePathTrailingSlash } from '../client/normalize-trailing-slash'
import { removeTrailingSlash } from '../shared/lib/router/utils/remove-trailing-slash'
import { clonableBodyForRequest } from './body-streams'
import { getMiddlewareRegex } from '../shared/lib/router/utils/route-regex'
import { getNextPathnameInfo } from '../shared/lib/router/utils/get-next-pathname-info'

export * from './base-server'

Expand Down Expand Up @@ -1098,14 +1097,12 @@ export default class NextNodeServer extends BaseServer {
protected async runMiddleware(params: {
request: BaseNextRequest
response: BaseNextResponse
parsedUrl: ParsedNextUrl
parsedUrl: ParsedUrl
parsed: UrlWithParsedQuery
onWarning?: (warning: Error) => void
}): Promise<FetchEventResult | null> {
middlewareBetaWarning()
const normalizedPathname = removePathTrailingSlash(
params.parsedUrl.pathname
)
const normalizedPathname = removeTrailingSlash(params.parsedUrl.pathname)

// For middleware to "fetch" we must always provide an absolute URL
const url = getRequestMeta(params.request, '__NEXT_INIT_URL')!
Expand Down Expand Up @@ -1217,17 +1214,13 @@ export default class NextNodeServer extends BaseServer {
}

const initUrl = getRequestMeta(req, '__NEXT_INIT_URL')!
const parsedUrl = parseNextUrl({
url: initUrl,
headers: req.headers,
nextConfig: {
basePath: this.nextConfig.basePath,
i18n: this.nextConfig.i18n,
trailingSlash: this.nextConfig.trailingSlash,
},
const parsedUrl = parseUrl(initUrl)
const pathnameInfo = getNextPathnameInfo(parsedUrl.pathname, {
nextConfig: this.nextConfig,
})

const normalizedPathname = removePathTrailingSlash(parsedUrl.pathname)
parsedUrl.pathname = pathnameInfo.pathname
const normalizedPathname = removeTrailingSlash(parsedUrl.pathname)
if (!middleware.some((m) => m.match(normalizedPathname))) {
return { finished: false }
}
Expand Down
17 changes: 0 additions & 17 deletions packages/next/server/router-utils.ts

This file was deleted.

Loading