diff --git a/README.md b/README.md index fc132fd..d3525e7 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,8 @@ const fastify = require('fastify')() fastify.register(require('@fastify/cookie'), { secret: "my-secret", // for cookies signature - parseOptions: {} // options for parsing cookies + hook: 'onRequest', // set to false to disable cookie autoparsing or set autoparsing on any of the following hooks: 'onRequest', 'preParsing', 'preHandler', 'preValidation'. default: 'onRequest' + parseOptions: {} // options for parsing cookies }) fastify.get('/', (req, reply) => { diff --git a/plugin.js b/plugin.js index a90dca5..a3d3a9a 100644 --- a/plugin.js +++ b/plugin.js @@ -50,19 +50,44 @@ function fastifyCookieClearCookie (reply, name, options) { return fastifyCookieSetCookie(reply, name, '', opts) } -function onReqHandlerWrapper (fastify) { - return function fastifyCookieOnReqHandler (fastifyReq, fastifyRes, done) { - fastifyReq.cookies = {} // New container per request. Issue #53 - const cookieHeader = fastifyReq.raw.headers.cookie - if (cookieHeader) { - fastifyReq.cookies = fastify.parseCookie(cookieHeader) +function onReqHandlerWrapper (fastify, hook) { + return hook === 'preParsing' + ? function fastifyCookieHandler (fastifyReq, fastifyRes, payload, done) { + fastifyReq.cookies = {} // New container per request. Issue #53 + const cookieHeader = fastifyReq.raw.headers.cookie + if (cookieHeader) { + fastifyReq.cookies = fastify.parseCookie(cookieHeader) + } + done() } - done() + : function fastifyCookieHandler (fastifyReq, fastifyRes, done) { + fastifyReq.cookies = {} // New container per request. Issue #53 + const cookieHeader = fastifyReq.raw.headers.cookie + if (cookieHeader) { + fastifyReq.cookies = fastify.parseCookie(cookieHeader) + } + done() + } +} + +function getHook (hook = 'onRequest') { + const hooks = { + onRequest: 'onRequest', + preParsing: 'preParsing', + preValidation: 'preValidation', + preHandler: 'preHandler', + [false]: false } + + return hooks[hook] } function plugin (fastify, options, next) { const secret = options.secret + const hook = getHook(options.hook) + if (hook === undefined) { + return next(new Error('@fastify/cookie: Invalid value provided for the hook-option. You can set the hook-option only to false, \'onRequest\' , \'preParsing\' , \'preValidation\' or \'preHandler\'')) + } const enableRotation = Array.isArray(secret) const algorithm = options.algorithm || 'sha256' const signer = typeof secret === 'string' || enableRotation ? new Signer(secret, algorithm) : secret @@ -86,7 +111,9 @@ function plugin (fastify, options, next) { fastify.decorateReply('setCookie', setCookie) fastify.decorateReply('clearCookie', clearCookie) - fastify.addHook('onRequest', onReqHandlerWrapper(fastify)) + if (hook) { + fastify.addHook(hook, onReqHandlerWrapper(fastify, hook)) + } next() diff --git a/test/cookie.test.js b/test/cookie.test.js index 426533e..d13570e 100644 --- a/test/cookie.test.js +++ b/test/cookie.test.js @@ -847,3 +847,98 @@ test('should not decorate fastify, request and reply if no secret was provided', message: 'req.unsignCookie is not a function' }) }) + +test('dont add auto cookie parsing to onRequest-hook if hook-option is set to false', (t) => { + t.plan(6) + const fastify = Fastify() + fastify.register(plugin, { hook: false }) + + for (const hook of ['preValidation', 'preHandler', 'preParsing']) { + fastify.addHook(hook, async (req) => { + t.equal(req.cookies, null) + }) + } + + fastify.get('/disable', (req, reply) => { + t.equal(req.cookies, null) + reply.send() + }) + + fastify.inject({ + method: 'GET', + url: '/disable', + headers: { + cookie: 'bar=bar' + } + }, (err, res) => { + t.error(err) + t.equal(res.statusCode, 200) + }) +}) + +test('result in an error if hook-option is set to an invalid value', (t) => { + t.plan(1) + const fastify = Fastify() + + t.rejects( + () => fastify.register(plugin, { hook: true }), + new Error("@fastify/cookie: Invalid value provided for the hook-option. You can set the hook-option only to false, 'onRequest' , 'preParsing' , 'preValidation' or 'preHandler'") + ) +}) + +test('correct working plugin if hook-option to preParsing', (t) => { + t.plan(5) + const fastify = Fastify() + fastify.register(plugin, { hook: 'preParsing' }) + + fastify.addHook('onRequest', async (req) => { + t.equal(req.cookies, null) + }) + + fastify.addHook('preValidation', async (req) => { + t.equal(req.cookies.bar, 'bar') + }) + + fastify.get('/preparsing', (req, reply) => { + t.equal(req.cookies.bar, 'bar') + reply.send() + }) + + fastify.inject({ + method: 'GET', + url: '/preparsing', + headers: { + cookie: 'bar=bar' + } + }, (err, res) => { + t.error(err) + t.equal(res.statusCode, 200) + }) +}) + +test('if cookies are not set, then the handler creates an empty req.cookies object', (t) => { + t.plan(5) + const fastify = Fastify() + fastify.register(plugin, { hook: 'preParsing' }) + + fastify.addHook('onRequest', async (req) => { + t.equal(req.cookies, null) + }) + + fastify.addHook('preValidation', async (req) => { + t.ok(req.cookies) + }) + + fastify.get('/preparsing', (req, reply) => { + t.ok(req.cookies) + reply.send() + }) + + fastify.inject({ + method: 'GET', + url: '/preparsing' + }, (err, res) => { + t.error(err) + t.equal(res.statusCode, 200) + }) +}) diff --git a/types/plugin.d.ts b/types/plugin.d.ts index 6525fbe..f5156db 100644 --- a/types/plugin.d.ts +++ b/types/plugin.d.ts @@ -125,8 +125,11 @@ declare namespace fastifyCookie { signed?: boolean; } + type HookType = 'onRequest' | 'preParsing' | 'preValidation' | 'preHandler' | 'preSerialization'; + export interface FastifyCookieOptions { secret?: string | string[] | Signer; + hook?: HookType | false; parseOptions?: fastifyCookie.CookieSerializeOptions; } diff --git a/types/plugin.test-d.ts b/types/plugin.test-d.ts index 4cef054..4d4fb56 100644 --- a/types/plugin.test-d.ts +++ b/types/plugin.test-d.ts @@ -1,9 +1,10 @@ import cookie from '..'; -import { expectType } from 'tsd'; +import { expectError, expectType } from 'tsd'; import * as fastifyCookieStar from '..'; import fastifyCookieCjsImport = require('..'); import fastifyCookieDefault, { fastifyCookie as fastifyCookieNamed } from '..'; -import fastify, { FastifyInstance, FastifyReply, setCookieWrapper } from 'fastify'; +import fastify, { FastifyInstance, FastifyPluginCallback, FastifyReply, setCookieWrapper } from 'fastify'; +import { Server } from 'http'; const fastifyCookieCjs = require('..'); @@ -213,4 +214,15 @@ new fastifyCookieStar.Signer('secretString') new fastifyCookieStar.Signer(['secretStringInArray']) const signer = new fastifyCookieStar.Signer(['secretStringInArray'], 'sha256') signer.sign('Lorem Ipsum') -signer.unsign('Lorem Ipsum') \ No newline at end of file +signer.unsign('Lorem Ipsum') + +const appWithHook: FastifyInstance = fastify(); + +appWithHook.register(cookie, { hook: false }); +appWithHook.register(cookie, { hook: 'onRequest' }); +appWithHook.register(cookie, { hook: 'preHandler' }); +appWithHook.register(cookie, { hook: 'preParsing' }); +appWithHook.register(cookie, { hook: 'preSerialization' }); +appWithHook.register(cookie, { hook: 'preValidation' }); +expectError(appWithHook.register(cookie, { hook: true })); +expectError(appWithHook.register(cookie, { hook: 'false' }));