Skip to content

Commit

Permalink
Merge branch 'main' into next
Browse files Browse the repository at this point in the history
# Conflicts:
#	lib/configValidator.js
  • Loading branch information
climba03003 committed Dec 30, 2023
2 parents fb5b596 8c0140a commit 4064cb5
Show file tree
Hide file tree
Showing 16 changed files with 801 additions and 44 deletions.
4 changes: 3 additions & 1 deletion build/build-validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 41,8 @@ const defaultInitOptions = {
requestIdHeader: false,
requestIdLogLabel: 'reqId',
http2SessionTimeout: 72000, // 72 seconds
exposeHeadRoutes: true
exposeHeadRoutes: true,
useSemicolonDelimiter: true
}

const schema = {
Expand Down Expand Up @@ -101,6 102,7 @@ const schema = {
requestIdLogLabel: { type: 'string', default: defaultInitOptions.requestIdLogLabel },
http2SessionTimeout: { type: 'integer', default: defaultInitOptions.http2SessionTimeout },
exposeHeadRoutes: { type: 'boolean', default: defaultInitOptions.exposeHeadRoutes },
useSemicolonDelimiter: { type: 'boolean', default: defaultInitOptions.useSemicolonDelimiter },
// deprecated style of passing the versioning constraint
versioning: {
type: 'object',
Expand Down
50 changes: 50 additions & 0 deletions docs/Reference/Server.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 44,7 @@ describes the properties available in that options object.
- [`frameworkErrors`](#frameworkerrors)
- [`clientErrorHandler`](#clienterrorhandler)
- [`rewriteUrl`](#rewriteurl)
- [`useSemicolonDelimiter`](#usesemicolondelimiter)
- [Instance](#instance)
- [Server Methods](#server-methods)
- [server](#server)
Expand All @@ -55,6 56,7 @@ describes the properties available in that options object.
- [routing](#routing)
- [route](#route)
- [hasRoute](#hasroute)
- [findRoute](#findroute)
- [close](#close)
- [decorate\*](#decorate)
- [register](#register)
Expand Down Expand Up @@ -865,6 867,31 @@ function rewriteUrl (req) {
}
```
### `useSemicolonDelimiter`
<a id="use-semicolon-delimiter"></a>
Default `true`
Fastify uses [find-my-way](https://github.com/delvedor/find-my-way) which supports,
separating the path and query string with a `;` character (code 59), e.g. `/dev;foo=bar`.
This decision originated from [delvedor/find-my-way#76]
(https://github.com/delvedor/find-my-way/issues/76). Thus, this option will support
backwards compatiblilty for the need to split on `;`. To disable support for splitting
on `;` set `useSemicolonDelimiter` to `false`.
```js
const fastify = require('fastify')({
useSemicolonDelimiter: true
})

fastify.get('/dev', async (request, reply) => {
// An example request such as `/dev;foo=bar`
// Will produce the following query params result `{ foo = 'bar' }`
return request.query
})
```
## Instance
### Server Methods
Expand Down Expand Up @@ -1109,6 1136,28 @@ if (routeExists === false) {
}
```
#### findRoute
<a id="findRoute"></a>
Method to retrieve a route already registered to the internal router. It
expects an object as the payload. `url` and `method` are mandatory fields. It
is possible to also specify `constraints`.
The method returns a route object or `null` if the route cannot be found.
```js
const route = fastify.findRoute({
url: '/artists/:artistId',
method: 'GET',
constraints: { version: '1.0.0' } // optional
})

if (route !== null) {
// perform some route checks
console.log(route.params) // `{artistId: ':artistId'}`
}
```
#### close
<a id="close"></a>
Expand Down Expand Up @@ -1881,6 1930,7 @@ The properties that can currently be exposed are:
- requestIdHeader
- requestIdLogLabel
- http2SessionTimeout
- useSemicolonDelimiter
```js
const { readFileSync } = require('node:fs')
Expand Down
3 changes: 2 additions & 1 deletion fastify.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 110,7 @@ declare namespace fastify {
allowUnsafeRegex?: boolean,
requestIdHeader?: string | false,
requestIdLogLabel?: string;
useSemicolonDelimiter?: boolean,
jsonShorthand?: boolean;
genReqId?: (req: RawRequestDefaultExpression<RawServer>) => string,
trustProxy?: boolean | string | string[] | number | TrustProxyFunction,
Expand Down Expand Up @@ -239,4 240,4 @@ declare function fastify<

// CJS export
// const fastify = require('fastify')
export = fastify
export = fastify
6 changes: 5 additions & 1 deletion fastify.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 181,8 @@ function fastify (options) {
caseSensitive: options.caseSensitive,
allowUnsafeRegex: options.allowUnsafeRegex || defaultInitOptions.allowUnsafeRegex,
buildPrettyMeta: defaultBuildPrettyMeta,
querystringParser: options.querystringParser
querystringParser: options.querystringParser,
useSemicolonDelimiter: options.useSemicolonDelimiter ?? defaultInitOptions.useSemicolonDelimiter
}
})

Expand Down Expand Up @@ -310,6 311,9 @@ function fastify (options) {
hasRoute: function _route (options) {
return router.hasRoute.call(this, { options })
},
findRoute: function _findRoute (options) {
return router.findRoute(options)
},
// expose logger instance
log: logger,
// type provider
Expand Down
85 changes: 57 additions & 28 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":{"anyOf":[{"type":"boolean"},{"type":"string"}],"default":false},"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},"useSemicolonDelimiter":{"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 @@ -69,6 69,9 @@ data.http2SessionTimeout = 72000;
if(data.exposeHeadRoutes === undefined){
data.exposeHeadRoutes = true;
}
if(data.useSemicolonDelimiter === undefined){
data.useSemicolonDelimiter = true;
}
const _errs1 = errors;
for(const key0 in data){
if(!(func2.call(schema11.properties, key0))){
Expand Down Expand Up @@ -1002,13 1005,38 @@ data["exposeHeadRoutes"] = coerced25;
}
var valid0 = _errs67 === errors;
if(valid0){
let data23 = data.useSemicolonDelimiter;
const _errs68 = errors;
if(typeof data23 !== "boolean"){
let coerced25 = undefined;
if(!(coerced25 !== undefined)){
if(data23 === "false" || data23 === 0 || data23 === null){
coerced25 = false;
}
else if(data23 === "true" || data23 === 1){
coerced25 = true;
}
else {
validate10.errors = [{instancePath:instancePath "/useSemicolonDelimiter",schemaPath:"#/properties/useSemicolonDelimiter/type",keyword:"type",params:{type: "boolean"},message:"must be boolean"}];
return false;
}
}
if(coerced25 !== undefined){
data23 = coerced25;
if(data !== undefined){
data["useSemicolonDelimiter"] = coerced25;
}
}
}
var valid0 = _errs68 === errors;
if(valid0){
if(data.versioning !== undefined){
let data23 = data.versioning;
const _errs69 = errors;
if(errors === _errs69){
if(data23 && typeof data23 == "object" && !Array.isArray(data23)){
let data24 = data.versioning;
const _errs70 = errors;
if(errors === _errs70){
if(data24 && typeof data24 == "object" && !Array.isArray(data24)){
let missing1;
if(((data23.storage === undefined) && (missing1 = "storage")) || ((data23.deriveVersion === undefined) && (missing1 = "deriveVersion"))){
if(((data24.storage === undefined) && (missing1 = "storage")) || ((data24.deriveVersion === undefined) && (missing1 = "deriveVersion"))){
validate10.errors = [{instancePath:instancePath "/versioning",schemaPath:"#/properties/versioning/required",keyword:"required",params:{missingProperty: missing1},message:"must have required property '" missing1 "'"}];
return false;
}
Expand All @@ -1018,38 1046,38 @@ validate10.errors = [{instancePath:instancePath "/versioning",schemaPath:"#/prop
return false;
}
}
var valid0 = _errs69 === errors;
var valid0 = _errs70 === errors;
}
else {
var valid0 = true;
}
if(valid0){
if(data.constraints !== undefined){
let data24 = data.constraints;
const _errs72 = errors;
if(errors === _errs72){
if(data24 && typeof data24 == "object" && !Array.isArray(data24)){
for(const key2 in data24){
let data25 = data24[key2];
const _errs75 = errors;
if(errors === _errs75){
let data25 = data.constraints;
const _errs73 = errors;
if(errors === _errs73){
if(data25 && typeof data25 == "object" && !Array.isArray(data25)){
for(const key2 in data25){
let data26 = data25[key2];
const _errs76 = errors;
if(errors === _errs76){
if(data26 && typeof data26 == "object" && !Array.isArray(data26)){
let missing2;
if(((((data25.name === undefined) && (missing2 = "name")) || ((data25.storage === undefined) && (missing2 = "storage"))) || ((data25.validate === undefined) && (missing2 = "validate"))) || ((data25.deriveConstraint === undefined) && (missing2 = "deriveConstraint"))){
if(((((data26.name === undefined) && (missing2 = "name")) || ((data26.storage === undefined) && (missing2 = "storage"))) || ((data26.validate === undefined) && (missing2 = "validate"))) || ((data26.deriveConstraint === undefined) && (missing2 = "deriveConstraint"))){
validate10.errors = [{instancePath:instancePath "/constraints/" key2.replace(/~/g, "~0").replace(/\//g, "~1"),schemaPath:"#/properties/constraints/additionalProperties/required",keyword:"required",params:{missingProperty: missing2},message:"must have required property '" missing2 "'"}];
return false;
}
else {
if(data25.name !== undefined){
let data26 = data25.name;
if(typeof data26 !== "string"){
let dataType26 = typeof data26;
if(data26.name !== undefined){
let data27 = data26.name;
if(typeof data27 !== "string"){
let dataType26 = typeof data27;
let coerced26 = undefined;
if(!(coerced26 !== undefined)){
if(dataType26 == "number" || dataType26 == "boolean"){
coerced26 = "" data26;
coerced26 = "" data27;
}
else if(data26 === null){
else if(data27 === null){
coerced26 = "";
}
else {
Expand All @@ -1058,9 1086,9 @@ return false;
}
}
if(coerced26 !== undefined){
data26 = coerced26;
if(data25 !== undefined){
data25["name"] = coerced26;
data27 = coerced26;
if(data26 !== undefined){
data26["name"] = coerced26;
}
}
}
Expand All @@ -1072,7 1100,7 @@ validate10.errors = [{instancePath:instancePath "/constraints/" key2.replace(/
return false;
}
}
var valid7 = _errs75 === errors;
var valid7 = _errs76 === errors;
if(!valid7){
break;
}
Expand All @@ -1083,7 1111,7 @@ validate10.errors = [{instancePath:instancePath "/constraints",schemaPath:"#/pro
return false;
}
}
var valid0 = _errs72 === errors;
var valid0 = _errs73 === errors;
}
else {
var valid0 = true;
Expand Down Expand Up @@ -1113,6 1141,7 @@ var valid0 = true;
}
}
}
}
else {
validate10.errors = [{instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"}];
return false;
Expand All @@ -1123,4 1152,4 @@ return errors === 0;
}


module.exports.defaultInitOptions = {"connectionTimeout":0,"keepAliveTimeout":72000,"maxRequestsPerSocket":0,"requestTimeout":0,"bodyLimit":1048576,"caseSensitive":true,"allowUnsafeRegex":false,"disableRequestLogging":false,"jsonShorthand":true,"ignoreTrailingSlash":false,"ignoreDuplicateSlashes":false,"maxParamLength":100,"onProtoPoisoning":"error","onConstructorPoisoning":"error","pluginTimeout":10000,"requestIdHeader":false,"requestIdLogLabel":"reqId","http2SessionTimeout":72000,"exposeHeadRoutes":true}
module.exports.defaultInitOptions = {"connectionTimeout":0,"keepAliveTimeout":72000,"maxRequestsPerSocket":0,"requestTimeout":0,"bodyLimit":1048576,"caseSensitive":true,"allowUnsafeRegex":false,"disableRequestLogging":false,"jsonShorthand":true,"ignoreTrailingSlash":false,"ignoreDuplicateSlashes":false,"maxParamLength":100,"onProtoPoisoning":"error","onConstructorPoisoning":"error","pluginTimeout":10000,"requestIdHeader":"request-id","requestIdLogLabel":"reqId","http2SessionTimeout":72000,"exposeHeadRoutes":true,"useSemicolonDelimiter":true}
2 changes: 1 addition & 1 deletion lib/error-handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 95,7 @@ function defaultErrorHandler (error, request, reply) {
function fallbackErrorHandler (error, reply, cb) {
const res = reply.raw
const statusCode = reply.statusCode
reply[kReplyHeaders]['content-type'] = reply[kReplyHeaders]['content-type'] ?? 'application/json; charset=utf-8'
let payload
try {
const serializerFn = getSchemaSerializer(reply[kRouteContext], statusCode, reply[kReplyHeaders]['content-type'])
Expand All @@ -121,7 122,6 @@ function fallbackErrorHandler (error, reply, cb) {
payload = serializeError(new FST_ERR_REP_INVALID_PAYLOAD_TYPE(typeof payload))
}

reply[kReplyHeaders]['content-type'] = 'application/json; charset=utf-8'
reply[kReplyHeaders]['content-length'] = '' Buffer.byteLength(payload)

cb(reply, payload)
Expand Down
10 changes: 7 additions & 3 deletions lib/route.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 56,6 @@ const { buildErrorHandler } = require('./error-handler')
const { createChildLogger } = require('./logger')

function buildRouting (options) {
options.config.useSemicolonDelimiter = true // To avoid a breaking change
const router = FindMyWay(options.config)

let avvio
Expand Down Expand Up @@ -105,7 104,8 @@ function buildRouting (options) {
printRoutes: router.prettyPrint.bind(router),
addConstraintStrategy,
hasConstraintStrategy,
isAsyncConstraint
isAsyncConstraint,
findRoute
}

function addConstraintStrategy (strategy) {
Expand Down Expand Up @@ -153,11 153,15 @@ function buildRouting (options) {
}

function hasRoute ({ options }) {
return findRoute(options) !== null
}

function findRoute (options) {
return router.find(
options.method,
options.url || '',
options.constraints
) !== null
)
}

/**
Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 7,8 @@
"types": "fastify.d.ts",
"scripts": {
"bench": "branchcmp -r 2 -g -s \"npm run benchmark\"",
"benchmark": "npx concurrently -k -s first \"node ./examples/benchmark/simple.js\" \"npx autocannon -c 100 -d 30 -p 10 localhost:3000/\"",
"benchmark:parser": "npx concurrently -k -s first \"node ./examples/benchmark/parser.js\" \"npx autocannon -c 100 -d 30 -p 10 -i ./examples/benchmark/body.json -H \"content-type:application/jsoff\" -m POST localhost:3000/\"",
"benchmark": "concurrently -k -s first \"node ./examples/benchmark/simple.js\" \"autocannon -c 100 -d 30 -p 10 localhost:3000/\"",
"benchmark:parser": "concurrently -k -s first \"node ./examples/benchmark/parser.js\" \"autocannon -c 100 -d 30 -p 10 -i ./examples/benchmark/body.json -H \"content-type:application/jsoff\" -m POST localhost:3000/\"",
"build:validation": "node build/build-error-serializer.js && node build/build-validation.js",
"coverage": "npm run unit -- --coverage-report=html",
"coverage:ci": "c8 --reporter=lcov tap --coverage-report=html --no-browser --no-check-coverage",
Expand Down Expand Up @@ -154,8 154,10 @@
"ajv-formats": "^2.1.1",
"ajv-i18n": "^4.2.0",
"ajv-merge-patch": "^5.0.1",
"autocannon": "^7.14.0",
"branch-comparer": "^1.1.0",
"c8": "^8.0.1",
"concurrently": "^8.2.2",
"cross-env": "^7.0.3",
"eslint": "^8.51.0",
"eslint-config-standard": "^17.1.0",
Expand Down
Loading

0 comments on commit 4064cb5

Please sign in to comment.