fastify 6.0.0-alpha.0 → 6.0.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.pi/self-learning-memory/README.md +8 -0
- package/.pi/self-learning-memory/core/CORE.md +33 -0
- package/.pi/self-learning-memory/core/index.json +735 -0
- package/.pi/self-learning-memory/daily/2026-07-06.md +22 -0
- package/.pi/self-learning-memory/daily/2026-07-29.md +50 -0
- package/.pi/self-learning-memory/daily/2026-08-04.md +41 -0
- package/.pi/self-learning-memory/daily/2026-08-18.md +40 -0
- package/.pi/self-learning-memory/long-term-memory.md +89 -0
- package/AGENTS.md +290 -0
- package/docs/Reference/ContentTypeParser.md +0 -4
- package/docs/Reference/Reply.md +14 -3
- package/docs/Reference/Request.md +0 -4
- package/docs/Reference/Server.md +8 -3
- package/fastify.d.ts +1 -1
- package/fastify.js +2 -2
- package/lib/content-type.js +4 -1
- package/lib/reply.js +11 -1
- package/lib/request.js +3 -2
- package/lib/route.js +2 -1
- package/lib/validation.js +10 -1
- package/package.json +2 -2
- package/test/close.test.js +34 -0
- package/test/content-parser.test.js +16 -18
- package/test/content-type.test.js +4 -0
- package/test/fix-root-primitive-coercion.test.js +35 -0
- package/test/helper.js +73 -1
- package/test/hooks-async.test.js +1 -2
- package/test/hooks.test.js +1 -2
- package/test/internals/reply.test.js +76 -1
- package/test/listen.1.test.js +17 -52
- package/test/listen.5.test.js +6 -9
- package/test/logger/instantiation.test.js +1 -1
- package/test/logger/logging.test.js +1 -1
- package/test/logger/request.test.js +1 -1
- package/test/logger/response.test.js +1 -1
- package/test/reply-media-type.test.js +131 -0
- package/test/route-prefix.test.js +33 -1
- package/test/route.6.test.js +4 -11
- package/test/schema-feature.test.js +36 -65
- package/test/schema-serialization.test.js +1 -1
- package/test/schema-special-usage.test.js +1 -1
- package/test/schema-validation.test.js +1 -1
- package/test/stream.2.test.js +1 -1
- package/test/trust-proxy.test.js +27 -21
- package/test/types/fastify.tst.ts +2 -0
- package/test/toolkit.js +0 -63
package/fastify.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const VERSION = '6.0.0-alpha.
|
|
3
|
+
const VERSION = '6.0.0-alpha.2'
|
|
4
4
|
|
|
5
5
|
const Avvio = require('avvio')
|
|
6
6
|
const http = require('node:http')
|
|
@@ -600,7 +600,7 @@ function fastify (serverOptions) {
|
|
|
600
600
|
|
|
601
601
|
if (name === 'onClose') {
|
|
602
602
|
this.onClose(fn.bind(this))
|
|
603
|
-
} else if (name === 'onReady' || name === 'onListen' || name === 'onRoute') {
|
|
603
|
+
} else if (name === 'onReady' || name === 'onListen' || name === 'onRoute' || name === 'preClose') {
|
|
604
604
|
this[kHooks].add(name, fn)
|
|
605
605
|
} else {
|
|
606
606
|
this.after((err, done) => {
|
package/lib/content-type.js
CHANGED
|
@@ -183,7 +183,10 @@ class ContentType {
|
|
|
183
183
|
|
|
184
184
|
get isValid () { return this.#valid }
|
|
185
185
|
|
|
186
|
-
get mediaType () {
|
|
186
|
+
get mediaType () {
|
|
187
|
+
if (this.#valid === false) return undefined
|
|
188
|
+
return `${this.#type}/${this.#subtype}`
|
|
189
|
+
}
|
|
187
190
|
|
|
188
191
|
get type () { return this.#type }
|
|
189
192
|
|
package/lib/reply.js
CHANGED
|
@@ -93,6 +93,12 @@ Object.defineProperties(Reply.prototype, {
|
|
|
93
93
|
return (this[kReplyEndTime] || now()) - this[kReplyStartTime]
|
|
94
94
|
}
|
|
95
95
|
},
|
|
96
|
+
mediaType: {
|
|
97
|
+
get () {
|
|
98
|
+
const contentTypeHeader = this[kReplyHeaders]['content-type']
|
|
99
|
+
return ContentType.from(contentTypeHeader).mediaType
|
|
100
|
+
}
|
|
101
|
+
},
|
|
96
102
|
server: {
|
|
97
103
|
get () {
|
|
98
104
|
return this.request[kRouteContext].server
|
|
@@ -254,9 +260,13 @@ Reply.prototype.hasHeader = function (key) {
|
|
|
254
260
|
}
|
|
255
261
|
|
|
256
262
|
Reply.prototype.removeHeader = function (key) {
|
|
263
|
+
key = key.toLowerCase()
|
|
257
264
|
// Node.js does not like headers with keys set to undefined,
|
|
258
265
|
// so we have to delete the key.
|
|
259
|
-
delete this[kReplyHeaders][key
|
|
266
|
+
delete this[kReplyHeaders][key]
|
|
267
|
+
if (!this.raw.headersSent) {
|
|
268
|
+
this.raw.removeHeader(key)
|
|
269
|
+
}
|
|
260
270
|
return this
|
|
261
271
|
}
|
|
262
272
|
|
package/lib/request.js
CHANGED
|
@@ -49,8 +49,9 @@ function getTrustProxyFn (tp) {
|
|
|
49
49
|
return function () { return true }
|
|
50
50
|
}
|
|
51
51
|
if (typeof tp === 'number') {
|
|
52
|
-
//
|
|
53
|
-
|
|
52
|
+
// Hop-count-only trust cannot validate the immediate peer. Fail closed so
|
|
53
|
+
// direct clients cannot spoof X-Forwarded-* values by supplying enough hops.
|
|
54
|
+
return function () { return false }
|
|
54
55
|
}
|
|
55
56
|
if (typeof tp === 'string') {
|
|
56
57
|
// Support comma-separated tps
|
package/lib/route.js
CHANGED
|
@@ -289,6 +289,7 @@ function buildRouting (options) {
|
|
|
289
289
|
opts.attachValidation = false
|
|
290
290
|
}
|
|
291
291
|
|
|
292
|
+
const routeConfigUrl = prefixing ? prefix : url
|
|
292
293
|
if (prefixing === false) {
|
|
293
294
|
// run 'onRoute' hooks
|
|
294
295
|
for (const hook of this[kHooks].onRoute) {
|
|
@@ -327,7 +328,7 @@ function buildRouting (options) {
|
|
|
327
328
|
const constraints = opts.constraints || {}
|
|
328
329
|
const config = {
|
|
329
330
|
...opts.config,
|
|
330
|
-
url,
|
|
331
|
+
url: routeConfigUrl,
|
|
331
332
|
method: opts.method
|
|
332
333
|
}
|
|
333
334
|
|
package/lib/validation.js
CHANGED
|
@@ -120,7 +120,16 @@ function validateParam (validatorFunction, request, paramName) {
|
|
|
120
120
|
let ret
|
|
121
121
|
|
|
122
122
|
try {
|
|
123
|
-
|
|
123
|
+
const data = isUndefined ? null : request[paramName]
|
|
124
|
+
// Ajv can only mutate a root value when its parent is provided.
|
|
125
|
+
if (validatorFunction?.schemaEnv) {
|
|
126
|
+
ret = validatorFunction(data, {
|
|
127
|
+
parentData: request,
|
|
128
|
+
parentDataProperty: paramName
|
|
129
|
+
})
|
|
130
|
+
} else {
|
|
131
|
+
ret = validatorFunction?.(data)
|
|
132
|
+
}
|
|
124
133
|
} catch (err) {
|
|
125
134
|
// If validator throws synchronously, ensure it propagates as an internal error
|
|
126
135
|
err.statusCode = 500
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fastify",
|
|
3
|
-
"version": "6.0.0-alpha.
|
|
3
|
+
"version": "6.0.0-alpha.2",
|
|
4
4
|
"description": "Fast and low overhead web framework, for Node.js",
|
|
5
5
|
"main": "fastify.js",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -213,7 +213,7 @@
|
|
|
213
213
|
"find-my-way": "^9.6.0",
|
|
214
214
|
"light-my-request": "^6.0.0",
|
|
215
215
|
"pino": "^9.14.0 || ^10.1.0",
|
|
216
|
-
"process-warning": "^5.
|
|
216
|
+
"process-warning": "^5.1.0",
|
|
217
217
|
"rfdc": "^1.3.1",
|
|
218
218
|
"secure-json-parse": "^4.0.0",
|
|
219
219
|
"semver": "^7.6.0",
|
package/test/close.test.js
CHANGED
|
@@ -665,6 +665,40 @@ test('preClose async', async t => {
|
|
|
665
665
|
await fastify.close()
|
|
666
666
|
})
|
|
667
667
|
|
|
668
|
+
test('preClose runs exactly once with a child plugin', async t => {
|
|
669
|
+
t.plan(1)
|
|
670
|
+
const fastify = Fastify()
|
|
671
|
+
let count = 0
|
|
672
|
+
|
|
673
|
+
fastify.register(async (child) => {
|
|
674
|
+
child.get('/x', async () => 'ok')
|
|
675
|
+
})
|
|
676
|
+
fastify.addHook('preClose', async () => { count++ })
|
|
677
|
+
|
|
678
|
+
await fastify.ready()
|
|
679
|
+
await fastify.close()
|
|
680
|
+
|
|
681
|
+
t.assert.strictEqual(count, 1)
|
|
682
|
+
})
|
|
683
|
+
|
|
684
|
+
test('preClose runs exactly once with nested child plugins', async t => {
|
|
685
|
+
t.plan(1)
|
|
686
|
+
const fastify = Fastify()
|
|
687
|
+
let count = 0
|
|
688
|
+
|
|
689
|
+
fastify.register(async (child) => {
|
|
690
|
+
child.register(async (grandchild) => {
|
|
691
|
+
grandchild.get('/y', async () => 'ok')
|
|
692
|
+
})
|
|
693
|
+
})
|
|
694
|
+
fastify.addHook('preClose', async () => { count++ })
|
|
695
|
+
|
|
696
|
+
await fastify.ready()
|
|
697
|
+
await fastify.close()
|
|
698
|
+
|
|
699
|
+
t.assert.strictEqual(count, 1)
|
|
700
|
+
})
|
|
701
|
+
|
|
668
702
|
test('preClose execution order', (t, done) => {
|
|
669
703
|
t.plan(4)
|
|
670
704
|
const fastify = Fastify()
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { test } = require('node:test')
|
|
4
|
+
const { spyWarning } = require('process-warning')
|
|
4
5
|
const Fastify = require('..')
|
|
5
6
|
const keys = require('../lib/symbols')
|
|
6
7
|
const { FST_ERR_CTP_ALREADY_PRESENT, FST_ERR_CTP_INVALID_TYPE, FST_ERR_CTP_INVALID_MEDIA_TYPE } = require('../lib/errors')
|
|
8
|
+
const { FSTSEC001 } = require('../lib/warnings')
|
|
7
9
|
|
|
8
10
|
const first = function (req, payload, done) {}
|
|
9
11
|
const second = function (req, payload, done) {}
|
|
@@ -488,41 +490,37 @@ test('Safeguard against content-type spoofing - string', async t => {
|
|
|
488
490
|
})
|
|
489
491
|
|
|
490
492
|
test('Warning against improper content-type - regexp', async t => {
|
|
491
|
-
await t.test('improper regex - text plain', (t
|
|
493
|
+
await t.test('improper regex - text plain', async (t) => {
|
|
492
494
|
t.plan(2)
|
|
493
|
-
const
|
|
495
|
+
const spyData = spyWarning(FSTSEC001)
|
|
496
|
+
t.after(spyData.restore)
|
|
494
497
|
|
|
495
|
-
|
|
496
|
-
function onWarning (warning) {
|
|
497
|
-
t.assert.strictEqual(warning.name, 'FastifySecurity')
|
|
498
|
-
t.assert.strictEqual(warning.code, 'FSTSEC001')
|
|
499
|
-
done()
|
|
500
|
-
}
|
|
501
|
-
t.after(() => process.removeListener('warning', onWarning))
|
|
498
|
+
const fastify = Fastify()
|
|
502
499
|
|
|
503
500
|
fastify.removeAllContentTypeParsers()
|
|
504
501
|
fastify.addContentTypeParser(/text\/plain/, function (request, body, done) {
|
|
505
502
|
done(null, body)
|
|
506
503
|
})
|
|
504
|
+
|
|
505
|
+
await fastify.ready()
|
|
506
|
+
t.assert.deepStrictEqual(spyData.calls, [{ arguments: ['text\\/plain'], result: true }])
|
|
507
|
+
t.assert.strictEqual(spyData.callCount(), 1)
|
|
507
508
|
})
|
|
508
509
|
|
|
509
|
-
await t.test('improper regex - application json', (t
|
|
510
|
+
await t.test('improper regex - application json', async (t) => {
|
|
510
511
|
t.plan(2)
|
|
512
|
+
const spyData = spyWarning(FSTSEC001)
|
|
513
|
+
t.after(spyData.restore)
|
|
511
514
|
const fastify = Fastify()
|
|
512
515
|
|
|
513
|
-
process.on('warning', onWarning)
|
|
514
|
-
function onWarning (warning) {
|
|
515
|
-
t.assert.strictEqual(warning.name, 'FastifySecurity')
|
|
516
|
-
t.assert.strictEqual(warning.code, 'FSTSEC001')
|
|
517
|
-
done()
|
|
518
|
-
}
|
|
519
|
-
t.after(() => process.removeListener('warning', onWarning))
|
|
520
|
-
|
|
521
516
|
fastify.removeAllContentTypeParsers()
|
|
522
517
|
|
|
523
518
|
fastify.addContentTypeParser(/application\/json/, function (request, body, done) {
|
|
524
519
|
done(null, body)
|
|
525
520
|
})
|
|
521
|
+
|
|
522
|
+
t.assert.deepStrictEqual(spyData.calls, [{ arguments: ['application\\/json'], result: true }])
|
|
523
|
+
t.assert.deepEqual(spyData.callCount(), 1)
|
|
526
524
|
})
|
|
527
525
|
})
|
|
528
526
|
|
|
@@ -46,18 +46,22 @@ describe('ContentType class', () => {
|
|
|
46
46
|
test('returns empty instance for empty value', (t) => {
|
|
47
47
|
let found = new ContentType('')
|
|
48
48
|
t.assert.equal(found.isEmpty, true)
|
|
49
|
+
t.assert.equal(found.mediaType, undefined)
|
|
49
50
|
|
|
50
51
|
found = new ContentType('undefined')
|
|
51
52
|
t.assert.equal(found.isEmpty, true)
|
|
53
|
+
t.assert.equal(found.mediaType, undefined)
|
|
52
54
|
|
|
53
55
|
found = new ContentType()
|
|
54
56
|
t.assert.equal(found.isEmpty, true)
|
|
57
|
+
t.assert.equal(found.mediaType, undefined)
|
|
55
58
|
})
|
|
56
59
|
|
|
57
60
|
test('indicates media type is not correct format', (t) => {
|
|
58
61
|
let found = new ContentType('foo')
|
|
59
62
|
t.assert.equal(found.isEmpty, true)
|
|
60
63
|
t.assert.equal(found.isValid, false)
|
|
64
|
+
t.assert.equal(found.mediaType, undefined)
|
|
61
65
|
|
|
62
66
|
found = new ContentType('foo /bar')
|
|
63
67
|
t.assert.equal(found.isEmpty, true)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { test } = require('node:test')
|
|
4
|
+
const Fastify = require('..')
|
|
5
|
+
|
|
6
|
+
test('root primitive values coerced by Ajv are assigned to the request', async (t) => {
|
|
7
|
+
const fastify = Fastify()
|
|
8
|
+
|
|
9
|
+
fastify.post('/', {
|
|
10
|
+
schema: {
|
|
11
|
+
body: {
|
|
12
|
+
type: 'integer',
|
|
13
|
+
minimum: 1,
|
|
14
|
+
maximum: 10
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}, (request, reply) => {
|
|
18
|
+
reply.send({ body: request.body, type: typeof request.body })
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
const response = await fastify.inject({
|
|
22
|
+
method: 'POST',
|
|
23
|
+
url: '/',
|
|
24
|
+
payload: '"10"',
|
|
25
|
+
headers: { 'content-type': 'application/json' }
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
t.assert.strictEqual(response.statusCode, 200)
|
|
29
|
+
t.assert.deepStrictEqual(JSON.parse(response.payload), {
|
|
30
|
+
body: 10,
|
|
31
|
+
type: 'number'
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
await fastify.close()
|
|
35
|
+
})
|
package/test/helper.js
CHANGED
|
@@ -4,11 +4,42 @@ const dns = require('node:dns').promises
|
|
|
4
4
|
const stream = require('node:stream')
|
|
5
5
|
const { promisify } = require('node:util')
|
|
6
6
|
const symbols = require('../lib/symbols')
|
|
7
|
-
const { waitForCb } = require('./toolkit')
|
|
8
7
|
const assert = require('node:assert')
|
|
9
8
|
|
|
10
9
|
module.exports.sleep = promisify(setTimeout)
|
|
11
10
|
|
|
11
|
+
function waitForCb (options) {
|
|
12
|
+
let count = null
|
|
13
|
+
let done = false
|
|
14
|
+
let iResolve
|
|
15
|
+
let iReject
|
|
16
|
+
|
|
17
|
+
function stepIn () {
|
|
18
|
+
if (done) {
|
|
19
|
+
iReject(new Error('Unexpected done call'))
|
|
20
|
+
return
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (--count) {
|
|
24
|
+
return
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
done = true
|
|
28
|
+
iResolve()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const patience = new Promise((resolve, reject) => {
|
|
32
|
+
iResolve = resolve
|
|
33
|
+
iReject = reject
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
count = options.steps || 1
|
|
37
|
+
done = false
|
|
38
|
+
|
|
39
|
+
return { stepIn, patience }
|
|
40
|
+
}
|
|
41
|
+
module.exports.waitForCb = waitForCb
|
|
42
|
+
|
|
12
43
|
/**
|
|
13
44
|
* @param method HTTP request method
|
|
14
45
|
* @param t node:test instance
|
|
@@ -494,3 +525,44 @@ module.exports.getServerUrl = function (app) {
|
|
|
494
525
|
? `http://[${address}]:${port}`
|
|
495
526
|
: `http://${address}:${port}`
|
|
496
527
|
}
|
|
528
|
+
|
|
529
|
+
module.exports.partialDeepStrictEqual = function partialDeepStrictEqual (actual, expected) {
|
|
530
|
+
if (typeof expected !== 'object' || expected === null) {
|
|
531
|
+
return actual === expected
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
if (typeof actual !== 'object' || actual === null) {
|
|
535
|
+
return false
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
if (Array.isArray(expected)) {
|
|
539
|
+
if (!Array.isArray(actual)) return false
|
|
540
|
+
if (expected.length > actual.length) return false
|
|
541
|
+
|
|
542
|
+
for (let i = 0; i < expected.length; i++) {
|
|
543
|
+
if (!partialDeepStrictEqual(actual[i], expected[i])) {
|
|
544
|
+
return false
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
return true
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
for (const key of Object.keys(expected)) {
|
|
551
|
+
if (!(key in actual)) return false
|
|
552
|
+
if (!partialDeepStrictEqual(actual[key], expected[key])) {
|
|
553
|
+
return false
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
return true
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
module.exports.assertNoWarning = function (t) {
|
|
561
|
+
function doNotWarn () {
|
|
562
|
+
t.assert.fail('no warning')
|
|
563
|
+
}
|
|
564
|
+
process.on('warning', doNotWarn)
|
|
565
|
+
t.after(() => {
|
|
566
|
+
process.off('warning', doNotWarn)
|
|
567
|
+
})
|
|
568
|
+
}
|
package/test/hooks-async.test.js
CHANGED
|
@@ -4,8 +4,7 @@ const { Readable } = require('node:stream')
|
|
|
4
4
|
const { test, describe } = require('node:test')
|
|
5
5
|
const Fastify = require('../fastify')
|
|
6
6
|
const fs = require('node:fs')
|
|
7
|
-
const { sleep } = require('./helper')
|
|
8
|
-
const { waitForCb } = require('./toolkit')
|
|
7
|
+
const { sleep, waitForCb } = require('./helper')
|
|
9
8
|
|
|
10
9
|
process.removeAllListeners('warning')
|
|
11
10
|
|
package/test/hooks.test.js
CHANGED
|
@@ -10,8 +10,7 @@ const symbols = require('../lib/symbols.js')
|
|
|
10
10
|
const payload = { hello: 'world' }
|
|
11
11
|
const proxyquire = require('proxyquire')
|
|
12
12
|
const { connect } = require('node:net')
|
|
13
|
-
const { sleep } = require('./helper')
|
|
14
|
-
const { waitForCb } = require('./toolkit.js')
|
|
13
|
+
const { sleep, waitForCb } = require('./helper')
|
|
15
14
|
const { fetch } = require('undici')
|
|
16
15
|
|
|
17
16
|
process.removeAllListeners('warning')
|
|
@@ -34,7 +34,7 @@ const doGet = async function (url) {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
test('Once called, Reply should return an object with methods', t => {
|
|
37
|
-
t.plan(
|
|
37
|
+
t.plan(16)
|
|
38
38
|
const response = { res: 'res' }
|
|
39
39
|
const context = {
|
|
40
40
|
config: { onSend: [] },
|
|
@@ -49,6 +49,7 @@ test('Once called, Reply should return an object with methods', t => {
|
|
|
49
49
|
t.assert.strictEqual(typeof reply[kReplyErrorHandlerCalled], 'boolean')
|
|
50
50
|
t.assert.strictEqual(typeof reply.send, 'function')
|
|
51
51
|
t.assert.strictEqual(typeof reply.code, 'function')
|
|
52
|
+
t.assert.strictEqual(typeof reply.mediaType, 'undefined')
|
|
52
53
|
t.assert.strictEqual(typeof reply.status, 'function')
|
|
53
54
|
t.assert.strictEqual(typeof reply.header, 'function')
|
|
54
55
|
t.assert.strictEqual(typeof reply.serialize, 'function')
|
|
@@ -1114,6 +1115,80 @@ test('reply.removeHeader can remove the value', async t => {
|
|
|
1114
1115
|
await fetch(`${fastifyServer}/headers`)
|
|
1115
1116
|
})
|
|
1116
1117
|
|
|
1118
|
+
test('reply.removeHeader removes raw headers', async t => {
|
|
1119
|
+
t.plan(9)
|
|
1120
|
+
|
|
1121
|
+
const fastify = require('../../')()
|
|
1122
|
+
|
|
1123
|
+
t.after(() => fastify.close())
|
|
1124
|
+
|
|
1125
|
+
fastify.get('/headers', function (req, reply) {
|
|
1126
|
+
reply.raw.setHeader('X-Foo', 'raw')
|
|
1127
|
+
t.assert.strictEqual(reply.getHeader('x-foo'), 'raw')
|
|
1128
|
+
t.assert.strictEqual(reply.getHeaders()['x-foo'], 'raw')
|
|
1129
|
+
t.assert.strictEqual(reply.hasHeader('x-foo'), true)
|
|
1130
|
+
|
|
1131
|
+
t.assert.strictEqual(reply.removeHeader('x-FoO'), reply)
|
|
1132
|
+
t.assert.strictEqual(reply.getHeader('x-foo'), undefined)
|
|
1133
|
+
t.assert.strictEqual(Object.hasOwn(reply.getHeaders(), 'x-foo'), false)
|
|
1134
|
+
t.assert.strictEqual(reply.hasHeader('x-foo'), false)
|
|
1135
|
+
t.assert.strictEqual(reply.removeHeader('X-FOO'), reply)
|
|
1136
|
+
|
|
1137
|
+
reply.send()
|
|
1138
|
+
})
|
|
1139
|
+
|
|
1140
|
+
const fastifyServer = await fastify.listen({ port: 0 })
|
|
1141
|
+
const response = await fetch(`${fastifyServer}/headers`)
|
|
1142
|
+
t.assert.strictEqual(response.headers.get('x-foo'), null)
|
|
1143
|
+
})
|
|
1144
|
+
|
|
1145
|
+
test('reply.removeHeader removes layered headers', async t => {
|
|
1146
|
+
t.plan(7)
|
|
1147
|
+
|
|
1148
|
+
const fastify = require('../../')()
|
|
1149
|
+
|
|
1150
|
+
t.after(() => fastify.close())
|
|
1151
|
+
|
|
1152
|
+
fastify.get('/headers', function (req, reply) {
|
|
1153
|
+
reply.raw.setHeader('x-foo', 'raw')
|
|
1154
|
+
reply.header('x-foo', 'fastify')
|
|
1155
|
+
t.assert.strictEqual(reply.getHeader('x-foo'), 'fastify')
|
|
1156
|
+
t.assert.strictEqual(reply.getHeaders()['x-foo'], 'fastify')
|
|
1157
|
+
|
|
1158
|
+
t.assert.strictEqual(reply.removeHeader('x-foo'), reply)
|
|
1159
|
+
t.assert.strictEqual(reply.getHeader('x-foo'), undefined)
|
|
1160
|
+
t.assert.strictEqual(Object.hasOwn(reply.getHeaders(), 'x-foo'), false)
|
|
1161
|
+
t.assert.strictEqual(reply.hasHeader('x-foo'), false)
|
|
1162
|
+
|
|
1163
|
+
reply.send()
|
|
1164
|
+
})
|
|
1165
|
+
|
|
1166
|
+
const fastifyServer = await fastify.listen({ port: 0 })
|
|
1167
|
+
const response = await fetch(`${fastifyServer}/headers`)
|
|
1168
|
+
t.assert.strictEqual(response.headers.get('x-foo'), null)
|
|
1169
|
+
})
|
|
1170
|
+
|
|
1171
|
+
test('reply.removeHeader does not throw after headers are sent', async t => {
|
|
1172
|
+
t.plan(3)
|
|
1173
|
+
|
|
1174
|
+
const fastify = require('../../')()
|
|
1175
|
+
|
|
1176
|
+
t.after(() => fastify.close())
|
|
1177
|
+
|
|
1178
|
+
fastify.get('/headers', function (req, reply) {
|
|
1179
|
+
reply.hijack()
|
|
1180
|
+
reply.raw.setHeader('x-foo', 'raw')
|
|
1181
|
+
reply.raw.flushHeaders()
|
|
1182
|
+
t.assert.strictEqual(reply.raw.headersSent, true)
|
|
1183
|
+
t.assert.doesNotThrow(() => reply.removeHeader('x-foo'))
|
|
1184
|
+
reply.raw.end()
|
|
1185
|
+
})
|
|
1186
|
+
|
|
1187
|
+
const fastifyServer = await fastify.listen({ port: 0 })
|
|
1188
|
+
const response = await fetch(`${fastifyServer}/headers`)
|
|
1189
|
+
t.assert.strictEqual(response.headers.get('x-foo'), 'raw')
|
|
1190
|
+
})
|
|
1191
|
+
|
|
1117
1192
|
test('reply.header can reset the value', async t => {
|
|
1118
1193
|
t.plan(1)
|
|
1119
1194
|
|
package/test/listen.1.test.js
CHANGED
|
@@ -4,6 +4,7 @@ const { networkInterfaces } = require('node:os')
|
|
|
4
4
|
const { test, before } = require('node:test')
|
|
5
5
|
const Fastify = require('..')
|
|
6
6
|
const helper = require('./helper')
|
|
7
|
+
const { assertNoWarning } = require('./helper')
|
|
7
8
|
|
|
8
9
|
let localhost
|
|
9
10
|
let localhostForURL
|
|
@@ -13,33 +14,22 @@ before(async function () {
|
|
|
13
14
|
})
|
|
14
15
|
|
|
15
16
|
test('listen works without arguments', async t => {
|
|
16
|
-
|
|
17
|
-
t.assert.fail('should not be deprecated')
|
|
18
|
-
}
|
|
19
|
-
process.on('warning', doNotWarn)
|
|
17
|
+
assertNoWarning(t)
|
|
20
18
|
|
|
21
19
|
const fastify = Fastify()
|
|
22
|
-
|
|
23
|
-
fastify.close()
|
|
24
|
-
process.removeListener('warning', doNotWarn)
|
|
25
|
-
})
|
|
20
|
+
|
|
26
21
|
await fastify.listen()
|
|
27
22
|
const address = fastify.server.address()
|
|
28
23
|
t.assert.strictEqual(address.address, localhost)
|
|
29
24
|
t.assert.ok(address.port > 0)
|
|
25
|
+
await fastify.close()
|
|
30
26
|
})
|
|
31
27
|
|
|
32
28
|
test('Async/await listen with arguments', async t => {
|
|
33
|
-
|
|
34
|
-
t.assert.fail('should not be deprecated')
|
|
35
|
-
}
|
|
36
|
-
process.on('warning', doNotWarn)
|
|
29
|
+
assertNoWarning(t)
|
|
37
30
|
|
|
38
31
|
const fastify = Fastify()
|
|
39
|
-
|
|
40
|
-
fastify.close()
|
|
41
|
-
process.removeListener('warning', doNotWarn)
|
|
42
|
-
})
|
|
32
|
+
|
|
43
33
|
const addr = await fastify.listen({ port: 0, host: '0.0.0.0' })
|
|
44
34
|
const address = fastify.server.address()
|
|
45
35
|
const { protocol, hostname, port, pathname } = new URL(addr)
|
|
@@ -55,39 +45,27 @@ test('Async/await listen with arguments', async t => {
|
|
|
55
45
|
family: 'IPv4',
|
|
56
46
|
port: address.port
|
|
57
47
|
})
|
|
48
|
+
await fastify.close()
|
|
58
49
|
})
|
|
59
50
|
|
|
60
51
|
test('listen accepts a callback', (t, done) => {
|
|
61
52
|
t.plan(2)
|
|
62
|
-
|
|
63
|
-
t.assert.fail('should not be deprecated')
|
|
64
|
-
}
|
|
65
|
-
process.on('warning', doNotWarn)
|
|
53
|
+
assertNoWarning(t)
|
|
66
54
|
|
|
67
55
|
const fastify = Fastify()
|
|
68
|
-
|
|
69
|
-
fastify.close()
|
|
70
|
-
process.removeListener('warning', doNotWarn)
|
|
71
|
-
})
|
|
56
|
+
|
|
72
57
|
fastify.listen({ port: 0 }, (err) => {
|
|
73
58
|
t.assert.ifError(err)
|
|
74
59
|
t.assert.strictEqual(fastify.server.address().address, localhost)
|
|
75
|
-
done
|
|
60
|
+
fastify.close(done)
|
|
76
61
|
})
|
|
77
62
|
})
|
|
78
63
|
|
|
79
64
|
test('listen accepts options and a callback', (t, done) => {
|
|
80
65
|
t.plan(1)
|
|
81
|
-
|
|
82
|
-
t.assert.fail('should not be deprecated')
|
|
83
|
-
}
|
|
84
|
-
process.on('warning', doNotWarn)
|
|
66
|
+
assertNoWarning(t)
|
|
85
67
|
|
|
86
68
|
const fastify = Fastify()
|
|
87
|
-
t.after(() => {
|
|
88
|
-
fastify.close()
|
|
89
|
-
process.removeListener('warning', doNotWarn)
|
|
90
|
-
})
|
|
91
69
|
fastify.listen({
|
|
92
70
|
port: 0,
|
|
93
71
|
host: 'localhost',
|
|
@@ -98,7 +76,7 @@ test('listen accepts options and a callback', (t, done) => {
|
|
|
98
76
|
ipv6Only: false
|
|
99
77
|
}, (err) => {
|
|
100
78
|
t.assert.ifError(err)
|
|
101
|
-
done
|
|
79
|
+
fastify.close(done)
|
|
102
80
|
})
|
|
103
81
|
})
|
|
104
82
|
|
|
@@ -118,37 +96,24 @@ test('listen after Promise.resolve()', (t, done) => {
|
|
|
118
96
|
})
|
|
119
97
|
|
|
120
98
|
test('listen works with undefined host', async t => {
|
|
121
|
-
|
|
122
|
-
t.assert.fail('should not be deprecated')
|
|
123
|
-
}
|
|
124
|
-
process.on('warning', doNotWarn)
|
|
99
|
+
assertNoWarning(t)
|
|
125
100
|
|
|
126
101
|
const fastify = Fastify()
|
|
127
|
-
t.after(() => fastify.close())
|
|
128
|
-
t.after(() => {
|
|
129
|
-
fastify.close()
|
|
130
|
-
process.removeListener('warning', doNotWarn)
|
|
131
|
-
})
|
|
132
102
|
await fastify.listen({ host: undefined, port: 0 })
|
|
133
103
|
const address = fastify.server.address()
|
|
134
104
|
t.assert.strictEqual(address.address, localhost)
|
|
135
105
|
t.assert.ok(address.port > 0)
|
|
106
|
+
await fastify.close()
|
|
136
107
|
})
|
|
137
108
|
|
|
138
109
|
test('listen works with null host', async t => {
|
|
139
|
-
|
|
140
|
-
t.fail('should not be deprecated')
|
|
141
|
-
}
|
|
142
|
-
process.on('warning', doNotWarn)
|
|
110
|
+
assertNoWarning(t)
|
|
143
111
|
|
|
144
112
|
const fastify = Fastify()
|
|
145
|
-
|
|
146
|
-
t.after(() => {
|
|
147
|
-
fastify.close()
|
|
148
|
-
process.removeListener('warning', doNotWarn)
|
|
149
|
-
})
|
|
113
|
+
|
|
150
114
|
await fastify.listen({ host: null, port: 0 })
|
|
151
115
|
const address = fastify.server.address()
|
|
152
116
|
t.assert.strictEqual(address.address, localhost)
|
|
153
117
|
t.assert.ok(address.port > 0)
|
|
118
|
+
await fastify.close()
|
|
154
119
|
})
|
package/test/listen.5.test.js
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
const { test } = require('node:test')
|
|
4
4
|
const net = require('node:net')
|
|
5
|
-
const Fastify = require('../fastify')
|
|
6
5
|
const { once } = require('node:events')
|
|
6
|
+
const { spyWarning } = require('process-warning')
|
|
7
|
+
const Fastify = require('../fastify')
|
|
7
8
|
const { FSTWRN003 } = require('../lib/warnings.js')
|
|
8
9
|
|
|
9
10
|
function createDeferredPromise () {
|
|
@@ -102,21 +103,17 @@ test('same port conflict and success should not fire callback multiple times - p
|
|
|
102
103
|
test('should emit a warning when using async callback', (t, done) => {
|
|
103
104
|
t.plan(2)
|
|
104
105
|
|
|
105
|
-
|
|
106
|
-
function onWarning (warning) {
|
|
107
|
-
t.assert.strictEqual(warning.name, 'FastifyWarning')
|
|
108
|
-
t.assert.strictEqual(warning.code, FSTWRN003.code)
|
|
109
|
-
}
|
|
110
|
-
|
|
106
|
+
const spyData = spyWarning(FSTWRN003)
|
|
111
107
|
const fastify = Fastify()
|
|
112
108
|
|
|
113
109
|
t.after(async () => {
|
|
114
110
|
await fastify.close()
|
|
115
|
-
|
|
116
|
-
FSTWRN003.emitted = false
|
|
111
|
+
spyData.restore()
|
|
117
112
|
})
|
|
118
113
|
|
|
119
114
|
fastify.listen({ port: 0 }, async function doNotUseAsyncCallback () {
|
|
115
|
+
t.assert.deepStrictEqual(spyData.calls, [{ arguments: ['listen method'], result: true }])
|
|
116
|
+
t.assert.strictEqual(spyData.callCount(), 1)
|
|
120
117
|
done()
|
|
121
118
|
})
|
|
122
119
|
})
|