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

Change Flight response content type to application/octet-stream #40665

Merged
merged 2 commits into from Sep 18, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
13 changes: 11 additions & 2 deletions packages/next/server/app-render.tsx
Expand Up @@ -51,6 +51,15 @@ export type RenderOptsPartial = {

export type RenderOpts = LoadComponentsReturnType & RenderOptsPartial

/**
* Flight Response is always set to application/octet-stream to ensure it does not
*/
class FlightRenderResult extends RenderResult {
constructor(response: string | ReadableStream<Uint8Array>) {
super(response, { contentType: 'application/octet-stream' })
}
}

/**
* Interop between "export default" and "module.exports".
*/
Expand Down Expand Up @@ -500,7 +509,7 @@ export async function renderToHTMLOrFlight(

// Empty so that the client-side router will do a full page navigation.
const flightData: FlightData = pathname + (search ? `?${search}` : '')
return new RenderResult(
return new FlightRenderResult(
renderToReadableStream(flightData, serverComponentManifest).pipeThrough(
createBufferedTransformStream()
)
Expand Down Expand Up @@ -1054,7 +1063,7 @@ export async function renderToHTMLOrFlight(
).slice(1),
]

return new RenderResult(
return new FlightRenderResult(
renderToReadableStream(flightData, serverComponentManifest, {
context: serverContexts,
}).pipeThrough(createBufferedTransformStream())
Expand Down
15 changes: 13 additions & 2 deletions packages/next/server/render-result.ts
@@ -1,10 +1,21 @@
import type { ServerResponse } from 'http'

type ContentTypeOption = string | undefined

export default class RenderResult {
_result: string | ReadableStream<Uint8Array>
private _result: string | ReadableStream<Uint8Array>
private _contentType: ContentTypeOption

constructor(response: string | ReadableStream<Uint8Array>) {
constructor(
response: string | ReadableStream<Uint8Array>,
{ contentType }: { contentType?: ContentTypeOption } = {}
) {
this._result = response
this._contentType = contentType
}

contentType(): ContentTypeOption {
return this._contentType
}

toUnchunkedString(): string {
Expand Down
8 changes: 7 additions & 1 deletion packages/next/server/send-payload/index.ts
Expand Up @@ -71,10 +71,16 @@ export async function sendRenderResult({
}
}

const resultContentType = result.contentType()

if (!res.getHeader('Content-Type')) {
res.setHeader(
'Content-Type',
type === 'json' ? 'application/json' : 'text/html; charset=utf-8'
resultContentType
? resultContentType
: type === 'json'
? 'application/json'
: 'text/html; charset=utf-8'
)
}

Expand Down
6 changes: 5 additions & 1 deletion packages/next/server/web-server.ts
Expand Up @@ -370,10 +370,14 @@ export default class NextWebServer extends BaseServer<WebServerOptions> {
if (options.poweredByHeader && options.type === 'html') {
res.setHeader('X-Powered-By', 'Next.js')
}
const resultContentType = options.result.contentType()

if (!res.getHeader('Content-Type')) {
res.setHeader(
'Content-Type',
options.type === 'json'
resultContentType
? resultContentType
: options.type === 'json'
? 'application/json'
: 'text/html; charset=utf-8'
)
Expand Down
13 changes: 13 additions & 0 deletions test/e2e/app-dir/index.test.ts
Expand Up @@ -44,6 +44,19 @@ describe('app dir', () => {
})
afterAll(() => next.destroy())

it('should use application/octet-stream for flight', async () => {
const res = await fetchViaHTTP(
next.url,
'/dashboard/deployments/123?__flight__'
)
expect(res.headers.get('Content-Type')).toBe('application/octet-stream')
})

it('should use application/octet-stream for flight with edge runtime', async () => {
const res = await fetchViaHTTP(next.url, '/dashboard?__flight__')
expect(res.headers.get('Content-Type')).toBe('application/octet-stream')
})

it('should pass props from getServerSideProps in root layout', async () => {
const html = await renderViaHTTP(next.url, '/dashboard')
const $ = cheerio.load(html)
Expand Down