mirror of
https://github.com/terribleplan/next.js.git
synced 2024-01-19 02:48:18 +00:00
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
This commit is contained in:
parent
c5bd36e373
commit
eff27bdcfb
11
readme.md
11
readme.md
|
@ -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
|
#### Configuring the onDemandEntries
|
||||||
|
|
||||||
Next exposes some options that give you some control over how the server will dispose or keep in memories pages built:
|
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: '',
|
assetPrefix: '',
|
||||||
configOrigin: 'default',
|
configOrigin: 'default',
|
||||||
useFileSystemPublicRoutes: true,
|
useFileSystemPublicRoutes: true,
|
||||||
|
generateEtags: true,
|
||||||
pageExtensions: ['jsx', 'js'] // jsx before js because otherwise regex matching will match js first
|
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')
|
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'))) {
|
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.`)
|
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)
|
process.exit(1)
|
||||||
|
@ -57,13 +61,10 @@ export default class Server {
|
||||||
dist: this.dist,
|
dist: this.dist,
|
||||||
hotReloader: this.hotReloader,
|
hotReloader: this.hotReloader,
|
||||||
buildId: this.buildId,
|
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
|
// Only the `publicRuntimeConfig` key is exposed to the client side
|
||||||
// It'll be rendered as part of __NEXT_DATA__ on the client side
|
// It'll be rendered as part of __NEXT_DATA__ on the client side
|
||||||
if (publicRuntimeConfig) {
|
if (publicRuntimeConfig) {
|
||||||
|
|
|
@ -138,9 +138,9 @@ export async function renderScriptError (req, res, page, error) {
|
||||||
res.end('500 - Internal 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
|
if (isResSent(res)) return
|
||||||
const etag = generateETag(html)
|
const etag = generateEtags && generateETag(html)
|
||||||
|
|
||||||
if (fresh(req.headers, { etag })) {
|
if (fresh(req.headers, { etag })) {
|
||||||
res.statusCode = 304
|
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('Cache-Control', 'no-store, must-revalidate')
|
||||||
}
|
}
|
||||||
|
|
||||||
res.setHeader('ETag', etag)
|
if (etag) {
|
||||||
|
res.setHeader('ETag', etag)
|
||||||
|
}
|
||||||
|
|
||||||
if (!res.getHeader('Content-Type')) {
|
if (!res.getHeader('Content-Type')) {
|
||||||
res.setHeader('Content-Type', 'text/html; charset=utf-8')
|
res.setHeader('Content-Type', 'text/html; charset=utf-8')
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,5 +2,6 @@ module.exports = {
|
||||||
onDemandEntries: {
|
onDemandEntries: {
|
||||||
// Make sure entries are not getting disposed.
|
// Make sure entries are not getting disposed.
|
||||||
maxInactiveAge: 1000 * 60 * 60
|
maxInactiveAge: 1000 * 60 * 60
|
||||||
}
|
},
|
||||||
|
generateEtags: process.env.GENERATE_ETAGS === 'true'
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,8 @@ import cheerio from 'cheerio'
|
||||||
import {
|
import {
|
||||||
initNextServerScript,
|
initNextServerScript,
|
||||||
killApp,
|
killApp,
|
||||||
renderViaHTTP
|
renderViaHTTP,
|
||||||
|
fetchViaHTTP
|
||||||
} from 'next-test-utils'
|
} from 'next-test-utils'
|
||||||
import webdriver from 'next-webdriver'
|
import webdriver from 'next-webdriver'
|
||||||
|
|
||||||
|
@ -18,18 +19,24 @@ jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 2
|
||||||
|
|
||||||
const context = {}
|
const context = {}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
describe('Custom Server', () => {
|
describe('Custom Server', () => {
|
||||||
beforeAll(async () => {
|
|
||||||
const scriptPath = join(appDir, 'server.js')
|
|
||||||
context.appPort = appPort = await getPort()
|
|
||||||
const env = clone(process.env)
|
|
||||||
env.PORT = `${appPort}`
|
|
||||||
|
|
||||||
server = await initNextServerScript(scriptPath, /Ready on/, env)
|
|
||||||
})
|
|
||||||
afterAll(() => killApp(server))
|
|
||||||
|
|
||||||
describe('with dynamic assetPrefix', () => {
|
describe('with dynamic assetPrefix', () => {
|
||||||
|
beforeAll(() => startServer())
|
||||||
|
afterAll(() => killApp(server))
|
||||||
|
|
||||||
it('should set the assetPrefix dynamically', async () => {
|
it('should set the assetPrefix dynamically', async () => {
|
||||||
const normalUsage = await renderViaHTTP(appPort, '/asset')
|
const normalUsage = await renderViaHTTP(appPort, '/asset')
|
||||||
expect(normalUsage).not.toMatch(/127\.0\.0\.1/)
|
expect(normalUsage).not.toMatch(/127\.0\.0\.1/)
|
||||||
|
@ -83,4 +90,24 @@ describe('Custom Server', () => {
|
||||||
browser2.close()
|
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()
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue