fastify 4.12.0 → 4.13.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.
@@ -0,0 +1,38 @@
1
+ 'use strict'
2
+
3
+ const { test } = require('tap')
4
+ const fastify = require('..')({ requestTimeout: 5, http: { connectionsCheckingInterval: 1000 } })
5
+ const { connect } = require('net')
6
+
7
+ test('requestTimeout should return 408', t => {
8
+ t.plan(1)
9
+
10
+ t.teardown(() => {
11
+ fastify.close()
12
+ })
13
+
14
+ fastify.post('/', async function (req, reply) {
15
+ await new Promise(resolve => setTimeout(resolve, 100))
16
+ return reply.send({ hello: 'world' })
17
+ })
18
+
19
+ fastify.listen({ port: 0 }, err => {
20
+ if (err) {
21
+ throw err
22
+ }
23
+
24
+ let data = Buffer.alloc(0)
25
+ const socket = connect(fastify.server.address().port)
26
+
27
+ socket.write('POST / HTTP/1.1\r\nHost: example.com\r\nConnection-Length: 1\r\n')
28
+
29
+ socket.on('data', c => (data = Buffer.concat([data, c])))
30
+ socket.on('end', () => {
31
+ t.equal(
32
+ data.toString('utf-8'),
33
+ 'HTTP/1.1 408 Request Timeout\r\nContent-Length: 71\r\nContent-Type: application/json\r\n\r\n{"error":"Request Timeout","message":"Client Timeout","statusCode":408}'
34
+ )
35
+ t.end()
36
+ })
37
+ })
38
+ })
@@ -165,7 +165,7 @@ test('Should return error while closing (promise) - injection', t => {
165
165
  url: '/'
166
166
  }).catch(err => {
167
167
  t.ok(err)
168
- t.equal(err.message, 'Server is closed')
168
+ t.equal(err.code, 'FST_ERR_REOPENED_CLOSE_SERVER')
169
169
  })
170
170
  }, 100)
171
171
  })
@@ -197,7 +197,7 @@ test('Should return error while closing (callback) - injection', t => {
197
197
  url: '/'
198
198
  }, (err, res) => {
199
199
  t.ok(err)
200
- t.equal(err.message, 'Server is closed')
200
+ t.equal(err.code, 'FST_ERR_REOPENED_CLOSE_SERVER')
201
201
  })
202
202
  }, 100)
203
203
  })
