fastify 3.27.2 → 3.28.0
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/docs/Reference/Reply.md +49 -0
- package/fastify.js +1 -1
- package/lib/errors.js +8 -0
- package/lib/fourOhFour.js +20 -1
- package/lib/reply.js +96 -9
- package/lib/symbols.js +1 -0
- package/package.json +1 -1
- package/test/404s.test.js +52 -0
- package/test/internals/reply.test.js +16 -14
- package/test/logger.test.js +20 -0
- package/test/reply-trailers.test.js +277 -0
- package/test/types/hooks.test-d.ts +52 -2
- package/test/types/request.test-d.ts +41 -1
- package/types/.eslintrc.json +3 -1
- package/types/hooks.d.ts +85 -61
- package/types/instance.d.ts +54 -38
- package/types/request.d.ts +2 -1
- package/types/route.d.ts +35 -30
package/docs/Reference/Reply.md
CHANGED
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
- [.getHeaders()](#getheaders)
|
|
14
14
|
- [.removeHeader(key)](#removeheaderkey)
|
|
15
15
|
- [.hasHeader(key)](#hasheaderkey)
|
|
16
|
+
- [.trailer(key, function)](#trailerkey-function)
|
|
17
|
+
- [.hasTrailer(key)](#hastrailerkey)
|
|
18
|
+
- [.removeTrailer(key)](#removetrailerkey)
|
|
16
19
|
- [.redirect([code,] dest)](#redirectcode--dest)
|
|
17
20
|
- [.callNotFound()](#callnotfound)
|
|
18
21
|
- [.getResponseTime()](#getresponsetime)
|
|
@@ -47,6 +50,9 @@ object that exposes the following functions and properties:
|
|
|
47
50
|
- `.getHeaders()` - Gets a shallow copy of all current response headers.
|
|
48
51
|
- `.removeHeader(key)` - Remove the value of a previously set header.
|
|
49
52
|
- `.hasHeader(name)` - Determine if a header has been set.
|
|
53
|
+
- `.trailer(key, function)` - Sets a response trailer.
|
|
54
|
+
- `.hasTrailer(key)` - Determine if a trailer has been set.
|
|
55
|
+
- `.removeTrailer(key)` - Remove the value of a previously set trailer.
|
|
50
56
|
- `.type(value)` - Sets the header `Content-Type`.
|
|
51
57
|
- `.redirect([code,] dest)` - Redirect to the specified url, the status code is
|
|
52
58
|
optional (default to `302`).
|
|
@@ -199,6 +205,49 @@ reply.getHeader('x-foo') // undefined
|
|
|
199
205
|
|
|
200
206
|
Returns a boolean indicating if the specified header has been set.
|
|
201
207
|
|
|
208
|
+
### .trailer(key, function)
|
|
209
|
+
<a id="trailer"></a>
|
|
210
|
+
|
|
211
|
+
Sets a response trailer. Trailer usually used when you want some header that require heavy resources to be sent after the `data`, for example `Server-Timing`, `Etag`. It can ensure the client get the response data as soon as possible.
|
|
212
|
+
|
|
213
|
+
*Note: The header `Transfer-Encoding: chunked` will be added once you use the trailer. It is a hard requipment for using trailer in Node.js.*
|
|
214
|
+
|
|
215
|
+
*Note: Currently, the computation function only supports synchronous function. That means `async-await` and `promise` are not supported.*
|
|
216
|
+
|
|
217
|
+
```js
|
|
218
|
+
reply.trailer('server-timing', function() {
|
|
219
|
+
return 'db;dur=53, app;dur=47.2'
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
const { createHash } = require('crypto')
|
|
223
|
+
// trailer function also recieve two argument
|
|
224
|
+
// @param {object} reply fastify reply
|
|
225
|
+
// @param {string|Buffer|null} payload payload that already sent, note that it will be null when stream is sent
|
|
226
|
+
reply.trailer('content-md5', function(reply, payload) {
|
|
227
|
+
const hash = createHash('md5')
|
|
228
|
+
hash.update(payload)
|
|
229
|
+
return hash.disgest('hex')
|
|
230
|
+
})
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### .hasTrailer(key)
|
|
234
|
+
<a id="hasTrailer"></a>
|
|
235
|
+
|
|
236
|
+
Returns a boolean indicating if the specified trailer has been set.
|
|
237
|
+
|
|
238
|
+
### .removeTrailer(key)
|
|
239
|
+
<a id="removeTrailer"></a>
|
|
240
|
+
|
|
241
|
+
Remove the value of a previously set trailer.
|
|
242
|
+
```js
|
|
243
|
+
reply.trailer('server-timing', function() {
|
|
244
|
+
return 'db;dur=53, app;dur=47.2'
|
|
245
|
+
})
|
|
246
|
+
reply.removeTrailer('server-timing')
|
|
247
|
+
reply.getTrailer('server-timing') // undefined
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
|
|
202
251
|
### .redirect([code ,] dest)
|
|
203
252
|
<a id="redirect"></a>
|
|
204
253
|
|
package/fastify.js
CHANGED
package/lib/errors.js
CHANGED
|
@@ -147,6 +147,14 @@ const codes = {
|
|
|
147
147
|
'FST_ERR_BAD_STATUS_CODE',
|
|
148
148
|
'Called reply with an invalid status code: %s'
|
|
149
149
|
),
|
|
150
|
+
FST_ERR_BAD_TRAILER_NAME: createError(
|
|
151
|
+
'FST_ERR_BAD_TRAILER_NAME',
|
|
152
|
+
'Called reply.trailer with an invalid header name: %s'
|
|
153
|
+
),
|
|
154
|
+
FST_ERR_BAD_TRAILER_VALUE: createError(
|
|
155
|
+
'FST_ERR_BAD_TRAILER_VALUE',
|
|
156
|
+
"Called reply.trailer('%s', fn) with an invalid type: %s. Expected a function."
|
|
157
|
+
),
|
|
150
158
|
|
|
151
159
|
/**
|
|
152
160
|
* schemas
|
package/lib/fourOhFour.js
CHANGED
|
@@ -37,7 +37,8 @@ function fourOhFour (options) {
|
|
|
37
37
|
const { logger, genReqId } = options
|
|
38
38
|
|
|
39
39
|
// 404 router, used for handling encapsulated 404 handlers
|
|
40
|
-
const router = FindMyWay({ defaultRoute: fourOhFourFallBack })
|
|
40
|
+
const router = FindMyWay({ onBadUrl: createOnBadUrl(), defaultRoute: fourOhFourFallBack })
|
|
41
|
+
let _onBadUrlHandler = null
|
|
41
42
|
|
|
42
43
|
return { router, setNotFoundHandler, setContext, arrange404 }
|
|
43
44
|
|
|
@@ -45,6 +46,8 @@ function fourOhFour (options) {
|
|
|
45
46
|
// Change the pointer of the fastify instance to itself, so register + prefix can add new 404 handler
|
|
46
47
|
instance[kFourOhFourLevelInstance] = instance
|
|
47
48
|
instance[kCanSetNotFoundHandler] = true
|
|
49
|
+
// we need to bind instance for the context
|
|
50
|
+
router.onBadUrl = router.onBadUrl.bind(instance)
|
|
48
51
|
}
|
|
49
52
|
|
|
50
53
|
function basic404 (request, reply) {
|
|
@@ -58,6 +61,18 @@ function fourOhFour (options) {
|
|
|
58
61
|
})
|
|
59
62
|
}
|
|
60
63
|
|
|
64
|
+
function createOnBadUrl () {
|
|
65
|
+
return function onBadUrl (path, req, res) {
|
|
66
|
+
const id = genReqId(req)
|
|
67
|
+
const childLogger = logger.child({ reqId: id })
|
|
68
|
+
const fourOhFourContext = this[kFourOhFourLevelInstance][kFourOhFourContext]
|
|
69
|
+
const request = new Request(id, null, req, null, childLogger, fourOhFourContext)
|
|
70
|
+
const reply = new Reply(res, request, childLogger)
|
|
71
|
+
|
|
72
|
+
_onBadUrlHandler(request, reply)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
61
76
|
function setContext (instance, context) {
|
|
62
77
|
const _404Context = Object.assign({}, instance[kFourOhFourContext])
|
|
63
78
|
_404Context.onSend = context.onSend
|
|
@@ -107,8 +122,12 @@ function fourOhFour (options) {
|
|
|
107
122
|
if (handler) {
|
|
108
123
|
this[kFourOhFourLevelInstance][kCanSetNotFoundHandler] = false
|
|
109
124
|
handler = handler.bind(this)
|
|
125
|
+
// update onBadUrl handler
|
|
126
|
+
_onBadUrlHandler = handler
|
|
110
127
|
} else {
|
|
111
128
|
handler = basic404
|
|
129
|
+
// update onBadUrl handler
|
|
130
|
+
_onBadUrlHandler = basic404
|
|
112
131
|
}
|
|
113
132
|
|
|
114
133
|
this.after((notHandledErr, done) => {
|
package/lib/reply.js
CHANGED
|
@@ -16,6 +16,7 @@ const {
|
|
|
16
16
|
kReplySerializerDefault,
|
|
17
17
|
kReplyIsError,
|
|
18
18
|
kReplyHeaders,
|
|
19
|
+
kReplyTrailers,
|
|
19
20
|
kReplyHasStatusCode,
|
|
20
21
|
kReplyIsRunningOnErrorHook,
|
|
21
22
|
kDisableRequestLogging
|
|
@@ -47,7 +48,9 @@ const {
|
|
|
47
48
|
FST_ERR_REP_ALREADY_SENT,
|
|
48
49
|
FST_ERR_REP_SENT_VALUE,
|
|
49
50
|
FST_ERR_SEND_INSIDE_ONERR,
|
|
50
|
-
FST_ERR_BAD_STATUS_CODE
|
|
51
|
+
FST_ERR_BAD_STATUS_CODE,
|
|
52
|
+
FST_ERR_BAD_TRAILER_NAME,
|
|
53
|
+
FST_ERR_BAD_TRAILER_VALUE
|
|
51
54
|
} = require('./errors')
|
|
52
55
|
const warning = require('./warnings')
|
|
53
56
|
|
|
@@ -60,6 +63,7 @@ function Reply (res, request, log) {
|
|
|
60
63
|
this[kReplyIsRunningOnErrorHook] = false
|
|
61
64
|
this.request = request
|
|
62
65
|
this[kReplyHeaders] = {}
|
|
66
|
+
this[kReplyTrailers] = null
|
|
63
67
|
this[kReplyHasStatusCode] = false
|
|
64
68
|
this[kReplyStartTime] = undefined
|
|
65
69
|
this.log = log
|
|
@@ -261,6 +265,47 @@ Reply.prototype.headers = function (headers) {
|
|
|
261
265
|
return this
|
|
262
266
|
}
|
|
263
267
|
|
|
268
|
+
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Trailer#directives
|
|
269
|
+
// https://httpwg.org/specs/rfc7230.html#chunked.trailer.part
|
|
270
|
+
const INVALID_TRAILERS = new Set([
|
|
271
|
+
'transfer-encoding',
|
|
272
|
+
'content-length',
|
|
273
|
+
'host',
|
|
274
|
+
'cache-control',
|
|
275
|
+
'max-forwards',
|
|
276
|
+
'te',
|
|
277
|
+
'authorization',
|
|
278
|
+
'set-cookie',
|
|
279
|
+
'content-encoding',
|
|
280
|
+
'content-type',
|
|
281
|
+
'content-range',
|
|
282
|
+
'trailer'
|
|
283
|
+
])
|
|
284
|
+
|
|
285
|
+
Reply.prototype.trailer = function (key, fn) {
|
|
286
|
+
key = key.toLowerCase()
|
|
287
|
+
if (INVALID_TRAILERS.has(key)) {
|
|
288
|
+
throw new FST_ERR_BAD_TRAILER_NAME(key)
|
|
289
|
+
}
|
|
290
|
+
if (typeof fn !== 'function') {
|
|
291
|
+
throw new FST_ERR_BAD_TRAILER_VALUE(key, typeof fn)
|
|
292
|
+
}
|
|
293
|
+
if (this[kReplyTrailers] === null) this[kReplyTrailers] = {}
|
|
294
|
+
this[kReplyTrailers][key] = fn
|
|
295
|
+
return this
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
Reply.prototype.hasTrailer = function (key) {
|
|
299
|
+
if (this[kReplyTrailers] === null) return false
|
|
300
|
+
return this[kReplyTrailers][key.toLowerCase()] !== undefined
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
Reply.prototype.removeTrailer = function (key) {
|
|
304
|
+
if (this[kReplyTrailers] === null) return this
|
|
305
|
+
this[kReplyTrailers][key.toLowerCase()] = undefined
|
|
306
|
+
return this
|
|
307
|
+
}
|
|
308
|
+
|
|
264
309
|
Reply.prototype.code = function (code) {
|
|
265
310
|
const intValue = parseInt(code)
|
|
266
311
|
if (isNaN(intValue) || intValue < 100 || intValue > 600) {
|
|
@@ -416,18 +461,35 @@ function onSendEnd (reply, payload) {
|
|
|
416
461
|
const req = reply.request
|
|
417
462
|
const statusCode = res.statusCode
|
|
418
463
|
|
|
464
|
+
// we check if we need to update the trailers header and set it
|
|
465
|
+
if (reply[kReplyTrailers] !== null) {
|
|
466
|
+
const trailerHeaders = Object.keys(reply[kReplyTrailers])
|
|
467
|
+
let header = ''
|
|
468
|
+
for (const trailerName of trailerHeaders) {
|
|
469
|
+
if (typeof reply[kReplyTrailers][trailerName] !== 'function') continue
|
|
470
|
+
header += ' '
|
|
471
|
+
header += trailerName
|
|
472
|
+
}
|
|
473
|
+
// it must be chunked for trailer to work
|
|
474
|
+
reply.header('Transfer-Encoding', 'chunked')
|
|
475
|
+
reply.header('Trailer', header.trim())
|
|
476
|
+
}
|
|
477
|
+
|
|
419
478
|
if (payload === undefined || payload === null) {
|
|
420
479
|
reply[kReplySent] = true
|
|
421
480
|
|
|
422
481
|
// according to https://tools.ietf.org/html/rfc7230#section-3.3.2
|
|
423
482
|
// we cannot send a content-length for 304 and 204, and all status code
|
|
424
|
-
// < 200
|
|
483
|
+
// < 200
|
|
484
|
+
// A sender MUST NOT send a Content-Length header field in any message
|
|
485
|
+
// that contains a Transfer-Encoding header field.
|
|
425
486
|
// For HEAD we don't overwrite the `content-length`
|
|
426
|
-
if (statusCode >= 200 && statusCode !== 204 && statusCode !== 304 && req.method !== 'HEAD') {
|
|
487
|
+
if (statusCode >= 200 && statusCode !== 204 && statusCode !== 304 && req.method !== 'HEAD' && reply[kReplyTrailers] === null) {
|
|
427
488
|
reply[kReplyHeaders]['content-length'] = '0'
|
|
428
489
|
}
|
|
429
490
|
|
|
430
491
|
res.writeHead(statusCode, reply[kReplyHeaders])
|
|
492
|
+
sendTrailer(payload, res, reply)
|
|
431
493
|
// avoid ArgumentsAdaptorTrampoline from V8
|
|
432
494
|
res.end(null, null, null)
|
|
433
495
|
return
|
|
@@ -444,18 +506,23 @@ function onSendEnd (reply, payload) {
|
|
|
444
506
|
throw new FST_ERR_REP_INVALID_PAYLOAD_TYPE(typeof payload)
|
|
445
507
|
}
|
|
446
508
|
|
|
447
|
-
if (
|
|
448
|
-
reply[kReplyHeaders]['content-length']
|
|
449
|
-
|
|
450
|
-
reply[kReplyHeaders]['content-length']
|
|
509
|
+
if (reply[kReplyTrailers] === null) {
|
|
510
|
+
if (!reply[kReplyHeaders]['content-length']) {
|
|
511
|
+
reply[kReplyHeaders]['content-length'] = '' + Buffer.byteLength(payload)
|
|
512
|
+
} else if (req.raw.method !== 'HEAD' && reply[kReplyHeaders]['content-length'] !== Buffer.byteLength(payload)) {
|
|
513
|
+
reply[kReplyHeaders]['content-length'] = '' + Buffer.byteLength(payload)
|
|
514
|
+
}
|
|
451
515
|
}
|
|
452
516
|
|
|
453
517
|
reply[kReplySent] = true
|
|
454
518
|
|
|
455
519
|
res.writeHead(statusCode, reply[kReplyHeaders])
|
|
456
|
-
|
|
520
|
+
// write payload first
|
|
521
|
+
res.write(payload)
|
|
522
|
+
// then send trailers
|
|
523
|
+
sendTrailer(payload, res, reply)
|
|
457
524
|
// avoid ArgumentsAdaptorTrampoline from V8
|
|
458
|
-
res.end(
|
|
525
|
+
res.end(null, null, null)
|
|
459
526
|
}
|
|
460
527
|
|
|
461
528
|
function logStreamError (logger, err, res) {
|
|
@@ -472,6 +539,9 @@ function sendStream (payload, res, reply) {
|
|
|
472
539
|
let sourceOpen = true
|
|
473
540
|
let errorLogged = false
|
|
474
541
|
|
|
542
|
+
// set trailer when stream ended
|
|
543
|
+
sendStreamTrailer(payload, res, reply)
|
|
544
|
+
|
|
475
545
|
eos(payload, { readable: true, writable: false }, function (err) {
|
|
476
546
|
sourceOpen = false
|
|
477
547
|
if (err != null) {
|
|
@@ -520,6 +590,22 @@ function sendStream (payload, res, reply) {
|
|
|
520
590
|
payload.pipe(res)
|
|
521
591
|
}
|
|
522
592
|
|
|
593
|
+
function sendTrailer (payload, res, reply) {
|
|
594
|
+
if (reply[kReplyTrailers] === null) return
|
|
595
|
+
const trailerHeaders = Object.keys(reply[kReplyTrailers])
|
|
596
|
+
const trailers = {}
|
|
597
|
+
for (const trailerName of trailerHeaders) {
|
|
598
|
+
if (typeof reply[kReplyTrailers][trailerName] !== 'function') continue
|
|
599
|
+
trailers[trailerName] = reply[kReplyTrailers][trailerName](reply, payload)
|
|
600
|
+
}
|
|
601
|
+
res.addTrailers(trailers)
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
function sendStreamTrailer (payload, res, reply) {
|
|
605
|
+
if (reply[kReplyTrailers] === null) return
|
|
606
|
+
payload.on('end', () => sendTrailer(null, res, reply))
|
|
607
|
+
}
|
|
608
|
+
|
|
523
609
|
function onErrorHook (reply, error, cb) {
|
|
524
610
|
reply[kReplySent] = true
|
|
525
611
|
if (reply.context.onError !== null && reply[kReplyErrorHandlerCalled] === true) {
|
|
@@ -684,6 +770,7 @@ function buildReply (R) {
|
|
|
684
770
|
this[kReplySerializer] = null
|
|
685
771
|
this.request = request
|
|
686
772
|
this[kReplyHeaders] = {}
|
|
773
|
+
this[kReplyTrailers] = null
|
|
687
774
|
this[kReplyStartTime] = undefined
|
|
688
775
|
this[kReplyEndTime] = undefined
|
|
689
776
|
this.log = log
|
package/lib/symbols.js
CHANGED
|
@@ -30,6 +30,7 @@ const keys = {
|
|
|
30
30
|
kReplySerializer: Symbol('fastify.reply.serializer'),
|
|
31
31
|
kReplyIsError: Symbol('fastify.reply.isError'),
|
|
32
32
|
kReplyHeaders: Symbol('fastify.reply.headers'),
|
|
33
|
+
kReplyTrailers: Symbol('fastify.reply.trailers'),
|
|
33
34
|
kReplyHasStatusCode: Symbol('fastify.reply.hasStatusCode'),
|
|
34
35
|
kReplySent: Symbol('fastify.reply.sent'),
|
|
35
36
|
kReplySentOverwritten: Symbol('fastify.reply.sentOverwritten'),
|
package/package.json
CHANGED
package/test/404s.test.js
CHANGED
|
@@ -1738,6 +1738,58 @@ test('400 in case of bad url (pre find-my-way v2.2.0 was a 404)', t => {
|
|
|
1738
1738
|
})
|
|
1739
1739
|
})
|
|
1740
1740
|
|
|
1741
|
+
t.test('No route registered', t => {
|
|
1742
|
+
t.plan(3)
|
|
1743
|
+
const fastify = Fastify()
|
|
1744
|
+
fastify.inject({
|
|
1745
|
+
url: '/%c0',
|
|
1746
|
+
method: 'GET'
|
|
1747
|
+
}, (err, response) => {
|
|
1748
|
+
t.error(err)
|
|
1749
|
+
t.equal(response.statusCode, 404)
|
|
1750
|
+
t.same(JSON.parse(response.payload), {
|
|
1751
|
+
error: 'Not Found',
|
|
1752
|
+
message: 'Route GET:/%c0 not found',
|
|
1753
|
+
statusCode: 404
|
|
1754
|
+
})
|
|
1755
|
+
})
|
|
1756
|
+
})
|
|
1757
|
+
|
|
1758
|
+
t.test('Only / is registered', t => {
|
|
1759
|
+
t.plan(3)
|
|
1760
|
+
const fastify = Fastify({ logger: true })
|
|
1761
|
+
fastify.get('/', () => t.fail('we should not be here'))
|
|
1762
|
+
fastify.inject({
|
|
1763
|
+
url: '/%c0',
|
|
1764
|
+
method: 'GET'
|
|
1765
|
+
}, (err, response) => {
|
|
1766
|
+
t.error(err)
|
|
1767
|
+
t.equal(response.statusCode, 404)
|
|
1768
|
+
t.same(JSON.parse(response.payload), {
|
|
1769
|
+
error: 'Not Found',
|
|
1770
|
+
message: 'Route GET:/%c0 not found',
|
|
1771
|
+
statusCode: 404
|
|
1772
|
+
})
|
|
1773
|
+
})
|
|
1774
|
+
})
|
|
1775
|
+
|
|
1776
|
+
t.test('customized 404', t => {
|
|
1777
|
+
t.plan(3)
|
|
1778
|
+
const fastify = Fastify({ logger: true })
|
|
1779
|
+
fastify.get('/', () => t.fail('we should not be here'))
|
|
1780
|
+
fastify.setNotFoundHandler(function (req, reply) {
|
|
1781
|
+
reply.code(404).send('this was not found')
|
|
1782
|
+
})
|
|
1783
|
+
fastify.inject({
|
|
1784
|
+
url: '/%c0',
|
|
1785
|
+
method: 'GET'
|
|
1786
|
+
}, (err, response) => {
|
|
1787
|
+
t.error(err)
|
|
1788
|
+
t.equal(response.statusCode, 404)
|
|
1789
|
+
t.same(response.payload, 'this was not found')
|
|
1790
|
+
})
|
|
1791
|
+
})
|
|
1792
|
+
|
|
1741
1793
|
t.end()
|
|
1742
1794
|
})
|
|
1743
1795
|
|
|
@@ -5,9 +5,8 @@ const test = t.test
|
|
|
5
5
|
const sget = require('simple-get').concat
|
|
6
6
|
const http = require('http')
|
|
7
7
|
const NotFound = require('http-errors').NotFound
|
|
8
|
-
const EventEmitter = require('events').EventEmitter
|
|
9
8
|
const Reply = require('../../lib/reply')
|
|
10
|
-
const { Writable } = require('stream')
|
|
9
|
+
const { Readable, Writable } = require('stream')
|
|
11
10
|
const {
|
|
12
11
|
kReplyErrorHandlerCalled,
|
|
13
12
|
kReplyHeaders,
|
|
@@ -40,30 +39,31 @@ test('Once called, Reply should return an object with methods', t => {
|
|
|
40
39
|
test('reply.send will logStream error and destroy the stream', { only: true }, t => {
|
|
41
40
|
t.plan(1)
|
|
42
41
|
let destroyCalled
|
|
43
|
-
const payload = new
|
|
42
|
+
const payload = new Readable({
|
|
43
|
+
read () {},
|
|
44
|
+
destroy (err, cb) {
|
|
45
|
+
destroyCalled = true
|
|
46
|
+
cb(err)
|
|
47
|
+
}
|
|
48
|
+
})
|
|
44
49
|
|
|
45
|
-
const response =
|
|
50
|
+
const response = new Writable()
|
|
51
|
+
Object.assign(response, {
|
|
46
52
|
setHeader: () => {},
|
|
47
53
|
hasHeader: () => false,
|
|
48
54
|
getHeader: () => undefined,
|
|
49
55
|
writeHead: () => {},
|
|
50
|
-
|
|
51
|
-
headersSent: true
|
|
52
|
-
|
|
53
|
-
on: () => {}
|
|
54
|
-
}
|
|
56
|
+
write: () => {},
|
|
57
|
+
headersSent: true
|
|
58
|
+
})
|
|
55
59
|
|
|
56
60
|
const log = {
|
|
57
61
|
warn: () => {}
|
|
58
62
|
}
|
|
59
63
|
|
|
60
|
-
Object.assign(payload, {
|
|
61
|
-
pipe: () => {},
|
|
62
|
-
destroy: () => {}
|
|
63
|
-
})
|
|
64
64
|
const reply = new Reply(response, { context: { onSend: null } }, log)
|
|
65
65
|
reply.send(payload)
|
|
66
|
-
payload.
|
|
66
|
+
payload.destroy(new Error('stream error'))
|
|
67
67
|
|
|
68
68
|
t.equal(destroyCalled, true, 'Error not logged and not streamed')
|
|
69
69
|
})
|
|
@@ -75,6 +75,7 @@ test('reply.send throw with circular JSON', t => {
|
|
|
75
75
|
hasHeader: () => false,
|
|
76
76
|
getHeader: () => undefined,
|
|
77
77
|
writeHead: () => {},
|
|
78
|
+
write: () => {},
|
|
78
79
|
end: () => {}
|
|
79
80
|
}
|
|
80
81
|
const reply = new Reply(response, { context: { onSend: [] } })
|
|
@@ -92,6 +93,7 @@ test('reply.send returns itself', t => {
|
|
|
92
93
|
hasHeader: () => false,
|
|
93
94
|
getHeader: () => undefined,
|
|
94
95
|
writeHead: () => {},
|
|
96
|
+
write: () => {},
|
|
95
97
|
end: () => {}
|
|
96
98
|
}
|
|
97
99
|
const reply = new Reply(response, { context: { onSend: [] } })
|
package/test/logger.test.js
CHANGED
|
@@ -1481,6 +1481,26 @@ test('should not log incoming request and outgoing response when disabled', t =>
|
|
|
1481
1481
|
})
|
|
1482
1482
|
})
|
|
1483
1483
|
|
|
1484
|
+
test('should not log incoming request and outgoing response for 404 onBadUrl when disabled', t => {
|
|
1485
|
+
t.plan(1)
|
|
1486
|
+
const lines = []
|
|
1487
|
+
const dest = new stream.Writable({
|
|
1488
|
+
write: function (chunk, enc, cb) {
|
|
1489
|
+
lines.push(JSON.parse(chunk))
|
|
1490
|
+
cb()
|
|
1491
|
+
}
|
|
1492
|
+
})
|
|
1493
|
+
const fastify = Fastify({ disableRequestLogging: true, logger: { level: 'info', stream: dest } })
|
|
1494
|
+
|
|
1495
|
+
fastify.inject({
|
|
1496
|
+
url: '/%c0',
|
|
1497
|
+
method: 'GET'
|
|
1498
|
+
}, (e, res) => {
|
|
1499
|
+
// it will log 1 line only because of basic404
|
|
1500
|
+
t.same(lines.length, 1)
|
|
1501
|
+
})
|
|
1502
|
+
})
|
|
1503
|
+
|
|
1484
1504
|
test('should pass when using unWritable props in the logger option', t => {
|
|
1485
1505
|
t.plan(1)
|
|
1486
1506
|
Fastify({
|