fastify
Advanced tools
Comparing version 4.4.0 to 4.5.0
@@ -100,3 +100,3 @@ 'use strict' | ||
pluginTimeout: { type: 'integer', default: defaultInitOptions.pluginTimeout }, | ||
requestIdHeader: { type: 'string', default: defaultInitOptions.requestIdHeader }, | ||
requestIdHeader: { anyOf: [{ enum: [false] }, { type: 'string' }], default: defaultInitOptions.requestIdHeader }, | ||
requestIdLogLabel: { type: 'string', default: defaultInitOptions.requestIdLogLabel }, | ||
@@ -103,0 +103,0 @@ http2SessionTimeout: { type: 'integer', default: defaultInitOptions.http2SessionTimeout }, |
@@ -331,2 +331,4 @@ <h1 align="center">Fastify</h1> | ||
on HTTP and HTTPS. | ||
- [`fastify-https-always`](https://github.com/mattbishop/fastify-https-always) | ||
Lightweight, proxy-aware redirect plugin from HTTP to HTTPS. | ||
- [`fastify-https-redirect`](https://github.com/tomsvogel/fastify-https-redirect) | ||
@@ -430,2 +432,4 @@ Fastify plugin for auto-redirect from HTTP to HTTPS. | ||
across every part of your server. | ||
- [`fastify-osm`](https://github.com/gzileni/fastify-osm) Fastify | ||
OSM plugin to run overpass queries by OpenStreetMap. | ||
- [`fastify-peekaboo`](https://github.com/simone-sanfratello/fastify-peekaboo) | ||
@@ -432,0 +436,0 @@ Fastify plugin for memoize responses by expressive settings. |
@@ -661,3 +661,3 @@ <h1 align="center">Fastify</h1> | ||
> const append = require('vary').append | ||
> fastify.addHook('onSend', async (req, reply) => { | ||
> fastify.addHook('onSend', (req, reply, payload, done) => { | ||
> if (req.headers['accept-version']) { // or the custom header you are using | ||
@@ -670,2 +670,3 @@ > let value = reply.getHeader('Vary') || '' | ||
> } | ||
> done() | ||
> }) | ||
@@ -672,0 +673,0 @@ > ``` |
@@ -58,3 +58,3 @@ <h1 align="center">Fastify</h1> | ||
- [close](#close) | ||
- [decorate\*](#decorate) | ||
- [decorate*](#decorate) | ||
- [register](#register) | ||
@@ -492,7 +492,14 @@ - [addHook](#addhook) | ||
The header name used to know the request-id. See [the | ||
The header name used to set the request-id. See [the | ||
request-id](./Logging.md#logging-request-id) section. | ||
Setting `requestIdHeader` to `false` will always use [genReqId](#genreqid) | ||
+ Default: `'request-id'` | ||
```js | ||
const fastify = require('fastify')({ | ||
requestIdHeader: 'x-custom-id', // -> use 'X-Custom-Id' header if available | ||
//requestIdHeader: false, // -> always use genReqId | ||
}) | ||
``` | ||
### `requestIdLogLabel` | ||
@@ -1117,8 +1124,11 @@ <a id="factory-request-id-log-label"></a> | ||
Name of the current plugin. The root plugin is called `'fastify'`. There are | ||
three ways to define a name (in order). | ||
different ways to define a name (in order). | ||
1. If you use [fastify-plugin](https://github.com/fastify/fastify-plugin) the | ||
metadata `name` is used. | ||
2. If you `module.exports` a plugin the filename is used. | ||
3. If you use a regular [function | ||
2. If the exported plugin has the `Symbol.for('fastify.display-name')` property, | ||
then the value of that property is used. | ||
Example: `pluginFn[Symbol.for('fastify.display-name')] = "Custom Name"` | ||
3. If you `module.exports` a plugin the filename is used. | ||
4. If you use a regular [function | ||
declaration](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions#Defining_functions) | ||
@@ -1125,0 +1135,0 @@ the function name is used. |
@@ -271,3 +271,3 @@ <h1 align="center">Fastify</h1> | ||
{"params":{"hello":["1"]}} | ||
{"params":{"ids":["1"]}} | ||
``` | ||
@@ -274,0 +274,0 @@ |
@@ -126,3 +126,3 @@ import * as http from 'http' | ||
caseSensitive?: boolean, | ||
requestIdHeader?: string, | ||
requestIdHeader?: string | false, | ||
requestIdLogLabel?: string; | ||
@@ -129,0 +129,0 @@ jsonShorthand?: boolean; |
'use strict' | ||
const VERSION = '4.4.0' | ||
const VERSION = '4.5.0' | ||
@@ -101,4 +101,4 @@ const Avvio = require('avvio') | ||
const requestIdHeader = options.requestIdHeader || defaultInitOptions.requestIdHeader | ||
const genReqId = options.genReqId || reqIdGenFactory() | ||
const requestIdHeader = (options.requestIdHeader === false) ? false : (options.requestIdHeader || defaultInitOptions.requestIdHeader) | ||
const genReqId = reqIdGenFactory(requestIdHeader, options.genReqId) | ||
const requestIdLogLabel = options.requestIdLogLabel || 'reqId' | ||
@@ -105,0 +105,0 @@ const bodyLimit = options.bodyLimit || defaultInitOptions.bodyLimit |
@@ -6,3 +6,3 @@ // This file is autogenerated by build/build-validation.js, do not edit | ||
module.exports.default = validate10; | ||
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":{}}}}}}; | ||
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":{}}}}}}; | ||
const func2 = Object.prototype.hasOwnProperty; | ||
@@ -841,2 +841,19 @@ const pattern0 = new RegExp("idle", "u"); | ||
const _errs57 = errors; | ||
const _errs58 = errors; | ||
let valid6 = false; | ||
const _errs59 = errors; | ||
if(!(data19 === false)){ | ||
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"}; | ||
if(vErrors === null){ | ||
vErrors = [err12]; | ||
} | ||
else { | ||
vErrors.push(err12); | ||
} | ||
errors++; | ||
} | ||
var _valid3 = _errs59 === errors; | ||
valid6 = valid6 || _valid3; | ||
if(!valid6){ | ||
const _errs60 = errors; | ||
if(typeof data19 !== "string"){ | ||
@@ -853,6 +870,12 @@ let dataType21 = typeof data19; | ||
else { | ||
validate10.errors = [{instancePath:instancePath+"/requestIdHeader",schemaPath:"#/properties/requestIdHeader/type",keyword:"type",params:{type: "string"},message:"must be string"}]; | ||
return false; | ||
const err13 = {instancePath:instancePath+"/requestIdHeader",schemaPath:"#/properties/requestIdHeader/anyOf/1/type",keyword:"type",params:{type: "string"},message:"must be string"}; | ||
if(vErrors === null){ | ||
vErrors = [err13]; | ||
} | ||
else { | ||
vErrors.push(err13); | ||
} | ||
errors++; | ||
} | ||
} | ||
if(coerced21 !== undefined){ | ||
@@ -865,6 +888,32 @@ data19 = coerced21; | ||
} | ||
var _valid3 = _errs60 === errors; | ||
valid6 = valid6 || _valid3; | ||
} | ||
if(!valid6){ | ||
const err14 = {instancePath:instancePath+"/requestIdHeader",schemaPath:"#/properties/requestIdHeader/anyOf",keyword:"anyOf",params:{},message:"must match a schema in anyOf"}; | ||
if(vErrors === null){ | ||
vErrors = [err14]; | ||
} | ||
else { | ||
vErrors.push(err14); | ||
} | ||
errors++; | ||
validate10.errors = vErrors; | ||
return false; | ||
} | ||
else { | ||
errors = _errs58; | ||
if(vErrors !== null){ | ||
if(_errs58){ | ||
vErrors.length = _errs58; | ||
} | ||
else { | ||
vErrors = null; | ||
} | ||
} | ||
} | ||
var valid0 = _errs57 === errors; | ||
if(valid0){ | ||
let data20 = data.requestIdLogLabel; | ||
const _errs59 = errors; | ||
const _errs62 = errors; | ||
if(typeof data20 !== "string"){ | ||
@@ -892,6 +941,6 @@ let dataType22 = typeof data20; | ||
} | ||
var valid0 = _errs59 === errors; | ||
var valid0 = _errs62 === errors; | ||
if(valid0){ | ||
let data21 = data.http2SessionTimeout; | ||
const _errs61 = errors; | ||
const _errs64 = errors; | ||
if(!(((typeof data21 == "number") && (!(data21 % 1) && !isNaN(data21))) && (isFinite(data21)))){ | ||
@@ -917,6 +966,6 @@ let dataType23 = typeof data21; | ||
} | ||
var valid0 = _errs61 === errors; | ||
var valid0 = _errs64 === errors; | ||
if(valid0){ | ||
let data22 = data.exposeHeadRoutes; | ||
const _errs63 = errors; | ||
const _errs66 = errors; | ||
if(typeof data22 !== "boolean"){ | ||
@@ -943,8 +992,8 @@ let coerced24 = undefined; | ||
} | ||
var valid0 = _errs63 === errors; | ||
var valid0 = _errs66 === errors; | ||
if(valid0){ | ||
if(data.versioning !== undefined){ | ||
let data23 = data.versioning; | ||
const _errs65 = errors; | ||
if(errors === _errs65){ | ||
const _errs68 = errors; | ||
if(errors === _errs68){ | ||
if(data23 && typeof data23 == "object" && !Array.isArray(data23)){ | ||
@@ -962,3 +1011,3 @@ let missing1; | ||
} | ||
var valid0 = _errs65 === errors; | ||
var valid0 = _errs68 === errors; | ||
} | ||
@@ -971,9 +1020,9 @@ else { | ||
let data24 = data.constraints; | ||
const _errs68 = errors; | ||
if(errors === _errs68){ | ||
const _errs71 = errors; | ||
if(errors === _errs71){ | ||
if(data24 && typeof data24 == "object" && !Array.isArray(data24)){ | ||
for(const key2 in data24){ | ||
let data25 = data24[key2]; | ||
const _errs71 = errors; | ||
if(errors === _errs71){ | ||
const _errs74 = errors; | ||
if(errors === _errs74){ | ||
if(data25 && typeof data25 == "object" && !Array.isArray(data25)){ | ||
@@ -1018,4 +1067,4 @@ let missing2; | ||
} | ||
var valid6 = _errs71 === errors; | ||
if(!valid6){ | ||
var valid7 = _errs74 === errors; | ||
if(!valid7){ | ||
break; | ||
@@ -1030,3 +1079,3 @@ } | ||
} | ||
var valid0 = _errs68 === errors; | ||
var valid0 = _errs71 === errors; | ||
} | ||
@@ -1033,0 +1082,0 @@ else { |
'use strict' | ||
module.exports = function () { | ||
module.exports = function (requestIdHeader, optGenReqId) { | ||
// 2,147,483,647 (2^31 − 1) stands for max SMI value (an internal optimization of V8). | ||
@@ -11,6 +11,17 @@ // With this upper bound, if you'll be generating 1k ids/sec, you're going to hit it in ~25 days. | ||
let nextReqId = 0 | ||
return function genReqId (req) { | ||
function defaultGenReqId (req) { | ||
nextReqId = (nextReqId + 1) & maxInt | ||
return `req-${nextReqId.toString(36)}` | ||
} | ||
const genReqId = optGenReqId || defaultGenReqId | ||
if (requestIdHeader) { | ||
// requestIdHeader = typeof requestIdHeader === 'string' ? requestIdHeader : 'request-id' | ||
return function (req) { | ||
return req.headers[requestIdHeader] || genReqId(req) | ||
} | ||
} | ||
return genReqId | ||
} |
@@ -49,3 +49,2 @@ 'use strict' | ||
let fourOhFour | ||
let requestIdHeader | ||
let requestIdLogLabel | ||
@@ -78,3 +77,2 @@ let logger | ||
globalExposeHeadRoutes = options.exposeHeadRoutes | ||
requestIdHeader = options.requestIdHeader | ||
requestIdLogLabel = options.requestIdLogLabel | ||
@@ -402,3 +400,3 @@ genReqId = options.genReqId | ||
const id = req.headers[requestIdHeader] || genReqId(req) | ||
const id = genReqId(req) | ||
@@ -405,0 +403,0 @@ const loggerBinding = { |
@@ -135,2 +135,3 @@ 'use strict' | ||
const closeSecondary = () => { secondaryServer.close(() => {}) } | ||
secondaryServer.on('upgrade', mainServer.emit.bind(mainServer, 'upgrade')) | ||
mainServer.on('unref', closeSecondary) | ||
@@ -137,0 +138,0 @@ mainServer.on('close', closeSecondary) |
{ | ||
"name": "fastify", | ||
"version": "4.4.0", | ||
"version": "4.5.0", | ||
"description": "Fast and low overhead web framework, for Node.js", | ||
@@ -140,4 +140,2 @@ "main": "fastify.js", | ||
"branch-comparer": "^1.1.0", | ||
"cors": "^2.8.5", | ||
"dns-prefetch-control": "^0.3.0", | ||
"eslint": "^8.16.0", | ||
@@ -154,6 +152,3 @@ "eslint-config-standard": "^17.0.0-1", | ||
"form-data": "^4.0.0", | ||
"frameguard": "^4.0.0", | ||
"h2url": "^0.2.0", | ||
"helmet": "^5.1.0", | ||
"hide-powered-by": "^1.1.0", | ||
"http-errors": "^2.0.0", | ||
@@ -169,3 +164,2 @@ "joi": "^17.6.0", | ||
"send": "^0.18.0", | ||
"serve-static": "^1.15.0", | ||
"simple-get": "^4.0.1", | ||
@@ -179,3 +173,3 @@ "snazzy": "^9.0.0", | ||
"undici": "^5.4.0", | ||
"x-xss-protection": "^2.0.0", | ||
"vary": "^1.1.2", | ||
"yup": "^0.32.11" | ||
@@ -182,0 +176,0 @@ }, |
@@ -333,2 +333,47 @@ 'use strict' | ||
test('The request id header key can be ignored', t => { | ||
t.plan(9) | ||
const REQUEST_ID = 'ignore-me' | ||
const stream = split(JSON.parse) | ||
const fastify = Fastify({ | ||
logger: { stream, level: 'info' }, | ||
requestIdHeader: false | ||
}) | ||
t.teardown(() => fastify.close()) | ||
fastify.get('/', (req, reply) => { | ||
t.equal(req.id, 'req-1') | ||
req.log.info('some log message') | ||
reply.send({ id: req.id }) | ||
}) | ||
fastify.inject({ | ||
method: 'GET', | ||
url: '/', | ||
headers: { | ||
'request-id': REQUEST_ID | ||
} | ||
}, (err, res) => { | ||
t.error(err) | ||
const payload = JSON.parse(res.payload) | ||
t.equal(payload.id, 'req-1') | ||
stream.once('data', line => { | ||
t.equal(line.reqId, 'req-1') | ||
t.equal(line.msg, 'incoming request', 'message is set') | ||
stream.once('data', line => { | ||
t.equal(line.reqId, 'req-1') | ||
t.equal(line.msg, 'some log message', 'message is set') | ||
stream.once('data', line => { | ||
t.equal(line.reqId, 'req-1') | ||
t.equal(line.msg, 'request completed', 'message is set') | ||
}) | ||
}) | ||
}) | ||
}) | ||
}) | ||
test('The request id header key can be customized along with a custom id generator', t => { | ||
@@ -397,2 +442,65 @@ t.plan(12) | ||
test('The request id header key can be ignored along with a custom id generator', t => { | ||
t.plan(12) | ||
const REQUEST_ID = 'ignore-me' | ||
const stream = split(JSON.parse) | ||
const fastify = Fastify({ | ||
logger: { stream, level: 'info' }, | ||
requestIdHeader: false, | ||
genReqId (req) { | ||
return 'foo' | ||
} | ||
}) | ||
t.teardown(() => fastify.close()) | ||
fastify.get('/one', (req, reply) => { | ||
t.equal(req.id, 'foo') | ||
req.log.info('some log message') | ||
reply.send({ id: req.id }) | ||
}) | ||
fastify.get('/two', (req, reply) => { | ||
t.equal(req.id, 'foo') | ||
req.log.info('some log message 2') | ||
reply.send({ id: req.id }) | ||
}) | ||
const matches = [ | ||
{ reqId: 'foo', msg: /incoming request/ }, | ||
{ reqId: 'foo', msg: /some log message/ }, | ||
{ reqId: 'foo', msg: /request completed/ }, | ||
{ reqId: 'foo', msg: /incoming request/ }, | ||
{ reqId: 'foo', msg: /some log message 2/ }, | ||
{ reqId: 'foo', msg: /request completed/ } | ||
] | ||
let i = 0 | ||
stream.on('data', line => { | ||
t.match(line, matches[i]) | ||
i += 1 | ||
}) | ||
fastify.inject({ | ||
method: 'GET', | ||
url: '/one', | ||
headers: { | ||
'request-id': REQUEST_ID | ||
} | ||
}, (err, res) => { | ||
t.error(err) | ||
const payload = JSON.parse(res.payload) | ||
t.equal(payload.id, 'foo') | ||
}) | ||
fastify.inject({ | ||
method: 'GET', | ||
url: '/two' | ||
}, (err, res) => { | ||
t.error(err) | ||
const payload = JSON.parse(res.payload) | ||
t.equal(payload.id, 'foo') | ||
}) | ||
}) | ||
test('The request id log label can be changed', t => { | ||
@@ -399,0 +507,0 @@ t.plan(6) |
@@ -512,2 +512,56 @@ 'use strict' | ||
test('should be able to handle formats of ajv-formats when added by plugins option', t => { | ||
t.plan(3) | ||
const fastify = Fastify({ | ||
ajv: { | ||
plugins: [ | ||
require('ajv-formats') | ||
] | ||
} | ||
}) | ||
const schema = { | ||
body: { | ||
type: 'object', | ||
properties: { | ||
id: { type: 'string', format: 'uuid' }, | ||
email: { type: 'string', format: 'email' } | ||
}, | ||
required: ['id', 'email'] | ||
} | ||
} | ||
fastify.post('/', { schema }, function (req, reply) { | ||
reply.code(200).send(req.body.id) | ||
}) | ||
fastify.inject({ | ||
method: 'POST', | ||
payload: { | ||
id: '254381a5-888c-4b41-8116-e3b1a54980bd', | ||
email: 'info@fastify.io' | ||
}, | ||
url: '/' | ||
}, (_err, res) => { | ||
t.equal(res.body, '254381a5-888c-4b41-8116-e3b1a54980bd') | ||
t.equal(res.statusCode, 200) | ||
}) | ||
fastify.inject({ | ||
method: 'POST', | ||
payload: { | ||
id: 'invalid', | ||
email: 'info@fastify.io' | ||
}, | ||
url: '/' | ||
}, (_err, res) => { | ||
t.same(JSON.parse(res.payload), { | ||
statusCode: 400, | ||
error: 'Bad Request', | ||
message: 'body/id must match format "uuid"' | ||
}) | ||
}) | ||
}) | ||
test('should return localized error messages with ajv-i18n', t => { | ||
@@ -514,0 +568,0 @@ t.plan(3) |
@@ -124,2 +124,3 @@ import fastify, { | ||
expectAssignable<FastifyInstance>(fastify({ requestIdHeader: 'request-id' })) | ||
expectAssignable<FastifyInstance>(fastify({ requestIdHeader: false })) | ||
expectAssignable<FastifyInstance>(fastify({ genReqId: () => 'request-id' })) | ||
@@ -126,0 +127,0 @@ expectAssignable<FastifyInstance>(fastify({ trustProxy: true })) |
@@ -270,3 +270,3 @@ import { expectAssignable, expectDeprecated, expectError, expectNotDeprecated, expectType } from 'tsd' | ||
pluginTimeout?: number, | ||
requestIdHeader?: string, | ||
requestIdHeader?: string | false, | ||
requestIdLogLabel?: string, | ||
@@ -273,0 +273,0 @@ http2SessionTimeout?: number |
@@ -576,3 +576,3 @@ import { FastifyError } from '@fastify/error' | ||
pluginTimeout?: number, | ||
requestIdHeader?: string, | ||
requestIdHeader?: string | false, | ||
requestIdLogLabel?: string, | ||
@@ -579,0 +579,0 @@ http2SessionTimeout?: number |
1905120
44
269
45701
47