Skip to content

Commit

Permalink
Fix mojibake in server action inputs (fixes #74843) (#74845)
Browse files Browse the repository at this point in the history
Fixes #74843

---------

Co-authored-by: Gabriel Pulido <[email protected]>
Co-authored-by: Hendrik Liebau <[email protected]>
  • Loading branch information
3 people authored Jan 14, 2025
1 parent fe7c60b commit 4a3ff85
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 5 deletions.
7 changes: 4 additions & 3 deletions packages/next/src/server/app-render/action-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -710,18 +710,19 @@ export async function handleAction({
}
}

let actionData = ''

const chunks: Buffer[] = []
const reader = req.body.getReader()
while (true) {
const { done, value } = await reader.read()
if (done) {
break
}

actionData += new TextDecoder().decode(value)
chunks.push(value)
}

const actionData = Buffer.concat(chunks).toString('utf-8')

if (isURLEncodedAction) {
const formData = formDataFromSearchQueryString(actionData)
boundActionArguments = await decodeReply(
Expand Down
20 changes: 18 additions & 2 deletions test/e2e/app-dir/actions/app-action.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -888,9 +888,9 @@ describe('app-dir action handling', () => {
async (runtime) => {
let redirectResponseCode
const browser = await next.browser(`/delayed-action/${runtime}`, {
beforePageLoad(page) {
beforePageLoad(page: Page) {
page.on('response', async (res: Response) => {
const headers = await res.allHeaders()
const headers = await res.allHeaders().catch(() => ({}))
if (headers['x-action-redirect']) {
redirectResponseCode = res.status()
}
Expand Down Expand Up @@ -1867,4 +1867,20 @@ describe('app-dir action handling', () => {
})
})
})

describe('request body decoding', () => {
it.each(['node', 'edge'])(
'should correctly decode multi-byte characters in the request body (%s)',
async (runtime) => {
const browser = await next.browser(`/decode-req-body/${runtime}`)

await browser.elementByCss('button').click()
const result = await browser.elementByCss('p').text()

expect(result).toEqual(
'Server responded with 100000 あ characters and 0 � characters.'
)
}
)
})
})
3 changes: 3 additions & 0 deletions test/e2e/app-dir/actions/app/decode-req-body/edge/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { default } from '../node/page'

export const runtime = 'edge'
28 changes: 28 additions & 0 deletions test/e2e/app-dir/actions/app/decode-req-body/node/form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
'use client'

import { useActionState } from 'react'

export function Form({
echoAction,
}: {
echoAction: (value: string) => Promise<string>
}) {
let [result, formAction] = useActionState(
() => echoAction(new Array(100000).fill('あ').join('')),
null
)

let aCount = result ? result.match(//g)!.length : 0

return (
<form action={formAction}>
{result && (
<p>
Server responded with {aCount} あ characters and{' '}
{result.length - aCount} � characters.
</p>
)}
<button>Submit</button>
</form>
)
}
13 changes: 13 additions & 0 deletions test/e2e/app-dir/actions/app/decode-req-body/node/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Form } from './form'

export default function Page() {
return (
<Form
echoAction={async (value) => {
'use server'

return value
}}
/>
)
}
9 changes: 9 additions & 0 deletions test/e2e/app-dir/actions/middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,12 @@ export async function middleware(req) {

return NextResponse.next()
}

/**
* @type {import('next/server').MiddlewareConfig}
*/
export const config = {
// Ensure that middleware doesn't interfere with the request body parsing for
// this test fixture.
matcher: ['/((?!decode-req-body).*)'],
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default function Root({ children }: { children: React.ReactNode }) {
return (
<html>
<body>{children}</body>
</html>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function Page() {
return <p>hello world</p>
}

0 comments on commit 4a3ff85

Please sign in to comment.