fastify 2.12.1 → 2.14.1

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.
@@ -552,9 +552,9 @@ fastify.route({
552
552
  method: 'POST',
553
553
  url: '/',
554
554
  schema: {
555
- body: ajv.getSchema('urn:schema:request').schema,
555
+ body: { $ref: 'urn:schema:request#' },
556
556
  response: {
557
- '2xx': ajv.getSchema('urn:schema:response').schema
557
+ '2xx': { $ref: 'urn:schema:response#' }
558
558
  }
559
559
  },
560
560
  handler (req, reply) {
@@ -658,7 +658,91 @@ fastify.setErrorHandler(function (error, request, reply) {
658
658
  })
659
659
  ```
660
660
 
661
- If you want custom error response in schema without headaches and quickly, you can take a look at [here](https://github.com/epoberezkin/ajv-errors)
661
+ If you want custom error response in schema without headaches and quickly, you can take a look at [`ajv-errors`](https://github.com/epoberezkin/ajv-errors). Checkout the [example](https://github.com/fastify/example/blob/master/validation-messages/custom-errors-messages.js) usage.
662
+
663
+ Below is an example showing how to add **custom error messages for each property** of a schema by supplying custom AJV options.
664
+ Inline comments in the schema below describe how to configure it to show a different error message for each case:
665
+
666
+ ```js
667
+ const fastify = Fastify({
668
+ ajv: {
669
+ customOptions: { allErrors: true, jsonPointers: true },
670
+ plugins: [
671
+ require('ajv-errors')
672
+ ]
673
+ }
674
+ })
675
+
676
+ const schema = {
677
+ body: {
678
+ type: 'object',
679
+ properties: {
680
+ name: {
681
+ type: 'string',
682
+ errorMessage: {
683
+ type: 'Bad name'
684
+ }
685
+ },
686
+ age: {
687
+ type: 'number',
688
+ errorMessage: {
689
+ type: 'Bad age', // specify custom message for
690
+ min: 'Too young' // all constraints except required
691
+ }
692
+ }
693
+ },
694
+ required: ['name', 'age'],
695
+ errorMessage: {
696
+ required: {
697
+ name: 'Why no name!', // specify error message for when the
698
+ age: 'Why no age!' // property is missing from input
699
+ }
700
+ }
701
+ }
702
+ }
703
+
704
+ fastify.post('/', { schema, }, (request, reply) => {
705
+ reply.send({
706
+ hello: 'world'
707
+ })
708
+ })
709
+ ```
710
+
711
+ If you want to return localized error messages, take a look at [ajv-i18n](https://github.com/epoberezkin/ajv-i18n)
712
+
713
+ ```js
714
+ const localize = require('ajv-i18n')
715
+
716
+ const fastify = Fastify({
717
+ ajv: {
718
+ customOptions: { allErrors: true }
719
+ }
720
+ })
721
+
722
+ const schema = {
723
+ body: {
724
+ type: 'object',
725
+ properties: {
726
+ name: {
727
+ type: 'string',
728
+ },
729
+ age: {
730
+ type: 'number',
731
+ }
732
+ },
733
+ required: ['name', 'age'],
734
+ }
735
+ }
736
+
737
+ fastify.setErrorHandler(function (error, request, reply) {
738
+ if (error.validation) {
739
+ localize.ru(error.validation)
740
+ reply.status(400).send(error.validation)
741
+ return
742
+ }
743
+ reply.send(error)
744
+ })
745
+ ```
662
746
 
663
747
  ### JSON Schema and Shared Schema support
664
748
 
package/fastify.d.ts CHANGED
@@ -8,6 +8,7 @@ import * as ajv from 'ajv'
8
8
  import * as http from 'http'
9
9
  import * as http2 from 'http2'
10
10
  import * as https from 'https'
11
+ import * as LightMyRequest from 'light-my-request'
11
12
 
12
13
  declare function fastify<
13
14
  HttpServer extends (http.Server | http2.Http2Server) = http.Server,
@@ -36,6 +37,10 @@ declare namespace fastify {
36
37
 
37
38
  type HTTPMethod = 'DELETE' | 'GET' | 'HEAD' | 'PATCH' | 'POST' | 'PUT' | 'OPTIONS'
38
39
 
40
+ // // Keep the original name of the interfaces to avoid braking change
41
+ interface HTTPInjectOptions extends LightMyRequest.InjectOptions {}
42
+ interface HTTPInjectResponse extends LightMyRequest.Response {}
43
+
39
44
  interface ValidationResult {
40
45
  keyword: string;
41
46
  dataPath: string;
@@ -317,42 +322,6 @@ declare namespace fastify {
317
322
  logSerializers?: Object
318
323
  }
319
324
 
320
- /**
321
- * Fake http inject options
322
- */
323
- interface HTTPInjectOptions {
324
- url: string,
325
- method?: HTTPMethod,
326
- authority?: string,
327
- headers?: DefaultHeaders,
328
- query?: DefaultQuery,
329
- remoteAddress?: string,
330
- payload?: string | object | Buffer | NodeJS.ReadableStream
331
- simulate?: {
332
- end?: boolean,
333
- split?: boolean,
334
- error?: boolean,
335
- close?: boolean
336
- },
337
- validate?: boolean
338
- }
339
-
340
- /**
341
- * Fake http inject response
342
- */
343
- interface HTTPInjectResponse {
344
- raw: {
345
- req: NodeJS.ReadableStream,
346
- res: http.ServerResponse
347
- },
348
- headers: Record<string, string>,
349
- statusCode: number,
350
- statusMessage: string,
351
- payload: string,
352
- rawPayload: Buffer,
353
- trailers: object
354
- }
355
-
356
325
  /**
357
326
  * Server listen options
358
327
  */
@@ -685,6 +654,11 @@ declare namespace fastify {
685
654
  */
686
655
  inject(opts: HTTPInjectOptions | string): Promise<HTTPInjectResponse>
687
656
 
657
+ /**
658
+ * Useful for testing http requests without running a sever
659
+ */
660
+ inject(): LightMyRequest.Chain;
661
+
688
662
  /**
689
663
  * Set the 404 handler
690
664
  */
package/fastify.js CHANGED
@@ -42,6 +42,11 @@ const { buildRouting, validateBodyLimitOption } = require('./lib/route')
42
42
  const build404 = require('./lib/fourOhFour')
43
43
  const getSecuredInitialConfig = require('./lib/initialConfigValidation')
44
44
  const { defaultInitOptions } = getSecuredInitialConfig
45
+ const {
46
+ codes: {
47
+ FST_ERR_BAD_URL
48
+ }
49
+ } = require('./lib/errors')
45
50
 
46
51
  function build (options) {
47
52
  // Options validations
@@ -73,6 +78,7 @@ function build (options) {
73
78
  customOptions: {},
74
79
  plugins: []
75
80
  }, options.ajv)
81
+ const frameworkErrors = options.frameworkErrors
76
82
 
77
83
  // Ajv options
78
84
  if (!ajvOptions.customOptions || Object.prototype.toString.call(ajvOptions.customOptions) !== '[object Object]') {
@@ -417,7 +423,11 @@ function build (options) {
417
423
  message: 'Client Error',
418
424
  statusCode: 400
419
425
  })
420
- logger.debug({ err }, 'client error')
426
+
427
+ // Most devs do not know what to do with this error.
428
+ // In the vast majority of cases, it's a network error and/or some
429
+ // config issue on the the load balancer side.
430
+ logger.trace({ err }, 'client error')
421
431
  socket.end(`HTTP/1.1 400 Bad Request\r\nContent-Length: ${body.length}\r\nContent-Type: application/json\r\n\r\n${body}`)
422
432
  }
423
433
 
@@ -431,6 +441,20 @@ function build (options) {
431
441
  }
432
442
 
433
443
  function onBadUrl (path, req, res) {
444
+ if (frameworkErrors) {
445
+ req.id = genReqId(req)
446
+ req.originalUrl = req.url
447
+ var childLogger = logger.child({ reqId: req.id })
448
+ if (modifyCoreObjects) {
449
+ req.log = res.log = childLogger
450
+ }
451
+
452
+ childLogger.info({ req }, 'incoming request')
453
+
454
+ const request = new Request(null, req, null, req.headers, childLogger)
455
+ const reply = new Reply(res, { onSend: [], onError: [] }, request, childLogger)
456
+ return frameworkErrors(new FST_ERR_BAD_URL(path), request, reply)
457
+ }
434
458
  const body = `{"error":"Bad Request","message":"'${path}' is not a valid url component","statusCode":400}`
435
459
  res.writeHead(400, {
436
460
  'Content-Type': 'application/json',
package/lib/decorate.js CHANGED
@@ -4,13 +4,15 @@
4
4
 
5
5
  const {
6
6
  kReply,
7
- kRequest
7
+ kRequest,
8
+ kState
8
9
  } = require('./symbols.js')
9
10
 
10
11
  const {
11
12
  codes: {
12
13
  FST_ERR_DEC_ALREADY_PRESENT,
13
- FST_ERR_DEC_MISSING_DEPENDENCY
14
+ FST_ERR_DEC_MISSING_DEPENDENCY,
15
+ FST_ERR_DEC_AFTER_START
14
16
  }
15
17
  } = require('./errors')
16
18
 
@@ -34,6 +36,7 @@ function decorate (instance, name, fn, dependencies) {
34
36
  }
35
37
 
36
38
  function decorateFastify (name, fn, dependencies) {
39
+ assertNotStarted(this, name)
37
40
  decorate(this, name, fn, dependencies)
38
41
  return this
39
42
  }
@@ -63,15 +66,23 @@ function checkDependencies (instance, deps) {
63
66
  }
64
67
 
65
68
  function decorateReply (name, fn, dependencies) {
69
+ assertNotStarted(this, name)
66
70
  decorate(this[kReply].prototype, name, fn, dependencies)
67
71
  return this
68
72
  }
69
73
 
70
74
  function decorateRequest (name, fn, dependencies) {
75
+ assertNotStarted(this, name)
71
76
  decorate(this[kRequest].prototype, name, fn, dependencies)
72
77
  return this
73
78
  }
74
79
 
80
+ function assertNotStarted (instance, name) {
81
+ if (instance[kState].started) {
82
+ throw new FST_ERR_DEC_AFTER_START(name)
83
+ }
84
+ }
85
+
75
86
  module.exports = {
76
87
  add: decorateFastify,
77
88
  exist: checkExistence,
package/lib/errors.js CHANGED
@@ -27,6 +27,7 @@ createError('FST_ERR_CTP_EMPTY_JSON_BODY', "Body cannot be empty when content-ty
27
27
  */
28
28
  createError('FST_ERR_DEC_ALREADY_PRESENT', "The decorator '%s' has already been added!")
29
29
  createError('FST_ERR_DEC_MISSING_DEPENDENCY', "The decorator is missing dependency '%s'.")
30
+ createError('FST_ERR_DEC_AFTER_START', "The decorator '%s' has been added after start!")
30
31
 
31
32
  /**
32
33
  * hooks
@@ -47,7 +48,7 @@ createError('FST_ERR_REP_ALREADY_SENT', 'Reply was already sent.')
47
48
  createError('FST_ERR_REP_SENT_VALUE', 'The only possible value for reply.sent is true.')
48
49
  createError('FST_ERR_SEND_INSIDE_ONERR', 'You cannot use `send` inside the `onError` hook')
49
50
  createError('FST_ERR_SEND_UNDEFINED_ERR', 'Undefined error has occured')
50
- createError('FST_ERR_BAD_STATUS_CODE', 'Called reply with malformed status code')
51
+ createError('FST_ERR_BAD_STATUS_CODE', 'Called reply with an invalid status code: %s')
51
52
 
52
53
  /**
53
54
  * schemas
@@ -74,6 +75,11 @@ createError('FST_ERR_HTTP2_INVALID_VERSION', 'HTTP2 is available only from node
74
75
  */
75
76
  createError('FST_ERR_INIT_OPTS_INVALID', "Invalid initialization options: '%s'")
76
77
 
78
+ /**
79
+ * router
80
+ */
81
+ createError('FST_ERR_BAD_URL', "'%s' is not a valid url component", 400)
82
+
77
83
  function createError (code, message, statusCode = 500, Base = Error) {
78
84
  if (!code) throw new Error('Fastify error code must not be empty')
79
85
  if (!message) throw new Error('Fastify error message must not be empty')
@@ -71,7 +71,10 @@ function handler (request, reply) {
71
71
  }
72
72
 
73
73
  function preValidationCallback (err, request, reply) {
74
- if (reply.sent === true || reply.res.finished === true) return
74
+ if (reply.sent === true ||
75
+ reply.res.writableEnded === true ||
76
+ (reply.res.writable === false && reply.res.finished === true)) return
77
+
75
78
  if (err != null) {
76
79
  reply.send(err)
77
80
  return
@@ -102,7 +105,10 @@ function preValidationCallback (err, request, reply) {
102
105
  }
103
106
 
104
107
  function preHandlerCallback (err, request, reply) {
105
- if (reply.sent || reply.res.finished === true) return
108
+ if (reply.sent ||
109
+ reply.res.writableEnded === true ||
110
+ (reply.res.writable === false && reply.res.finished === true)) return
111
+
106
112
  if (err != null) {
107
113
  reply.send(err)
108
114
  return
@@ -115,4 +121,4 @@ function preHandlerCallback (err, request, reply) {
115
121
  }
116
122
 
117
123
  module.exports = handleRequest
118
- module.exports[Symbol.for('internals')] = { handler }
124
+ module.exports[Symbol.for('internals')] = { handler, preHandlerCallback }
package/lib/reply.js CHANGED
@@ -18,10 +18,11 @@ const {
18
18
  kReplyIsRunningOnErrorHook,
19
19
  kDisableRequestLogging
20
20
  } = require('./symbols.js')
21
- const { hookRunner, onSendHookRunner } = require('./hooks')
21
+ const { hookRunner, hookIterator, onSendHookRunner } = require('./hooks')
22
22
  const validation = require('./validation')
23
23
  const serialize = validation.serialize
24
24
 
25
+ const internals = require('./handleRequest')[Symbol.for('internals')]
25
26
  const loggerUtils = require('./logger')
26
27
  const now = loggerUtils.now
27
28
  const wrapThenable = require('./wrapThenable')
@@ -198,11 +199,12 @@ Reply.prototype.headers = function (headers) {
198
199
  }
199
200
 
200
201
  Reply.prototype.code = function (code) {
201
- if (statusCodes[code] === undefined) {
202
- throw new FST_ERR_BAD_STATUS_CODE()
202
+ const intValue = parseInt(code)
203
+ if (isNaN(intValue) || intValue < 100 || intValue > 600) {
204
+ throw new FST_ERR_BAD_STATUS_CODE(String(code))
203
205
  }
204
206
 
205
- this.res.statusCode = code
207
+ this.res.statusCode = intValue
206
208
  this[kReplyHasStatusCode] = true
207
209
  return this
208
210
  }
@@ -475,13 +477,13 @@ function handleError (reply, error, cb) {
475
477
  })
476
478
  flatstr(payload)
477
479
  reply[kReplyHeaders]['content-type'] = CONTENT_TYPE.JSON
480
+ reply[kReplyHeaders]['content-length'] = '' + Buffer.byteLength(payload)
478
481
 
479
482
  if (cb) {
480
483
  cb(reply, payload)
481
484
  return
482
485
  }
483
486
 
484
- reply[kReplyHeaders]['content-length'] = '' + Buffer.byteLength(payload)
485
487
  reply[kReplySent] = true
486
488
  res.writeHead(res.statusCode, reply[kReplyHeaders])
487
489
  res.end(payload)
@@ -568,7 +570,19 @@ function notFound (reply) {
568
570
  }
569
571
 
570
572
  reply.context = reply.context[kFourOhFourContext]
571
- reply.context.handler(reply.request, reply)
573
+
574
+ // preHandler hook
575
+ if (reply.context.preHandler !== null) {
576
+ hookRunner(
577
+ reply.context.preHandler,
578
+ hookIterator,
579
+ reply.request,
580
+ reply,
581
+ internals.preHandlerCallback
582
+ )
583
+ } else {
584
+ internals.preHandlerCallback(null, reply.request, reply)
585
+ }
572
586
  }
573
587
 
574
588
  function noop () {}
package/lib/route.js CHANGED
@@ -218,29 +218,6 @@ function buildRouting (options) {
218
218
  this[kReplySerializerDefault]
219
219
  )
220
220
 
221
- // TODO this needs to be refactored so that buildSchemaCompiler is
222
- // not called for every single route. Creating a new one for every route
223
- // is going to be very expensive.
224
- if (opts.schema) {
225
- if (this[kSchemaCompiler] == null && this[kSchemaResolver]) {
226
- done(new FST_ERR_SCH_MISSING_COMPILER(opts.method, url))
227
- return
228
- }
229
-
230
- try {
231
- if (opts.schemaCompiler == null && this[kSchemaCompiler] == null) {
232
- const externalSchemas = this[kSchemas].getJsonSchemas({ onlyAbsoluteUri: true })
233
- this.setSchemaCompiler(buildSchemaCompiler(externalSchemas, this[kOptions].ajv, schemaCache))
234
- }
235
-
236
- buildSchema(context, opts.schemaCompiler || this[kSchemaCompiler], this[kSchemas], this[kSchemaResolver])
237
- } catch (error) {
238
- // bubble up the FastifyError instance
239
- done(error.code ? error : new FST_ERR_SCH_BUILD(opts.method, url, error.message))
240
- return
241
- }
242
- }
243
-
244
221
  for (const hook of supportedHooks) {
245
222
  if (opts[hook]) {
246
223
  if (Array.isArray(opts[hook])) {
@@ -280,6 +257,24 @@ function buildRouting (options) {
280
257
  // Must store the 404 Context in 'preReady' because it is only guaranteed to
281
258
  // be available after all of the plugins and routes have been loaded.
282
259
  fourOhFour.setContext(this, context)
260
+
261
+ if (opts.schema) {
262
+ if (this[kSchemaCompiler] == null && this[kSchemaResolver]) {
263
+ throw new FST_ERR_SCH_MISSING_COMPILER(opts.method, url)
264
+ }
265
+
266
+ try {
267
+ if (opts.schemaCompiler == null && this[kSchemaCompiler] == null) {
268
+ const externalSchemas = this[kSchemas].getJsonSchemas({ onlyAbsoluteUri: true })
269
+ this.setSchemaCompiler(buildSchemaCompiler(externalSchemas, this[kOptions].ajv, schemaCache))
270
+ }
271
+
272
+ buildSchema(context, opts.schemaCompiler || this[kSchemaCompiler], this[kSchemas], this[kSchemaResolver])
273
+ } catch (error) {
274
+ // bubble up the FastifyError instance
275
+ throw (error.code ? error : new FST_ERR_SCH_BUILD(opts.method, url, error.message))
276
+ }
277
+ }
283
278
  })
284
279
 
285
280
  done(notHandledErr)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fastify",
3
- "version": "2.12.1",
3
+ "version": "2.14.1",
4
4
  "description": "Fast and low overhead web framework, for Node.js",
5
5
  "main": "fastify.js",
6
6
  "typings": "fastify.d.ts",
@@ -92,28 +92,30 @@
92
92
  "node": ">=6"
93
93
  },
94
94
  "devDependencies": {
95
- "@types/node": "^12.12.17",
96
- "@typescript-eslint/eslint-plugin": "^2.11.0",
97
- "@typescript-eslint/parser": "^2.11.0",
95
+ "@types/node": "^12.12.30",
96
+ "@typescript-eslint/eslint-plugin": "^2.24.0",
97
+ "@typescript-eslint/parser": "^2.24.0",
98
98
  "JSONStream": "^1.3.5",
99
+ "ajv-errors": "^1.0.1",
100
+ "ajv-i18n": "^3.5.0",
99
101
  "ajv-merge-patch": "^4.1.0",
100
102
  "ajv-pack": "^0.3.1",
101
103
  "autocannon": "^3.2.2",
102
104
  "branch-comparer": "^0.4.0",
103
- "concurrently": "^5.0.1",
105
+ "concurrently": "^5.1.0",
104
106
  "cors": "^2.8.5",
105
- "coveralls": "^3.0.9",
107
+ "coveralls": "^3.0.11",
106
108
  "dns-prefetch-control": "^0.2.0",
107
109
  "eslint": "^6.7.2",
108
- "eslint-import-resolver-node": "^0.3.2",
110
+ "eslint-import-resolver-node": "^0.3.3",
109
111
  "events.once": "^2.0.2",
110
112
  "fast-json-body": "^1.1.0",
111
- "fastify-plugin": "^1.5.0",
113
+ "fastify-plugin": "^1.6.1",
112
114
  "fluent-schema": "^0.10.0",
113
115
  "form-data": "^3.0.0",
114
116
  "frameguard": "^3.0.0",
115
117
  "h2url": "^0.2.0",
116
- "helmet": "^3.20.0",
118
+ "helmet": "^3.21.3",
117
119
  "hide-powered-by": "^1.0.0",
118
120
  "hsts": "^2.1.0",
119
121
  "http-errors": "^1.7.1",
@@ -130,43 +132,30 @@
130
132
  "simple-get": "^3.0.3",
131
133
  "snazzy": "^8.0.0",
132
134
  "split2": "^3.1.0",
133
- "standard": "^14.0.0",
135
+ "standard": "^14.3.3",
134
136
  "tap": "^12.5.2",
135
137
  "tap-mocha-reporter": "^3.0.7",
136
138
  "then-sleep": "^1.0.1",
137
- "typescript": "^3.7.3",
139
+ "typescript": "^3.8.3",
138
140
  "x-xss-protection": "^1.1.0",
139
- "yup": "^0.28.1"
141
+ "yup": "^0.28.3"
140
142
  },
141
143
  "dependencies": {
142
144
  "abstract-logging": "^2.0.0",
143
- "ajv": "^6.10.2",
144
- "avvio": "^6.3.0",
145
- "fast-json-stringify": "^1.16.0",
146
- "find-my-way": "^2.2.0",
145
+ "ajv": "^6.12.0",
146
+ "avvio": "^6.3.1",
147
+ "fast-json-stringify": "^1.18.0",
148
+ "find-my-way": "^2.2.2",
147
149
  "flatstr": "^1.0.12",
148
- "light-my-request": "^3.7.0",
150
+ "light-my-request": "^3.7.3",
149
151
  "middie": "^4.1.0",
150
- "pino": "^5.15.0",
151
- "proxy-addr": "^2.0.4",
152
- "readable-stream": "^3.1.1",
152
+ "pino": "^5.17.0",
153
+ "proxy-addr": "^2.0.6",
154
+ "readable-stream": "^3.6.0",
153
155
  "rfdc": "^1.1.2",
154
- "secure-json-parse": "^2.0.0",
156
+ "secure-json-parse": "^2.1.0",
155
157
  "tiny-lru": "^7.0.2"
156
158
  },
157
- "greenkeeper": {
158
- "ignore": [
159
- "autocannon",
160
- "boom",
161
- "joi",
162
- "@types/node",
163
- "semver",
164
- "tap",
165
- "tap-mocha-reporter",
166
- "@typescript-eslint/eslint-plugin",
167
- "lolex"
168
- ]
169
- },
170
159
  "standard": {
171
160
  "ignore": [
172
161
  "lib/configValidator.js"
package/test/404s.test.js CHANGED
@@ -1290,7 +1290,7 @@ test('onSend hooks run when an encapsulated route invokes the notFound handler',
1290
1290
 
1291
1291
  // https://github.com/fastify/fastify/issues/713
1292
1292
  test('preHandler option for setNotFoundHandler', t => {
1293
- t.plan(8)
1293
+ t.plan(10)
1294
1294
 
1295
1295
  t.test('preHandler option', t => {
1296
1296
  t.plan(2)
@@ -1316,6 +1316,69 @@ test('preHandler option for setNotFoundHandler', t => {
1316
1316
  })
1317
1317
  })
1318
1318
 
1319
+ // https://github.com/fastify/fastify/issues/2229
1320
+ t.test('preHandler hook in setNotFoundHandler should be called when callNotFound', t => {
1321
+ t.plan(2)
1322
+ const fastify = Fastify()
1323
+
1324
+ fastify.setNotFoundHandler({
1325
+ preHandler: (req, reply, done) => {
1326
+ req.body.preHandler = true
1327
+ done()
1328
+ }
1329
+ }, function (req, reply) {
1330
+ reply.code(404).send(req.body)
1331
+ })
1332
+
1333
+ fastify.post('/', function (req, reply) {
1334
+ reply.callNotFound()
1335
+ })
1336
+
1337
+ fastify.inject({
1338
+ method: 'POST',
1339
+ url: '/',
1340
+ payload: { hello: 'world' }
1341
+ }, (err, res) => {
1342
+ t.error(err)
1343
+ var payload = JSON.parse(res.payload)
1344
+ t.deepEqual(payload, { preHandler: true, hello: 'world' })
1345
+ })
1346
+ })
1347
+
1348
+ t.test('preHandler hook in setNotFoundHandler should accept an array of functions and be called when callNotFound', t => {
1349
+ t.plan(2)
1350
+ const fastify = Fastify()
1351
+
1352
+ fastify.setNotFoundHandler({
1353
+ preHandler: [
1354
+ (req, reply, done) => {
1355
+ req.body.preHandler1 = true
1356
+ done()
1357
+ },
1358
+ (req, reply, done) => {
1359
+ req.body.preHandler2 = true
1360
+ done()
1361
+ }
1362
+ ]
1363
+ }, function (req, reply) {
1364
+ reply.code(404).send(req.body)
1365
+ })
1366
+
1367
+ fastify.post('/', function (req, reply) {
1368
+ reply.callNotFound()
1369
+ })
1370
+
1371
+ fastify.inject({
1372
+ method: 'POST',
1373
+ url: '/',
1374
+ payload: { hello: 'world' }
1375
+ }, (err, res) => {
1376
+ t.error(err)
1377
+ var payload = JSON.parse(res.payload)
1378
+ t.deepEqual(payload, { preHandler1: true, preHandler2: true, hello: 'world' })
1379
+ })
1380
+ })
1381
+
1319
1382
  t.test('preHandler option should be called after preHandler hook', t => {
1320
1383
  t.plan(2)
1321
1384
  const fastify = Fastify()
@@ -0,0 +1,26 @@
1
+ 'use strict'
2
+
3
+ const t = require('tap')
4
+ const test = t.test
5
+ const Fastify = require('../fastify')
6
+
7
+ test('#2214 - wrong content-length', t => {
8
+ const fastify = Fastify()
9
+
10
+ fastify.get('/', async () => {
11
+ const error = new Error('MY_ERROR_MESSAGE')
12
+ error.headers = {
13
+ 'content-length': 2
14
+ }
15
+ throw error
16
+ })
17
+
18
+ fastify.inject({
19
+ method: 'GET',
20
+ path: '/'
21
+ })
22
+ .then(response => {
23
+ t.strictEqual(response.headers['content-length'], '' + response.rawPayload.length)
24
+ t.end()
25
+ })
26
+ })
@@ -3,6 +3,11 @@
3
3
  const t = require('tap')
4
4
  const test = t.test
5
5
  const Fastify = require('..')
6
+ const semver = require('semver')
7
+
8
+ if (semver.gt(process.versions.node, '8.0.0')) {
9
+ require('./content-length')
10
+ }
6
11
 
7
12
  test('default 413 with bodyLimit option', t => {
8
13
  t.plan(4)