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

feat: add body parser limit for server actions #51104

Merged
merged 23 commits into from Jun 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
8941247
feat: add body parser limit for server actions
devjiwonchoi Jun 10, 2023
bd5a4e2
chore: add validation for server actions in use
devjiwonchoi Jun 10, 2023
c69d44d
fix: fix validating serverActionsLimit as ternary
devjiwonchoi Jun 10, 2023
aaaaea3
Merge branch 'canary' into feat/server-actions-bodyparser-limit
devjiwonchoi Jun 10, 2023
f244f03
Merge branch 'canary' into feat/server-actions-bodyparser-limit
devjiwonchoi Jun 11, 2023
540a05f
Merge branch 'canary' into feat/server-actions-bodyparser-limit
devjiwonchoi Jun 12, 2023
e3d8323
Merge branch 'canary' into feat/server-actions-bodyparser-limit
devjiwonchoi Jun 12, 2023
38e7a37
Merge branch 'canary' into feat/server-actions-bodyparser-limit
devjiwonchoi Jun 13, 2023
b6bd10d
Merge branch 'canary' into feat/server-actions-bodyparser-limit
devjiwonchoi Jun 13, 2023
119afe3
refac: remove unnecessary condition
devjiwonchoi Jun 14, 2023
3cc4a47
Merge branch 'canary' into feat/server-actions-bodyparser-limit
devjiwonchoi Jun 15, 2023
710d810
chore: add serverActionsSizeLimit type to config-schema
devjiwonchoi Jun 15, 2023
d08d078
chore: move loadConfig to renderOpts
devjiwonchoi Jun 15, 2023
7e6c80d
Merge branch 'canary' into feat/server-actions-bodyparser-limit
devjiwonchoi Jun 15, 2023
aa9e0db
chore: add error case for serverActionsSizeLimit
devjiwonchoi Jun 15, 2023
b7daec8
chore: add fail test for invalid size limit config
devjiwonchoi Jun 15, 2023
7d918db
chore: modify test condition text
devjiwonchoi Jun 15, 2023
402e61c
chore: correct grammar of test condition text
devjiwonchoi Jun 15, 2023
8ce8a96
Merge branch 'canary' into feat/server-actions-bodyparser-limit
shuding Jun 23, 2023
915b9b9
avoid loading nextConfig again
shuding Jun 23, 2023
2e39afe
fix lint and early error
shuding Jun 23, 2023
351c20e
fix test
shuding Jun 23, 2023
cf79508
Merge branch 'canary' into feat/server-actions-bodyparser-limit
shuding Jun 23, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/next/src/build/entries.ts
Expand Up @@ -377,6 +377,7 @@ export function getEdgeServerEntry(opts: {
middlewareConfig: Buffer.from(
JSON.stringify(opts.middlewareConfig || {})
).toString('base64'),
serverActionsSizeLimit: opts.config.experimental.serverActionsSizeLimit,
}

return {
Expand Down
6 changes: 3 additions & 3 deletions packages/next/src/build/webpack-config.ts
Expand Up @@ -773,8 +773,8 @@ export default async function getBaseWebpackConfig(
const hasServerComponents = hasAppDir
const disableOptimizedLoading = true
const enableTypedRoutes = !!config.experimental.typedRoutes && hasAppDir
const serverActions = !!config.experimental.serverActions && hasAppDir
const bundledReactChannel = serverActions ? '-experimental' : ''
const useServerActions = !!config.experimental.serverActions && hasAppDir
const bundledReactChannel = useServerActions ? '-experimental' : ''

if (isClient) {
if (
Expand Down Expand Up @@ -2418,7 +2418,7 @@ export default async function getBaseWebpackConfig(
appDir,
dev,
isEdgeServer,
useServerActions: serverActions,
useServerActions,
})),
hasAppDir &&
!isClient &&
Expand Down
@@ -1,4 +1,6 @@
import type webpack from 'webpack'
import type { SizeLimit } from '../../../../../types'

import { getModuleBuildInfo } from '../get-module-build-info'
import { WEBPACK_RESOURCE_QUERIES } from '../../../../lib/constants'
import { stringifyRequest } from '../../stringify-request'
Expand All @@ -21,6 +23,7 @@ export type EdgeSSRLoaderQuery = {
incrementalCacheHandlerPath?: string
preferredRegion: string | string[] | undefined
middlewareConfig: string
serverActionsSizeLimit?: SizeLimit
}

/*
Expand Down Expand Up @@ -54,6 +57,7 @@ const edgeSSRLoader: webpack.LoaderDefinitionFunction<EdgeSSRLoaderQuery> =
incrementalCacheHandlerPath,
preferredRegion,
middlewareConfig: middlewareConfigBase64,
serverActionsSizeLimit,
} = this.getOptions()

const middlewareConfig: MiddlewareConfig = JSON.parse(
Expand Down Expand Up @@ -173,6 +177,11 @@ const edgeSSRLoader: webpack.LoaderDefinitionFunction<EdgeSSRLoaderQuery> =
reactLoadableManifest,
clientReferenceManifest: ${isServerComponent} ? rscManifest : null,
serverActionsManifest: ${isServerComponent} ? rscServerManifest : null,
serverActionsSizeLimit: ${isServerComponent} ? ${
typeof serverActionsSizeLimit === 'undefined'
? 'undefined'
: JSON.stringify(serverActionsSizeLimit)
} : undefined,
subresourceIntegrityManifest,
config: ${stringifiedConfig},
buildId: ${JSON.stringify(buildId)},
Expand Down
Expand Up @@ -14,6 +14,7 @@ import {
import { SERVER_RUNTIME } from '../../../../lib/constants'
import { PrerenderManifest } from '../../..'
import { normalizeAppPath } from '../../../../shared/lib/router/utils/app-paths'
import { SizeLimit } from '../../../../../types'

export function getRender({
dev,
Expand All @@ -31,6 +32,7 @@ export function getRender({
clientReferenceManifest,
subresourceIntegrityManifest,
serverActionsManifest,
serverActionsSizeLimit,
config,
buildId,
nextFontManifest,
Expand All @@ -51,6 +53,7 @@ export function getRender({
subresourceIntegrityManifest?: Record<string, string>
clientReferenceManifest?: ClientReferenceManifest
serverActionsManifest: any
serverActionsSizeLimit?: SizeLimit
appServerMod: any
config: NextConfigComplete
buildId: string
Expand Down Expand Up @@ -85,6 +88,7 @@ export function getRender({
disableOptimizedLoading: true,
clientReferenceManifest,
serverActionsManifest,
serverActionsSizeLimit,
},
renderToHTML,
incrementalCacheHandler,
Expand Down
Expand Up @@ -30,12 +30,14 @@ import {
import { traverseModules, forEachEntryModule } from '../utils'
import { normalizePathSep } from '../../../shared/lib/page-path/normalize-path-sep'
import { getProxiedPluginState } from '../../build-context'
import { SizeLimit } from '../../../../types'

interface Options {
dev: boolean
appDir: string
isEdgeServer: boolean
useServerActions: boolean
serverActionsSizeLimit?: SizeLimit
}

const PLUGIN_NAME = 'ClientEntryPlugin'
Expand Down Expand Up @@ -151,13 +153,15 @@ export class ClientReferenceEntryPlugin {
appDir: string
isEdgeServer: boolean
useServerActions: boolean
serverActionsSizeLimit?: SizeLimit
assetPrefix: string

constructor(options: Options) {
this.dev = options.dev
this.appDir = options.appDir
this.isEdgeServer = options.isEdgeServer
this.useServerActions = options.useServerActions
this.serverActionsSizeLimit = options.serverActionsSizeLimit
this.assetPrefix = !this.dev && !this.isEdgeServer ? '../' : ''
}

Expand Down
1 change: 1 addition & 0 deletions packages/next/src/export/index.ts
Expand Up @@ -472,6 +472,7 @@ export default async function exportApp(
largePageDataBytes: nextConfig.experimental.largePageDataBytes,
serverComponents: options.hasAppDir,
hasServerComponents: options.hasAppDir,
serverActionsSizeLimit: nextConfig.experimental.serverActionsSizeLimit,
nextFontManifest: require(join(
distDir,
'server',
Expand Down
7 changes: 6 additions & 1 deletion packages/next/src/server/app-render/action-handler.ts
Expand Up @@ -5,6 +5,7 @@ import type {
ServerResponse,
} from 'http'
import type { WebNextRequest } from '../base-http/web'
import type { SizeLimit } from '../../../types'

import {
ACTION,
Expand Down Expand Up @@ -245,6 +246,7 @@ export async function handleAction({
generateFlight,
staticGenerationStore,
requestStore,
serverActionsSizeLimit,
}: {
req: IncomingMessage
res: ServerResponse
Expand All @@ -258,6 +260,7 @@ export async function handleAction({
}) => Promise<RenderResult>
staticGenerationStore: StaticGenerationStore
requestStore: RequestStore
serverActionsSizeLimit?: SizeLimit
}): Promise<undefined | RenderResult | 'not-found'> {
let actionId = req.headers[ACTION.toLowerCase()] as string
const contentType = req.headers['content-type']
Expand Down Expand Up @@ -372,7 +375,9 @@ export async function handleAction({
} else {
const { parseBody } =
require('../api-utils/node') as typeof import('../api-utils/node')
const actionData = (await parseBody(req, '1mb')) || ''

const actionData =
(await parseBody(req, serverActionsSizeLimit ?? '1mb')) || ''

if (isURLEncodedAction) {
const formData = formDataFromSearchQueryString(actionData)
Expand Down
2 changes: 2 additions & 0 deletions packages/next/src/server/app-render/app-render.tsx
Expand Up @@ -156,6 +156,7 @@ export async function renderToHTMLOrFlight(
nextFontManifest,
supportsDynamicHTML,
nextConfigOutput,
serverActionsSizeLimit,
} = renderOpts

const appUsingSizeAdjust = nextFontManifest?.appUsingSizeAdjust
Expand Down Expand Up @@ -1606,6 +1607,7 @@ export async function renderToHTMLOrFlight(
generateFlight,
staticGenerationStore,
requestStore,
serverActionsSizeLimit,
})

if (actionRequestResult === 'not-found') {
Expand Down
11 changes: 10 additions & 1 deletion packages/next/src/server/app-render/types.ts
@@ -1,5 +1,6 @@
import type { LoadComponentsReturnType } from '../load-components'
import type { ServerRuntime } from '../../../types'
import type { ServerRuntime, SizeLimit } from '../../../types'
import { NextConfigComplete } from '../../server/config-shared'
import type { ClientReferenceManifest } from '../../build/webpack/plugins/flight-manifest-plugin'
import type { NextFontManifest } from '../../build/webpack/plugins/next-font-manifest-plugin'

Expand Down Expand Up @@ -138,6 +139,14 @@ export type RenderOptsPartial = {
originalPathname?: string
isDraftMode?: boolean
deploymentId?: string
loadConfig?: (
phase: string,
dir: string,
customConfig?: object | null,
rawConfig?: boolean,
silent?: boolean
) => Promise<NextConfigComplete>
serverActionsSizeLimit?: SizeLimit
}

export type RenderOpts = LoadComponentsReturnType & RenderOptsPartial
5 changes: 4 additions & 1 deletion packages/next/src/server/base-server.ts
Expand Up @@ -19,7 +19,7 @@ import {
normalizeRepeatedSlashes,
MissingStaticPage,
} from '../shared/lib/utils'
import type { PreviewData, ServerRuntime } from 'next/types'
import type { PreviewData, ServerRuntime, SizeLimit } from 'next/types'
import type { PagesManifest } from '../build/webpack/plugins/pages-manifest-plugin'
import type { OutgoingHttpHeaders } from 'http2'
import type { BaseNextRequest, BaseNextResponse } from './base-http'
Expand Down Expand Up @@ -255,6 +255,7 @@ export default abstract class Server<ServerOptions extends Options = Options> {
supportsDynamicHTML?: boolean
isBot?: boolean
clientReferenceManifest?: ClientReferenceManifest
serverActionsSizeLimit?: SizeLimit
serverActionsManifest?: any
nextFontManifest?: NextFontManifest
renderServerComponentData?: boolean
Expand Down Expand Up @@ -1650,6 +1651,8 @@ export default abstract class Server<ServerOptions extends Options = Options> {
incrementalCache,
isRevalidate: isSSG,
originalPathname: components.ComponentMod.originalPathname,
serverActionsSizeLimit:
this.nextConfig.experimental.serverActionsSizeLimit,
}
: {}),
isDataReq,
Expand Down
10 changes: 10 additions & 0 deletions packages/next/src/server/config-schema.ts
Expand Up @@ -304,6 +304,16 @@ const configSchema = {
serverActions: {
type: 'boolean',
},
serverActionsSizeLimit: {
oneOf: [
{
type: 'number',
},
{
type: 'string',
},
] as any,
},
extensionAlias: {
type: 'object',
},
Expand Down
6 changes: 6 additions & 0 deletions packages/next/src/server/config-shared.ts
Expand Up @@ -9,6 +9,7 @@ import {
import { SubresourceIntegrityAlgorithm } from '../build/webpack/plugins/subresource-integrity-plugin'
import { WEB_VITALS } from '../shared/lib/utils'
import type { NextParsedUrlQuery } from './request-meta'
import { SizeLimit } from '../../types'

export type NextConfigComplete = Required<NextConfig> & {
images: Required<ImageConfigComplete>
Expand Down Expand Up @@ -281,6 +282,11 @@ export interface ExperimentalConfig {
* Enable `react@experimental` channel for the `app` directory.
*/
serverActions?: boolean

/**
* Allows adjusting body parser size limit for server actions.
*/
serverActionsSizeLimit?: SizeLimit
devjiwonchoi marked this conversation as resolved.
Show resolved Hide resolved
}

export type ExportPathMap = {
Expand Down
11 changes: 11 additions & 0 deletions packages/next/src/server/config.ts
Expand Up @@ -416,6 +416,17 @@ function assignDefaults(
result.output = 'standalone'
}

if (typeof result.experimental?.serverActionsSizeLimit !== 'undefined') {
const value = parseInt(
result.experimental.serverActionsSizeLimit.toString()
)
if (isNaN(value) || value < 1) {
throw new Error(
'Server Actions Size Limit must be a valid number or filesize format lager than 1MB: https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions'
)
}
}

warnOptionHasBeenMovedOutOfExperimental(
result,
'transpilePackages',
Expand Down
2 changes: 2 additions & 0 deletions packages/next/src/server/render.tsx
Expand Up @@ -23,6 +23,7 @@ import type {
GetStaticProps,
PreviewData,
ServerRuntime,
SizeLimit,
} from 'next/types'
import type { UnwrapPromise } from '../lib/coalesced-function'
import type { ReactReadableStream } from './stream-utils/node-web-streams-helper'
Expand Down Expand Up @@ -260,6 +261,7 @@ export type RenderOptsPartial = {
isBot?: boolean
runtime?: ServerRuntime
serverComponents?: boolean
serverActionsSizeLimit?: SizeLimit
customServer?: boolean
crossOrigin?: 'anonymous' | 'use-credentials' | '' | undefined
images: ImageConfigComplete
Expand Down