Skip to content

Commit

Permalink
Apply middleware patches
Browse files Browse the repository at this point in the history
  • Loading branch information
ijjk committed Nov 8, 2022
1 parent 58dcf30 commit 1b8ab4e
Show file tree
Hide file tree
Showing 19 changed files with 494 additions and 59 deletions.
8 changes: 8 additions & 0 deletions packages/next/build/webpack-config.ts
Expand Up @@ -155,6 +155,12 @@ export function getDefineEnv({
'process.env.__NEXT_OPTIMISTIC_CLIENT_CACHE': JSON.stringify(
config.experimental.optimisticClientCache
),
'process.env.__NEXT_NO_MIDDLEWARE_URL_NORMALIZE': JSON.stringify(
config.experimental.skipMiddlewareUrlNormalize
),
'process.env.__NEXT_ALLOW_MIDDLEWARE_RESPONSE_BODY': JSON.stringify(
config.experimental.allowMiddlewareResponseBody
),
'process.env.__NEXT_CROSS_ORIGIN': JSON.stringify(config.crossOrigin),
'process.browser': JSON.stringify(isClient),
'process.env.__NEXT_TEST_MODE': JSON.stringify(
Expand Down Expand Up @@ -1779,6 +1785,8 @@ export default async function getBaseWebpackConfig(
new MiddlewarePlugin({
dev,
sriEnabled: !dev && !!config.experimental.sri?.algorithm,
allowMiddlewareResponseBody:
!!config.experimental.allowMiddlewareResponseBody,
}),
isClient &&
new BuildManifestPlugin({
Expand Down
22 changes: 18 additions & 4 deletions packages/next/build/webpack/plugins/middleware-plugin.ts
Expand Up @@ -335,12 +335,14 @@ function getCodeAnalyzer(params: {
dev: boolean
compiler: webpack.Compiler
compilation: webpack.Compilation
allowMiddlewareResponseBody: boolean
}) {
return (parser: webpack.javascript.JavascriptParser) => {
const {
dev,
compiler: { webpack: wp },
compilation,
allowMiddlewareResponseBody,
} = params
const { hooks } = parser

Expand Down Expand Up @@ -557,8 +559,10 @@ Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime`,
.for(`${prefix}WebAssembly.instantiate`)
.tap(NAME, handleWrapWasmInstantiateExpression)
}
hooks.new.for('Response').tap(NAME, handleNewResponseExpression)
hooks.new.for('NextResponse').tap(NAME, handleNewResponseExpression)
if (!allowMiddlewareResponseBody) {
hooks.new.for('Response').tap(NAME, handleNewResponseExpression)
hooks.new.for('NextResponse').tap(NAME, handleNewResponseExpression)
}
hooks.callMemberChain.for('process').tap(NAME, handleCallMemberChain)
hooks.expressionMemberChain.for('process').tap(NAME, handleCallMemberChain)
hooks.importCall.tap(NAME, handleImport)
Expand Down Expand Up @@ -797,10 +801,19 @@ function getExtractMetadata(params: {
export default class MiddlewarePlugin {
private readonly dev: boolean
private readonly sriEnabled: boolean

constructor({ dev, sriEnabled }: { dev: boolean; sriEnabled: boolean }) {
private readonly allowMiddlewareResponseBody: boolean
constructor({
dev,
sriEnabled,
allowMiddlewareResponseBody,
}: {
dev: boolean
sriEnabled: boolean
allowMiddlewareResponseBody: boolean
}) {
this.dev = dev
this.sriEnabled = sriEnabled
this.allowMiddlewareResponseBody = allowMiddlewareResponseBody
}

public apply(compiler: webpack.Compiler) {
Expand All @@ -813,6 +826,7 @@ export default class MiddlewarePlugin {
dev: this.dev,
compiler,
compilation,
allowMiddlewareResponseBody: this.allowMiddlewareResponseBody,
})
hooks.parser.for('javascript/auto').tap(NAME, codeAnalyzer)
hooks.parser.for('javascript/dynamic').tap(NAME, codeAnalyzer)
Expand Down
80 changes: 41 additions & 39 deletions packages/next/lib/load-custom-routes.ts
Expand Up @@ -624,50 +624,52 @@ export default async function loadCustomRoutes(
)
}

if (config.trailingSlash) {
redirects.unshift(
{
source: '/:file((?!\\.well-known(?:/.*)?)(?:[^/]+/)*[^/]+\\.\\w+)/',
destination: '/:file',
permanent: true,
locale: config.i18n ? false : undefined,
internal: true,
} as Redirect,
{
source: '/:notfile((?!\\.well-known(?:/.*)?)(?:[^/]+/)*[^/\\.]+)',
destination: '/:notfile/',
permanent: true,
locale: config.i18n ? false : undefined,
internal: true,
} as Redirect
)
if (config.basePath) {
redirects.unshift({
source: config.basePath,
destination: config.basePath + '/',
permanent: true,
basePath: false,
locale: config.i18n ? false : undefined,
internal: true,
} as Redirect)
}
} else {
redirects.unshift({
source: '/:path+/',
destination: '/:path+',
permanent: true,
locale: config.i18n ? false : undefined,
internal: true,
} as Redirect)
if (config.basePath) {
if (!config.experimental?.skipTrailingSlashRedirect) {
if (config.trailingSlash) {
redirects.unshift(
{
source: '/:file((?!\\.well-known(?:/.*)?)(?:[^/]+/)*[^/]+\\.\\w+)/',
destination: '/:file',
permanent: true,
locale: config.i18n ? false : undefined,
internal: true,
} as Redirect,
{
source: '/:notfile((?!\\.well-known(?:/.*)?)(?:[^/]+/)*[^/\\.]+)',
destination: '/:notfile/',
permanent: true,
locale: config.i18n ? false : undefined,
internal: true,
} as Redirect
)
if (config.basePath) {
redirects.unshift({
source: config.basePath,
destination: config.basePath + '/',
permanent: true,
basePath: false,
locale: config.i18n ? false : undefined,
internal: true,
} as Redirect)
}
} else {
redirects.unshift({
source: config.basePath + '/',
destination: config.basePath,
source: '/:path+/',
destination: '/:path+',
permanent: true,
basePath: false,
locale: config.i18n ? false : undefined,
internal: true,
} as Redirect)
if (config.basePath) {
redirects.unshift({
source: config.basePath + '/',
destination: config.basePath,
permanent: true,
basePath: false,
locale: config.i18n ? false : undefined,
internal: true,
} as Redirect)
}
}
}

Expand Down
27 changes: 27 additions & 0 deletions packages/next/server/base-server.ts
Expand Up @@ -434,6 +434,33 @@ export default abstract class Server<ServerOptions extends Options = Options> {
parsedUrl?: NextUrlWithParsedQuery
): Promise<void> {
try {
// ensure cookies set in middleware are merged and
// not overridden by API routes/getServerSideProps
const _res = (res as any).originalResponse || res
const origSetHeader = _res.setHeader.bind(_res)

_res.setHeader = (name: string, val: string | string[]) => {
if (name.toLowerCase() === 'set-cookie') {
const middlewareValue = getRequestMeta(req, '_nextMiddlewareCookie')

if (
!middlewareValue ||
!Array.isArray(val) ||
!val.every((item, idx) => item === middlewareValue[idx])
) {
val = [
...(middlewareValue || []),
...(typeof val === 'string'
? [val]
: Array.isArray(val)
? val
: []),
]
}
}
return origSetHeader(name, val)
}

const urlParts = (req.url || '').split('?')
const urlNoQuery = urlParts[0]

Expand Down
9 changes: 9 additions & 0 deletions packages/next/server/config-schema.ts
Expand Up @@ -222,6 +222,9 @@ const configSchema = {
adjustFontFallbacks: {
type: 'boolean',
},
allowMiddlewareResponseBody: {
type: 'boolean',
},
amp: {
additionalProperties: false,
properties: {
Expand Down Expand Up @@ -348,6 +351,12 @@ const configSchema = {
sharedPool: {
type: 'boolean',
},
skipMiddlewareUrlNormalize: {
type: 'boolean',
},
skipTrailingSlashRedirect: {
type: 'boolean',
},
sri: {
properties: {
algorithm: {
Expand Down
3 changes: 3 additions & 0 deletions packages/next/server/config-shared.ts
Expand Up @@ -78,6 +78,9 @@ export interface NextJsWebpackConfig {
}

export interface ExperimentalConfig {
allowMiddlewareResponseBody?: boolean
skipMiddlewareUrlNormalize?: boolean
skipTrailingSlashRedirect?: boolean
optimisticClientCache?: boolean
legacyBrowsers?: boolean
browsersListForSwc?: boolean
Expand Down
27 changes: 24 additions & 3 deletions packages/next/server/next-server.ts
Expand Up @@ -83,7 +83,7 @@ import { normalizePagePath } from '../shared/lib/page-path/normalize-page-path'
import { loadComponents } from './load-components'
import isError, { getProperError } from '../lib/is-error'
import { FontManifest } from './font-utils'
import { toNodeHeaders } from './web/utils'
import { splitCookiesString, toNodeHeaders } from './web/utils'
import { relativizeURL } from '../shared/lib/router/utils/relativize-url'
import { prepareDestination } from '../shared/lib/router/utils/prepare-destination'
import { normalizeLocalePath } from '../shared/lib/i18n/normalize-locale-path'
Expand Down Expand Up @@ -1051,9 +1051,17 @@ export default class NextNodeServer extends BaseServer {
name: '_next/data catchall',
check: true,
fn: async (req, res, params, _parsedUrl) => {
const isNextDataNormalizing = getRequestMeta(
req,
'_nextDataNormalizing'
)

// Make sure to 404 for /_next/data/ itself and
// we also want to 404 if the buildId isn't correct
if (!params.path || params.path[0] !== this.buildId) {
if (isNextDataNormalizing) {
return { finished: false }
}
await this.render404(req, res, _parsedUrl)
return {
finished: true,
Expand Down Expand Up @@ -1797,6 +1805,14 @@ export default class NextNodeServer extends BaseServer {
} else {
for (let [key, value] of allHeaders) {
result.response.headers.set(key, value)

if (key.toLowerCase() === 'set-cookie') {
addRequestMeta(
params.request,
'_nextMiddlewareCookie',
splitCookiesString(value)
)
}
}
}

Expand Down Expand Up @@ -2105,8 +2121,13 @@ export default class NextNodeServer extends BaseServer {
params.res.statusCode = result.response.status
params.res.statusMessage = result.response.statusText

result.response.headers.forEach((value, key) => {
params.res.appendHeader(key, value)
result.response.headers.forEach((value: string, key) => {
// the append handling is special cased for `set-cookie`
if (key.toLowerCase() === 'set-cookie') {
params.res.setHeader(key, value)
} else {
params.res.appendHeader(key, value)
}
})

if (result.response.body) {
Expand Down
2 changes: 2 additions & 0 deletions packages/next/server/request-meta.ts
Expand Up @@ -22,6 +22,8 @@ export interface RequestMeta {
_nextHadBasePath?: boolean
_nextRewroteUrl?: string
_protocol?: string
_nextMiddlewareCookie?: string[]
_nextDataNormalizing?: boolean
}

export function getRequestMeta(
Expand Down
20 changes: 18 additions & 2 deletions packages/next/server/router.ts
Expand Up @@ -7,7 +7,11 @@ import type {
} from '../shared/lib/router/utils/route-matcher'
import type { RouteHas } from '../lib/load-custom-routes'

import { getNextInternalQuery, NextUrlWithParsedQuery } from './request-meta'
import {
addRequestMeta,
getNextInternalQuery,
NextUrlWithParsedQuery,
} from './request-meta'
import { getPathMatch } from '../shared/lib/router/utils/path-match'
import { removeTrailingSlash } from '../shared/lib/router/utils/remove-trailing-slash'
import { normalizeLocalePath } from '../shared/lib/i18n/normalize-locale-path'
Expand Down Expand Up @@ -189,7 +193,11 @@ export default class Router {
...(middlewareCatchAllRoute
? this.fsRoutes
.filter((route) => route.name === '_next/data catchall')
.map((route) => ({ ...route, check: false }))
.map((route) => ({
...route,
name: '_next/data normalizing',
check: false,
}))
: []),
...this.headers,
...this.redirects,
Expand Down Expand Up @@ -433,6 +441,11 @@ export default class Router {
}

if (params) {
const isNextDataNormalizing = route.name === '_next/data normalizing'

if (isNextDataNormalizing) {
addRequestMeta(req, '_nextDataNormalizing', true)
}
parsedUrlUpdated.pathname = matchPathname
const result = await route.fn(
req,
Expand All @@ -441,6 +454,9 @@ export default class Router {
parsedUrlUpdated,
upgradeHead
)
if (isNextDataNormalizing) {
addRequestMeta(req, '_nextDataNormalizing', false)
}
if (result.finished) {
return true
}
Expand Down

0 comments on commit 1b8ab4e

Please sign in to comment.