提交 f82e5293 编写于 作者: A Arunoda Susiripala 提交者: GitHub

Implement ETag support for server rendered pages. (#1693)

上级 e0f71d84
......@@ -225,7 +225,7 @@ export default class Server {
res.setHeader('X-Powered-By', `Next.js ${pkg.version}`)
}
const html = await this.renderToHTML(req, res, pathname, query)
return sendHTML(res, html, req.method)
return sendHTML(req, res, html, req.method)
}
async renderToHTML (req, res, pathname, query) {
......@@ -253,7 +253,7 @@ export default class Server {
async renderError (err, req, res, pathname, query) {
const html = await this.renderErrorToHTML(err, req, res, pathname, query)
return sendHTML(res, html, req.method)
return sendHTML(req, res, html, req.method)
}
async renderErrorToHTML (err, req, res, pathname, query) {
......
......@@ -2,6 +2,8 @@ import { join } from 'path'
import { createElement } from 'react'
import { renderToString, renderToStaticMarkup } from 'react-dom/server'
import send from 'send'
import generateETag from 'etag'
import fresh from 'fresh'
import requireModule from './require'
import getConfig from './config'
import resolvePath from './resolve'
......@@ -13,7 +15,7 @@ import ErrorDebug from '../lib/error-debug'
export async function render (req, res, pathname, query, opts) {
const html = await renderToHTML(req, res, pathname, opts)
sendHTML(res, html, req.method)
sendHTML(req, res, html, req.method)
}
export function renderToHTML (req, res, pathname, query, opts) {
......@@ -22,7 +24,7 @@ export function renderToHTML (req, res, pathname, query, opts) {
export async function renderError (err, req, res, pathname, query, opts) {
const html = await renderErrorToHTML(err, req, res, query, opts)
sendHTML(res, html, req.method)
sendHTML(req, res, html, req.method)
}
export function renderErrorToHTML (err, req, res, pathname, query, opts = {}) {
......@@ -148,9 +150,17 @@ export async function renderScriptError (req, res, page, error, customFields, op
`)
}
export function sendHTML (res, html, method) {
export function sendHTML (req, res, html, method) {
if (res.finished) return
const etag = generateETag(html)
if (fresh(req.headers, { etag })) {
res.statusCode = 304
res.end()
return
}
res.setHeader('ETag', etag)
res.setHeader('Content-Type', 'text/html')
res.setHeader('Content-Length', Buffer.byteLength(html))
res.end(method === 'HEAD' ? null : html)
......
/* global describe, test, expect */
import fetch from 'node-fetch'
export default function (context) {
describe('Misc', () => {
......@@ -12,5 +13,14 @@ export default function (context) {
const html = await context.app.renderToHTML({}, res, '/finish-response', {})
expect(html).toBeFalsy()
})
test('allow etag header support', async () => {
const url = `http://localhost:${context.appPort}/stateless`
const etag = (await fetch(url)).headers.get('ETag')
const headers = { 'If-None-Match': etag }
const res2 = await fetch(url, { headers })
expect(res2.status).toBe(304)
})
})
}
......@@ -4,7 +4,7 @@ import { pkg } from 'next-test-utils'
export default function ({ app }) {
describe('X-Powered-By header', () => {
test('set it by default', async () => {
const req = { url: '/stateless' }
const req = { url: '/stateless', headers: {} }
const headers = {}
const res = {
setHeader (key, value) {
......@@ -18,7 +18,7 @@ export default function ({ app }) {
})
test('do not set it when poweredByHeader==false', async () => {
const req = { url: '/stateless' }
const req = { url: '/stateless', headers: {} }
const originalConfigValue = app.config.poweredByHeader
app.config.poweredByHeader = false
const res = {
......
......@@ -1972,7 +1972,7 @@ esutils@^2.0.0, esutils@^2.0.2:
version "2.0.2"
resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b"
etag@~1.8.0:
etag@1.8.0, etag@~1.8.0:
version "1.8.0"
resolved "https://registry.npmjs.org/etag/-/etag-1.8.0.tgz#6f631aef336d6c46362b51764044ce216be3c051"
......@@ -2331,24 +2331,24 @@ glob-promise@3.1.0:
version "3.1.0"
resolved "https://registry.npmjs.org/glob-promise/-/glob-promise-3.1.0.tgz#198882a3817be7dc2c55f92623aa9e7b3f82d1eb"
glob@^6.0.1:
version "6.0.4"
resolved "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22"
glob@7.1.1, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.0.6, glob@^7.1.1:
version "7.1.1"
resolved "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8"
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "2 || 3"
minimatch "^3.0.2"
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.0.6, glob@^7.1.1:
version "7.1.1"
resolved "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8"
glob@^6.0.1:
version "6.0.4"
resolved "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22"
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^3.0.2"
minimatch "2 || 3"
once "^1.3.0"
path-is-absolute "^1.0.0"
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册