fastify 4.4.0 → 4.5.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.
@@ -97,7 +97,7 @@ const schema = {
97
97
  onProtoPoisoning: { type: 'string', default: defaultInitOptions.onProtoPoisoning },
98
98
  onConstructorPoisoning: { type: 'string', default: defaultInitOptions.onConstructorPoisoning },
99
99
  pluginTimeout: { type: 'integer', default: defaultInitOptions.pluginTimeout },
100
- requestIdHeader: { type: 'string', default: defaultInitOptions.requestIdHeader },
100
+ requestIdHeader: { anyOf: [{ enum: [false] }, { type: 'string' }], default: defaultInitOptions.requestIdHeader },
101
101
  requestIdLogLabel: { type: 'string', default: defaultInitOptions.requestIdLogLabel },
102
102
  http2SessionTimeout: { type: 'integer', default: defaultInitOptions.http2SessionTimeout },
103
103
  exposeHeadRoutes: { type: 'boolean', default: defaultInitOptions.exposeHeadRoutes },
@@ -328,6 +328,8 @@ section.
328
328
  - [`fastify-http2https`](https://github.com/lolo32/fastify-http2https) Redirect
329
329
  HTTP requests to HTTPS, both using the same port number, or different response
330
330
  on HTTP and HTTPS.
331
+ - [`fastify-https-always`](https://github.com/mattbishop/fastify-https-always)
332
+ Lightweight, proxy-aware redirect plugin from HTTP to HTTPS.
331
333
  - [`fastify-https-redirect`](https://github.com/tomsvogel/fastify-https-redirect)
332
334
  Fastify plugin for auto-redirect from HTTP to HTTPS.
333
335
  - [`fastify-impressions`](https://github.com/manju4ever/fastify-impressions)
@@ -426,6 +428,8 @@ section.
426
428
  - [`fastify-orientdb`](https://github.com/mahmed8003/fastify-orientdb) Fastify
427
429
  OrientDB connection plugin, with which you can share the OrientDB connection
428
430
  across every part of your server.
431
+ - [`fastify-osm`](https://github.com/gzileni/fastify-osm) Fastify
432
+ OSM plugin to run overpass queries by OpenStreetMap.
429
433
  - [`fastify-peekaboo`](https://github.com/simone-sanfratello/fastify-peekaboo)
430
434
  Fastify plugin for memoize responses by expressive settings.
431
435
  - [`fastify-piscina`](https://github.com/piscinajs/fastify-piscina) A worker
@@ -658,7 +658,7 @@ fastify.inject({
658
658
  >
659
659
  > ```js
660
660
  > const append = require('vary').append
661
- > fastify.addHook('onSend', async (req, reply) => {
661
+ > fastify.addHook('onSend', (req, reply, payload, done) => {
662
662
  > if (req.headers['accept-version']) { // or the custom header you are using
663
663
  > let value = reply.getHeader('Vary') || ''
664
664
  > const header = Array.isArray(value) ? value.join(', ') : String(value)
@@ -666,6 +666,7 @@ fastify.inject({
666
666
  > reply.header('Vary', value)
667
667
  > }
668
668
  > }
669
+ > done()
669
670
  > })
670
671
  > ```
671
672
 
@@ -55,7 +55,7 @@ describes the properties available in that options object.
55
55
  - [routing](#routing)
56
56
  - [route](#route)
57
57
  - [close](#close)
58
- - [decorate\*](#decorate)
58
+ - [decorate*](#decorate)
59
59
  - [register](#register)
60
60
  - [addHook](#addhook)
61
61
  - [prefix](#prefix)
@@ -488,11 +488,18 @@ about safe regexp: [Safe-regex2](https://www.npmjs.com/package/safe-regex2)
488
488
  ### `requestIdHeader`
489
489
  <a id="factory-request-id-header"></a>
490
490
 
491
- The header name used to know the request-id. See [the
491
+ The header name used to set the request-id. See [the
492
492
  request-id](./Logging.md#logging-request-id) section.
493
+ Setting `requestIdHeader` to `false` will always use [genReqId](#genreqid)
493
494
 
494
495
  + Default: `'request-id'`
495
-
496
+
497
+ ```js
498
+ const fastify = require('fastify')({
499
+ requestIdHeader: 'x-custom-id', // -> use 'X-Custom-Id' header if available
500
+ //requestIdHeader: false, // -> always use genReqId
501
+ })
502
+ ```
496
503
  ### `requestIdLogLabel`
497
504
  <a id="factory-request-id-log-label"></a>
498
505
 
@@ -1112,12 +1119,15 @@ fastify.register(function (instance, opts, done) {
1112
1119
  <a id="pluginName"></a>
1113
1120
 
1114
1121
  Name of the current plugin. The root plugin is called `'fastify'`. There are
1115
- three ways to define a name (in order).
1122
+ different ways to define a name (in order).
1116
1123
 
1117
1124
  1. If you use [fastify-plugin](https://github.com/fastify/fastify-plugin) the
1118
1125
  metadata `name` is used.
1119
- 2. If you `module.exports` a plugin the filename is used.
1120
- 3. If you use a regular [function
1126
+ 2. If the exported plugin has the `Symbol.for('fastify.display-name')` property,
1127
+ then the value of that property is used.
1128
+ Example: `pluginFn[Symbol.for('fastify.display-name')] = "Custom Name"`
1129
+ 3. If you `module.exports` a plugin the filename is used.
1130
+ 4. If you use a regular [function
1121
1131
  declaration](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions#Defining_functions)
1122
1132
  the function name is used.
1123
1133
 
@@ -268,7 +268,7 @@ fastify.listen({ port: 3000 }, (err) => {
268
268
  ```sh
269
269
  curl -X GET "http://localhost:3000/?ids=1
270
270
 
271
- {"params":{"hello":["1"]}}
271
+ {"params":{"ids":["1"]}}
272
272
  ```
273
273
 
274
274
  You can also specify a custom schema validator for each parameter type (body,
package/fastify.d.ts CHANGED
@@ -123,7 +123,7 @@ export type FastifyServerOptions<
123
123
  serializerOpts?: FJSOptions | Record<string, unknown>,
124
124
  serverFactory?: FastifyServerFactory<RawServer>,
125
125
  caseSensitive?: boolean,
126
- requestIdHeader?: string,
126
+ requestIdHeader?: string | false,
127
127
  requestIdLogLabel?: string;
128
128
  jsonShorthand?: boolean;
129
129
  genReqId?: <RequestGeneric extends RequestGenericInterface = RequestGenericInterface, TypeProvider extends FastifyTypeProvider = FastifyTypeProviderDefault>(req: FastifyRequest<RequestGeneric, RawServer, RawRequestDefaultExpression<RawServer>, FastifySchema, TypeProvider>) => string,
package/fastify.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const VERSION = '4.4.0'
3
+ const VERSION = '4.5.2'
4
4
 
5
5
  const Avvio = require('avvio')
6
6
  const http = require('http')
@@ -98,8 +98,8 @@ function fastify (options) {
98
98
 
99
99
  validateBodyLimitOption(options.bodyLimit)
100
100
 
101
- const requestIdHeader = options.requestIdHeader || defaultInitOptions.requestIdHeader
102
- const genReqId = options.genReqId || reqIdGenFactory()
101
+ const requestIdHeader = (options.requestIdHeader === false) ? false : (options.requestIdHeader || defaultInitOptions.requestIdHeader)
102
+ const genReqId = reqIdGenFactory(requestIdHeader, options.genReqId)
103
103
  const requestIdLogLabel = options.requestIdLogLabel || 'reqId'
104
104
  const bodyLimit = options.bodyLimit || defaultInitOptions.bodyLimit
105
105
  const disableRequestLogging = options.disableRequestLogging || false
@@ -3,7 +3,7 @@
3
3
  "use strict";
4
4
  module.exports = validate10;
5
5
  module.exports.default = validate10;
6
- const schema11 = {"type":"object","additionalProperties":false,"properties":{"connectionTimeout":{"type":"integer","default":0},"keepAliveTimeout":{"type":"integer","default":72000},"forceCloseConnections":{"oneOf":[{"type":"string","pattern":"idle"},{"type":"boolean"}]},"maxRequestsPerSocket":{"type":"integer","default":0,"nullable":true},"requestTimeout":{"type":"integer","default":0},"bodyLimit":{"type":"integer","default":1048576},"caseSensitive":{"type":"boolean","default":true},"allowUnsafeRegex":{"type":"boolean","default":false},"http2":{"type":"boolean"},"https":{"if":{"not":{"oneOf":[{"type":"boolean"},{"type":"null"},{"type":"object","additionalProperties":false,"required":["allowHTTP1"],"properties":{"allowHTTP1":{"type":"boolean"}}}]}},"then":{"setDefaultValue":true}},"ignoreTrailingSlash":{"type":"boolean","default":false},"ignoreDuplicateSlashes":{"type":"boolean","default":false},"disableRequestLogging":{"type":"boolean","default":false},"jsonShorthand":{"type":"boolean","default":true},"maxParamLength":{"type":"integer","default":100},"onProtoPoisoning":{"type":"string","default":"error"},"onConstructorPoisoning":{"type":"string","default":"error"},"pluginTimeout":{"type":"integer","default":10000},"requestIdHeader":{"type":"string","default":"request-id"},"requestIdLogLabel":{"type":"string","default":"reqId"},"http2SessionTimeout":{"type":"integer","default":72000},"exposeHeadRoutes":{"type":"boolean","default":true},"versioning":{"type":"object","additionalProperties":true,"required":["storage","deriveVersion"],"properties":{"storage":{},"deriveVersion":{}}},"constraints":{"type":"object","additionalProperties":{"type":"object","required":["name","storage","validate","deriveConstraint"],"additionalProperties":true,"properties":{"name":{"type":"string"},"storage":{},"validate":{},"deriveConstraint":{}}}}}};
6
+ const schema11 = {"type":"object","additionalProperties":false,"properties":{"connectionTimeout":{"type":"integer","default":0},"keepAliveTimeout":{"type":"integer","default":72000},"forceCloseConnections":{"oneOf":[{"type":"string","pattern":"idle"},{"type":"boolean"}]},"maxRequestsPerSocket":{"type":"integer","default":0,"nullable":true},"requestTimeout":{"type":"integer","default":0},"bodyLimit":{"type":"integer","default":1048576},"caseSensitive":{"type":"boolean","default":true},"allowUnsafeRegex":{"type":"boolean","default":false},"http2":{"type":"boolean"},"https":{"if":{"not":{"oneOf":[{"type":"boolean"},{"type":"null"},{"type":"object","additionalProperties":false,"required":["allowHTTP1"],"properties":{"allowHTTP1":{"type":"boolean"}}}]}},"then":{"setDefaultValue":true}},"ignoreTrailingSlash":{"type":"boolean","default":false},"ignoreDuplicateSlashes":{"type":"boolean","default":false},"disableRequestLogging":{"type":"boolean","default":false},"jsonShorthand":{"type":"boolean","default":true},"maxParamLength":{"type":"integer","default":100},"onProtoPoisoning":{"type":"string","default":"error"},"onConstructorPoisoning":{"type":"string","default":"error"},"pluginTimeout":{"type":"integer","default":10000},"requestIdHeader":{"anyOf":[{"enum":[false]},{"type":"string"}],"default":"request-id"},"requestIdLogLabel":{"type":"string","default":"reqId"},"http2SessionTimeout":{"type":"integer","default":72000},"exposeHeadRoutes":{"type":"boolean","default":true},"versioning":{"type":"object","additionalProperties":true,"required":["storage","deriveVersion"],"properties":{"storage":{},"deriveVersion":{}}},"constraints":{"type":"object","additionalProperties":{"type":"object","required":["name","storage","validate","deriveConstraint"],"additionalProperties":true,"properties":{"name":{"type":"string"},"storage":{},"validate":{},"deriveConstraint":{}}}}}};
7
7
  const func2 = Object.prototype.hasOwnProperty;
8
8
  const pattern0 = new RegExp("idle", "u");
9
9
 
@@ -837,6 +837,23 @@ var valid0 = _errs55 === errors;
837
837
  if(valid0){
838
838
  let data19 = data.requestIdHeader;
839
839
  const _errs57 = errors;
840
+ const _errs58 = errors;
841
+ let valid6 = false;
842
+ const _errs59 = errors;
843
+ if(!(data19 === false)){
844
+ const err12 = {instancePath:instancePath+"/requestIdHeader",schemaPath:"#/properties/requestIdHeader/anyOf/0/enum",keyword:"enum",params:{allowedValues: schema11.properties.requestIdHeader.anyOf[0].enum},message:"must be equal to one of the allowed values"};
845
+ if(vErrors === null){
846
+ vErrors = [err12];
847
+ }
848
+ else {
849
+ vErrors.push(err12);
850
+ }
851
+ errors++;
852
+ }
853
+ var _valid3 = _errs59 === errors;
854
+ valid6 = valid6 || _valid3;
855
+ if(!valid6){
856
+ const _errs60 = errors;
840
857
  if(typeof data19 !== "string"){
841
858
  let dataType21 = typeof data19;
842
859
  let coerced21 = undefined;
@@ -848,8 +865,14 @@ else if(data19 === null){
848
865
  coerced21 = "";
849
866
  }
850
867
  else {
851
- validate10.errors = [{instancePath:instancePath+"/requestIdHeader",schemaPath:"#/properties/requestIdHeader/type",keyword:"type",params:{type: "string"},message:"must be string"}];
852
- return false;
868
+ const err13 = {instancePath:instancePath+"/requestIdHeader",schemaPath:"#/properties/requestIdHeader/anyOf/1/type",keyword:"type",params:{type: "string"},message:"must be string"};
869
+ if(vErrors === null){
870
+ vErrors = [err13];
871
+ }
872
+ else {
873
+ vErrors.push(err13);
874
+ }
875
+ errors++;
853
876
  }
854
877
  }
855
878
  if(coerced21 !== undefined){
@@ -859,10 +882,36 @@ data["requestIdHeader"] = coerced21;
859
882
  }
860
883
  }
861
884
  }
885
+ var _valid3 = _errs60 === errors;
886
+ valid6 = valid6 || _valid3;
887
+ }
888
+ if(!valid6){
889
+ const err14 = {instancePath:instancePath+"/requestIdHeader",schemaPath:"#/properties/requestIdHeader/anyOf",keyword:"anyOf",params:{},message:"must match a schema in anyOf"};
890
+ if(vErrors === null){
891
+ vErrors = [err14];
892
+ }
893
+ else {
894
+ vErrors.push(err14);
895
+ }
896
+ errors++;
897
+ validate10.errors = vErrors;
898
+ return false;
899
+ }
900
+ else {
901
+ errors = _errs58;
902
+ if(vErrors !== null){
903
+ if(_errs58){
904
+ vErrors.length = _errs58;
905
+ }
906
+ else {
907
+ vErrors = null;
908
+ }
909
+ }
910
+ }
862
911
  var valid0 = _errs57 === errors;
863
912
  if(valid0){
864
913
  let data20 = data.requestIdLogLabel;
865
- const _errs59 = errors;
914
+ const _errs62 = errors;
866
915
  if(typeof data20 !== "string"){
867
916
  let dataType22 = typeof data20;
868
917
  let coerced22 = undefined;
@@ -885,10 +934,10 @@ data["requestIdLogLabel"] = coerced22;
885
934
  }
886
935
  }
887
936
  }
888
- var valid0 = _errs59 === errors;
937
+ var valid0 = _errs62 === errors;
889
938
  if(valid0){
890
939
  let data21 = data.http2SessionTimeout;
891
- const _errs61 = errors;
940
+ const _errs64 = errors;
892
941
  if(!(((typeof data21 == "number") && (!(data21 % 1) && !isNaN(data21))) && (isFinite(data21)))){
893
942
  let dataType23 = typeof data21;
894
943
  let coerced23 = undefined;
@@ -909,10 +958,10 @@ data["http2SessionTimeout"] = coerced23;
909
958
  }
910
959
  }
911
960
  }
912
- var valid0 = _errs61 === errors;
961
+ var valid0 = _errs64 === errors;
913
962
  if(valid0){
914
963
  let data22 = data.exposeHeadRoutes;
915
- const _errs63 = errors;
964
+ const _errs66 = errors;
916
965
  if(typeof data22 !== "boolean"){
917
966
  let coerced24 = undefined;
918
967
  if(!(coerced24 !== undefined)){
@@ -934,12 +983,12 @@ data["exposeHeadRoutes"] = coerced24;
934
983
  }
935
984
  }
936
985
  }
937
- var valid0 = _errs63 === errors;
986
+ var valid0 = _errs66 === errors;
938
987
  if(valid0){
939
988
  if(data.versioning !== undefined){
940
989
  let data23 = data.versioning;
941
- const _errs65 = errors;
942
- if(errors === _errs65){
990
+ const _errs68 = errors;
991
+ if(errors === _errs68){
943
992
  if(data23 && typeof data23 == "object" && !Array.isArray(data23)){
944
993
  let missing1;
945
994
  if(((data23.storage === undefined) && (missing1 = "storage")) || ((data23.deriveVersion === undefined) && (missing1 = "deriveVersion"))){
@@ -952,7 +1001,7 @@ validate10.errors = [{instancePath:instancePath+"/versioning",schemaPath:"#/prop
952
1001
  return false;
953
1002
  }
954
1003
  }
955
- var valid0 = _errs65 === errors;
1004
+ var valid0 = _errs68 === errors;
956
1005
  }
957
1006
  else {
958
1007
  var valid0 = true;
@@ -960,13 +1009,13 @@ var valid0 = true;
960
1009
  if(valid0){
961
1010
  if(data.constraints !== undefined){
962
1011
  let data24 = data.constraints;
963
- const _errs68 = errors;
964
- if(errors === _errs68){
1012
+ const _errs71 = errors;
1013
+ if(errors === _errs71){
965
1014
  if(data24 && typeof data24 == "object" && !Array.isArray(data24)){
966
1015
  for(const key2 in data24){
967
1016
  let data25 = data24[key2];
968
- const _errs71 = errors;
969
- if(errors === _errs71){
1017
+ const _errs74 = errors;
1018
+ if(errors === _errs74){
970
1019
  if(data25 && typeof data25 == "object" && !Array.isArray(data25)){
971
1020
  let missing2;
972
1021
  if(((((data25.name === undefined) && (missing2 = "name")) || ((data25.storage === undefined) && (missing2 = "storage"))) || ((data25.validate === undefined) && (missing2 = "validate"))) || ((data25.deriveConstraint === undefined) && (missing2 = "deriveConstraint"))){
@@ -1006,8 +1055,8 @@ validate10.errors = [{instancePath:instancePath+"/constraints/" + key2.replace(/
1006
1055
  return false;
1007
1056
  }
1008
1057
  }
1009
- var valid6 = _errs71 === errors;
1010
- if(!valid6){
1058
+ var valid7 = _errs74 === errors;
1059
+ if(!valid7){
1011
1060
  break;
1012
1061
  }
1013
1062
  }
@@ -1017,7 +1066,7 @@ validate10.errors = [{instancePath:instancePath+"/constraints",schemaPath:"#/pro
1017
1066
  return false;
1018
1067
  }
1019
1068
  }
1020
- var valid0 = _errs68 === errors;
1069
+ var valid0 = _errs71 === errors;
1021
1070
  }
1022
1071
  else {
1023
1072
  var valid0 = true;
package/lib/context.js CHANGED
@@ -44,6 +44,7 @@ function Context ({
44
44
  this.onTimeout = null
45
45
  this.preHandler = null
46
46
  this.onResponse = null
47
+ this.preSerialization = null
47
48
  this.config = config
48
49
  this.errorHandler = errorHandler || server[kErrorHandler]
49
50
  this._middie = null
package/lib/fourOhFour.js CHANGED
@@ -10,7 +10,8 @@ const {
10
10
  kCanSetNotFoundHandler,
11
11
  kFourOhFourLevelInstance,
12
12
  kFourOhFourContext,
13
- kHooks
13
+ kHooks,
14
+ kErrorHandler
14
15
  } = require('./symbols.js')
15
16
  const { lifecycleHooks } = require('./hooks')
16
17
  const { buildErrorHandler } = require('./error-handler.js')
@@ -148,6 +149,7 @@ function fourOhFour (options) {
148
149
  .map(h => h.bind(this))
149
150
  context[hook] = toSet.length ? toSet : null
150
151
  }
152
+ context.errorHandler = opts.errorHandler ? buildErrorHandler(this[kErrorHandler], opts.errorHandler) : this[kErrorHandler]
151
153
  })
152
154
 
153
155
  if (this[kFourOhFourContext] !== null && prefix === '/') {
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- module.exports = function () {
3
+ module.exports = function (requestIdHeader, optGenReqId) {
4
4
  // 2,147,483,647 (2^31 − 1) stands for max SMI value (an internal optimization of V8).
5
5
  // With this upper bound, if you'll be generating 1k ids/sec, you're going to hit it in ~25 days.
6
6
  // This is very likely to happen in real-world applications, hence the limit is enforced.
@@ -8,8 +8,19 @@ module.exports = function () {
8
8
  // In the worst cases, it will become a float, losing accuracy.
9
9
  const maxInt = 2147483647
10
10
  let nextReqId = 0
11
- return function genReqId (req) {
11
+ function defaultGenReqId (req) {
12
12
  nextReqId = (nextReqId + 1) & maxInt
13
13
  return `req-${nextReqId.toString(36)}`
14
14
  }
15
+
16
+ const genReqId = optGenReqId || defaultGenReqId
17
+
18
+ if (requestIdHeader) {
19
+ // requestIdHeader = typeof requestIdHeader === 'string' ? requestIdHeader : 'request-id'
20
+ return function (req) {
21
+ return req.headers[requestIdHeader] || genReqId(req)
22
+ }
23
+ }
24
+
25
+ return genReqId
15
26
  }
package/lib/route.js CHANGED
@@ -46,7 +46,6 @@ function buildRouting (options) {
46
46
 
47
47
  let avvio
48
48
  let fourOhFour
49
- let requestIdHeader
50
49
  let requestIdLogLabel
51
50
  let logger
52
51
  let hasLogger
@@ -74,7 +73,6 @@ function buildRouting (options) {
74
73
  validateHTTPVersion = fastifyArgs.validateHTTPVersion
75
74
 
76
75
  globalExposeHeadRoutes = options.exposeHeadRoutes
77
- requestIdHeader = options.requestIdHeader
78
76
  requestIdLogLabel = options.requestIdLogLabel
79
77
  genReqId = options.genReqId
80
78
  disableRequestLogging = options.disableRequestLogging
@@ -397,7 +395,7 @@ function buildRouting (options) {
397
395
  req.headers[kRequestAcceptVersion] = undefined
398
396
  }
399
397
 
400
- const id = req.headers[requestIdHeader] || genReqId(req)
398
+ const id = genReqId(req)
401
399
 
402
400
  const loggerBinding = {
403
401
  [requestIdLogLabel]: id
package/lib/server.js CHANGED
@@ -132,6 +132,7 @@ function multipleBindings (mainServer, httpHandler, serverOpts, listenOptions, o
132
132
 
133
133
  const secondaryServer = getServerInstance(serverOpts, httpHandler)
134
134
  const closeSecondary = () => { secondaryServer.close(() => {}) }
135
+ secondaryServer.on('upgrade', mainServer.emit.bind(mainServer, 'upgrade'))
135
136
  mainServer.on('unref', closeSecondary)
136
137
  mainServer.on('close', closeSecondary)
137
138
  mainServer.on('error', closeSecondary)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fastify",
3
- "version": "4.4.0",
3
+ "version": "4.5.2",
4
4
  "description": "Fast and low overhead web framework, for Node.js",
5
5
  "main": "fastify.js",
6
6
  "type": "commonjs",
@@ -137,8 +137,6 @@
137
137
  "ajv-i18n": "^4.2.0",
138
138
  "ajv-merge-patch": "^5.0.1",
139
139
  "branch-comparer": "^1.1.0",
140
- "cors": "^2.8.5",
141
- "dns-prefetch-control": "^0.3.0",
142
140
  "eslint": "^8.16.0",
143
141
  "eslint-config-standard": "^17.0.0-1",
144
142
  "eslint-import-resolver-node": "^0.3.6",
@@ -150,10 +148,7 @@
150
148
  "fastify-plugin": "^4.0.0",
151
149
  "fluent-json-schema": "^3.1.0",
152
150
  "form-data": "^4.0.0",
153
- "frameguard": "^4.0.0",
154
151
  "h2url": "^0.2.0",
155
- "helmet": "^5.1.0",
156
- "hide-powered-by": "^1.1.0",
157
152
  "http-errors": "^2.0.0",
158
153
  "joi": "^17.6.0",
159
154
  "json-schema-to-ts": "^2.5.3",
@@ -164,7 +159,6 @@
164
159
  "pump": "^3.0.0",
165
160
  "self-cert": "^2.0.0",
166
161
  "send": "^0.18.0",
167
- "serve-static": "^1.15.0",
168
162
  "simple-get": "^4.0.1",
169
163
  "snazzy": "^9.0.0",
170
164
  "split2": "^4.1.0",
@@ -173,7 +167,7 @@
173
167
  "tsd": "^0.22.0",
174
168
  "typescript": "^4.7.2",
175
169
  "undici": "^5.4.0",
176
- "x-xss-protection": "^2.0.0",
170
+ "vary": "^1.1.2",
177
171
  "yup": "^0.32.11"
178
172
  },
179
173
  "dependencies": {
package/test/404s.test.js CHANGED
@@ -1944,3 +1944,68 @@ test('Send 404 when frameworkError calls reply.callNotFound', t => {
1944
1944
 
1945
1945
  t.end()
1946
1946
  })
1947
+
1948
+ test('hooks are applied to not found handlers', async ({ equal }) => {
1949
+ const fastify = Fastify()
1950
+
1951
+ // adding await here is fundamental for this test
1952
+ await fastify.register(async function (fastify) {
1953
+ })
1954
+
1955
+ fastify.setErrorHandler(function (_, request, reply) {
1956
+ return reply.code(401).send({ error: 'Unauthorized' })
1957
+ })
1958
+
1959
+ fastify.addHook('preValidation', async function (request, reply) {
1960
+ throw new Error('kaboom')
1961
+ })
1962
+
1963
+ const { statusCode } = await fastify.inject('/')
1964
+ equal(statusCode, 401)
1965
+ })
1966
+
1967
+ test('hooks are applied to not found handlers /2', async ({ equal }) => {
1968
+ const fastify = Fastify()
1969
+
1970
+ async function plugin (fastify) {
1971
+ fastify.setErrorHandler(function (_, request, reply) {
1972
+ return reply.code(401).send({ error: 'Unauthorized' })
1973
+ })
1974
+ }
1975
+
1976
+ plugin[Symbol.for('skip-override')] = true
1977
+
1978
+ fastify.register(plugin)
1979
+
1980
+ fastify.addHook('preValidation', async function (request, reply) {
1981
+ throw new Error('kaboom')
1982
+ })
1983
+
1984
+ const { statusCode } = await fastify.inject('/')
1985
+ equal(statusCode, 401)
1986
+ })
1987
+
1988
+ test('hooks are applied to not found handlers /3', async ({ equal, fail }) => {
1989
+ const fastify = Fastify()
1990
+
1991
+ async function plugin (fastify) {
1992
+ fastify.setNotFoundHandler({ errorHandler }, async () => {
1993
+ fail('this should never be called')
1994
+ })
1995
+
1996
+ function errorHandler (_, request, reply) {
1997
+ return reply.code(401).send({ error: 'Unauthorized' })
1998
+ }
1999
+ }
2000
+
2001
+ plugin[Symbol.for('skip-override')] = true
2002
+
2003
+ fastify.register(plugin)
2004
+
2005
+ fastify.addHook('preValidation', async function (request, reply) {
2006
+ throw new Error('kaboom')
2007
+ })
2008
+
2009
+ const { statusCode } = await fastify.inject('/')
2010
+ equal(statusCode, 401)
2011
+ })
@@ -710,3 +710,46 @@ test('preSerializationEnd should handle errors if the serialize method throws',
710
710
 
711
711
  t.end()
712
712
  })
713
+
714
+ t.test('nested hooks to do not crash on 404', t => {
715
+ t.plan(2)
716
+ const fastify = Fastify()
717
+
718
+ fastify.get('/hello', (req, reply) => {
719
+ reply.send({ hello: 'world' })
720
+ })
721
+
722
+ fastify.register(async function (fastify) {
723
+ fastify.get('/something', (req, reply) => {
724
+ reply.callNotFound()
725
+ })
726
+
727
+ fastify.setNotFoundHandler(async (request, reply) => {
728
+ reply.statusCode = 404
729
+ return { status: 'nested-not-found' }
730
+ })
731
+
732
+ fastify.setErrorHandler(async (error, request, reply) => {
733
+ reply.statusCode = 500
734
+ return { status: 'nested-error', error }
735
+ })
736
+ }, { prefix: '/nested' })
737
+
738
+ fastify.setNotFoundHandler(async (request, reply) => {
739
+ reply.statusCode = 404
740
+ return { status: 'not-found' }
741
+ })
742
+
743
+ fastify.setErrorHandler(async (error, request, reply) => {
744
+ reply.statusCode = 500
745
+ return { status: 'error', error }
746
+ })
747
+
748
+ fastify.inject({
749
+ method: 'GET',
750
+ url: '/nested/something'
751
+ }, (err, res) => {
752
+ t.error(err)
753
+ t.equal(res.statusCode, 404)
754
+ })
755
+ })
@@ -330,6 +330,51 @@ test('The request id header key can be customized', t => {
330
330
  })
331
331
  })
332
332
 
333
+ test('The request id header key can be ignored', t => {
334
+ t.plan(9)
335
+ const REQUEST_ID = 'ignore-me'
336
+
337
+ const stream = split(JSON.parse)
338
+ const fastify = Fastify({
339
+ logger: { stream, level: 'info' },
340
+ requestIdHeader: false
341
+ })
342
+ t.teardown(() => fastify.close())
343
+
344
+ fastify.get('/', (req, reply) => {
345
+ t.equal(req.id, 'req-1')
346
+ req.log.info('some log message')
347
+ reply.send({ id: req.id })
348
+ })
349
+
350
+ fastify.inject({
351
+ method: 'GET',
352
+ url: '/',
353
+ headers: {
354
+ 'request-id': REQUEST_ID
355
+ }
356
+ }, (err, res) => {
357
+ t.error(err)
358
+ const payload = JSON.parse(res.payload)
359
+ t.equal(payload.id, 'req-1')
360
+
361
+ stream.once('data', line => {
362
+ t.equal(line.reqId, 'req-1')
363
+ t.equal(line.msg, 'incoming request', 'message is set')
364
+
365
+ stream.once('data', line => {
366
+ t.equal(line.reqId, 'req-1')
367
+ t.equal(line.msg, 'some log message', 'message is set')
368
+
369
+ stream.once('data', line => {
370
+ t.equal(line.reqId, 'req-1')
371
+ t.equal(line.msg, 'request completed', 'message is set')
372
+ })
373
+ })
374
+ })
375
+ })
376
+ })
377
+
333
378
  test('The request id header key can be customized along with a custom id generator', t => {
334
379
  t.plan(12)
335
380
  const REQUEST_ID = '42'
@@ -393,6 +438,69 @@ test('The request id header key can be customized along with a custom id generat
393
438
  })
394
439
  })
395
440
 
441
+ test('The request id header key can be ignored along with a custom id generator', t => {
442
+ t.plan(12)
443
+ const REQUEST_ID = 'ignore-me'
444
+
445
+ const stream = split(JSON.parse)
446
+ const fastify = Fastify({
447
+ logger: { stream, level: 'info' },
448
+ requestIdHeader: false,
449
+ genReqId (req) {
450
+ return 'foo'
451
+ }
452
+ })
453
+ t.teardown(() => fastify.close())
454
+
455
+ fastify.get('/one', (req, reply) => {
456
+ t.equal(req.id, 'foo')
457
+ req.log.info('some log message')
458
+ reply.send({ id: req.id })
459
+ })
460
+
461
+ fastify.get('/two', (req, reply) => {
462
+ t.equal(req.id, 'foo')
463
+ req.log.info('some log message 2')
464
+ reply.send({ id: req.id })
465
+ })
466
+
467
+ const matches = [
468
+ { reqId: 'foo', msg: /incoming request/ },
469
+ { reqId: 'foo', msg: /some log message/ },
470
+ { reqId: 'foo', msg: /request completed/ },
471
+ { reqId: 'foo', msg: /incoming request/ },
472
+ { reqId: 'foo', msg: /some log message 2/ },
473
+ { reqId: 'foo', msg: /request completed/ }
474
+ ]
475
+
476
+ let i = 0
477
+ stream.on('data', line => {
478
+ t.match(line, matches[i])
479
+ i += 1
480
+ })
481
+
482
+ fastify.inject({
483
+ method: 'GET',
484
+ url: '/one',
485
+ headers: {
486
+ 'request-id': REQUEST_ID
487
+ }
488
+ }, (err, res) => {
489
+ t.error(err)
490
+ const payload = JSON.parse(res.payload)
491
+ t.equal(payload.id, 'foo')
492
+ })
493
+
494
+ fastify.inject({
495
+ method: 'GET',
496
+ url: '/two'
497
+ }, (err, res) => {
498
+ t.error(err)
499
+ const payload = JSON.parse(res.payload)
500
+ t.equal(payload.id, 'foo')
501
+ })
502
+ })
503
+
396
504
  test('The request id log label can be changed', t => {
397
505
  t.plan(6)
398
506
  const REQUEST_ID = '42'
@@ -509,6 +509,60 @@ test('should return custom error messages with ajv-errors', t => {
509
509
  })
510
510
  })
511
511
 
512
+ test('should be able to handle formats of ajv-formats when added by plugins option', t => {
513
+ t.plan(3)
514
+
515
+ const fastify = Fastify({
516
+ ajv: {
517
+ plugins: [
518
+ require('ajv-formats')
519
+ ]
520
+ }
521
+ })
522
+
523
+ const schema = {
524
+ body: {
525
+ type: 'object',
526
+ properties: {
527
+ id: { type: 'string', format: 'uuid' },
528
+ email: { type: 'string', format: 'email' }
529
+ },
530
+ required: ['id', 'email']
531
+ }
532
+ }
533
+
534
+ fastify.post('/', { schema }, function (req, reply) {
535
+ reply.code(200).send(req.body.id)
536
+ })
537
+
538
+ fastify.inject({
539
+ method: 'POST',
540
+ payload: {
541
+ id: '254381a5-888c-4b41-8116-e3b1a54980bd',
542
+ email: 'info@fastify.io'
543
+ },
544
+ url: '/'
545
+ }, (_err, res) => {
546
+ t.equal(res.body, '254381a5-888c-4b41-8116-e3b1a54980bd')
547
+ t.equal(res.statusCode, 200)
548
+ })
549
+
550
+ fastify.inject({
551
+ method: 'POST',
552
+ payload: {
553
+ id: 'invalid',
554
+ email: 'info@fastify.io'
555
+ },
556
+ url: '/'
557
+ }, (_err, res) => {
558
+ t.same(JSON.parse(res.payload), {
559
+ statusCode: 400,
560
+ error: 'Bad Request',
561
+ message: 'body/id must match format "uuid"'
562
+ })
563
+ })
564
+ })
565
+
512
566
  test('should return localized error messages with ajv-i18n', t => {
513
567
  t.plan(3)
514
568
 
@@ -121,6 +121,7 @@ expectAssignable<FastifyInstance<http.Server, http.IncomingMessage, http.ServerR
121
121
  expectAssignable<FastifyInstance>(fastify({ serverFactory: () => http.createServer() }))
122
122
  expectAssignable<FastifyInstance>(fastify({ caseSensitive: true }))
123
123
  expectAssignable<FastifyInstance>(fastify({ requestIdHeader: 'request-id' }))
124
+ expectAssignable<FastifyInstance>(fastify({ requestIdHeader: false }))
124
125
  expectAssignable<FastifyInstance>(fastify({ genReqId: () => 'request-id' }))
125
126
  expectAssignable<FastifyInstance>(fastify({ trustProxy: true }))
126
127
  expectAssignable<FastifyInstance>(fastify({ querystringParser: () => ({ foo: 'bar' }) }))
@@ -267,7 +267,7 @@ type InitialConfig = Readonly<{
267
267
  onProtoPoisoning?: 'error' | 'remove' | 'ignore',
268
268
  onConstructorPoisoning?: 'error' | 'remove' | 'ignore',
269
269
  pluginTimeout?: number,
270
- requestIdHeader?: string,
270
+ requestIdHeader?: string | false,
271
271
  requestIdLogLabel?: string,
272
272
  http2SessionTimeout?: number
273
273
  }>
@@ -0,0 +1,53 @@
1
+ 'use strict'
2
+
3
+ const { test, skip } = require('tap')
4
+ const { lookup } = require('dns').promises
5
+ const Fastify = require('..')
6
+ const { connect } = require('net')
7
+ const { once } = require('events')
8
+
9
+ async function setup () {
10
+ const results = await lookup('localhost', { all: true })
11
+ if (results.length === 1) {
12
+ skip('requires both IPv4 and IPv6')
13
+ return
14
+ }
15
+
16
+ test('upgrade to both servers', async t => {
17
+ t.plan(2)
18
+ const app = Fastify()
19
+ app.server.on('upgrade', (req, socket, head) => {
20
+ t.pass(`upgrade event ${JSON.stringify(socket.address())}`)
21
+ socket.end()
22
+ })
23
+ app.get('/', (req, res) => {
24
+ })
25
+ await app.listen()
26
+ t.teardown(app.close.bind(app))
27
+
28
+ {
29
+ const client = connect(app.server.address().port, '127.0.0.1')
30
+ client.write('GET / HTTP/1.1\r\n')
31
+ client.write('Upgrade: websocket\r\n')
32
+ client.write('Connection: Upgrade\r\n')
33
+ client.write('Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n')
34
+ client.write('Sec-WebSocket-Protocol: com.xxx.service.v1\r\n')
35
+ client.write('Sec-WebSocket-Version: 13\r\n\r\n')
36
+ client.write('\r\n\r\n')
37
+ await once(client, 'close')
38
+ }
39
+
40
+ {
41
+ const client = connect(app.server.address().port, '::1')
42
+ client.write('GET / HTTP/1.1\r\n')
43
+ client.write('Upgrade: websocket\r\n')
44
+ client.write('Connection: Upgrade\r\n')
45
+ client.write('Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n')
46
+ client.write('Sec-WebSocket-Protocol: com.xxx.service.v1\r\n')
47
+ client.write('Sec-WebSocket-Version: 13\r\n\r\n')
48
+ await once(client, 'close')
49
+ }
50
+ })
51
+ }
52
+
53
+ setup()
@@ -573,7 +573,7 @@ export interface FastifyInstance<
573
573
  onProtoPoisoning?: ProtoAction,
574
574
  onConstructorPoisoning?: ConstructorAction,
575
575
  pluginTimeout?: number,
576
- requestIdHeader?: string,
576
+ requestIdHeader?: string | false,
577
577
  requestIdLogLabel?: string,
578
578
  http2SessionTimeout?: number
579
579
  }>