rsnext/packages/next/server/send-payload/index.ts
Tim Neutkens 4cd8b23032
Enable @typescript-eslint/no-use-before-define for functions (#39602)
Follow-up to the earlier enabling of classes/variables etc.

Bug

 Related issues linked using fixes #number
 Integration tests added
 Errors have 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 helpful link attached, see contributing.md

Documentation / Examples

 Make sure the linting passes by running pnpm lint
 The examples guidelines are followed from our contributing doc

Co-authored-by: Steven <steven@ceriously.com>
2022-08-15 10:29:51 -04:00

96 lines
2.2 KiB
TypeScript

import type { IncomingMessage, ServerResponse } from 'http'
import { isResSent } from '../../shared/lib/utils'
import { generateETag } from '../lib/etag'
import fresh from 'next/dist/compiled/fresh'
import RenderResult from '../render-result'
import { setRevalidateHeaders } from './revalidate-headers'
export type PayloadOptions =
| { private: true }
| { private: boolean; stateful: true }
| { private: boolean; stateful: false; revalidate: number | false }
export { setRevalidateHeaders }
export function sendEtagResponse(
req: IncomingMessage,
res: ServerResponse,
etag: string | undefined
): boolean {
if (etag) {
/**
* The server generating a 304 response MUST generate any of the
* following header fields that would have been sent in a 200 (OK)
* response to the same request: Cache-Control, Content-Location, Date,
* ETag, Expires, and Vary. https://tools.ietf.org/html/rfc7232#section-4.1
*/
res.setHeader('ETag', etag)
}
if (fresh(req.headers, { etag })) {
res.statusCode = 304
res.end()
return true
}
return false
}
export async function sendRenderResult({
req,
res,
result,
type,
generateEtags,
poweredByHeader,
options,
}: {
req: IncomingMessage
res: ServerResponse
result: RenderResult
type: 'html' | 'json'
generateEtags: boolean
poweredByHeader: boolean
options?: PayloadOptions
}): Promise<void> {
if (isResSent(res)) {
return
}
if (poweredByHeader && type === 'html') {
res.setHeader('X-Powered-By', 'Next.js')
}
const payload = result.isDynamic() ? null : await result.toUnchunkedString()
if (payload) {
const etag = generateEtags ? generateETag(payload) : undefined
if (sendEtagResponse(req, res, etag)) {
return
}
}
if (!res.getHeader('Content-Type')) {
res.setHeader(
'Content-Type',
type === 'json' ? 'application/json' : 'text/html; charset=utf-8'
)
}
if (payload) {
res.setHeader('Content-Length', Buffer.byteLength(payload))
}
if (options != null) {
setRevalidateHeaders(res, options)
}
if (req.method === 'HEAD') {
res.end(null)
} else if (payload) {
res.end(payload)
} else {
await result.pipe(res)
}
}