From b2c62901cca913d726a9d76b6c48247c6643922f Mon Sep 17 00:00:00 2001 From: Debadutta Panda Date: Thu, 3 Nov 2022 00:59:29 +0530 Subject: [PATCH 01/17] add a new routeBodyLimit to request object --- docs/Reference/Request.md | 2 ++ lib/request.js | 7 ++++ test/bodyLimit.test.js | 63 +++++++++++++++++++++++++++++++++++- test/types/request.test-d.ts | 1 + types/request.d.ts | 1 + 5 files changed, 73 insertions(+), 1 deletion(-) diff --git a/docs/Reference/Request.md b/docs/Reference/Request.md index 93af618f9c..9e57251ad6 100644 --- a/docs/Reference/Request.md +++ b/docs/Reference/Request.md @@ -27,6 +27,7 @@ Request is a core Fastify object containing the following fields: - `protocol` - the protocol of the incoming request (`https` or `http`) - `method` - the method of the incoming request - `url` - the URL of the incoming request +- `routeBodyLimit` - either server limit or route limit - `routerMethod` - the method defined for the router that is handling the request - `routerPath` - the path pattern defined for the router that is handling the @@ -90,6 +91,7 @@ fastify.post('/:params', options, function (request, reply) { console.log(request.protocol) console.log(request.url) console.log(request.routerMethod) + console.log(request.routeBodyLimit) console.log(request.routerPath) request.log.info('some info') }) diff --git a/lib/request.js b/lib/request.js index ab47c6f030..3a274a85c6 100644 --- a/lib/request.js +++ b/lib/request.js @@ -165,6 +165,13 @@ Object.defineProperties(Request.prototype, { return this[kRouteContext].config.url } }, + routeBodyLimit: { + get () { + const serverLimit = this[kRouteContext]?.server?.initialConfig?.bodyLimit + const routeLimit = this[kRouteContext]?._parserOptions?.limit + return (routeLimit || serverLimit) + } + }, routerMethod: { get () { return this[kRouteContext].config.method diff --git a/test/bodyLimit.test.js b/test/bodyLimit.test.js index f1c50b8a37..1ce628d6ab 100644 --- a/test/bodyLimit.test.js +++ b/test/bodyLimit.test.js @@ -6,7 +6,7 @@ const t = require('tap') const test = t.test test('bodyLimit', t => { - t.plan(5) + t.plan(10) try { Fastify({ bodyLimit: 1.3 }) @@ -28,6 +28,21 @@ test('bodyLimit', t => { reply.send({ error: 'handler should not be called' }) }) + fastify.post('/route-limit', { + bodyLimit: 1000, + handler (request, reply) { + t.equal(1000, request.routeBodyLimit) + reply.send({ error: 'handler should not be called' }) + } + }) + + fastify.post('/server-limit', { + handler (request, reply) { + t.equal(1, request.routeBodyLimit) + reply.send({ error: 'handler should not be called' }) + } + }) + fastify.listen({ port: 0 }, function (err) { t.error(err) t.teardown(() => { fastify.close() }) @@ -42,5 +57,51 @@ test('bodyLimit', t => { t.error(err) t.equal(response.statusCode, 413) }) + sget({ + method: 'POST', + url: 'http://localhost:' + fastify.server.address().port + '/route-limit', + headers: { 'Content-Type': 'application/json' }, + body: [], + json: true + }, (err, response, body) => { + t.error(err) + t.equal(response.statusCode, 200) + }) + sget({ + method: 'POST', + url: 'http://localhost:' + fastify.server.address().port + '/server-limit', + headers: { 'Content-Type': 'application/json' }, + body: [], + json: true + }, (err, response, body) => { + t.error(err) + t.equal(response.statusCode, 413) + }) + }) +}) + +test('default request.routeBodyLimit should be 1048576', t => { + t.plan(4) + const fastify = Fastify() + fastify.post('/default-bodylimit', { + handler (request, reply) { + t.equal(1048576, request.routeBodyLimit) + reply.send({ error: 'handler should not be called' }) + } + }) + fastify.listen({ port: 30 }, function (err) { + t.error(err) + t.teardown(() => { fastify.close() }) + + sget({ + method: 'POST', + url: 'http://localhost:' + fastify.server.address().port + '/default-bodylimit', + headers: { 'Content-Type': 'application/json' }, + body: [], + json: true + }, (err, response, body) => { + t.error(err) + t.equal(response.statusCode, 200) + }) }) }) diff --git a/test/types/request.test-d.ts b/test/types/request.test-d.ts index 6b041dab30..3b4cfd88b2 100644 --- a/test/types/request.test-d.ts +++ b/test/types/request.test-d.ts @@ -66,6 +66,7 @@ const getHandler: RouteHandler = function (request, _reply) { expectType(request.method) expectType(request.routerPath) expectType(request.routerMethod) + expectType(request.routeBodyLimit) expectType(request.is404) expectType(request.hostname) expectType(request.ip) diff --git a/types/request.d.ts b/types/request.d.ts index 2944fca6d4..7796297107 100644 --- a/types/request.d.ts +++ b/types/request.d.ts @@ -66,6 +66,7 @@ export interface FastifyRequest Date: Thu, 3 Nov 2022 01:19:07 +0530 Subject: [PATCH 02/17] change testcase port number --- test/bodyLimit.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/bodyLimit.test.js b/test/bodyLimit.test.js index 1ce628d6ab..27a7c3b35a 100644 --- a/test/bodyLimit.test.js +++ b/test/bodyLimit.test.js @@ -89,7 +89,7 @@ test('default request.routeBodyLimit should be 1048576', t => { reply.send({ error: 'handler should not be called' }) } }) - fastify.listen({ port: 30 }, function (err) { + fastify.listen({ port: 0 }, function (err) { t.error(err) t.teardown(() => { fastify.close() }) From 82c0f2f2c07f246dac4e8679e8addf36bef9086c Mon Sep 17 00:00:00 2001 From: Debadutta Panda Date: Thu, 3 Nov 2022 11:40:59 +0530 Subject: [PATCH 03/17] move routeBodyLimit under routeConfig on Request.md --- docs/Reference/Request.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Reference/Request.md b/docs/Reference/Request.md index 9e57251ad6..be11511f6f 100644 --- a/docs/Reference/Request.md +++ b/docs/Reference/Request.md @@ -27,7 +27,6 @@ Request is a core Fastify object containing the following fields: - `protocol` - the protocol of the incoming request (`https` or `http`) - `method` - the method of the incoming request - `url` - the URL of the incoming request -- `routeBodyLimit` - either server limit or route limit - `routerMethod` - the method defined for the router that is handling the request - `routerPath` - the path pattern defined for the router that is handling the @@ -43,6 +42,7 @@ Request is a core Fastify object containing the following fields: handling the request - `routeConfig` - The route [`config`](./Routes.md#routes-config) object. +- `routeBodyLimit` - either server limit or route limit. - [.getValidationFunction(schema | httpPart)](#getvalidationfunction) - Returns a validation function for the specified schema or http part, if any of either are set or cached. From 8662f75350e1042fe7e5e368f875c18d00b12a42 Mon Sep 17 00:00:00 2001 From: Debadutta Panda Date: Thu, 3 Nov 2022 13:14:36 +0530 Subject: [PATCH 04/17] update testcase and update request.js --- lib/request.js | 4 +-- test/bodyLimit.test.js | 68 ++++++++++++++++++++++++++---------------- 2 files changed, 45 insertions(+), 27 deletions(-) diff --git a/lib/request.js b/lib/request.js index 3a274a85c6..1a7f547b02 100644 --- a/lib/request.js +++ b/lib/request.js @@ -167,8 +167,8 @@ Object.defineProperties(Request.prototype, { }, routeBodyLimit: { get () { - const serverLimit = this[kRouteContext]?.server?.initialConfig?.bodyLimit - const routeLimit = this[kRouteContext]?._parserOptions?.limit + const serverLimit = this[kRouteContext].server.initialConfig.bodyLimit + const routeLimit = this[kRouteContext]._parserOptions.limit return (routeLimit || serverLimit) } }, diff --git a/test/bodyLimit.test.js b/test/bodyLimit.test.js index 27a7c3b35a..76aafc1afa 100644 --- a/test/bodyLimit.test.js +++ b/test/bodyLimit.test.js @@ -6,7 +6,7 @@ const t = require('tap') const test = t.test test('bodyLimit', t => { - t.plan(10) + t.plan(5) try { Fastify({ bodyLimit: 1.3 }) @@ -28,21 +28,6 @@ test('bodyLimit', t => { reply.send({ error: 'handler should not be called' }) }) - fastify.post('/route-limit', { - bodyLimit: 1000, - handler (request, reply) { - t.equal(1000, request.routeBodyLimit) - reply.send({ error: 'handler should not be called' }) - } - }) - - fastify.post('/server-limit', { - handler (request, reply) { - t.equal(1, request.routeBodyLimit) - reply.send({ error: 'handler should not be called' }) - } - }) - fastify.listen({ port: 0 }, function (err) { t.error(err) t.teardown(() => { fastify.close() }) @@ -57,9 +42,25 @@ test('bodyLimit', t => { t.error(err) t.equal(response.statusCode, 413) }) + }) +}) + +test('default request.routeBodyLimit should be 1048576', t => { + t.plan(4) + const fastify = Fastify() + fastify.post('/default-bodylimit', { + handler (request, reply) { + t.equal(1048576, request.routeBodyLimit) + reply.send({ }) + } + }) + fastify.listen({ port: 0 }, function (err) { + t.error(err) + t.teardown(() => { fastify.close() }) + sget({ method: 'POST', - url: 'http://localhost:' + fastify.server.address().port + '/route-limit', + url: 'http://localhost:' + fastify.server.address().port + '/default-bodylimit', headers: { 'Content-Type': 'application/json' }, body: [], json: true @@ -67,26 +68,43 @@ test('bodyLimit', t => { t.error(err) t.equal(response.statusCode, 200) }) + }) +}) + +test('request.routeBodyLimit should be equal to route limit', t => { + t.plan(4) + const fastify = Fastify({ bodyLimit: 1 }) + fastify.post('/route-limit', { + bodyLimit: 1000, + handler (request, reply) { + t.equal(1000, request.routeBodyLimit) + reply.send({}) + } + }) + fastify.listen({ port: 0 }, function (err) { + t.error(err) + t.teardown(() => { fastify.close() }) + sget({ method: 'POST', - url: 'http://localhost:' + fastify.server.address().port + '/server-limit', + url: 'http://localhost:' + fastify.server.address().port + '/route-limit', headers: { 'Content-Type': 'application/json' }, body: [], json: true }, (err, response, body) => { t.error(err) - t.equal(response.statusCode, 413) + t.equal(response.statusCode, 200) }) }) }) -test('default request.routeBodyLimit should be 1048576', t => { +test('request.routeBodyLimit should be equal to server limit', t => { t.plan(4) - const fastify = Fastify() - fastify.post('/default-bodylimit', { + const fastify = Fastify({ bodyLimit: 100 }) + fastify.post('/server-limit', { handler (request, reply) { - t.equal(1048576, request.routeBodyLimit) - reply.send({ error: 'handler should not be called' }) + t.equal(100, request.routeBodyLimit) + reply.send({}) } }) fastify.listen({ port: 0 }, function (err) { @@ -95,7 +113,7 @@ test('default request.routeBodyLimit should be 1048576', t => { sget({ method: 'POST', - url: 'http://localhost:' + fastify.server.address().port + '/default-bodylimit', + url: 'http://localhost:' + fastify.server.address().port + '/server-limit', headers: { 'Content-Type': 'application/json' }, body: [], json: true From 00a3819384df2f43db9e270641794ecae2cd259c Mon Sep 17 00:00:00 2001 From: Debadutta Panda Date: Thu, 3 Nov 2022 18:58:26 +0530 Subject: [PATCH 05/17] change the bodylimit property path to request.routeOptions.bodyLimit --- docs/Reference/Request.md | 3 ++- lib/request.js | 11 +++++++---- test/bodyLimit.test.js | 12 ++++++------ 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/docs/Reference/Request.md b/docs/Reference/Request.md index be11511f6f..7a0cc57f99 100644 --- a/docs/Reference/Request.md +++ b/docs/Reference/Request.md @@ -42,7 +42,8 @@ Request is a core Fastify object containing the following fields: handling the request - `routeConfig` - The route [`config`](./Routes.md#routes-config) object. -- `routeBodyLimit` - either server limit or route limit. +- `routeOptions` + - `bodyLimit` - either server limit or route limit - [.getValidationFunction(schema | httpPart)](#getvalidationfunction) - Returns a validation function for the specified schema or http part, if any of either are set or cached. diff --git a/lib/request.js b/lib/request.js index 1a7f547b02..3690276371 100644 --- a/lib/request.js +++ b/lib/request.js @@ -165,11 +165,14 @@ Object.defineProperties(Request.prototype, { return this[kRouteContext].config.url } }, - routeBodyLimit: { + routeOptions: { get () { - const serverLimit = this[kRouteContext].server.initialConfig.bodyLimit - const routeLimit = this[kRouteContext]._parserOptions.limit - return (routeLimit || serverLimit) + const context = this[kRouteContext] + const serverLimit = context.server.initialConfig.bodyLimit + const routeLimit = context._parserOptions.limit + return { + bodyLimit: ((routeLimit || serverLimit)) + } } }, routerMethod: { diff --git a/test/bodyLimit.test.js b/test/bodyLimit.test.js index 76aafc1afa..980dd23b2a 100644 --- a/test/bodyLimit.test.js +++ b/test/bodyLimit.test.js @@ -45,12 +45,12 @@ test('bodyLimit', t => { }) }) -test('default request.routeBodyLimit should be 1048576', t => { +test('default request.routeOptions.bodyLimit should be 1048576', t => { t.plan(4) const fastify = Fastify() fastify.post('/default-bodylimit', { handler (request, reply) { - t.equal(1048576, request.routeBodyLimit) + t.equal(1048576, request.routeOptions.bodyLimit) reply.send({ }) } }) @@ -71,13 +71,13 @@ test('default request.routeBodyLimit should be 1048576', t => { }) }) -test('request.routeBodyLimit should be equal to route limit', t => { +test('request.routeOptions.bodyLimit should be equal to route limit', t => { t.plan(4) const fastify = Fastify({ bodyLimit: 1 }) fastify.post('/route-limit', { bodyLimit: 1000, handler (request, reply) { - t.equal(1000, request.routeBodyLimit) + t.equal(1000, request.routeOptions.bodyLimit) reply.send({}) } }) @@ -98,12 +98,12 @@ test('request.routeBodyLimit should be equal to route limit', t => { }) }) -test('request.routeBodyLimit should be equal to server limit', t => { +test('request.routeOptions.bodyLimit should be equal to server limit', t => { t.plan(4) const fastify = Fastify({ bodyLimit: 100 }) fastify.post('/server-limit', { handler (request, reply) { - t.equal(100, request.routeBodyLimit) + t.equal(100, request.routeOptions.bodyLimit) reply.send({}) } }) From c92f9f68fc1d83c4e6d62386292b9206fd32d7eb Mon Sep 17 00:00:00 2001 From: Debadutta Panda Date: Thu, 3 Nov 2022 19:11:31 +0530 Subject: [PATCH 06/17] update request.md --- docs/Reference/Request.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Reference/Request.md b/docs/Reference/Request.md index 7a0cc57f99..ddf0cf1e11 100644 --- a/docs/Reference/Request.md +++ b/docs/Reference/Request.md @@ -42,7 +42,7 @@ Request is a core Fastify object containing the following fields: handling the request - `routeConfig` - The route [`config`](./Routes.md#routes-config) object. -- `routeOptions` +- `routeOptions` - The route [`option`](./Routes.md#routes-options) object - `bodyLimit` - either server limit or route limit - [.getValidationFunction(schema | httpPart)](#getvalidationfunction) - Returns a validation function for the specified schema or http part, @@ -92,7 +92,7 @@ fastify.post('/:params', options, function (request, reply) { console.log(request.protocol) console.log(request.url) console.log(request.routerMethod) - console.log(request.routeBodyLimit) + console.log(request.routeOptions.bodyLimit) console.log(request.routerPath) request.log.info('some info') }) From fb5032c721b9aee9f3d2c24fe12eaa216182ccb9 Mon Sep 17 00:00:00 2001 From: Debadutta Panda Date: Thu, 3 Nov 2022 19:19:00 +0530 Subject: [PATCH 07/17] remove double parenthesis --- lib/request.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/request.js b/lib/request.js index 3690276371..636c1fc237 100644 --- a/lib/request.js +++ b/lib/request.js @@ -171,7 +171,7 @@ Object.defineProperties(Request.prototype, { const serverLimit = context.server.initialConfig.bodyLimit const routeLimit = context._parserOptions.limit return { - bodyLimit: ((routeLimit || serverLimit)) + bodyLimit: (routeLimit || serverLimit) } } }, From cb8dce798e949290e8467455249a21fd5b8989ca Mon Sep 17 00:00:00 2001 From: Debadutta Panda Date: Fri, 4 Nov 2022 10:45:26 +0530 Subject: [PATCH 08/17] - Add more options to request.routeOptions - Change the types - Update testcases --- docs/Reference/Request.md | 6 ++++- lib/request.js | 47 +++++++++++++++++++++++++++--------- test/request-error.test.js | 42 ++++++++++++++++++++++++++++++++ test/types/request.test-d.ts | 4 +-- types/request.d.ts | 10 +++++++- 5 files changed, 93 insertions(+), 16 deletions(-) diff --git a/docs/Reference/Request.md b/docs/Reference/Request.md index ddf0cf1e11..02f5e4083e 100644 --- a/docs/Reference/Request.md +++ b/docs/Reference/Request.md @@ -43,7 +43,11 @@ Request is a core Fastify object containing the following fields: - `routeConfig` - The route [`config`](./Routes.md#routes-config) object. - `routeOptions` - The route [`option`](./Routes.md#routes-options) object - - `bodyLimit` - either server limit or route limit + - `bodyLimit` - either get server limit or route limit + - `method` - get http method + - `url` - get the path of the URL to match this route + - `attachValidation` - attach `validationError` to request, if there is a schema + - `logLevel` - get log level for this route. - [.getValidationFunction(schema | httpPart)](#getvalidationfunction) - Returns a validation function for the specified schema or http part, if any of either are set or cached. diff --git a/lib/request.js b/lib/request.js index 636c1fc237..7a547358a4 100644 --- a/lib/request.js +++ b/lib/request.js @@ -137,6 +137,21 @@ function buildRequestWithTrustProxy (R, trustProxy) { return _Request } +/** + * + * @param {object} target + * @returns {Proxy} + */ +function readOnly (target) { + return new Proxy(target, { + get (target, key) { + return target[key] + }, + set (_, key) { + throw new Error(`property ${key} is immutable`) + } + }) +} Object.defineProperties(Request.prototype, { server: { @@ -168,11 +183,19 @@ Object.defineProperties(Request.prototype, { routeOptions: { get () { const context = this[kRouteContext] - const serverLimit = context.server.initialConfig.bodyLimit const routeLimit = context._parserOptions.limit - return { - bodyLimit: (routeLimit || serverLimit) + const serverLimit = context.server.initialConfig.bodyLimit + const options = { + method: context.config.method, + url: this.raw.url, + bodyLimit: (routeLimit || serverLimit), + attachValidation: context.attachValidation, + logLevel: context.logLevel } + return readOnly(options) + }, + set () { + throw new Error('routerOptions is immutable') } }, routerMethod: { @@ -258,13 +281,13 @@ Object.defineProperties(Request.prototype, { } const validatorCompiler = this[kRouteContext].validatorCompiler || - this.server[kSchemaController].validatorCompiler || - ( - // We compile the schemas if no custom validatorCompiler is provided - // nor set - this.server[kSchemaController].setupValidator(this.server[kOptions]) || - this.server[kSchemaController].validatorCompiler - ) + this.server[kSchemaController].validatorCompiler || + ( + // We compile the schemas if no custom validatorCompiler is provided + // nor set + this.server[kSchemaController].setupValidator(this.server[kOptions]) || + this.server[kSchemaController].validatorCompiler + ) const validateFn = validatorCompiler({ schema, @@ -301,8 +324,8 @@ Object.defineProperties(Request.prototype, { // We cannot compile if the schema is missed if (validate == null && (schema == null || - typeof schema !== 'object' || - Array.isArray(schema)) + typeof schema !== 'object' || + Array.isArray(schema)) ) { throw new FST_ERR_REQ_INVALID_VALIDATION_INVOCATION(httpPart) } diff --git a/test/request-error.test.js b/test/request-error.test.js index a3be6e1870..8c5b2ffa51 100644 --- a/test/request-error.test.js +++ b/test/request-error.test.js @@ -1,6 +1,7 @@ 'use strict' const { connect } = require('net') +const sget = require('simple-get').concat const t = require('tap') const test = t.test const Fastify = require('..') @@ -313,3 +314,44 @@ test('default clientError replies with bad request on reused keep-alive connecti client.write('\r\n\r\n') }) }) + +test('request.routeOptions should be immutable', t => { + t.plan(7) + const fastify = Fastify() + const handler = function (req, res) { + t.equal('POST', req.routeOptions.method) + t.equal('/', req.routeOptions.url) + try { + req.routeOptions.bodyLimit = 1001 + t.fail('property bodyLimit is immutable') + } catch (err) { + t.ok(err) + } + try { + req.routeOptions = null + t.fail('routerOptions is immutable') + } catch (err) { + t.ok(err) + } + res.send({ hello: 'world' }) + } + fastify.post('/', { + bodyLimit: 1000, + handler + }) + fastify.listen({ port: 0 }, function (err) { + t.error(err) + t.teardown(() => { fastify.close() }) + + sget({ + method: 'POST', + url: 'http://localhost:' + fastify.server.address().port, + headers: { 'Content-Type': 'application/json' }, + body: [], + json: true + }, (err, response, body) => { + t.error(err) + t.equal(response.statusCode, 200) + }) + }) +}) diff --git a/test/types/request.test-d.ts b/test/types/request.test-d.ts index 3b4cfd88b2..5f5cd827e4 100644 --- a/test/types/request.test-d.ts +++ b/test/types/request.test-d.ts @@ -17,7 +17,7 @@ import fastify, { } from '../../fastify' import { RequestParamsDefault, RequestHeadersDefault, RequestQuerystringDefault } from '../../types/utils' import { FastifyLoggerInstance } from '../../types/logger' -import { FastifyRequest } from '../../types/request' +import { FastifyRequest, RequestRouteOptions } from '../../types/request' import { FastifyReply } from '../../types/reply' import { FastifyInstance } from '../../types/instance' import { RouteGenericInterface } from '../../types/route' @@ -66,7 +66,7 @@ const getHandler: RouteHandler = function (request, _reply) { expectType(request.method) expectType(request.routerPath) expectType(request.routerMethod) - expectType(request.routeBodyLimit) + expectType>(request.routeOptions) expectType(request.is404) expectType(request.hostname) expectType(request.ip) diff --git a/types/request.d.ts b/types/request.d.ts index 7796297107..dd5ffbd605 100644 --- a/types/request.d.ts +++ b/types/request.d.ts @@ -20,6 +20,14 @@ export interface ValidationFunction { errors?: null | ErrorObject[]; } +export interface RequestRouteOptions { + method: string, + url: string, + bodyLimit:number, + attachValidation:boolean, + logLevel:string +} + /** * FastifyRequest is an instance of the standard http or http2 request objects. * It defaults to http.IncomingMessage, and it also extends the relative request object. @@ -66,7 +74,7 @@ export interface FastifyRequest readonly is404: boolean; readonly socket: RawRequest['socket']; From ac665abbeee3724216d1bc8b4b120e96ec9a377c Mon Sep 17 00:00:00 2001 From: Debadutta Panda Date: Fri, 4 Nov 2022 10:56:25 +0530 Subject: [PATCH 09/17] update Request.md --- docs/Reference/Request.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/Reference/Request.md b/docs/Reference/Request.md index 02f5e4083e..9784de13cb 100644 --- a/docs/Reference/Request.md +++ b/docs/Reference/Request.md @@ -97,7 +97,11 @@ fastify.post('/:params', options, function (request, reply) { console.log(request.url) console.log(request.routerMethod) console.log(request.routeOptions.bodyLimit) - console.log(request.routerPath) + console.log(request.routeOptions.method) + console.log(request.routeOptions.url) + console.log(request.routeOptions.attachValidation) + console.log(request.routeOptions.logLevel) + console.log(request.routerPath.logLevel) request.log.info('some info') }) ``` From a149fa8b1184a75eb78411d4f2b1d6e6ea2b8a8d Mon Sep 17 00:00:00 2001 From: Debadutta Panda Date: Fri, 4 Nov 2022 11:34:40 +0530 Subject: [PATCH 10/17] update request.js --- lib/request.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/request.js b/lib/request.js index 7a547358a4..f3e652476a 100644 --- a/lib/request.js +++ b/lib/request.js @@ -187,7 +187,7 @@ Object.defineProperties(Request.prototype, { const serverLimit = context.server.initialConfig.bodyLimit const options = { method: context.config.method, - url: this.raw.url, + url: context.config.url, bodyLimit: (routeLimit || serverLimit), attachValidation: context.attachValidation, logLevel: context.logLevel @@ -281,13 +281,13 @@ Object.defineProperties(Request.prototype, { } const validatorCompiler = this[kRouteContext].validatorCompiler || - this.server[kSchemaController].validatorCompiler || - ( - // We compile the schemas if no custom validatorCompiler is provided - // nor set - this.server[kSchemaController].setupValidator(this.server[kOptions]) || - this.server[kSchemaController].validatorCompiler - ) + this.server[kSchemaController].validatorCompiler || + ( + // We compile the schemas if no custom validatorCompiler is provided + // nor set + this.server[kSchemaController].setupValidator(this.server[kOptions]) || + this.server[kSchemaController].validatorCompiler + ) const validateFn = validatorCompiler({ schema, @@ -324,8 +324,8 @@ Object.defineProperties(Request.prototype, { // We cannot compile if the schema is missed if (validate == null && (schema == null || - typeof schema !== 'object' || - Array.isArray(schema)) + typeof schema !== 'object' || + Array.isArray(schema)) ) { throw new FST_ERR_REQ_INVALID_VALIDATION_INVOCATION(httpPart) } From f87b119bd636a2d26384bf41757ed44a8699e9ca Mon Sep 17 00:00:00 2001 From: Debadutta Panda Date: Fri, 4 Nov 2022 17:21:34 +0530 Subject: [PATCH 11/17] add version property to route options --- docs/Reference/Request.md | 2 ++ lib/request.js | 11 ++++---- test/request-error.test.js | 52 +++++++++++++++++++++++++++++++++++++- types/request.d.ts | 3 ++- 4 files changed, 60 insertions(+), 8 deletions(-) diff --git a/docs/Reference/Request.md b/docs/Reference/Request.md index 9784de13cb..df17c07c90 100644 --- a/docs/Reference/Request.md +++ b/docs/Reference/Request.md @@ -48,6 +48,7 @@ Request is a core Fastify object containing the following fields: - `url` - get the path of the URL to match this route - `attachValidation` - attach `validationError` to request, if there is a schema - `logLevel` - get log level for this route. + - `version` - a semver compatible string that defined the version of the endpoint - [.getValidationFunction(schema | httpPart)](#getvalidationfunction) - Returns a validation function for the specified schema or http part, if any of either are set or cached. @@ -101,6 +102,7 @@ fastify.post('/:params', options, function (request, reply) { console.log(request.routeOptions.url) console.log(request.routeOptions.attachValidation) console.log(request.routeOptions.logLevel) + console.log(request.routeOptions.version) console.log(request.routerPath.logLevel) request.log.info('some info') }) diff --git a/lib/request.js b/lib/request.js index f3e652476a..b53feb02af 100644 --- a/lib/request.js +++ b/lib/request.js @@ -185,17 +185,16 @@ Object.defineProperties(Request.prototype, { const context = this[kRouteContext] const routeLimit = context._parserOptions.limit const serverLimit = context.server.initialConfig.bodyLimit + const version = context.server.hasConstraintStrategy('version') ? this.raw.headers['accept-version'] : undefined const options = { method: context.config.method, url: context.config.url, bodyLimit: (routeLimit || serverLimit), attachValidation: context.attachValidation, - logLevel: context.logLevel + logLevel: context.logLevel, + version } return readOnly(options) - }, - set () { - throw new Error('routerOptions is immutable') } }, routerMethod: { @@ -283,8 +282,8 @@ Object.defineProperties(Request.prototype, { const validatorCompiler = this[kRouteContext].validatorCompiler || this.server[kSchemaController].validatorCompiler || ( - // We compile the schemas if no custom validatorCompiler is provided - // nor set + // We compile the schemas if no custom validatorCompiler is provided + // nor set this.server[kSchemaController].setupValidator(this.server[kOptions]) || this.server[kSchemaController].validatorCompiler ) diff --git a/test/request-error.test.js b/test/request-error.test.js index 8c5b2ffa51..96e43f24cd 100644 --- a/test/request-error.test.js +++ b/test/request-error.test.js @@ -333,7 +333,7 @@ test('request.routeOptions should be immutable', t => { } catch (err) { t.ok(err) } - res.send({ hello: 'world' }) + res.send({ }) } fastify.post('/', { bodyLimit: 1000, @@ -355,3 +355,53 @@ test('request.routeOptions should be immutable', t => { }) }) }) + +test('test request.routeOptions.version', t => { + t.plan(7) + const fastify = Fastify() + + fastify.route({ + method: 'POST', + url: '/version', + constraints: { version: '1.2.0' }, + handler: function (request, reply) { + t.equal('1.2.0', request.routeOptions.version) + reply.send({}) + } + }) + + fastify.route({ + method: 'POST', + url: '/version-undefined', + handler: function (request, reply) { + t.equal(undefined, request.routeOptions.version) + reply.send({}) + } + }) + fastify.listen({ port: 0 }, function (err) { + t.error(err) + t.teardown(() => { fastify.close() }) + + sget({ + method: 'POST', + url: 'http://localhost:' + fastify.server.address().port + '/version', + headers: { 'Content-Type': 'application/json', 'Accept-Version': '1.2.0' }, + body: [], + json: true + }, (err, response, body) => { + t.error(err) + t.equal(response.statusCode, 200) + }) + + sget({ + method: 'POST', + url: 'http://localhost:' + fastify.server.address().port + '/version-undefined', + headers: { 'Content-Type': 'application/json' }, + body: [], + json: true + }, (err, response, body) => { + t.error(err) + t.equal(response.statusCode, 200) + }) + }) +}) diff --git a/types/request.d.ts b/types/request.d.ts index dd5ffbd605..ad48926b40 100644 --- a/types/request.d.ts +++ b/types/request.d.ts @@ -25,7 +25,8 @@ export interface RequestRouteOptions { url: string, bodyLimit:number, attachValidation:boolean, - logLevel:string + logLevel:string, + version: string | undefined } /** From 81732f500d527bf4442d119b73136a739b038a71 Mon Sep 17 00:00:00 2001 From: Debadutta Panda Date: Fri, 4 Nov 2022 17:27:18 +0530 Subject: [PATCH 12/17] replace readOnly with Object.freeze --- lib/request.js | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/lib/request.js b/lib/request.js index b53feb02af..15bb4cfbb0 100644 --- a/lib/request.js +++ b/lib/request.js @@ -137,21 +137,6 @@ function buildRequestWithTrustProxy (R, trustProxy) { return _Request } -/** - * - * @param {object} target - * @returns {Proxy} - */ -function readOnly (target) { - return new Proxy(target, { - get (target, key) { - return target[key] - }, - set (_, key) { - throw new Error(`property ${key} is immutable`) - } - }) -} Object.defineProperties(Request.prototype, { server: { @@ -194,7 +179,7 @@ Object.defineProperties(Request.prototype, { logLevel: context.logLevel, version } - return readOnly(options) + return Object.freeze(options) } }, routerMethod: { From 33817b474896e0a286e891470f0fe51cbd39d071 Mon Sep 17 00:00:00 2001 From: Debadutta Panda Date: Fri, 4 Nov 2022 17:46:17 +0530 Subject: [PATCH 13/17] update request.md --- docs/Reference/Request.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/Reference/Request.md b/docs/Reference/Request.md index df17c07c90..a687c769c7 100644 --- a/docs/Reference/Request.md +++ b/docs/Reference/Request.md @@ -43,12 +43,12 @@ Request is a core Fastify object containing the following fields: - `routeConfig` - The route [`config`](./Routes.md#routes-config) object. - `routeOptions` - The route [`option`](./Routes.md#routes-options) object - - `bodyLimit` - either get server limit or route limit - - `method` - get http method - - `url` - get the path of the URL to match this route - - `attachValidation` - attach `validationError` to request, if there is a schema - - `logLevel` - get log level for this route. - - `version` - a semver compatible string that defined the version of the endpoint + - `bodyLimit` - either server limit or route limit + - `method` - the http method for the route + - `url` - the path of the URL to match this route + - `attachValidation` - attach `validationError` to request (if there is a schema) + - `logLevel` - log level defined for this route + - `version` - a semver compatible string that defines the version of the endpoint - [.getValidationFunction(schema | httpPart)](#getvalidationfunction) - Returns a validation function for the specified schema or http part, if any of either are set or cached. From 6f16f0203fd5918624b78f6042b0de55ba8a5a44 Mon Sep 17 00:00:00 2001 From: Debadutta Panda Date: Fri, 4 Nov 2022 18:06:58 +0530 Subject: [PATCH 14/17] update request.md with some changes --- docs/Reference/Request.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Reference/Request.md b/docs/Reference/Request.md index a687c769c7..122f473d4c 100644 --- a/docs/Reference/Request.md +++ b/docs/Reference/Request.md @@ -46,7 +46,7 @@ Request is a core Fastify object containing the following fields: - `bodyLimit` - either server limit or route limit - `method` - the http method for the route - `url` - the path of the URL to match this route - - `attachValidation` - attach `validationError` to request (if there is a schema) + - `attachValidation` - attach `validationError` to request(if schema is defined) - `logLevel` - log level defined for this route - `version` - a semver compatible string that defines the version of the endpoint - [.getValidationFunction(schema | httpPart)](#getvalidationfunction) - From 679d8761e07c62ddd41a9a4bd71650e1bb582c05 Mon Sep 17 00:00:00 2001 From: Debadutta Panda Date: Fri, 4 Nov 2022 19:30:19 +0530 Subject: [PATCH 15/17] exposeHeadRoute and prefixTrailingSlash to routeOptions --- docs/Reference/Request.md | 6 +++++- lib/context.js | 4 ++++ lib/request.js | 2 ++ lib/route.js | 2 ++ types/request.d.ts | 4 +++- 5 files changed, 16 insertions(+), 2 deletions(-) diff --git a/docs/Reference/Request.md b/docs/Reference/Request.md index 122f473d4c..be173405e2 100644 --- a/docs/Reference/Request.md +++ b/docs/Reference/Request.md @@ -46,9 +46,13 @@ Request is a core Fastify object containing the following fields: - `bodyLimit` - either server limit or route limit - `method` - the http method for the route - `url` - the path of the URL to match this route - - `attachValidation` - attach `validationError` to request(if schema is defined) + - `attachValidation` - attach `validationError` to request + (if there is a schema defined) - `logLevel` - log level defined for this route - `version` - a semver compatible string that defines the version of the endpoint + - `exposeHeadRoute` - creates a sibling HEAD route for any GET routes + - `prefixTrailingSlash` - string used to determine how to handle passing / + as a route with a prefix. - [.getValidationFunction(schema | httpPart)](#getvalidationfunction) - Returns a validation function for the specified schema or http part, if any of either are set or cached. diff --git a/lib/context.js b/lib/context.js index 91953f947c..226d09280d 100644 --- a/lib/context.js +++ b/lib/context.js @@ -31,6 +31,8 @@ function Context ({ serializerCompiler, replySerializer, schemaErrorFormatter, + exposeHeadRoute, + prefixTrailingSlash, server, isFastify }) { @@ -52,6 +54,8 @@ function Context ({ this._parserOptions = { limit: bodyLimit || server[kBodyLimit] } + this.exposeHeadRoute = exposeHeadRoute + this.prefixTrailingSlash = prefixTrailingSlash this.logLevel = logLevel || server[kLogLevel] this.logSerializers = logSerializers this[kFourOhFourContext] = null diff --git a/lib/request.js b/lib/request.js index 15bb4cfbb0..25d4c4ec0c 100644 --- a/lib/request.js +++ b/lib/request.js @@ -177,6 +177,8 @@ Object.defineProperties(Request.prototype, { bodyLimit: (routeLimit || serverLimit), attachValidation: context.attachValidation, logLevel: context.logLevel, + exposeHeadRoute: context.exposeHeadRoute, + prefixTrailingSlash: context.prefixTrailingSlash, version } return Object.freeze(options) diff --git a/lib/route.js b/lib/route.js index 621aa871a1..f48467de88 100644 --- a/lib/route.js +++ b/lib/route.js @@ -278,6 +278,8 @@ function buildRouting (options) { replySerializer: this[kReplySerializerDefault], validatorCompiler: opts.validatorCompiler, serializerCompiler: opts.serializerCompiler, + exposeHeadRoute: shouldExposeHead, + prefixTrailingSlash: (opts.prefixTrailingSlash || 'both'), server: this, isFastify }) diff --git a/types/request.d.ts b/types/request.d.ts index ad48926b40..a23ba41f39 100644 --- a/types/request.d.ts +++ b/types/request.d.ts @@ -26,7 +26,9 @@ export interface RequestRouteOptions { bodyLimit:number, attachValidation:boolean, logLevel:string, - version: string | undefined + version: string | undefined, + exposeHeadRoute: boolean, + prefixTrailingSlash: string } /** From 501785f546e6a891787813a8a27d9a7ae3e7122f Mon Sep 17 00:00:00 2001 From: Debadutta Panda Date: Fri, 4 Nov 2022 19:40:58 +0530 Subject: [PATCH 16/17] update request.md doc --- docs/Reference/Request.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/Reference/Request.md b/docs/Reference/Request.md index be173405e2..1472fd2d0d 100644 --- a/docs/Reference/Request.md +++ b/docs/Reference/Request.md @@ -107,6 +107,8 @@ fastify.post('/:params', options, function (request, reply) { console.log(request.routeOptions.attachValidation) console.log(request.routeOptions.logLevel) console.log(request.routeOptions.version) + console.log(request.routeOptions.exposeHeadRoute) + console.log(request.routeOptions.prefixTrailingSlash) console.log(request.routerPath.logLevel) request.log.info('some info') }) From c053b2289a526367b449c8d976af4b4510cd178c Mon Sep 17 00:00:00 2001 From: Debadutta Panda Date: Sat, 5 Nov 2022 18:28:20 +0530 Subject: [PATCH 17/17] update request-error.test.js - request.routeOptions should be immutable. --- test/request-error.test.js | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/test/request-error.test.js b/test/request-error.test.js index 96e43f24cd..9108f9aa4e 100644 --- a/test/request-error.test.js +++ b/test/request-error.test.js @@ -316,24 +316,28 @@ test('default clientError replies with bad request on reused keep-alive connecti }) test('request.routeOptions should be immutable', t => { - t.plan(7) + t.plan(14) const fastify = Fastify() const handler = function (req, res) { t.equal('POST', req.routeOptions.method) t.equal('/', req.routeOptions.url) - try { - req.routeOptions.bodyLimit = 1001 - t.fail('property bodyLimit is immutable') - } catch (err) { - t.ok(err) - } - try { - req.routeOptions = null - t.fail('routerOptions is immutable') - } catch (err) { - t.ok(err) + t.throws(() => { req.routeOptions = null }, new TypeError('Cannot set property routeOptions of # which has only a getter')) + t.throws(() => { req.routeOptions.method = 'INVALID' }, new TypeError('Cannot assign to read only property \'method\' of object \'#\'')) + t.throws(() => { req.routeOptions.url = '//' }, new TypeError('Cannot assign to read only property \'url\' of object \'#\'')) + t.throws(() => { req.routeOptions.bodyLimit = 0xDEADBEEF }, new TypeError('Cannot assign to read only property \'bodyLimit\' of object \'#\'')) + t.throws(() => { req.routeOptions.attachValidation = true }, new TypeError('Cannot assign to read only property \'attachValidation\' of object \'#\'')) + t.throws(() => { req.routeOptions.logLevel = 'invalid' }, new TypeError('Cannot assign to read only property \'logLevel\' of object \'#\'')) + t.throws(() => { req.routeOptions.version = '95.0.1' }, new TypeError('Cannot assign to read only property \'version\' of object \'#\'')) + t.throws(() => { req.routeOptions.prefixTrailingSlash = true }, new TypeError('Cannot assign to read only property \'prefixTrailingSlash\' of object \'#\'')) + t.throws(() => { req.routeOptions.newAttribute = {} }, new TypeError('Cannot add property newAttribute, object is not extensible')) + + for (const key of Object.keys(req.routeOptions)) { + if (typeof req.routeOptions[key] === 'object' && req.routeOptions[key] !== null) { + t.fail('Object.freeze must run recursively on nested structures to ensure that routeOptions is immutable.') + } } - res.send({ }) + + res.send({}) } fastify.post('/', { bodyLimit: 1000,