Skip to content

Commit b23f5c9

Browse files
clarkdopi0
authored andcommitted
feat(server): timing option for Server-Timing header (nuxt#4800)
1 parent a7ba73e commit b23f5c9

File tree

12 files changed

+122
-17
lines changed

12 files changed

+122
-17
lines changed

.eslintrc.js

+3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ module.exports = {
1010
extends: [
1111
'@nuxtjs'
1212
],
13+
"globals": {
14+
"BigInt": true
15+
},
1316
overrides: [{
1417
files: [ 'test/fixtures/*/.nuxt*/**' ],
1518
rules: {

packages/config/src/config/server.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ export default ({ env = {} } = {}) => ({
99
env.npm_package_config_nuxt_host ||
1010
'localhost',
1111
socket: env.UNIX_SOCKET ||
12-
env.npm_package_config_unix_socket
12+
env.npm_package_config_unix_socket,
13+
timing: false
1314
})

packages/config/test/__snapshots__/options.test.js.snap

+1
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,7 @@ Object {
306306
"https": false,
307307
"port": 3000,
308308
"socket": undefined,
309+
"timing": false,
309310
},
310311
"serverMiddleware": Array [],
311312
"srcDir": "/var/nuxt/test",

packages/config/test/config/__snapshots__/index.test.js.snap

+2
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,7 @@ Object {
282282
"https": false,
283283
"port": 3000,
284284
"socket": undefined,
285+
"timing": false,
285286
},
286287
"serverMiddleware": Array [],
287288
"srcDir": undefined,
@@ -605,6 +606,7 @@ Object {
605606
"https": false,
606607
"port": "3001",
607608
"socket": "/var/run/nuxt.sock",
609+
"timing": false,
608610
},
609611
"serverMiddleware": Array [],
610612
"srcDir": undefined,

packages/config/test/config/server.test.js

+6-14
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,17 @@
11
import serverConfig from '../../src/config/server'
22

3-
describe('config: server', () => {
4-
test('should return default server configurations', () => {
5-
expect(serverConfig()).toEqual({
6-
https: false,
7-
port: 3000,
8-
host: 'localhost',
9-
socket: undefined
10-
})
11-
})
3+
const serverDefaults = serverConfig()
124

5+
describe('config: server', () => {
136
test('should return server configurations with NUXT_* env', () => {
147
const env = {
158
NUXT_PORT: 3001,
169
NUXT_HOST: '127.0.0.1'
1710
}
1811
expect(serverConfig({ env })).toEqual({
19-
https: false,
12+
...serverDefaults,
2013
port: env.NUXT_PORT,
21-
host: env.NUXT_HOST,
22-
socket: undefined
14+
host: env.NUXT_HOST
2315
})
2416
})
2517

@@ -30,7 +22,7 @@ describe('config: server', () => {
3022
UNIX_SOCKET: '/var/run/env.sock'
3123
}
3224
expect(serverConfig({ env })).toEqual({
33-
https: false,
25+
...serverDefaults,
3426
port: env.PORT,
3527
host: env.HOST,
3628
socket: env.UNIX_SOCKET
@@ -44,7 +36,7 @@ describe('config: server', () => {
4436
npm_package_config_unix_socket: '/var/run/env.npm.sock'
4537
}
4638
expect(serverConfig({ env })).toEqual({
47-
https: false,
39+
...serverDefaults,
4840
port: env.npm_package_config_nuxt_port,
4941
host: env.npm_package_config_nuxt_host,
5042
socket: env.npm_package_config_unix_socket

packages/server/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"fs-extra": "^7.0.1",
2121
"ip": "^1.1.5",
2222
"launch-editor-middleware": "^2.2.1",
23+
"on-headers": "^1.0.1",
2324
"pify": "^4.0.1",
2425
"semver": "^5.6.0",
2526
"serve-placeholder": "^1.1.0",
+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import consola from 'consola'
2+
import onHeaders from 'on-headers'
3+
import { Timer } from '@nuxt/utils'
4+
5+
export default options => (req, res, next) => {
6+
if (res.timing) {
7+
consola.warn('server-timing is already registered.')
8+
}
9+
res.timing = new ServerTiming()
10+
11+
if (options && options.total) {
12+
res.timing.start('total', 'Nuxt Server Time')
13+
}
14+
15+
onHeaders(res, () => {
16+
res.timing.end('total')
17+
18+
res.setHeader(
19+
'Server-Timing',
20+
[]
21+
.concat(res.getHeader('Server-Timing') || [])
22+
.concat(res.timing.headers)
23+
.join(', ')
24+
)
25+
})
26+
27+
next()
28+
}
29+
30+
class ServerTiming extends Timer {
31+
constructor(...args) {
32+
super(...args)
33+
this.headers = []
34+
}
35+
36+
end(...args) {
37+
const time = super.end(...args)
38+
this.headers.push(this.formatHeader(time))
39+
return time
40+
}
41+
42+
clear() {
43+
super.clear()
44+
this.headers.length = 0
45+
}
46+
47+
formatHeader(time) {
48+
const desc = time.description ? `;desc="${time.description}"` : ''
49+
return `${time.name};dur=${time.duration}${desc}`
50+
}
51+
}

packages/server/src/server.js

+5
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import nuxtMiddleware from './middleware/nuxt'
1212
import errorMiddleware from './middleware/error'
1313
import Listener from './listener'
1414
import createModernMiddleware from './middleware/modern'
15+
import createTimingMiddleware from './middleware/timing'
1516

1617
export default class Server {
1718
constructor(nuxt) {
@@ -75,6 +76,10 @@ export default class Server {
7576
}
7677
}
7778

79+
if (this.options.server.timing) {
80+
this.useMiddleware(createTimingMiddleware(this.options.server.timing))
81+
}
82+
7883
const modernMiddleware = createModernMiddleware({
7984
context: this.renderer.context
8085
})

packages/utils/src/timer.js

+39
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,42 @@ export const timeout = function timeout(fn, ms, msg) {
2424
export const waitFor = function waitFor(ms) {
2525
return new Promise(resolve => setTimeout(resolve, ms || 0))
2626
}
27+
export class Timer {
28+
constructor() {
29+
this._times = new Map()
30+
}
31+
32+
start(name, description) {
33+
const time = {
34+
name,
35+
description,
36+
start: this.hrtime()
37+
}
38+
this._times.set(name, time)
39+
return time
40+
}
41+
42+
end(name) {
43+
if (this._times.has(name)) {
44+
const time = this._times.get(name)
45+
time.duration = this.hrtime(time.start)
46+
this._times.delete(name)
47+
return time
48+
}
49+
}
50+
51+
hrtime(start) {
52+
const useBigInt = typeof process.hrtime.bigint === 'function'
53+
if (start) {
54+
const end = useBigInt ? process.hrtime.bigint() : process.hrtime(start)
55+
return useBigInt
56+
? (end - start) / BigInt(1000000)
57+
: (end[0] * 1e3) + (end[1] * 1e-6)
58+
}
59+
return useBigInt ? process.hrtime.bigint() : process.hrtime()
60+
}
61+
62+
clear() {
63+
this._times.clear()
64+
}
65+
}

test/fixtures/with-config/nuxt.config.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ export default {
66
srcDir: __dirname,
77
server: {
88
port: 8000,
9-
host: '0.0.0.0'
9+
host: '0.0.0.0',
10+
timing: {
11+
total: true
12+
}
1013
},
1114
router: {
1215
base: '/test/',

test/unit/with-config.test.js

+7
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,13 @@ describe('with-config', () => {
197197
expect(fakeErrorLog).toHaveBeenCalled()
198198
})
199199

200+
test('/ with Server-Timing header', async () => {
201+
const { headers } = await rp(url('/test'), {
202+
resolveWithFullResponse: true
203+
})
204+
expect(headers['server-timing']).toMatch(/total;dur=\d+;desc="Nuxt Server Time"/)
205+
})
206+
200207
// Close server and ask nuxt to stop listening to file changes
201208
afterAll(async () => {
202209
await nuxt.close()

yarn.lock

+1-1
Original file line numberDiff line numberDiff line change
@@ -7771,7 +7771,7 @@ on-finished@~2.3.0:
77717771
dependencies:
77727772
ee-first "1.1.1"
77737773

7774-
on-headers@~1.0.1:
7774+
on-headers@^1.0.1, on-headers@~1.0.1:
77757775
version "1.0.1"
77767776
resolved "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz#928f5d0f470d49342651ea6794b0857c100693f7"
77777777
integrity sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=

0 commit comments

Comments
 (0)