Skip to content

Commit

Permalink
Forward response to edge runtime for response modification (#49145)
Browse files Browse the repository at this point in the history
For edge runtime, we're modifying `res.statusCode` in actions handler, but we didn't forward the `res` object to it, previously we're using a fake `res` `{}`. This PR fixes it so that the propery status code will be returned to the client

[slack-thread](https://vercel.slack.com/archives/C052S77L05C/p1682988777740609)
  • Loading branch information
huozhi authored May 3, 2023
1 parent c546847 commit a623653
Show file tree
Hide file tree
Showing 6 changed files with 39 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ const edgeSSRLoader: webpack.LoaderDefinitionFunction<EdgeSSRLoaderQuery> =
import {IncrementalCache} from 'next/dist/esm/server/lib/incremental-cache'
enhanceGlobals()
const pageType = ${JSON.stringify(pagesType)}
${
isAppDir
Expand All @@ -126,7 +126,7 @@ const edgeSSRLoader: webpack.LoaderDefinitionFunction<EdgeSSRLoaderQuery> =
const appRenderToHTML = null
`
}
const incrementalCacheHandler = ${
incrementalCacheHandlerPath
? `require("${incrementalCacheHandlerPath}")`
Expand Down
4 changes: 2 additions & 2 deletions packages/next/src/server/app-render/app-render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -920,12 +920,12 @@ export async function renderToHTMLOrFlight(
<Component {...props} />
)}
{/* This null is currently critical. The wrapped Component can render null and if there was not fragment
surrounding it this would look like a pending tree data state on the client which will cause an errror
surrounding it this would look like a pending tree data state on the client which will cause an errror
and break the app. Long-term we need to move away from using null as a partial tree identifier since it
is a valid return type for the components we wrap. Once we make this change we can safely remove the
fragment. The reason the extra null here is required is that fragments which only have 1 child are elided.
If the Component above renders null the actual treedata will look like `[null, null]`. If we remove the extra
null it will look like `null` (the array is elided) and this is what confuses the client router.
null it will look like `null` (the array is elided) and this is what confuses the client router.
TODO-APP update router to use a Symbol for partial tree detection */}
{null}
</>
Expand Down
4 changes: 2 additions & 2 deletions packages/next/src/server/web-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ export default class NextWebServer extends BaseServer<WebServerOptions> {

protected async renderHTML(
req: WebNextRequest,
_res: WebNextResponse,
res: WebNextResponse,
pathname: string,
query: NextParsedUrlQuery,
renderOpts: RenderOpts
Expand All @@ -379,7 +379,7 @@ export default class NextWebServer extends BaseServer<WebServerOptions> {
headers: req.headers,
body: req.body,
} as any,
{} as any,
res as any,
pathname,
query,
Object.assign(renderOpts, {
Expand Down
12 changes: 12 additions & 0 deletions test/e2e/app-dir/actions/app-action.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,18 @@ createNextDescribe(
await browser.elementByCss('#dec').click()
await check(() => browser.elementByCss('h1').text(), '3')
})

it('should return error response for hoc auth wrappers in edge runtime', async () => {
const browser = await next.browser('/header/edge')
await await browser.eval(`document.cookie = 'auth=0'`)
await browser.elementByCss('#authed').click()

await check(async () => {
const text = await browser.elementByCss('h1').text()
console.log('text', text)
return text && text.length > 0 ? text : 'failed'
}, /Multipart form data is not supported/)
})
})
}
)
20 changes: 20 additions & 0 deletions test/e2e/app-dir/actions/app/header/edge/page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { getCookie, getHeader, setCookie } from '../actions'
import UI from '../ui'
import { validator } from '../validator'

export default function Page() {
const prefix = 'Prefix:'
return (
<UI
getCookie={getCookie}
getHeader={getHeader}
setCookie={setCookie}
getAuthedUppercase={validator(async (str) => {
'use server'
return prefix + ' ' + str.toUpperCase()
})}
/>
)
}

export const runtime = 'edge'
3 changes: 1 addition & 2 deletions test/e2e/app-dir/actions/app/header/page.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import UI from './ui'

import { getCookie, getHeader, setCookie } from './actions'
import UI from './ui'
import { validator } from './validator'

export default function Page() {
Expand Down

0 comments on commit a623653

Please sign in to comment.