-
Notifications
You must be signed in to change notification settings - Fork 26.1k
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
fix: allow some recursion for middleware subrequests #60615
Changes from 7 commits
a116cbd
4c91e02
5c75071
d5840a7
60a54c4
b180b21
b1701e3
4542f83
47e7430
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
import type { NodejsRequestData, FetchEventResult, RequestData } from '../types' | ||
import type { EdgeFunctionDefinition } from '../../../build/webpack/plugins/middleware-plugin' | ||
import type { EdgeRuntime } from 'next/dist/compiled/edge-runtime' | ||
import { getModuleContext } from './context' | ||
import { getModuleContext, requestStore } from './context' | ||
import { requestToBodyStream } from '../../body-streams' | ||
import { NEXT_RSC_UNION_QUERY } from '../../../client/components/app-router-headers' | ||
|
||
|
@@ -82,7 +82,14 @@ export const run = withTaggedErrors(async function runWithTaggedErrors(params) { | |
const runtime = await getRuntimeContext(params) | ||
const subreq = params.request.headers[`x-middleware-subrequest`] | ||
const subrequests = typeof subreq === 'string' ? subreq.split(':') : [] | ||
if (subrequests.includes(params.name)) { | ||
|
||
const MAX_RECURSION_DEPTH = 5 | ||
const depth = subrequests.reduce( | ||
(acc, curr) => (curr === params.name ? acc + 1 : acc), | ||
0 | ||
) | ||
|
||
if (depth >= MAX_RECURSION_DEPTH) { | ||
return { | ||
waitUntil: Promise.resolve(), | ||
response: new runtime.context.Response(null, { | ||
|
@@ -108,17 +115,26 @@ export const run = withTaggedErrors(async function runWithTaggedErrors(params) { | |
|
||
params.request.url = urlInstance.toString() | ||
|
||
const headers = new Headers() | ||
for (const [key, value] of Object.entries(params.request.headers)) { | ||
headers.set(key, value?.toString() ?? '') | ||
} | ||
|
||
try { | ||
const result = await edgeFunction({ | ||
request: { | ||
...params.request, | ||
body: | ||
cloned && requestToBodyStream(runtime.context, KUint8Array, cloned), | ||
}, | ||
let result: FetchEventResult | undefined = undefined | ||
await requestStore.run({ headers }, async () => { | ||
result = await edgeFunction({ | ||
request: { | ||
...params.request, | ||
body: | ||
cloned && requestToBodyStream(runtime.context, KUint8Array, cloned), | ||
}, | ||
}) | ||
for (const headerName of FORBIDDEN_HEADERS) { | ||
result.response.headers.delete(headerName) | ||
} | ||
}) | ||
for (const headerName of FORBIDDEN_HEADERS) { | ||
result.response.headers.delete(headerName) | ||
} | ||
if (!result) throw new Error('Edge function did not return a response') | ||
Comment on lines
+124
to
+137
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this looks very sketchy - idk how far up the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hoisting to this level seems alright as it looks like we only need to access it in the same Node.js context. |
||
return result | ||
} finally { | ||
await params.request.body?.finalize() | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { createNextDescribe } from 'e2e-utils' | ||
|
||
const bathPath = process.env.BASE_PATH ?? '' | ||
|
||
createNextDescribe( | ||
'app-routes-subrequests', | ||
{ | ||
files: __dirname, | ||
skipDeployment: true, | ||
}, | ||
({ next }) => { | ||
it('shortcuts after 5 subrequests', async () => { | ||
expect(JSON.parse(await next.render(bathPath + '/'))).toEqual({ | ||
count: 5, | ||
}) | ||
}) | ||
} | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { NextRequest, NextResponse } from 'next/server' | ||
|
||
export const runtime = 'edge' | ||
|
||
let count = 0 | ||
|
||
export const GET = async (req: NextRequest) => { | ||
await fetch(req.nextUrl) | ||
count++ | ||
return NextResponse.json({ count }) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
/** | ||
* @type {import('next').NextConfig} | ||
*/ | ||
const config = { | ||
typescript: { | ||
ignoreBuildErrors: true, | ||
}, | ||
} | ||
|
||
module.exports = config |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we needing to augment the store from headers in a
new Request()
, shouldn't this only be done from the initial request event in the sandbox?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that does sound correct - should be able to remove this here then
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
confirmed it was unnecessary, removed it 👍🏼