Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add option to disable/ignore request-id header #4193

Merged
merged 5 commits into from
Aug 11, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion build/build-validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ const schema = {
onProtoPoisoning: { type: 'string', default: defaultInitOptions.onProtoPoisoning },
onConstructorPoisoning: { type: 'string', default: defaultInitOptions.onConstructorPoisoning },
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 },
http2SessionTimeout: { type: 'integer', default: defaultInitOptions.http2SessionTimeout },
exposeHeadRoutes: { type: 'boolean', default: defaultInitOptions.exposeHeadRoutes },
Expand Down
2 changes: 1 addition & 1 deletion fastify.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ export type FastifyServerOptions<
serializerOpts?: FJSOptions | Record<string, unknown>,
serverFactory?: FastifyServerFactory<RawServer>,
caseSensitive?: boolean,
requestIdHeader?: string,
requestIdHeader?: string | false,
requestIdLogLabel?: string;
jsonShorthand?: boolean;
genReqId?: <RequestGeneric extends RequestGenericInterface = RequestGenericInterface, TypeProvider extends FastifyTypeProvider = FastifyTypeProviderDefault>(req: FastifyRequest<RequestGeneric, RawServer, RawRequestDefaultExpression<RawServer>, FastifySchema, TypeProvider>) => string,
Expand Down
4 changes: 2 additions & 2 deletions fastify.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ function fastify (options) {

validateBodyLimitOption(options.bodyLimit)

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'
const bodyLimit = options.bodyLimit || defaultInitOptions.bodyLimit
const disableRequestLogging = options.disableRequestLogging || false
Expand Down
87 changes: 68 additions & 19 deletions lib/configValidator.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"use strict";
module.exports = validate10;
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;
const pattern0 = new RegExp("idle", "u");

Expand Down Expand Up @@ -837,6 +837,23 @@ var valid0 = _errs55 === errors;
if(valid0){
let data19 = data.requestIdHeader;
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"){
let dataType21 = typeof data19;
let coerced21 = undefined;
Expand All @@ -848,8 +865,14 @@ else if(data19 === null){
coerced21 = "";
}
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){
Expand All @@ -859,10 +882,36 @@ data["requestIdHeader"] = 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"){
let dataType22 = typeof data20;
let coerced22 = undefined;
Expand All @@ -885,10 +934,10 @@ data["requestIdLogLabel"] = coerced22;
}
}
}
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)))){
let dataType23 = typeof data21;
let coerced23 = undefined;
Expand All @@ -909,10 +958,10 @@ data["http2SessionTimeout"] = coerced23;
}
}
}
var valid0 = _errs61 === errors;
var valid0 = _errs64 === errors;
if(valid0){
let data22 = data.exposeHeadRoutes;
const _errs63 = errors;
const _errs66 = errors;
if(typeof data22 !== "boolean"){
let coerced24 = undefined;
if(!(coerced24 !== undefined)){
Expand All @@ -934,12 +983,12 @@ data["exposeHeadRoutes"] = coerced24;
}
}
}
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)){
let missing1;
if(((data23.storage === undefined) && (missing1 = "storage")) || ((data23.deriveVersion === undefined) && (missing1 = "deriveVersion"))){
Expand All @@ -952,21 +1001,21 @@ validate10.errors = [{instancePath:instancePath+"/versioning",schemaPath:"#/prop
return false;
}
}
var valid0 = _errs65 === errors;
var valid0 = _errs68 === errors;
}
else {
var valid0 = true;
}
if(valid0){
if(data.constraints !== undefined){
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)){
let missing2;
if(((((data25.name === undefined) && (missing2 = "name")) || ((data25.storage === undefined) && (missing2 = "storage"))) || ((data25.validate === undefined) && (missing2 = "validate"))) || ((data25.deriveConstraint === undefined) && (missing2 = "deriveConstraint"))){
Expand Down Expand Up @@ -1006,8 +1055,8 @@ validate10.errors = [{instancePath:instancePath+"/constraints/" + key2.replace(/
return false;
}
}
var valid6 = _errs71 === errors;
if(!valid6){
var valid7 = _errs74 === errors;
if(!valid7){
break;
}
}
Expand All @@ -1017,7 +1066,7 @@ validate10.errors = [{instancePath:instancePath+"/constraints",schemaPath:"#/pro
return false;
}
}
var valid0 = _errs68 === errors;
var valid0 = _errs71 === errors;
}
else {
var valid0 = true;
Expand Down
15 changes: 13 additions & 2 deletions lib/reqIdGenFactory.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,26 @@
'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).
// With this upper bound, if you'll be generating 1k ids/sec, you're going to hit it in ~25 days.
// This is very likely to happen in real-world applications, hence the limit is enforced.
// Growing beyond this value will make the id generation slower and cause a deopt.
// In the worst cases, it will become a float, losing accuracy.
const maxInt = 2147483647
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
}
4 changes: 1 addition & 3 deletions lib/route.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ function buildRouting (options) {

let avvio
let fourOhFour
let requestIdHeader
let requestIdLogLabel
let logger
let hasLogger
Expand Down Expand Up @@ -74,7 +73,6 @@ function buildRouting (options) {
validateHTTPVersion = fastifyArgs.validateHTTPVersion

globalExposeHeadRoutes = options.exposeHeadRoutes
requestIdHeader = options.requestIdHeader
requestIdLogLabel = options.requestIdLogLabel
genReqId = options.genReqId
disableRequestLogging = options.disableRequestLogging
Expand Down Expand Up @@ -397,7 +395,7 @@ function buildRouting (options) {
req.headers[kRequestAcceptVersion] = undefined
}

const id = req.headers[requestIdHeader] || genReqId(req)
const id = genReqId(req)

const loggerBinding = {
[requestIdLogLabel]: id
Expand Down
108 changes: 108 additions & 0 deletions test/logger.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,51 @@ test('The request id header key can be customized', t => {
})
})

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 => {
t.plan(12)
const REQUEST_ID = '42'
Expand Down Expand Up @@ -393,6 +438,69 @@ test('The request id header key can be customized along with a custom id generat
})
})

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 => {
t.plan(6)
const REQUEST_ID = '42'
Expand Down
1 change: 1 addition & 0 deletions test/types/fastify.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ expectAssignable<FastifyInstance<http.Server, http.IncomingMessage, http.ServerR
expectAssignable<FastifyInstance>(fastify({ serverFactory: () => http.createServer() }))
expectAssignable<FastifyInstance>(fastify({ caseSensitive: true }))
expectAssignable<FastifyInstance>(fastify({ requestIdHeader: 'request-id' }))
expectAssignable<FastifyInstance>(fastify({ requestIdHeader: false }))
expectAssignable<FastifyInstance>(fastify({ genReqId: () => 'request-id' }))
expectAssignable<FastifyInstance>(fastify({ trustProxy: true }))
expectAssignable<FastifyInstance>(fastify({ querystringParser: () => ({ foo: 'bar' }) }))
Expand Down
2 changes: 1 addition & 1 deletion test/types/instance.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ type InitialConfig = Readonly<{
onProtoPoisoning?: 'error' | 'remove' | 'ignore',
onConstructorPoisoning?: 'error' | 'remove' | 'ignore',
pluginTimeout?: number,
requestIdHeader?: string,
requestIdHeader?: string | false,
requestIdLogLabel?: string,
http2SessionTimeout?: number
}>
Expand Down