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

feat: option disabling parsing in the onRequest phase #204

Merged
merged 20 commits into from
Aug 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down
43 changes: 35 additions & 8 deletions plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
vitaly-sazonov marked this conversation as resolved.
Show resolved Hide resolved
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 = {
Uzlopak marked this conversation as resolved.
Show resolved Hide resolved
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
Expand All @@ -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()

Expand Down
95 changes: 95 additions & 0 deletions test/cookie.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
})
})
3 changes: 3 additions & 0 deletions types/plugin.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
vitaly-sazonov marked this conversation as resolved.
Show resolved Hide resolved
parseOptions?: fastifyCookie.CookieSerializeOptions;
}

Expand Down
18 changes: 15 additions & 3 deletions types/plugin.test-d.ts
Original file line number Diff line number Diff line change
@@ -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('..');

Expand Down Expand Up @@ -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')
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' }));