4cd8b23032
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>
96 lines
2.2 KiB
TypeScript
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)
|
|
}
|
|
}
|