Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/canary' into topic-pr-21535
Browse files Browse the repository at this point in the history
  • Loading branch information
ijjk committed Jul 21, 2021
2 parents dc8b5e0 + e65c56e commit ee6d417
Show file tree
Hide file tree
Showing 15 changed files with 331 additions and 267 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ export function getPageHandler(ctx: ServerlessHandlerCtx) {

let hasValidParams = true

setLazyProp({ req: req as any }, 'cookies', getCookieParser(req))
setLazyProp({ req: req as any }, 'cookies', getCookieParser(req.headers))

const options = {
App,
Expand Down
14 changes: 7 additions & 7 deletions packages/next/server/api-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export async function apiResolver(
const externalResolver = config.api?.externalResolver || false

// Parsing of cookies
setLazyProp({ req: apiReq }, 'cookies', getCookieParser(req))
setLazyProp({ req: apiReq }, 'cookies', getCookieParser(req.headers))
// Parsing query string
apiReq.query = query
// Parsing preview data
Expand Down Expand Up @@ -185,14 +185,14 @@ function parseJson(str: string): object {
}

/**
* Parse cookies from `req` header
* Parse cookies from the `headers` of request
* @param req request object
*/
export function getCookieParser(
req: IncomingMessage
): () => NextApiRequestCookies {
export function getCookieParser(headers: {
[key: string]: undefined | string | string[]
}): () => NextApiRequestCookies {
return function parseCookie(): NextApiRequestCookies {
const header: undefined | string | string[] = req.headers.cookie
const header: undefined | string | string[] = headers.cookie

if (!header) {
return {}
Expand Down Expand Up @@ -321,7 +321,7 @@ export function tryGetPreviewData(
return (req as any)[SYMBOL_PREVIEW_DATA] as any
}

const getCookies = getCookieParser(req)
const getCookies = getCookieParser(req.headers)
let cookies: NextApiRequestCookies
try {
cookies = getCookies()
Expand Down
28 changes: 15 additions & 13 deletions packages/next/server/config-shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,28 @@ import os from 'os'
import { Header, Redirect, Rewrite } from '../lib/load-custom-routes'
import { ImageConfig, imageConfigDefault } from './image-config'

export type DomainLocales = Array<{
http?: true
domain: string
locales?: string[]
defaultLocale: string
}>

type NoOptionals<T> = {
[P in keyof T]-?: T[P]
}

export type NextConfigComplete = NoOptionals<NextConfig>

export interface I18NConfig {
defaultLocale: string
domains?: DomainLocale[]
localeDetection?: false
locales: string[]
}

export interface DomainLocale {
defaultLocale: string
domain: string
http?: true
locales?: string[]
}

export type NextConfig = { [key: string]: any } & {
i18n?: {
locales: string[]
defaultLocale: string
domains?: DomainLocales
localeDetection?: false
} | null
i18n?: I18NConfig | null

headers?: () => Promise<Header[]>
rewrites?: () => Promise<
Expand Down
2 changes: 1 addition & 1 deletion packages/next/server/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { loadWebpackHook } from './config-utils'
import { ImageConfig, imageConfigDefault, VALID_LOADERS } from './image-config'
import { loadEnvConfig } from '@next/env'

export { DomainLocales, NextConfig, normalizeConfig } from './config-shared'
export { DomainLocale, NextConfig, normalizeConfig } from './config-shared'

const targets = ['server', 'serverless', 'experimental-serverless-trace']

Expand Down
186 changes: 33 additions & 153 deletions packages/next/server/next-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ import {
tryGetPreviewData,
__ApiPreviewProps,
} from './api-utils'
import { DomainLocales, isTargetLikeServerless, NextConfig } from './config'
import { DomainLocale, isTargetLikeServerless, NextConfig } from './config'
import pathMatch from '../shared/lib/router/utils/path-match'
import { recursiveReadDirSync } from './lib/recursive-readdir-sync'
import { loadComponents, LoadComponentsReturnType } from './load-components'
Expand Down Expand Up @@ -83,13 +83,10 @@ import { removePathTrailingSlash } from '../client/normalize-trailing-slash'
import getRouteFromAssetPath from '../shared/lib/router/utils/get-route-from-asset-path'
import { FontManifest } from './font-utils'
import { denormalizePagePath } from './denormalize-page-path'
import accept from '@hapi/accept'
import { normalizeLocalePath } from '../shared/lib/i18n/normalize-locale-path'
import { detectLocaleCookie } from '../shared/lib/i18n/detect-locale-cookie'
import * as Log from '../build/output/log'
import { imageOptimizer } from './image-optimizer'
import { detectDomainLocale } from '../shared/lib/i18n/detect-domain-locale'
import cookie from 'next/dist/compiled/cookie'
import escapePathDelimiters from '../shared/lib/router/utils/escape-path-delimiters'
import { getUtils } from '../build/webpack/loaders/next-serverless-loader/utils'
import { PreviewData } from 'next/types'
Expand All @@ -98,6 +95,7 @@ import ResponseCache, {
ResponseCacheValue,
} from './response-cache'
import { NextConfigComplete } from './config-shared'
import { parseNextUrl } from '../shared/lib/router/utils/parse-next-url'

const getCustomRouteMatcher = pathMatch(true)

Expand Down Expand Up @@ -175,7 +173,7 @@ export default class Server {
locale?: string
locales?: string[]
defaultLocale?: string
domainLocales?: DomainLocales
domainLocales?: DomainLocale[]
distDir: string
}
private compression?: Middleware
Expand Down Expand Up @@ -310,7 +308,7 @@ export default class Server {
res: ServerResponse,
parsedUrl?: UrlWithParsedQuery
): Promise<void> {
setLazyProp({ req: req as any }, 'cookies', getCookieParser(req))
setLazyProp({ req: req as any }, 'cookies', getCookieParser(req.headers))

// Parse url if parsedUrl not provided
if (!parsedUrl || typeof parsedUrl !== 'object') {
Expand All @@ -325,9 +323,13 @@ export default class Server {
}
;(req as any).__NEXT_INIT_QUERY = Object.assign({}, parsedUrl.query)

if (basePath && req.url?.startsWith(basePath)) {
// store original URL to allow checking if basePath was
// provided or not
const url = parseNextUrl({
headers: req.headers,
nextConfig: this.nextConfig,
url: req.url?.replace(/^\/+/, '/'),
})

if (url.basePath) {
;(req as any)._nextHadBasePath = true
req.url = req.url!.replace(basePath, '') || '/'
}
Expand Down Expand Up @@ -437,156 +439,34 @@ export default class Server {
}`
}

if (i18n) {
// get pathname from URL with basePath stripped for locale detection
let { pathname, ...parsed } = parseUrl(req.url || '/')
pathname = pathname || '/'

let defaultLocale = i18n.defaultLocale
let detectedLocale = detectLocaleCookie(req, i18n.locales)
let acceptPreferredLocale
try {
acceptPreferredLocale =
i18n.localeDetection !== false
? accept.language(req.headers['accept-language'], i18n.locales)
: detectedLocale
} catch (_) {
acceptPreferredLocale = detectedLocale
}
const { host } = req?.headers || {}
// remove port from host if present
const hostname = host?.split(':')[0].toLowerCase()

const detectedDomain = detectDomainLocale(i18n.domains, hostname)
if (detectedDomain) {
defaultLocale = detectedDomain.defaultLocale
detectedLocale = defaultLocale
;(req as any).__nextIsLocaleDomain = true
}

// if not domain specific locale use accept-language preferred
detectedLocale = detectedLocale || acceptPreferredLocale

let localeDomainRedirect: string | undefined
;(req as any).__nextHadTrailingSlash = pathname!.endsWith('/')

if (pathname === '/') {
;(req as any).__nextHadTrailingSlash = this.nextConfig.trailingSlash
}
const localePathResult = normalizeLocalePath(pathname!, i18n.locales)

if (localePathResult.detectedLocale) {
detectedLocale = localePathResult.detectedLocale
req.url = formatUrl({
...parsed,
pathname: localePathResult.pathname,
})
;(req as any).__nextStrippedLocale = true

if (
localePathResult.pathname === '/api' ||
localePathResult.pathname.startsWith('/api/')
) {
return this.render404(req, res, parsedUrl)
}
}

// If a detected locale is a domain specific locale and we aren't already
// on that domain and path prefix redirect to it to prevent duplicate
// content from multiple domains
if (detectedDomain && pathname === '/') {
const localeToCheck = acceptPreferredLocale
// const localeToCheck = localePathResult.detectedLocale
// ? detectedLocale
// : acceptPreferredLocale

const matchedDomain = detectDomainLocale(
i18n.domains,
undefined,
localeToCheck
)
;(req as any).__nextHadTrailingSlash = url.locale?.trailingSlash
if (url.locale?.domain) {
;(req as any).__nextIsLocaleDomain = true
}

if (
matchedDomain &&
(matchedDomain.domain !== detectedDomain.domain ||
localeToCheck !== matchedDomain.defaultLocale)
) {
localeDomainRedirect = `http${matchedDomain.http ? '' : 's'}://${
matchedDomain.domain
}/${
localeToCheck === matchedDomain.defaultLocale ? '' : localeToCheck
}`
}
if (url.locale?.path.detectedLocale) {
req.url = formatUrl(url)
;(req as any).__nextStrippedLocale = true
if (url.pathname === '/api' || url.pathname.startsWith('/api/')) {
return this.render404(req, res, parsedUrl)
}
}

const denormalizedPagePath = denormalizePagePath(pathname || '/')
const detectedDefaultLocale =
!detectedLocale ||
detectedLocale.toLowerCase() === defaultLocale.toLowerCase()
const shouldStripDefaultLocale = false
// detectedDefaultLocale &&
// denormalizedPagePath.toLowerCase() ===
// `/${i18n.defaultLocale.toLowerCase()}`

const shouldAddLocalePrefix =
!detectedDefaultLocale && denormalizedPagePath === '/'

detectedLocale = detectedLocale || i18n.defaultLocale

if (
i18n.localeDetection !== false &&
(localeDomainRedirect ||
shouldAddLocalePrefix ||
shouldStripDefaultLocale)
) {
// set the NEXT_LOCALE cookie when a user visits the default locale
// with the locale prefix so that they aren't redirected back to
// their accept-language preferred locale
if (
shouldStripDefaultLocale &&
acceptPreferredLocale !== defaultLocale
) {
const previous = res.getHeader('set-cookie')

res.setHeader('set-cookie', [
...(typeof previous === 'string'
? [previous]
: Array.isArray(previous)
? previous
: []),
cookie.serialize('NEXT_LOCALE', defaultLocale, {
httpOnly: true,
path: '/',
}),
])
}

res.setHeader(
'Location',
localeDomainRedirect
? localeDomainRedirect
: formatUrl({
// make sure to include any query values when redirecting
...parsed,
pathname: shouldStripDefaultLocale
? basePath || `/`
: `${basePath || ''}/${detectedLocale}`,
})
)
res.statusCode = TEMPORARY_REDIRECT_STATUS
res.end()
return
if (!this.minimalMode || !parsedUrl.query.__nextLocale) {
if (url?.locale?.locale) {
parsedUrl.query.__nextLocale = url.locale.locale
}
}

parsedUrl.query.__nextDefaultLocale =
detectedDomain?.defaultLocale || i18n.defaultLocale
if (url?.locale?.defaultLocale) {
parsedUrl.query.__nextDefaultLocale = url.locale.defaultLocale
}

if (!this.minimalMode || !parsedUrl.query.__nextLocale) {
parsedUrl.query.__nextLocale =
localePathResult.detectedLocale ||
detectedDomain?.defaultLocale ||
defaultLocale
}
if (url.locale?.redirect) {
res.setHeader('Location', url.locale.redirect)
res.statusCode = TEMPORARY_REDIRECT_STATUS
res.end()
return
}

res.statusCode = 200
Expand Down
8 changes: 4 additions & 4 deletions packages/next/server/render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ import {
getRedirectStatus,
Redirect,
} from '../lib/load-custom-routes'
import { DomainLocales } from './config'
import type { DomainLocale } from './config'

function noRouter() {
const message =
Expand All @@ -79,7 +79,7 @@ class ServerRouter implements NextRouter {
isReady: boolean
locales?: string[]
defaultLocale?: string
domainLocales?: DomainLocales
domainLocales?: DomainLocale[]
isPreview: boolean
isLocaleDomain: boolean

Expand All @@ -93,7 +93,7 @@ class ServerRouter implements NextRouter {
locale?: string,
locales?: string[],
defaultLocale?: string,
domainLocales?: DomainLocales,
domainLocales?: DomainLocale[],
isPreview?: boolean,
isLocaleDomain?: boolean
) {
Expand Down Expand Up @@ -186,7 +186,7 @@ export type RenderOptsPartial = {
locale?: string
locales?: string[]
defaultLocale?: string
domainLocales?: DomainLocales
domainLocales?: DomainLocale[]
disableOptimizedLoading?: boolean
requireStaticHTML?: boolean
}
Expand Down
Loading

0 comments on commit ee6d417

Please sign in to comment.