@@ -220,13 +220,13 @@ t.test('Current opened connection should continue to work after closing and retu
220
220
 
221
221
  const port = fastify.server.address().port
222
222
  const client = net.createConnection({ port }, () => {
223
- client.write('GET / HTTP/1.1\r\n\r\n')
223
+ client.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
224
224
 
225
225
  client.once('data', data => {
226
226
  t.match(data.toString(), /Connection:\s*keep-alive/i)
227
227
  t.match(data.toString(), /200 OK/i)
228
228
 
229
- client.write('GET / HTTP/1.1\r\n\r\n')
229
+ client.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
230
230
 
231
231
  client.once('data', data => {
232
232
  t.match(data.toString(), /Connection:\s*close/i)
@@ -259,7 +259,7 @@ t.test('Current opened connection should NOT continue to work after closing and
259
259
 
260
260
  const port = fastify.server.address().port
261
261
  const client = net.createConnection({ port }, () => {
262
- client.write('GET / HTTP/1.1\r\n\r\n')
262
+ client.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
263
263
 
264
264
  client.on('error', function () {
265
265
  // Dependending on the Operating System
@@ -275,7 +275,7 @@ t.test('Current opened connection should NOT continue to work after closing and
275
275
  t.match(data.toString(), /Connection:\s*keep-alive/i)
276
276
  t.match(data.toString(), /200 OK/i)
277
277
 
278
- client.write('GET / HTTP/1.1\r\n\r\n')
278
+ client.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
279
279
  })
280
280
  })
281
281
  })
@@ -256,27 +256,25 @@ test('Error thrown 415 from content type is null and make post request to server
256
256
 
257
257
  test('remove', t => {
258
258
  test('should remove default parser', t => {
259
- t.plan(2)
259
+ t.plan(3)
260
260
 
261
261
  const fastify = Fastify()
262
262
  const contentTypeParser = fastify[keys.kContentTypeParser]
263
263
 
264
- contentTypeParser.remove('application/json')
265
-
264
+ t.ok(contentTypeParser.remove('application/json'))
266
265
  t.notOk(contentTypeParser.customParsers['application/json'])
267
266
  t.notOk(contentTypeParser.parserList.find(parser => parser === 'application/json'))
268
267
  })
269
268
 
270
269
  test('should remove RegExp parser', t => {
271
- t.plan(2)
270
+ t.plan(3)
272
271
 
273
272
  const fastify = Fastify()
274
273
  fastify.addContentTypeParser(/^text\/*/, first)
275
274
 
276
275
  const contentTypeParser = fastify[keys.kContentTypeParser]
277
276
 
278
- contentTypeParser.remove(/^text\/*/)
279
-
277
+ t.ok(contentTypeParser.remove(/^text\/*/))
280
278
  t.notOk(contentTypeParser.customParsers[/^text\/*/])
281
279
  t.notOk(contentTypeParser.parserRegExpList.find(parser => parser.toString() === /^text\/*/.toString()))
282
280
  })
@@ -289,23 +287,22 @@ test('remove', t => {
289
287
  t.throws(() => fastify[keys.kContentTypeParser].remove(12), FST_ERR_CTP_INVALID_TYPE)
290
288
  })
291
289
 
292
- test('should not throw error if content type does not exist', t => {
290
+ test('should return false if content type does not exist', t => {
293
291
  t.plan(1)
294
292
 
295
293
  const fastify = Fastify()
296
294
 
297
- t.doesNotThrow(() => fastify[keys.kContentTypeParser].remove('image/png'))
295
+ t.notOk(fastify[keys.kContentTypeParser].remove('image/png'))
298
296
  })
299
297
 
300
298
  test('should not remove any content type parser if content type does not exist', t => {
301
- t.plan(1)
299
+ t.plan(2)
302
300
 
303
301
  const fastify = Fastify()
304
302
 
305
303
  const contentTypeParser = fastify[keys.kContentTypeParser]
306
304
 
307
- contentTypeParser.remove('image/png')
308
-
305
+ t.notOk(contentTypeParser.remove('image/png'))
309
306
  t.same(contentTypeParser.customParsers.size, 2)
310
307
  })
311
308
 
@@ -372,3 +372,16 @@ t.test('ready return registered', t => {
372
372
  .then(instance => { t.same(instance, fastify) })
373
373
  .catch(err => { t.fail(err) })
374
374
  })
375
+
376
+ t.test('do not crash with error in follow up onReady hook', async t => {
377
+ const fastify = Fastify()
378
+
379
+ fastify.addHook('onReady', async function () {
380
+ })
381
+
382
+ fastify.addHook('onReady', function () {
383
+ throw new Error('kaboom')
384
+ })
385
+
386
+ await t.rejects(fastify.ready())
387
+ })
@@ -10,6 +10,7 @@ const fs = require('fs')
10
10
  const split = require('split2')
11
11
  const symbols = require('../lib/symbols.js')
12
12
  const payload = { hello: 'world' }
13
+ const proxyquire = require('proxyquire')
13
14
 
14
15
  process.removeAllListeners('warning')
15
16
 
@@ -1269,7 +1270,14 @@ test('clear payload', t => {
1269
1270
  })
1270
1271
 
1271
1272
  test('onSend hook throws', t => {
1272
- t.plan(9)
1273
+ t.plan(11)
1274
+ const Fastify = proxyquire('..', {
1275
+ './lib/schemas.js': {
1276
+ getSchemaSerializer: (param1, param2, param3) => {
1277
+ t.equal(param3, 'application/json; charset=utf-8', 'param3 should be "application/json; charset=utf-8"')
1278
+ }
1279
+ }
1280
+ })
1273
1281
  const fastify = Fastify()
1274
1282
  fastify.addHook('onSend', function (request, reply, payload, done) {
1275
1283
  if (request.raw.method === 'DELETE') {
@@ -1281,6 +1289,10 @@ test('onSend hook throws', t => {
1281
1289
  throw new Error('some error')
1282
1290
  }
1283
1291
 
1292
+ if (request.raw.method === 'POST') {
1293
+ throw new Error('some error')
1294
+ }
1295
+
1284
1296
  done()
1285
1297
  })
1286
1298
 
@@ -1288,6 +1300,26 @@ test('onSend hook throws', t => {
1288
1300
  reply.send({ hello: 'world' })
1289
1301
  })
1290
1302
 
1303
+ fastify.post('/', {
1304
+ schema: {
1305
+ response: {
1306
+ 200: {
1307
+ content: {
1308
+ 'application/json': {
1309
+ schema: {
1310
+ name: { type: 'string' },
1311
+ image: { type: 'string' },
1312
+ address: { type: 'string' }
1313
+ }
1314
+ }
1315
+ }
1316
+ }
1317
+ }
1318
+ }
1319
+ }, (req, reply) => {
1320
+ reply.send({ hello: 'world' })
1321
+ })
1322
+
1291
1323
  fastify.delete('/', (req, reply) => {
1292
1324
  reply.send({ hello: 'world' })
1293
1325
  })
@@ -1309,6 +1341,13 @@ test('onSend hook throws', t => {
1309
1341
  t.equal(response.headers['content-length'], '' + body.length)
1310
1342
  t.same(JSON.parse(body), { hello: 'world' })
1311
1343
  })
1344
+ sget({
1345
+ method: 'POST',
1346
+ url: 'http://localhost:' + fastify.server.address().port
1347
+ }, (err, response, body) => {
1348
+ t.error(err)
1349
+ t.equal(response.statusCode, 500)
1350
+ })
1312
1351
  sget({
1313
1352
  method: 'DELETE',
1314
1353
  url: 'http://localhost:' + fastify.server.address().port
@@ -27,6 +27,7 @@ fastify.listen({ port: 0 }, err => {
27
27
  t.equal(res.headers[':status'], 404)
28
28
  t.same(JSON.parse(res.body), {
29
29
  statusCode: 404,
30
+ code: 'FST_ERR_NOT_FOUND',
30
31
  error: 'Not Found',
31
32
  message: 'Not Found'
32
33
  })
@@ -1740,7 +1740,7 @@ test('cannot set the replySerializer when the server is running', t => {
1740
1740
  fastify.setReplySerializer(() => {})
1741
1741
  t.fail('this serializer should not be setup')
1742
1742
  } catch (e) {
1743
- t.equal(e.message, 'Cannot call "setReplySerializer" when fastify instance is already started!')
1743
+ t.equal(e.code, 'FST_ERR_INSTANCE_ALREADY_LISTENING')
1744
1744
  }
1745
1745
  })
1746
1746
  })
@@ -22,20 +22,20 @@ test('maxRequestsPerSocket on node version >= 16.10.0', { skip }, t => {
22
22
 
23
23
  const port = fastify.server.address().port
24
24
  const client = net.createConnection({ port }, () => {
25
- client.write('GET / HTTP/1.1\r\n\r\n')
25
+ client.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
26
26
 
27
27
  client.once('data', data => {
28
28
  t.match(data.toString(), /Connection:\s*keep-alive/i)
29
29
  t.match(data.toString(), /Keep-Alive:\s*timeout=\d+/i)
30
30
  t.match(data.toString(), /200 OK/i)
31
31
 
32
- client.write('GET / HTTP/1.1\r\n\r\n')
32
+ client.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
33
33
 
34
34
  client.once('data', data => {
35
35
  t.match(data.toString(), /Connection:\s*close/i)
36
36
  t.match(data.toString(), /200 OK/i)
37
37
 
38
- client.write('GET / HTTP/1.1\r\n\r\n')
38
+ client.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
39
39
 
40
40
  client.once('data', data => {
41
41
  t.match(data.toString(), /Connection:\s*close/i)
@@ -63,21 +63,21 @@ test('maxRequestsPerSocket zero should behave same as null', { skip }, t => {
63
63
 
64
64
  const port = fastify.server.address().port
65
65
  const client = net.createConnection({ port }, () => {
66
- client.write('GET / HTTP/1.1\r\n\r\n')
66
+ client.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
67
67
 
68
68
  client.once('data', data => {
69
69
  t.match(data.toString(), /Connection:\s*keep-alive/i)
70
70
  t.match(data.toString(), /Keep-Alive:\s*timeout=\d+/i)
71
71
  t.match(data.toString(), /200 OK/i)
72
72
 
73
- client.write('GET / HTTP/1.1\r\n\r\n')
73
+ client.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
74
74
 
75
75
  client.once('data', data => {
76
76
  t.match(data.toString(), /Connection:\s*keep-alive/i)
77
77
  t.match(data.toString(), /Keep-Alive:\s*timeout=\d+/i)
78
78
  t.match(data.toString(), /200 OK/i)
79
79
 
80
- client.write('GET / HTTP/1.1\r\n\r\n')
80
+ client.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
81
81
 
82
82
  client.once('data', data => {
83
83
  t.match(data.toString(), /Connection:\s*keep-alive/i)
@@ -204,7 +204,7 @@ function testHandlerOrBeforeHandlerHook (test, hookOrHandler) {
204
204
  app.listen({ port: 0 }, err => {
205
205
  t.error(err)
206
206
  const client = net.createConnection({ port: (app.server.address()).port }, () => {
207
- client.write('GET / HTTP/1.1\r\n\r\n')
207
+ client.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
208
208
 
209
209
  let chunks = ''
210
210
  client.setEncoding('utf8')
@@ -25,6 +25,7 @@ import { Socket } from 'net'
25
25
  // http server
26
26
  expectType<FastifyInstance<http.Server, http.IncomingMessage, http.ServerResponse> & PromiseLike<FastifyInstance<http.Server, http.IncomingMessage, http.ServerResponse>>>(fastify())
27
27
  expectType<FastifyInstance<http.Server, http.IncomingMessage, http.ServerResponse> & PromiseLike<FastifyInstance<http.Server, http.IncomingMessage, http.ServerResponse>>>(fastify({}))
28
+ expectType<FastifyInstance<http.Server, http.IncomingMessage, http.ServerResponse> & PromiseLike<FastifyInstance<http.Server, http.IncomingMessage, http.ServerResponse>>>(fastify({ http: {} }))
28
29
  // https server
29
30
  expectType<FastifyInstance<https.Server, http.IncomingMessage, http.ServerResponse> & PromiseLike<FastifyInstance<https.Server, http.IncomingMessage, http.ServerResponse>>>(fastify({ https: {} }))
30
31
  expectType<FastifyInstance<https.Server, http.IncomingMessage, http.ServerResponse> & PromiseLike<FastifyInstance<https.Server, http.IncomingMessage, http.ServerResponse>>>(fastify({ https: null }))
@@ -326,6 +326,8 @@ const versionConstraintStrategy = {
326
326
  expectType<void>(server.addConstraintStrategy(versionConstraintStrategy))
327
327
  expectType<boolean>(server.hasConstraintStrategy(versionConstraintStrategy.name))
328
328
 
329
+ expectType<boolean>(server.hasPlugin(''))
330
+
329
331
  expectAssignable<DefaultRoute<RawRequestDefaultExpression, RawReplyDefaultExpression>>(server.getDefaultRoute())
330
332
 
331
333
  expectType<FastifySchemaCompiler<any> | undefined>(server.validatorCompiler)
@@ -18,7 +18,7 @@ t.test('Will return 505 HTTP error if HTTP version (2.0 when server is 1.1) is n
18
18
 
19
19
  const port = fastify.server.address().port
20
20
  const client = net.createConnection({ port }, () => {
21
- client.write('GET / HTTP/2.0\r\n\r\n')
21
+ client.write('GET / HTTP/2.0\r\nHost: example.com\r\n\r\n')
22
22
 
23
23
  client.once('data', data => {
24
24
  t.match(data.toString(), /505 HTTP Version Not Supported/i)
@@ -568,7 +568,7 @@ test('cannot create a fastify instance with wrong type of errorFormatter', t =>
568
568
  }
569
569
  })
570
570
  } catch (err) {
571
- t.equal(err.message, 'schemaErrorFormatter option should not be an async function')
571
+ t.equal(err.code, 'FST_ERR_SCHEMA_ERROR_FORMATTER_NOT_FN')
572
572
  }
573
573
 
574
574
  try {
@@ -576,14 +576,14 @@ test('cannot create a fastify instance with wrong type of errorFormatter', t =>
576
576
  schemaErrorFormatter: 500
577
577
  })
578
578
  } catch (err) {
579
- t.equal(err.message, 'schemaErrorFormatter option should be a function, instead got number')
579
+ t.equal(err.code, 'FST_ERR_SCHEMA_ERROR_FORMATTER_NOT_FN')
580
580
  }
581
581
 
582
582
  try {
583
583
  const fastify = Fastify()
584
584
  fastify.setSchemaErrorFormatter(500)
585
585
  } catch (err) {
586
- t.equal(err.message, 'schemaErrorFormatter option should be a function, instead got number')
586
+ t.equal(err.code, 'FST_ERR_SCHEMA_ERROR_FORMATTER_NOT_FN')
587
587
  }
588
588
  })
589
589
 
package/types/errors.d.ts CHANGED
@@ -49,5 +49,25 @@ export type FastifyErrorCodes = Record<
49
49
  'FST_ERR_PLUGIN_NOT_VALID' |
50
50
  'FST_ERR_ROOT_PLG_BOOTED' |
51
51
  'FST_ERR_PARENT_PLUGIN_BOOTED' |
52
- 'FST_ERR_PLUGIN_TIMEOUT'
52
+ 'FST_ERR_PLUGIN_TIMEOUT' |
53
+ 'FST_ERR_OPTIONS_NOT_OBJ' |
54
+ 'FST_ERR_QSP_NOT_FN' |
55
+ 'FST_ERR_SCHEMA_CONTROLLER_BUCKET_OPT_NOT_FN' |
56
+ 'FST_ERR_SCHEMA_ERROR_FORMATTER_NOT_FN' |
57
+ 'FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_OBJ' |
58
+ 'FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_ARR' |
59
+ 'FST_ERR_VERSION_CONSTRAINT_NOT_STR' |
60
+ 'FST_ERR_CTP_INSTANCE_ALREADY_STARTED' |
61
+ 'FST_ERR_HOOK_NOT_SUPPORTED' |
62
+ 'FST_ERR_SCH_RESPONSE_SCHEMA_NOT_NESTED_2XX' |
63
+ 'FST_ERR_ROUTE_OPTIONS_NOT_OBJ' |
64
+ 'FST_ERR_ROUTE_DUPLICATED_HANDLER' |
65
+ 'FST_ERR_ROUTE_HANDLER_NOT_FN' |
66
+ 'FST_ERR_ROUTE_MISSING_HANDLER' |
67
+ 'FST_ERR_ROUTE_METHOD_NOT_SUPPORTED' |
68
+ 'FST_ERR_ROUTE_BODY_VALIDATION_SCHEMA_NOT_SUPPORTED' |
69
+ 'FST_ERR_ROUTE_BODY_LIMIT_OPTION_NOT_INT' |
70
+ 'FST_ERR_ROUTE_REWRITE_NOT_STR' |
71
+ 'FST_ERR_PLUGIN_NOT_PRESENT_IN_INSTANCE' |
72
+ 'FST_ERR_INSTANCE_ALREADY_LISTENING'
53
73
  , FastifyErrorConstructor>
@@ -131,6 +131,7 @@ export interface FastifyInstance<
131
131
  hasDecorator(decorator: string | symbol): boolean;
132
132
  hasRequestDecorator(decorator: string | symbol): boolean;
133
133
  hasReplyDecorator(decorator: string | symbol): boolean;
134
+ hasPlugin(name: string): boolean;
134
135
 
135
136
  addConstraintStrategy(strategy: ConstraintStrategy<FindMyWayVersion<RawServer>, unknown>): void;
136
137
  hasConstraintStrategy(strategyName: string): boolean;