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: support function to set domain #38

Merged
merged 2 commits into from
Aug 4, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
9 changes: 7 additions & 2 deletions lib/cookie.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,20 @@ const fieldContentRegExp = /^[\u0009\u0020-\u007e\u0080-\u00ff]+$/; // eslint-di
const sameSiteRegExp = /^(?:none|lax|strict)$/i;

class Cookie {
constructor(name, value, attrs) {
constructor(ctx, name, value, attrs) {
onlylovermb marked this conversation as resolved.
Show resolved Hide resolved
assert(fieldContentRegExp.test(name), 'argument name is invalid');
assert(!value || fieldContentRegExp.test(value), 'argument value is invalid');

this.ctx = ctx;
this.name = name;
this.value = value || '';
this.attrs = mergeDefaultAttrs(attrs);
assert(!this.attrs.path || fieldContentRegExp.test(this.attrs.path), 'argument option path is invalid');
assert(!this.attrs.domain || fieldContentRegExp.test(this.attrs.domain), 'argument option domain is invalid');
assert(!this.attrs.domain || typeof this.attrs.domain === 'string' || typeof this.attrs.domain === 'function', 'argument option domain is invalid');
Copy link
Member

@fengmk2 fengmk2 Aug 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nodejs.AssertionError: argument option domain is invalid
    at new Cookie (/home/admin/app/node_modules/_egg-cookies@2.7.0@egg-cookies/lib/cookie.js:35:5)
    at Cookies.set (/home/admin/app/node_modules/_egg-cookies@2.7.0@egg-cookies/lib/cookies.js:124:20)
    at updateCookie (/home/admin/app/node_modules/_koa-locales@1.12.0@koa-locales/index.js:254:17)
    at Object.app.context.__getLocale (/home/admin/app/node_modules/_koa-locales@1.12.0@koa-locales/index.js:224:7)
    at Object.get locale [as locale] (/home/admin/app/node_modules/_egg-i18n@2.1.1@egg-i18n/app/extend/context.js:10:17)

if (typeof this.attrs.domain === 'function') {
this.attrs.domain = this.attrs.domain(this.ctx);
}
assert(fieldContentRegExp.test(this.attrs.domain), 'argument option domain is invalid');
assert(!this.attrs.sameSite || this.attrs.sameSite === true || sameSiteRegExp.test(this.attrs.sameSite), 'argument option sameSite is invalid');
if (!value) {
this.attrs.expires = new Date(0);
Expand Down
2 changes: 1 addition & 1 deletion lib/cookies.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ class Cookies {
}
}

const cookie = new Cookie(name, value, opts);
const cookie = new Cookie(this.ctx, name, value, opts);

// if user not set secure, reset secure to ctx.secure
if (opts.secure === undefined) cookie.attrs.secure = this.secure;
Expand Down
58 changes: 35 additions & 23 deletions test/lib/cookie.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,25 @@ function assertExceptionCheck(expectedMsg) {
}
describe('test/lib/cookie.test.js', () => {
it('create cookies contains invalid string error should throw', () => {
assert.throws(() => new Cookie('中文', 'value'), assertExceptionCheck('argument name is invalid'));
assert.throws(() => new Cookie('name', '中文'), assertExceptionCheck('argument value is invalid'));
assert.throws(() => new Cookie('name', 'value', { path: '中文' }), assertExceptionCheck('argument option path is invalid'));
assert.throws(() => new Cookie('name', 'value', { domain: '中文' }), assertExceptionCheck('argument option domain is invalid'));
assert.throws(() => new Cookie({}, '中文', 'value'), assertExceptionCheck('argument name is invalid'));
assert.throws(() => new Cookie({}, 'name', '中文'), assertExceptionCheck('argument value is invalid'));
assert.throws(() => new Cookie({}, 'name', 'value', { path: '中文' }), assertExceptionCheck('argument option path is invalid'));
assert.throws(() => new Cookie({}, 'name', 'value', { domain: '中文' }), assertExceptionCheck('argument option domain is invalid'));
});

it('set expires to 0 if value not present', () => {
assert(new Cookie('name', null).attrs.expires.getTime() === 0);
assert(new Cookie({}, 'name', null).attrs.expires.getTime() === 0);
});

describe('toString()', () => {
it('return name=vaule', () => {
assert(new Cookie('name', 'value').toString() === 'name=value');
assert(new Cookie({}, 'name', 'value').toString() === 'name=value');
});
});

describe('toHeader()', () => {
it('return name=vaule;params', () => {
assert(new Cookie('name', 'value', {
assert(new Cookie({}, 'name', 'value', {
secure: true,
maxAge: 1000,
domain: 'eggjs.org',
Expand All @@ -37,15 +37,27 @@ describe('test/lib/cookie.test.js', () => {
}).toHeader().match(/^name=value; path=\/; max-age=1; expires=(.*?)GMT; domain=eggjs\.org; secure; httponly$/));
});

it('set domain when domain is a function', () => {
assert(new Cookie({
hostname: 'eggjs.org',
}, 'name', 'value', {
secure: true,
maxAge: 1000,
domain: ctx => ctx.hostname,
path: '/',
httpOnly: true,
}).toHeader().match(/^name=value; path=\/; max-age=1; expires=(.*?)GMT; domain=eggjs\.org; secure; httponly$/));
});

it('donnot set path when set path to null', () => {
const header = new Cookie('name', 'value', {
const header = new Cookie({}, 'name', 'value', {
path: null,
}).toHeader();
assert(!header.match(/path=/));
});

it('donnot set httponly when set httpOnly to false', () => {
const header = new Cookie('name', 'value', {
const header = new Cookie({}, 'name', 'value', {
httpOnly: false,
}).toHeader();
assert(!header.match(/httponly/));
Expand All @@ -55,15 +67,15 @@ describe('test/lib/cookie.test.js', () => {
describe('maxAge', () => {
it('maxAge overwrite expires', () => {
const expires = new Date('2020-01-01');
let header = new Cookie('name', 'value', {
let header = new Cookie({}, 'name', 'value', {
secure: true,
expires,
domain: 'eggjs.org',
path: '/',
httpOnly: true,
}).toHeader();
assert(header.match(/expires=Wed, 01 Jan 2020 00:00:00 GMT/));
header = new Cookie('name', 'value', {
header = new Cookie({}, 'name', 'value', {
secure: true,
maxAge: 1000,
expires,
Expand All @@ -75,7 +87,7 @@ describe('test/lib/cookie.test.js', () => {
});

it('ignore maxage NaN', () => {
const header = new Cookie('name', 'value', {
const header = new Cookie({}, 'name', 'value', {
secure: true,
maxAge: 'session',
domain: 'eggjs.org',
Expand All @@ -88,7 +100,7 @@ describe('test/lib/cookie.test.js', () => {

it('ignore maxage 0', () => {
// In previous implementations, maxAge = 0 was considered unnecessary to set this header
const header = new Cookie('name', 'value', {
const header = new Cookie({}, 'name', 'value', {
secure: true,
maxAge: 0,
domain: 'eggjs.org',
Expand All @@ -102,21 +114,21 @@ describe('test/lib/cookie.test.js', () => {

describe('sameSite', () => {
it('should default to false', () => {
const cookie = new Cookie('foo', 'bar');
const cookie = new Cookie({}, 'foo', 'bar');
assert.equal(cookie.attrs.sameSite, false);
});

it('should throw on invalid value', () => {
assert.throws(() => {
new Cookie('foo', 'bar', { sameSite: 'foo' });
new Cookie({}, 'foo', 'bar', { sameSite: 'foo' });
}, /argument option sameSite is invalid/);
});

describe('when set to falsy values', () => {
it('should not add "samesite" attribute in header', () => {
const falsyValues = [ false, 0, '', null, undefined, NaN ];
falsyValues.forEach(falsy => {
const cookie = new Cookie('foo', 'bar', { sameSite: falsy });
const cookie = new Cookie({}, 'foo', 'bar', { sameSite: falsy });
assert.ok(Object.is(cookie.attrs.sameSite, falsy));
assert.equal(cookie.toHeader(), 'foo=bar; path=/; httponly');
});
Expand All @@ -125,7 +137,7 @@ describe('test/lib/cookie.test.js', () => {

describe('when set to "true"', () => {
it('should set "samesite=strict" attribute in header', () => {
const cookie = new Cookie('foo', 'bar', { sameSite: true });
const cookie = new Cookie({}, 'foo', 'bar', { sameSite: true });
assert.equal(cookie.attrs.sameSite, true);
assert.equal(cookie.toHeader(), 'foo=bar; path=/; samesite=strict; httponly');
});
Expand All @@ -134,11 +146,11 @@ describe('test/lib/cookie.test.js', () => {
describe('when set to "none"', () => {
it('should set "samesite=none" attribute in header', () => {
{
const cookie = new Cookie('foo', 'bar', { sameSite: 'none' });
const cookie = new Cookie({}, 'foo', 'bar', { sameSite: 'none' });
assert.equal(cookie.toHeader(), 'foo=bar; path=/; samesite=none; httponly');
}
{
const cookie = new Cookie('foo', 'bar', { sameSite: 'None' });
const cookie = new Cookie({}, 'foo', 'bar', { sameSite: 'None' });
assert.equal(cookie.toHeader(), 'foo=bar; path=/; samesite=none; httponly');
}
});
Expand All @@ -147,11 +159,11 @@ describe('test/lib/cookie.test.js', () => {
describe('when set to "lax"', () => {
it('should set "samesite=lax" attribute in header', () => {
{
const cookie = new Cookie('foo', 'bar', { sameSite: 'lax' });
const cookie = new Cookie({}, 'foo', 'bar', { sameSite: 'lax' });
assert.equal(cookie.toHeader(), 'foo=bar; path=/; samesite=lax; httponly');
}
{
const cookie = new Cookie('foo', 'bar', { sameSite: 'Lax' });
const cookie = new Cookie({}, 'foo', 'bar', { sameSite: 'Lax' });
assert.equal(cookie.toHeader(), 'foo=bar; path=/; samesite=lax; httponly');
}
});
Expand All @@ -160,11 +172,11 @@ describe('test/lib/cookie.test.js', () => {
describe('when set to "strict"', () => {
it('should set "samesite=strict" attribute in header', () => {
{
const cookie = new Cookie('foo', 'bar', { sameSite: 'strict' });
const cookie = new Cookie({}, 'foo', 'bar', { sameSite: 'strict' });
assert.equal(cookie.toHeader(), 'foo=bar; path=/; samesite=strict; httponly');
}
{
const cookie = new Cookie('foo', 'bar', { sameSite: 'Strict' });
const cookie = new Cookie({}, 'foo', 'bar', { sameSite: 'Strict' });
assert.equal(cookie.toHeader(), 'foo=bar; path=/; samesite=strict; httponly');
}
});
Expand Down
11 changes: 11 additions & 0 deletions test/lib/cookies.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,17 @@ describe('test/lib/cookies.test.js', () => {
assert(cookie.indexOf('secure') > 0);
});

it('should work with domain ok, when domain is a function', () => {
const cookies = Cookies({
host: 'foo.com',
}, {
secure: true,
});
cookies.set('foo', 'bar', { encrypt: true, domain: ctx => ctx.request.host });
const cookie = cookies.ctx.response.headers['set-cookie'][0];
assert(cookie.indexOf('domain=foo.com') > 0);
});

it('should signed work fine', () => {
const cookies = Cookies();
cookies.set('foo', 'bar', { signed: true });
Expand Down