fastify 3.27.0 → 3.27.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.
@@ -117,7 +117,7 @@ an `id` ), and then pass it to our validation library, anything passed through
117
117
  via `__proto__` would sneak in undetected.
118
118
 
119
119
  ### Oh joi!
120
- <a id="pp-oh-joi">
120
+ <a id="pp-oh-joi"></a>
121
121
 
122
122
  The first question is, of course, why does the validation module **joi** ignore
123
123
  the prototype and let potentially harmful data through? We asked ourselves the
@@ -383,9 +383,9 @@ Because this work is important, I decided to try and make it not just
383
383
  financially sustainable but to grow and expand it. There is so much to improve.
384
384
  This is exactly what motivates me to implement the new [commercial licensing
385
385
  plan](https://web.archive.org/web/20190201220503/https://hueniverse.com/on-hapi-licensing-a-preview-f982662ee898)
386
- coming in March. You can ready more about it
386
+ coming in March. You can read more about it
387
387
  [here](https://web.archive.org/web/20190201220503/https://hueniverse.com/on-hapi-licensing-a-preview-f982662ee898).
388
388
 
389
389
  Of all the time consuming things, security is at the very top. I hope this story
390
- successful conveyed not just the technical details, but also the human drama and
390
+ successfully conveyed not just the technical details, but also the human drama and
391
391
  what it takes to keep the web secure.
@@ -13,7 +13,7 @@ describes the properties available in that options object.
13
13
  - [`https`](#https)
14
14
  - [`connectionTimeout`](#connectiontimeout)
15
15
  - [`keepAliveTimeout`](#keepalivetimeout)
16
- - [`forceCloseConnections](#forcecloseconnections)
16
+ - [`forceCloseConnections`](#forcecloseconnections)
17
17
  - [`maxRequestsPerSocket`](#maxrequestspersocket)
18
18
  - [`requestTimeout`](#requesttimeout)
19
19
  - [`ignoreTrailingSlash`](#ignoretrailingslash)
package/fastify.d.ts CHANGED
@@ -98,6 +98,7 @@ export type FastifyServerOptions<
98
98
  connectionTimeout?: number,
99
99
  keepAliveTimeout?: number,
100
100
  maxRequestsPerSocket?: number,
101
+ forceCloseConnections?: boolean,
101
102
  requestTimeout?: number,
102
103
  pluginTimeout?: number,
103
104
  bodyLimit?: number,
package/fastify.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const VERSION = '3.27.0'
3
+ const VERSION = '3.27.1'
4
4
 
5
5
  const Avvio = require('avvio')
6
6
  const http = require('http')
package/lib/errors.js CHANGED
@@ -210,6 +210,11 @@ const codes = {
210
210
  500,
211
211
  TypeError
212
212
  ),
213
+ FST_ERR_INVALID_URL: createError(
214
+ 'FST_ERR_INVALID_URL',
215
+ "URL must be a string. Received '%s'",
216
+ 400
217
+ ),
213
218
 
214
219
  /**
215
220
  * again listen when close server
package/lib/reply.js CHANGED
@@ -365,12 +365,18 @@ function preserializeHookEnd (err, request, reply, payload) {
365
365
  return
366
366
  }
367
367
 
368
- if (reply[kReplySerializer] !== null) {
369
- payload = reply[kReplySerializer](payload)
370
- } else if (reply.context && reply.context[kReplySerializerDefault]) {
371
- payload = reply.context[kReplySerializerDefault](payload, reply.raw.statusCode)
372
- } else {
373
- payload = serialize(reply.context, payload, reply.raw.statusCode)
368
+ try {
369
+ if (reply[kReplySerializer] !== null) {
370
+ payload = reply[kReplySerializer](payload)
371
+ } else if (reply.context && reply.context[kReplySerializerDefault]) {
372
+ payload = reply.context[kReplySerializerDefault](payload, reply.raw.statusCode)
373
+ } else {
374
+ payload = serialize(reply.context, payload, reply.raw.statusCode)
375
+ }
376
+ } catch (e) {
377
+ wrapSeralizationError(e, reply)
378
+ onErrorHook(reply, e)
379
+ return
374
380
  }
375
381
 
376
382
  flatstr(payload)
@@ -378,6 +384,10 @@ function preserializeHookEnd (err, request, reply, payload) {
378
384
  onSendHook(reply, payload)
379
385
  }
380
386
 
387
+ function wrapSeralizationError (error, reply) {
388
+ error.serialization = reply.context.config
389
+ }
390
+
381
391
  function onSendHook (reply, payload) {
382
392
  if (reply.context.onSend !== null) {
383
393
  reply[kReplySent] = true
package/lib/route.js CHANGED
@@ -18,7 +18,8 @@ const {
18
18
  const {
19
19
  FST_ERR_SCH_VALIDATION_BUILD,
20
20
  FST_ERR_SCH_SERIALIZATION_BUILD,
21
- FST_ERR_DEFAULT_ROUTE_INVALID_TYPE
21
+ FST_ERR_DEFAULT_ROUTE_INVALID_TYPE,
22
+ FST_ERR_INVALID_URL
22
23
  } = require('./errors')
23
24
 
24
25
  const {
@@ -99,6 +100,10 @@ function buildRouting (options) {
99
100
 
100
101
  // Convert shorthand to extended route declaration
101
102
  function prepareRoute (method, url, options, handler) {
103
+ if (typeof url !== 'string') {
104
+ throw new FST_ERR_INVALID_URL(typeof url)
105
+ }
106
+
102
107
  if (!handler && typeof options === 'function') {
103
108
  handler = options // for support over direct function calls such as fastify.get() options are reused as the handler
104
109
  options = {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fastify",
3
- "version": "3.27.0",
3
+ "version": "3.27.1",
4
4
  "description": "Fast and low overhead web framework, for Node.js",
5
5
  "main": "fastify.js",
6
6
  "type": "commonjs",
@@ -8,6 +8,7 @@ const sget = require('simple-get').concat
8
8
  const joi = require('@hapi/joi')
9
9
  const Fastify = require('..')
10
10
  const proxyquire = require('proxyquire')
11
+ const { FST_ERR_INVALID_URL } = require('../lib/errors')
11
12
 
12
13
  test('route', t => {
13
14
  t.plan(9)
@@ -1268,3 +1269,14 @@ test('Correct error message is produced if "path" option is used', t => {
1268
1269
  })
1269
1270
  }, new Error('Error Handler for POST:/test route, if defined, must be a function'))
1270
1271
  })
1272
+
1273
+ test('invalid url attribute - non string URL', t => {
1274
+ t.plan(1)
1275
+ const fastify = Fastify()
1276
+
1277
+ try {
1278
+ fastify.get(/^\/(donations|skills|blogs)/, () => {})
1279
+ } catch (error) {
1280
+ t.equal(error.code, FST_ERR_INVALID_URL().code)
1281
+ }
1282
+ })
@@ -670,3 +670,44 @@ test('error in custom schema serialize compiler, throw FST_ERR_SCH_SERIALIZATION
670
670
  t.equal(err.code, 'FST_ERR_SCH_SERIALIZATION_BUILD')
671
671
  })
672
672
  })
673
+
674
+ test('Errors in searilizer sended to errorHandler', async t => {
675
+ let savedError
676
+
677
+ const fastify = Fastify()
678
+ fastify.get('/', {
679
+ schema: {
680
+ response: {
681
+ 200: {
682
+ type: 'object',
683
+ properties: {
684
+ name: { type: 'string' },
685
+ power: { type: 'string' }
686
+ },
687
+ required: ['name']
688
+ }
689
+ }
690
+ }
691
+
692
+ }, function (req, reply) {
693
+ reply.code(200).send({ no: 'thing' })
694
+ })
695
+ fastify.setErrorHandler((error, request, reply) => {
696
+ savedError = error
697
+ reply.code(500).send(error)
698
+ })
699
+
700
+ const res = await fastify.inject('/')
701
+
702
+ t.equal(res.statusCode, 500)
703
+
704
+ // t.same(savedError, new Error('"name" is required!'));
705
+ t.same(res.json(), {
706
+ statusCode: 500,
707
+ error: 'Internal Server Error',
708
+ message: '"name" is required!'
709
+ })
710
+ t.ok(savedError, 'error presents')
711
+ t.ok(savedError.serialization, 'Serialization sign presents')
712
+ t.end()
713
+ })
@@ -57,6 +57,7 @@ fastify({ http2: true, https: {} }).inject({}, lightMyRequestCallback)
57
57
  expectAssignable<FastifyInstance<http2.Http2Server, http2.Http2ServerRequest, http2.Http2ServerResponse>>(fastify({ http2: true }))
58
58
  expectAssignable<FastifyInstance>(fastify({ ignoreTrailingSlash: true }))
59
59
  expectAssignable<FastifyInstance>(fastify({ connectionTimeout: 1000 }))
60
+ expectAssignable<FastifyInstance>(fastify({ forceCloseConnections: true }))
60
61
  expectAssignable<FastifyInstance>(fastify({ keepAliveTimeout: 1000 }))
61
62
  expectAssignable<FastifyInstance>(fastify({ pluginTimeout: 1000 }))
62
63
  expectAssignable<FastifyInstance>(fastify({ bodyLimit: 100 }))
@@ -10,6 +10,7 @@ import { expectAssignable, expectError, expectType } from 'tsd'
10
10
  import { FastifyRequest } from '../../types/request'
11
11
  import { FastifyReply } from '../../types/reply'
12
12
  import { HookHandlerDoneFunction } from '../../types/hooks'
13
+ import { FastifySchemaControllerOptions } from '../../types/schema'
13
14
 
14
15
  const server = fastify()
15
16
 
@@ -112,6 +113,29 @@ server.setNotFoundHandler({ preHandler: notFoundpreHandlerHandler, preValidation
112
113
  function invalidErrorHandler (error: number) {}
113
114
  expectError(server.setErrorHandler(invalidErrorHandler))
114
115
 
116
+ server.setSchemaController({
117
+ bucket: (parentSchemas: unknown) => {
118
+ return {
119
+ addSchema (schema: unknown) {
120
+ expectType<unknown>(schema)
121
+ expectType<FastifyInstance>(server.addSchema({ type: 'null' }))
122
+ return server.addSchema({ type: 'null' })
123
+ },
124
+ getSchema (schemaId: string) {
125
+ expectType<string>(schemaId)
126
+ return server.getSchema('SchemaId')
127
+ },
128
+ getSchemas () {
129
+ expectType<Record<string, unknown>>(server.getSchemas())
130
+ return server.getSchemas()
131
+ }
132
+ }
133
+ }
134
+ })
135
+
136
+ function invalidSchemaController (schemaControllerOptions: FastifySchemaControllerOptions) {}
137
+ expectError(server.setSchemaController(invalidSchemaController))
138
+
115
139
  server.setReplySerializer(function (payload, statusCode) {
116
140
  expectType<unknown>(payload)
117
141
  expectType<number>(statusCode)
@@ -171,6 +195,7 @@ type InitialConfig = Readonly<{
171
195
  keepAliveTimeout?: number,
172
196
  bodyLimit?: number,
173
197
  caseSensitive?: boolean,
198
+ forceCloseConnections?: boolean,
174
199
  http2?: boolean,
175
200
  https?: boolean | Readonly<{ allowHTTP1: boolean }>,
176
201
  ignoreTrailingSlash?: boolean,
@@ -1,6 +1,12 @@
1
1
  import { Chain as LightMyRequestChain, InjectOptions, Response as LightMyRequestResponse, CallbackFunc as LightMyRequestCallback } from 'light-my-request'
2
2
  import { RouteOptions, RouteShorthandMethod, RouteGenericInterface, DefaultRoute } from './route'
3
- import { FastifySchema, FastifySchemaCompiler, FastifySchemaValidationError, FastifySerializerCompiler } from './schema'
3
+ import {
4
+ FastifySchema,
5
+ FastifySchemaCompiler,
6
+ FastifySchemaValidationError,
7
+ FastifySerializerCompiler,
8
+ FastifySchemaControllerOptions
9
+ } from './schema'
4
10
  import { RawServerBase, RawRequestDefaultExpression, RawServerDefault, RawReplyDefaultExpression, ContextConfigDefault } from './utils'
5
11
  import { FastifyLoggerInstance } from './logger'
6
12
  import { FastifyRegister } from './register'
@@ -380,6 +386,11 @@ export interface FastifyInstance<
380
386
  */
381
387
  setSerializerCompiler<T = FastifySchema>(schemaCompiler: FastifySerializerCompiler<T>): FastifyInstance<RawServer, RawRequest, RawReply, Logger>;
382
388
 
389
+ /**
390
+ * Set the schema controller for all routes.
391
+ */
392
+ setSchemaController(schemaControllerOpts: FastifySchemaControllerOptions): FastifyInstance<RawServer, RawRequest, RawReply, Logger>;
393
+
383
394
  /**
384
395
  * Set the reply serializer for all routes.
385
396
  */
@@ -427,6 +438,7 @@ export interface FastifyInstance<
427
438
  initialConfig: Readonly<{
428
439
  connectionTimeout?: number,
429
440
  keepAliveTimeout?: number,
441
+ forceCloseConnections?: boolean,
430
442
  bodyLimit?: number,
431
443
  caseSensitive?: boolean,
432
444
  http2?: boolean,
package/types/schema.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { ValidatorCompiler } from '@fastify/ajv-compiler'
2
+ import { FastifyInstance, FastifyServerOptions } from '../fastify'
1
3
  /**
2
4
  * Schemas in Fastify follow the JSON-Schema standard. For this reason
3
5
  * we have opted to not ship strict schema based types. Instead we provide
@@ -36,3 +38,15 @@ export interface FastifyValidationResult {
36
38
  export type FastifySchemaCompiler<T> = (routeSchema: FastifyRouteSchemaDef<T>) => FastifyValidationResult
37
39
 
38
40
  export type FastifySerializerCompiler<T> = (routeSchema: FastifyRouteSchemaDef<T>) => (data: any) => string
41
+
42
+ export interface FastifySchemaControllerOptions{
43
+ bucket?: (parentSchemas?: unknown) => {
44
+ addSchema(schema: unknown): FastifyInstance;
45
+ getSchema(schemaId: string): unknown;
46
+ getSchemas(): Record<string, unknown>;
47
+ };
48
+ compilersFactory?: {
49
+ buildValidator?: ValidatorCompiler;
50
+ buildSerializer?: (externalSchemas: unknown, serializerOptsServerOption: FastifyServerOptions['serializerOpts']) => FastifySerializerCompiler<unknown>;
51
+ };
52
+ }