feat(server): timing option for `Server-Timing` header (#4800)

This commit is contained in:
Xin Du (Clark) 2019-01-19 18:14:54 +00:00 committed by Pooya Parsa
parent a7ba73ed13
commit b23f5c9b4b
12 changed files with 122 additions and 17 deletions

View File

@ -10,6 +10,9 @@ module.exports = {
extends: [
'@nuxtjs'
],
"globals": {
"BigInt": true
},
overrides: [{
files: [ 'test/fixtures/*/.nuxt*/**' ],
rules: {

View File

@ -9,5 +9,6 @@ export default ({ env = {} } = {}) => ({
env.npm_package_config_nuxt_host ||
'localhost',
socket: env.UNIX_SOCKET ||
env.npm_package_config_unix_socket
env.npm_package_config_unix_socket,
timing: false
})

View File

@ -306,6 +306,7 @@ Object {
"https": false,
"port": 3000,
"socket": undefined,
"timing": false,
},
"serverMiddleware": Array [],
"srcDir": "/var/nuxt/test",

View File

@ -282,6 +282,7 @@ Object {
"https": false,
"port": 3000,
"socket": undefined,
"timing": false,
},
"serverMiddleware": Array [],
"srcDir": undefined,
@ -605,6 +606,7 @@ Object {
"https": false,
"port": "3001",
"socket": "/var/run/nuxt.sock",
"timing": false,
},
"serverMiddleware": Array [],
"srcDir": undefined,

View File

@ -1,25 +1,17 @@
import serverConfig from '../../src/config/server'
describe('config: server', () => {
test('should return default server configurations', () => {
expect(serverConfig()).toEqual({
https: false,
port: 3000,
host: 'localhost',
socket: undefined
})
})
const serverDefaults = serverConfig()
describe('config: server', () => {
test('should return server configurations with NUXT_* env', () => {
const env = {
NUXT_PORT: 3001,
NUXT_HOST: '127.0.0.1'
}
expect(serverConfig({ env })).toEqual({
https: false,
...serverDefaults,
port: env.NUXT_PORT,
host: env.NUXT_HOST,
socket: undefined
host: env.NUXT_HOST
})
})
@ -30,7 +22,7 @@ describe('config: server', () => {
UNIX_SOCKET: '/var/run/env.sock'
}
expect(serverConfig({ env })).toEqual({
https: false,
...serverDefaults,
port: env.PORT,
host: env.HOST,
socket: env.UNIX_SOCKET
@ -44,7 +36,7 @@ describe('config: server', () => {
npm_package_config_unix_socket: '/var/run/env.npm.sock'
}
expect(serverConfig({ env })).toEqual({
https: false,
...serverDefaults,
port: env.npm_package_config_nuxt_port,
host: env.npm_package_config_nuxt_host,
socket: env.npm_package_config_unix_socket

View File

@ -20,6 +20,7 @@
"fs-extra": "^7.0.1",
"ip": "^1.1.5",
"launch-editor-middleware": "^2.2.1",
"on-headers": "^1.0.1",
"pify": "^4.0.1",
"semver": "^5.6.0",
"serve-placeholder": "^1.1.0",

View File

@ -0,0 +1,51 @@
import consola from 'consola'
import onHeaders from 'on-headers'
import { Timer } from '@nuxt/utils'
export default options => (req, res, next) => {
if (res.timing) {
consola.warn('server-timing is already registered.')
}
res.timing = new ServerTiming()
if (options && options.total) {
res.timing.start('total', 'Nuxt Server Time')
}
onHeaders(res, () => {
res.timing.end('total')
res.setHeader(
'Server-Timing',
[]
.concat(res.getHeader('Server-Timing') || [])
.concat(res.timing.headers)
.join(', ')
)
})
next()
}
class ServerTiming extends Timer {
constructor(...args) {
super(...args)
this.headers = []
}
end(...args) {
const time = super.end(...args)
this.headers.push(this.formatHeader(time))
return time
}
clear() {
super.clear()
this.headers.length = 0
}
formatHeader(time) {
const desc = time.description ? `;desc="${time.description}"` : ''
return `${time.name};dur=${time.duration}${desc}`
}
}

View File

@ -12,6 +12,7 @@ import nuxtMiddleware from './middleware/nuxt'
import errorMiddleware from './middleware/error'
import Listener from './listener'
import createModernMiddleware from './middleware/modern'
import createTimingMiddleware from './middleware/timing'
export default class Server {
constructor(nuxt) {
@ -75,6 +76,10 @@ export default class Server {
}
}
if (this.options.server.timing) {
this.useMiddleware(createTimingMiddleware(this.options.server.timing))
}
const modernMiddleware = createModernMiddleware({
context: this.renderer.context
})

View File

@ -24,3 +24,42 @@ export const timeout = function timeout(fn, ms, msg) {
export const waitFor = function waitFor(ms) {
return new Promise(resolve => setTimeout(resolve, ms || 0))
}
export class Timer {
constructor() {
this._times = new Map()
}
start(name, description) {
const time = {
name,
description,
start: this.hrtime()
}
this._times.set(name, time)
return time
}
end(name) {
if (this._times.has(name)) {
const time = this._times.get(name)
time.duration = this.hrtime(time.start)
this._times.delete(name)
return time
}
}
hrtime(start) {
const useBigInt = typeof process.hrtime.bigint === 'function'
if (start) {
const end = useBigInt ? process.hrtime.bigint() : process.hrtime(start)
return useBigInt
? (end - start) / BigInt(1000000)
: (end[0] * 1e3) + (end[1] * 1e-6)
}
return useBigInt ? process.hrtime.bigint() : process.hrtime()
}
clear() {
this._times.clear()
}
}

View File

@ -6,7 +6,10 @@ export default {
srcDir: __dirname,
server: {
port: 8000,
host: '0.0.0.0'
host: '0.0.0.0',
timing: {
total: true
}
},
router: {
base: '/test/',

View File

@ -197,6 +197,13 @@ describe('with-config', () => {
expect(fakeErrorLog).toHaveBeenCalled()
})
test('/ with Server-Timing header', async () => {
const { headers } = await rp(url('/test'), {
resolveWithFullResponse: true
})
expect(headers['server-timing']).toMatch(/total;dur=\d+;desc="Nuxt Server Time"/)
})
// Close server and ask nuxt to stop listening to file changes
afterAll(async () => {
await nuxt.close()

View File

@ -7771,7 +7771,7 @@ on-finished@~2.3.0:
dependencies:
ee-first "1.1.1"
on-headers@~1.0.1:
on-headers@^1.0.1, on-headers@~1.0.1:
version "1.0.1"
resolved "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz#928f5d0f470d49342651ea6794b0857c100693f7"
integrity sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=