提交 eff27bdc 编写于 作者: O Oscar Martinez 提交者: Tim Neutkens

Allow etags to be disabled with config option (#3915)

* Allow etags to be disabled with config option

- CR Change: Rename option to generateEtags
- CR Change: Add tests for etag generation
- CR Change: Refactor to use next.config.js
- Update documentation

* Use renderOpts instead of passing nextConfig
上级 c5bd36e3
......@@ -1047,6 +1047,17 @@ module.exports = {
}
```
#### Disabling etag generation
You can disable etag generation for HTML pages depending on your cache strategy. If no configuration is specified then Next will generate etags for every page.
```js
// next.config.js
module.exports = {
generateEtags: false
}
```
#### Configuring the onDemandEntries
Next exposes some options that give you some control over how the server will dispose or keep in memories pages built:
......
......@@ -10,6 +10,7 @@ const defaultConfig = {
assetPrefix: '',
configOrigin: 'default',
useFileSystemPublicRoutes: true,
generateEtags: true,
pageExtensions: ['jsx', 'js'] // jsx before js because otherwise regex matching will match js first
}
......
......@@ -45,6 +45,10 @@ export default class Server {
updateNotifier(pkg, 'next')
}
// Only serverRuntimeConfig needs the default
// publicRuntimeConfig gets it's default in client/index.js
const {serverRuntimeConfig = {}, publicRuntimeConfig, assetPrefix, generateEtags} = this.nextConfig
if (!dev && !fs.existsSync(resolve(dir, this.dist, 'BUILD_ID'))) {
console.error(`> Could not find a valid build in the '${this.dist}' directory! Try building your app with 'next build' before starting the server.`)
process.exit(1)
......@@ -57,13 +61,10 @@ export default class Server {
dist: this.dist,
hotReloader: this.hotReloader,
buildId: this.buildId,
availableChunks: dev ? {} : getAvailableChunks(this.dir, this.dist)
availableChunks: dev ? {} : getAvailableChunks(this.dir, this.dist),
generateEtags
}
// Only serverRuntimeConfig needs the default
// publicRuntimeConfig gets it's default in client/index.js
const {serverRuntimeConfig = {}, publicRuntimeConfig, assetPrefix} = this.nextConfig
// Only the `publicRuntimeConfig` key is exposed to the client side
// It'll be rendered as part of __NEXT_DATA__ on the client side
if (publicRuntimeConfig) {
......
......@@ -138,9 +138,9 @@ export async function renderScriptError (req, res, page, error) {
res.end('500 - Internal Error')
}
export function sendHTML (req, res, html, method, { dev }) {
export function sendHTML (req, res, html, method, { dev, generateEtags }) {
if (isResSent(res)) return
const etag = generateETag(html)
const etag = generateEtags && generateETag(html)
if (fresh(req.headers, { etag })) {
res.statusCode = 304
......@@ -154,7 +154,10 @@ export function sendHTML (req, res, html, method, { dev }) {
res.setHeader('Cache-Control', 'no-store, must-revalidate')
}
res.setHeader('ETag', etag)
if (etag) {
res.setHeader('ETag', etag)
}
if (!res.getHeader('Content-Type')) {
res.setHeader('Content-Type', 'text/html; charset=utf-8')
}
......
......@@ -2,5 +2,6 @@ module.exports = {
onDemandEntries: {
// Make sure entries are not getting disposed.
maxInactiveAge: 1000 * 60 * 60
}
},
generateEtags: process.env.GENERATE_ETAGS === 'true'
}
......@@ -7,7 +7,8 @@ import cheerio from 'cheerio'
import {
initNextServerScript,
killApp,
renderViaHTTP
renderViaHTTP,
fetchViaHTTP
} from 'next-test-utils'
import webdriver from 'next-webdriver'
......@@ -18,18 +19,24 @@ jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 2
const context = {}
describe('Custom Server', () => {
beforeAll(async () => {
const scriptPath = join(appDir, 'server.js')
context.appPort = appPort = await getPort()
const env = clone(process.env)
env.PORT = `${appPort}`
const startServer = async (optEnv = {}) => {
const scriptPath = join(appDir, 'server.js')
context.appPort = appPort = await getPort()
const env = Object.assign(
{},
clone(process.env),
{ PORT: `${appPort}` },
optEnv
)
server = await initNextServerScript(scriptPath, /Ready on/, env)
})
afterAll(() => killApp(server))
server = await initNextServerScript(scriptPath, /Ready on/, env)
}
describe('Custom Server', () => {
describe('with dynamic assetPrefix', () => {
beforeAll(() => startServer())
afterAll(() => killApp(server))
it('should set the assetPrefix dynamically', async () => {
const normalUsage = await renderViaHTTP(appPort, '/asset')
expect(normalUsage).not.toMatch(/127\.0\.0\.1/)
......@@ -83,4 +90,24 @@ describe('Custom Server', () => {
browser2.close()
})
})
describe('with generateEtags enabled', () => {
beforeAll(() => startServer({ GENERATE_ETAGS: 'true' }))
afterAll(() => killApp(server))
it('response includes etag header', async () => {
const response = await fetchViaHTTP(appPort, '/')
expect(response.headers.get('etag')).toBeTruthy()
})
})
describe('with generateEtags disabled', () => {
beforeAll(() => startServer({ GENERATE_ETAGS: 'false' }))
afterAll(() => killApp(server))
it('response does not include etag header', async () => {
const response = await fetchViaHTTP(appPort, '/')
expect(response.headers.get('etag')).toBeNull()
})
})
})
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册