Skip to content

Commit

Permalink
Rename RSC and Router headers (#42482)
Browse files Browse the repository at this point in the history
Removes the `_` prefix from the RSC and Router headers as nginx strips
`_` prefixed headers by default.

Fixes #42164

<!--
Thanks for opening a PR! Your contribution is much appreciated.
To make sure your PR is handled as smoothly as possible we request that
you follow the checklist sections below.
Choose the right checklist for the change that you're making:
-->

## Bug

- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Errors have a helpful link attached, see `contributing.md`

## Feature

- [ ] Implements an existing feature request or RFC. Make sure the
feature request has been accepted for implementation before opening a
PR.
- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not.
- [ ] Errors have a helpful link attached, see `contributing.md`

## Documentation / Examples

- [ ] Make sure the linting passes by running `pnpm build && pnpm lint`
- [ ] The "examples guidelines" are followed from [our contributing
doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)
  • Loading branch information
timneutkens committed Nov 8, 2022
1 parent c5bccf4 commit 2358e9c
Show file tree
Hide file tree
Showing 13 changed files with 86 additions and 78 deletions.
9 changes: 9 additions & 0 deletions packages/next/build/index.ts
Expand Up @@ -123,6 +123,7 @@ import { RemotePattern } from '../shared/lib/image-config'
import { eventSwcPlugins } from '../telemetry/events/swc-plugins'
import { normalizeAppPath } from '../shared/lib/router/utils/app-paths'
import { AppBuildManifest } from './webpack/plugins/app-build-manifest-plugin'
import { RSC, RSC_VARY_HEADER } from '../client/components/app-router-headers'

export type SsgRoute = {
initialRevalidateSeconds: number | false
Expand Down Expand Up @@ -755,6 +756,10 @@ export default async function build(
defaultLocale: string
localeDetection?: false
}
rsc: {
header: typeof RSC
varyHeader: typeof RSC_VARY_HEADER
}
} = nextBuildSpan.traceChild('generate-routes-manifest').traceFn(() => {
const sortedRoutes = getSortedRoutes([
...pageKeys.pages,
Expand All @@ -781,6 +786,10 @@ export default async function build(
staticRoutes,
dataRoutes: [],
i18n: config.i18n || undefined,
rsc: {
header: RSC,
varyHeader: RSC_VARY_HEADER,
},
}
})

Expand Down
5 changes: 5 additions & 0 deletions packages/next/client/components/app-router-headers.ts
@@ -0,0 +1,5 @@
export const RSC = 'RSC' as const
export const NEXT_ROUTER_STATE_TREE = 'Next-Router-State-Tree' as const
export const NEXT_ROUTER_PREFETCH = 'Next-Router-Prefetch' as const
export const RSC_VARY_HEADER =
`${RSC}, ${NEXT_ROUTER_STATE_TREE}, ${NEXT_ROUTER_PREFETCH}` as const
17 changes: 11 additions & 6 deletions packages/next/client/components/app-router.tsx
Expand Up @@ -29,6 +29,11 @@ import {
} from '../../shared/lib/hooks-client-context'
import { useReducerWithReduxDevtools } from './use-reducer-with-devtools'
import { ErrorBoundary, GlobalErrorComponent } from './error-boundary'
import {
NEXT_ROUTER_PREFETCH,
NEXT_ROUTER_STATE_TREE,
RSC,
} from './app-router-headers'

function urlToUrlWithoutFlightMarker(url: string): URL {
const urlWithoutFlightParameters = new URL(url, location.origin)
Expand All @@ -53,18 +58,18 @@ export async function fetchServerResponse(
prefetch?: true
): Promise<[FlightData: FlightData, canonicalUrlOverride: URL | undefined]> {
const headers: {
__rsc__: '1'
__next_router_state_tree__: string
__next_router_prefetch__?: '1'
[RSC]: '1'
[NEXT_ROUTER_STATE_TREE]: string
[NEXT_ROUTER_PREFETCH]?: '1'
} = {
// Enable flight response
__rsc__: '1',
[RSC]: '1',
// Provide the current router state
__next_router_state_tree__: JSON.stringify(flightRouterState),
[NEXT_ROUTER_STATE_TREE]: JSON.stringify(flightRouterState),
}
if (prefetch) {
// Enable prefetch response
headers.__next_router_prefetch__ = '1'
headers[NEXT_ROUTER_PREFETCH] = '1'
}

const res = await fetch(url.toString(), {
Expand Down
26 changes: 17 additions & 9 deletions packages/next/server/app-render.tsx
Expand Up @@ -38,6 +38,11 @@ import { NOT_FOUND_ERROR_CODE } from '../client/components/not-found'
import { HeadManagerContext } from '../shared/lib/head-manager-context'
import { Writable } from 'stream'
import stringHash from 'next/dist/compiled/string-hash'
import {
NEXT_ROUTER_PREFETCH,
NEXT_ROUTER_STATE_TREE,
RSC,
} from '../client/components/app-router-headers'

const isEdgeRuntime = process.env.NEXT_RUNTIME === 'edge'

Expand Down Expand Up @@ -686,16 +691,16 @@ function getScriptNonceFromHeader(cspHeaderValue: string): string | undefined {
return nonce
}

const FLIGHT_PARAMETERS = [
'__rsc__',
'__next_router_state_tree__',
'__next_router_prefetch__',
export const FLIGHT_PARAMETERS = [
[RSC],
[NEXT_ROUTER_STATE_TREE],
[NEXT_ROUTER_PREFETCH],
] as const

function headersWithoutFlight(headers: IncomingHttpHeaders) {
const newHeaders = { ...headers }
for (const param of FLIGHT_PARAMETERS) {
delete newHeaders[param]
delete newHeaders[param.toString().toLowerCase()]
}
return newHeaders
}
Expand Down Expand Up @@ -728,7 +733,7 @@ export async function renderToHTMLOrFlight(
*/
const isStaticGeneration =
renderOpts.supportsDynamicHTML !== true && !renderOpts.isBot
const isFlight = req.headers.__rsc__ !== undefined
const isFlight = req.headers[RSC.toLowerCase()] !== undefined

const capturedErrors: Error[] = []
const allCapturedErrors: Error[] = []
Expand Down Expand Up @@ -786,7 +791,8 @@ export async function renderToHTMLOrFlight(
// don't modify original query object
query = Object.assign({}, query)

const isPrefetch = req.headers.__next_router_prefetch__ !== undefined
const isPrefetch =
req.headers[NEXT_ROUTER_PREFETCH.toLowerCase()] !== undefined

// TODO-APP: verify the tree is valid
// TODO-APP: verify query param is single value (not an array)
Expand All @@ -795,8 +801,10 @@ export async function renderToHTMLOrFlight(
* Router state provided from the client-side router. Used to handle rendering from the common layout down.
*/
let providedFlightRouterState: FlightRouterState = isFlight
? req.headers.__next_router_state_tree__
? JSON.parse(req.headers.__next_router_state_tree__ as string)
? req.headers[NEXT_ROUTER_STATE_TREE.toLowerCase()]
? JSON.parse(
req.headers[NEXT_ROUTER_STATE_TREE.toLowerCase()] as string
)
: undefined
: undefined

Expand Down
19 changes: 9 additions & 10 deletions packages/next/server/base-server.ts
Expand Up @@ -74,6 +74,8 @@ import { getHostname } from '../shared/lib/get-hostname'
import { parseUrl as parseUrlUtil } from '../shared/lib/router/utils/parse-url'
import { getNextPathnameInfo } from '../shared/lib/router/utils/get-next-pathname-info'
import { MiddlewareMatcher } from '../build/analysis/get-page-static-info'
import { RSC, RSC_VARY_HEADER } from '../client/components/app-router-headers'
import { FLIGHT_PARAMETERS } from './app-render'

export type FindComponentsResult = {
components: LoadComponentsReturnType
Expand Down Expand Up @@ -1053,12 +1055,9 @@ export default abstract class Server<ServerOptions extends Options = Options> {
(isSSG || hasServerProps)

if (isAppPath) {
res.setHeader(
'vary',
'__rsc__, __next_router_state_tree__, __next_router_prefetch__'
)
res.setHeader('vary', RSC_VARY_HEADER)

if (isSSG && req.headers['__rsc__']) {
if (isSSG && req.headers[RSC.toLowerCase()]) {
if (!this.minimalMode) {
isDataReq = true
}
Expand All @@ -1067,9 +1066,9 @@ export default abstract class Server<ServerOptions extends Options = Options> {
opts.runtime !== 'experimental-edge' ||
(this.serverOptions as any).webServerConfig
) {
delete req.headers['__rsc__']
delete req.headers['__next_router_state_tree__']
delete req.headers['__next_router_prefetch__']
for (const param of FLIGHT_PARAMETERS) {
delete req.headers[param.toString().toLowerCase()]
}
}
}
}
Expand All @@ -1096,9 +1095,9 @@ export default abstract class Server<ServerOptions extends Options = Options> {
)
}

// Don't delete query.__rsc__ yet, it still needs to be used in renderToHTML later
// Don't delete headers[RSC] yet, it still needs to be used in renderToHTML later
const isFlightRequest = Boolean(
this.serverComponentManifest && req.headers.__rsc__
this.serverComponentManifest && req.headers[RSC.toLowerCase()]
)

// we need to ensure the status code if /404 is visited directly
Expand Down
5 changes: 0 additions & 5 deletions packages/next/server/internal-utils.ts
Expand Up @@ -5,11 +5,6 @@ const INTERNAL_QUERY_NAMES = [
'__nextLocale',
'__nextDefaultLocale',
'__nextIsNotFound',
// RSC
'__rsc__',
// Routing
'__next_router_state_tree__',
'__next_router_prefetch__',
] as const

const EXTENDED_INTERNAL_QUERY_NAMES = ['__nextDataReq'] as const
Expand Down
13 changes: 9 additions & 4 deletions packages/next/server/web/adapter.ts
Expand Up @@ -10,6 +10,11 @@ import { waitUntilSymbol } from './spec-extension/fetch-event'
import { NextURL } from './next-url'
import { stripInternalSearchParams } from '../internal-utils'
import { normalizeRscPath } from '../../shared/lib/router/utils/app-paths'
import {
NEXT_ROUTER_PREFETCH,
NEXT_ROUTER_STATE_TREE,
RSC,
} from '../../client/components/app-router-headers'

class NextRequestHint extends NextRequest {
sourcePage: string
Expand Down Expand Up @@ -37,9 +42,9 @@ class NextRequestHint extends NextRequest {
}

const FLIGHT_PARAMETERS = [
'__rsc__',
'__next_router_state_tree__',
'__next_router_prefetch__',
[RSC],
[NEXT_ROUTER_STATE_TREE],
[NEXT_ROUTER_PREFETCH],
] as const

export async function adapter(params: {
Expand Down Expand Up @@ -71,7 +76,7 @@ export async function adapter(params: {
// Parameters should only be stripped for middleware
if (!isEdgeRendering) {
for (const param of FLIGHT_PARAMETERS) {
requestHeaders.delete(param)
requestHeaders.delete(param.toString().toLowerCase())
}
}

Expand Down
4 changes: 2 additions & 2 deletions test/e2e/app-dir/app/middleware.js
Expand Up @@ -35,8 +35,8 @@ export function middleware(request) {
? 'rewrite'
: 'redirect'

const internal = ['__rsc__', '__next_router_state_tree__']
if (internal.some((name) => request.headers.has(name))) {
const internal = ['RSC', 'Next-Router-State-Tree']
if (internal.some((name) => request.headers.has(name.toLowerCase()))) {
return NextResponse[method](new URL('/internal/failure', request.url))
}

Expand Down
4 changes: 2 additions & 2 deletions test/e2e/app-dir/index.test.ts
Expand Up @@ -84,7 +84,7 @@ describe('app dir', () => {
{},
{
headers: {
__rsc__: '1',
['RSC'.toString()]: '1',
},
}
)
Expand All @@ -98,7 +98,7 @@ describe('app dir', () => {
{},
{
headers: {
__rsc__: '1',
['RSC'.toString()]: '1',
},
}
)
Expand Down
50 changes: 12 additions & 38 deletions test/e2e/app-dir/rsc-basic.test.ts
Expand Up @@ -88,9 +88,6 @@ describe('app dir - rsc basics', () => {
'__nextLocale',
'__nextDefaultLocale',
'__nextIsNotFound',
'__rsc__',
'__next_router_state_tree__',
'__next_router_prefetch__',
]

const hasNextInternalQuery = inlineFlightContents.some((content) =>
Expand All @@ -108,9 +105,9 @@ describe('app dir - rsc basics', () => {
requestsCount++
return request.allHeaders().then((headers) => {
if (
headers.__rsc__ === '1' &&
// Prefetches also include `__rsc__`
headers.__next_router_prefetch__ !== '1'
headers['RSC'.toLowerCase()] === '1' &&
// Prefetches also include `RSC`
headers['Next-Router-Prefetch'.toLowerCase()] !== '1'
) {
hasFlightRequest = true
}
Expand Down Expand Up @@ -185,41 +182,18 @@ describe('app dir - rsc basics', () => {
}
})

it('should refresh correctly with next/link', async () => {
it('should link correctly with next/link without mpa navigation to the page', async () => {
// Select the button which is not hidden but rendered
const selector = '#goto-next-link'
let hasFlightRequest = false
const browser = await webdriver(next.url, '/root', {
beforePageLoad(page) {
page.on('request', (request) => {
return request.allHeaders().then((headers) => {
if (
headers.__rsc__ === '1' &&
headers.__next_router_prefetch__ !== '1'
) {
hasFlightRequest = true
}
})
})
},
})
const browser = await webdriver(next.url, '/root', {})

// wait for hydration
await new Promise((res) => setTimeout(res, 1000))
if (isNextDev) {
expect(hasFlightRequest).toBe(false)
}
await browser.elementByCss(selector).click()
await browser.eval('window.didNotReloadPage = true')
await browser.elementByCss(selector).click().waitForElementByCss('#query')

// wait for re-hydration
if (isNextDev) {
await check(
() => (hasFlightRequest ? 'success' : hasFlightRequest),
'success'
)
}
const refreshText = await browser.elementByCss(selector).text()
expect(refreshText).toBe('next link')
expect(await browser.eval('window.didNotReloadPage')).toBe(true)

const text = await browser.elementByCss('#query').text()
expect(text).toBe('query:0')
})

it('should escape streaming data correctly', async () => {
Expand Down Expand Up @@ -381,7 +355,7 @@ describe('app dir - rsc basics', () => {
{},
{
headers: {
__rsc__: '1',
['RSC'.toString()]: '1',
},
}
).then(async (response) => {
Expand Down
4 changes: 2 additions & 2 deletions test/e2e/switchable-runtime/index.test.ts
Expand Up @@ -120,7 +120,7 @@ describe('Switchable runtime', () => {
beforePageLoad(page) {
page.on('request', (request) => {
return request.allHeaders().then((headers) => {
if (headers.__rsc__ === '1') {
if (headers['RSC'.toLowerCase()] === '1') {
flightRequest = request.url()
}
})
Expand Down Expand Up @@ -680,7 +680,7 @@ describe('Switchable runtime', () => {
beforePageLoad(page) {
page.on('request', (request) => {
request.allHeaders().then((headers) => {
if (headers.__rsc__ === '1') {
if (headers['RSC'.toLowerCase()] === '1') {
flightRequest = request.url()
}
})
Expand Down
4 changes: 4 additions & 0 deletions test/integration/custom-routes/test/index.test.js
Expand Up @@ -2215,6 +2215,10 @@ const runTests = (isDev = false) => {
routeKeys: {},
},
],
rsc: {
header: 'RSC',
varyHeader: 'RSC, Next-Router-State-Tree, Next-Router-Prefetch',
},
})
})

Expand Down
4 changes: 4 additions & 0 deletions test/integration/dynamic-routing/test/index.test.js
Expand Up @@ -1426,6 +1426,10 @@ function runTests({ dev }) {
},
},
],
rsc: {
header: 'RSC',
varyHeader: 'RSC, Next-Router-State-Tree, Next-Router-Prefetch',
},
})
})

Expand Down

0 comments on commit 2358e9c

Please sign in to comment.