perf: lazy init cookies and ip when first time use it (#1216)
This commit is contained in:
parent
74170caf0b
commit
162a5b3e78
5 changed files with 134 additions and 66 deletions
|
@ -14,7 +14,6 @@ const isJSON = require('koa-is-json');
|
|||
const context = require('./context');
|
||||
const request = require('./request');
|
||||
const statuses = require('statuses');
|
||||
const Cookies = require('cookies');
|
||||
const Emitter = require('events');
|
||||
const util = require('util');
|
||||
const Stream = require('stream');
|
||||
|
@ -169,11 +168,6 @@ module.exports = class Application extends Emitter {
|
|||
request.response = response;
|
||||
response.request = request;
|
||||
context.originalUrl = request.originalUrl = req.url;
|
||||
context.cookies = new Cookies(req, res, {
|
||||
keys: this.keys,
|
||||
secure: request.secure
|
||||
});
|
||||
request.ip = request.ips[0] || req.socket.remoteAddress || '';
|
||||
context.state = {};
|
||||
return context;
|
||||
}
|
||||
|
|
|
@ -10,6 +10,9 @@ const createError = require('http-errors');
|
|||
const httpAssert = require('http-assert');
|
||||
const delegate = require('delegates');
|
||||
const statuses = require('statuses');
|
||||
const Cookies = require('cookies');
|
||||
|
||||
const COOKIES = Symbol('context#cookies');
|
||||
|
||||
/**
|
||||
* Context prototype.
|
||||
|
@ -151,6 +154,20 @@ const proto = module.exports = {
|
|||
this.status = err.status;
|
||||
this.length = Buffer.byteLength(msg);
|
||||
this.res.end(msg);
|
||||
},
|
||||
|
||||
get cookies() {
|
||||
if (!this[COOKIES]) {
|
||||
this[COOKIES] = new Cookies(this.req, this.res, {
|
||||
keys: this.app.keys,
|
||||
secure: this.request.secure
|
||||
});
|
||||
}
|
||||
return this[COOKIES];
|
||||
},
|
||||
|
||||
set cookies(_cookies) {
|
||||
this[COOKIES] = _cookies;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -17,6 +17,8 @@ const fresh = require('fresh');
|
|||
const only = require('only');
|
||||
const util = require('util');
|
||||
|
||||
const IP = Symbol('context#ip');
|
||||
|
||||
/**
|
||||
* Prototype.
|
||||
*/
|
||||
|
@ -439,6 +441,26 @@ module.exports = {
|
|||
: [];
|
||||
},
|
||||
|
||||
/**
|
||||
* Return request's remote address
|
||||
* When `app.proxy` is `true`, parse
|
||||
* the "X-Forwarded-For" ip address list and return the first one
|
||||
*
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
get ip() {
|
||||
if (!this[IP]) {
|
||||
this[IP] = this.ips[0] || this.socket.remoteAddress || '';
|
||||
}
|
||||
return this[IP];
|
||||
},
|
||||
|
||||
set ip(_ip) {
|
||||
this[IP] = _ip;
|
||||
},
|
||||
|
||||
/**
|
||||
* Return subdomains as an array.
|
||||
*
|
||||
|
|
|
@ -5,51 +5,13 @@ const assert = require('assert');
|
|||
const request = require('supertest');
|
||||
const Koa = require('../..');
|
||||
|
||||
describe('ctx.cookies.set()', () => {
|
||||
it('should set an unsigned cookie', async () => {
|
||||
const app = new Koa();
|
||||
|
||||
app.use((ctx, next) => {
|
||||
ctx.cookies.set('name', 'jon');
|
||||
ctx.status = 204;
|
||||
});
|
||||
|
||||
const server = app.listen();
|
||||
|
||||
const res = await request(server)
|
||||
.get('/')
|
||||
.expect(204);
|
||||
|
||||
const cookie = res.headers['set-cookie'].some(cookie => /^name=/.test(cookie));
|
||||
assert.equal(cookie, true);
|
||||
});
|
||||
|
||||
describe('with .signed', () => {
|
||||
describe('when no .keys are set', () => {
|
||||
it('should error', () => {
|
||||
const app = new Koa();
|
||||
|
||||
app.use((ctx, next) => {
|
||||
try {
|
||||
ctx.cookies.set('foo', 'bar', { signed: true });
|
||||
} catch (err) {
|
||||
ctx.body = err.message;
|
||||
}
|
||||
});
|
||||
|
||||
return request(app.callback())
|
||||
.get('/')
|
||||
.expect('.keys required for signed cookies');
|
||||
});
|
||||
});
|
||||
|
||||
it('should send a signed cookie', async () => {
|
||||
describe('ctx.cookies', () => {
|
||||
describe('ctx.cookies.set()', () => {
|
||||
it('should set an unsigned cookie', async () => {
|
||||
const app = new Koa();
|
||||
|
||||
app.keys = ['a', 'b'];
|
||||
|
||||
app.use((ctx, next) => {
|
||||
ctx.cookies.set('name', 'jon', { signed: true });
|
||||
ctx.cookies.set('name', 'jon');
|
||||
ctx.status = 204;
|
||||
});
|
||||
|
||||
|
@ -59,36 +21,99 @@ describe('ctx.cookies.set()', () => {
|
|||
.get('/')
|
||||
.expect(204);
|
||||
|
||||
const cookies = res.headers['set-cookie'];
|
||||
const cookie = res.headers['set-cookie'].some(cookie => /^name=/.test(cookie));
|
||||
assert.equal(cookie, true);
|
||||
});
|
||||
|
||||
assert.equal(cookies.some(cookie => /^name=/.test(cookie)), true);
|
||||
assert.equal(cookies.some(cookie => /(,|^)name\.sig=/.test(cookie)), true);
|
||||
describe('with .signed', () => {
|
||||
describe('when no .keys are set', () => {
|
||||
it('should error', () => {
|
||||
const app = new Koa();
|
||||
|
||||
app.use((ctx, next) => {
|
||||
try {
|
||||
ctx.cookies.set('foo', 'bar', { signed: true });
|
||||
} catch (err) {
|
||||
ctx.body = err.message;
|
||||
}
|
||||
});
|
||||
|
||||
return request(app.callback())
|
||||
.get('/')
|
||||
.expect('.keys required for signed cookies');
|
||||
});
|
||||
});
|
||||
|
||||
it('should send a signed cookie', async () => {
|
||||
const app = new Koa();
|
||||
|
||||
app.keys = ['a', 'b'];
|
||||
|
||||
app.use((ctx, next) => {
|
||||
ctx.cookies.set('name', 'jon', { signed: true });
|
||||
ctx.status = 204;
|
||||
});
|
||||
|
||||
const server = app.listen();
|
||||
|
||||
const res = await request(server)
|
||||
.get('/')
|
||||
.expect(204);
|
||||
|
||||
const cookies = res.headers['set-cookie'];
|
||||
|
||||
assert.equal(cookies.some(cookie => /^name=/.test(cookie)), true);
|
||||
assert.equal(cookies.some(cookie => /(,|^)name\.sig=/.test(cookie)), true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with secure', () => {
|
||||
it('should get secure from request', async () => {
|
||||
const app = new Koa();
|
||||
|
||||
app.proxy = true;
|
||||
app.keys = ['a', 'b'];
|
||||
|
||||
app.use(ctx => {
|
||||
ctx.cookies.set('name', 'jon', { signed: true });
|
||||
ctx.status = 204;
|
||||
});
|
||||
|
||||
const server = app.listen();
|
||||
|
||||
const res = await request(server)
|
||||
.get('/')
|
||||
.set('x-forwarded-proto', 'https') // mock secure
|
||||
.expect(204);
|
||||
|
||||
const cookies = res.headers['set-cookie'];
|
||||
assert.equal(cookies.some(cookie => /^name=/.test(cookie)), true);
|
||||
assert.equal(cookies.some(cookie => /(,|^)name\.sig=/.test(cookie)), true);
|
||||
assert.equal(cookies.every(cookie => /secure/.test(cookie)), true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('with secure', () => {
|
||||
it('should get secure from request', async () => {
|
||||
describe('ctx.cookies=', () => {
|
||||
it('should override cookie work', async () => {
|
||||
const app = new Koa();
|
||||
|
||||
app.proxy = true;
|
||||
app.keys = ['a', 'b'];
|
||||
|
||||
app.use(ctx => {
|
||||
ctx.cookies.set('name', 'jon', { signed: true });
|
||||
app.use((ctx, next) => {
|
||||
ctx.cookies = {
|
||||
set(key, value){
|
||||
ctx.set(key, value);
|
||||
}
|
||||
};
|
||||
ctx.cookies.set('name', 'jon');
|
||||
ctx.status = 204;
|
||||
});
|
||||
|
||||
const server = app.listen();
|
||||
|
||||
const res = await request(server)
|
||||
await request(server)
|
||||
.get('/')
|
||||
.set('x-forwarded-proto', 'https') // mock secure
|
||||
.expect('name', 'jon')
|
||||
.expect(204);
|
||||
|
||||
const cookies = res.headers['set-cookie'];
|
||||
assert.equal(cookies.some(cookie => /^name=/.test(cookie)), true);
|
||||
assert.equal(cookies.some(cookie => /(,|^)name\.sig=/.test(cookie)), true);
|
||||
assert.equal(cookies.every(cookie => /secure/.test(cookie)), true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -39,11 +39,21 @@ describe('req.ip', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should be cached', () => {
|
||||
it('should be lazy inited and cached', () => {
|
||||
const req = { socket: new Stream.Duplex() };
|
||||
req.socket.remoteAddress = '127.0.0.2';
|
||||
const request = Request(req);
|
||||
assert.equal(request.ip, '127.0.0.2');
|
||||
req.socket.remoteAddress = '127.0.0.1';
|
||||
assert.equal(request.ip, '127.0.0.2');
|
||||
});
|
||||
|
||||
it('should reset ip work', () => {
|
||||
const req = { socket: new Stream.Duplex() };
|
||||
req.socket.remoteAddress = '127.0.0.2';
|
||||
const request = Request(req);
|
||||
assert.equal(request.ip, '127.0.0.2');
|
||||
request.ip = '127.0.0.1';
|
||||
assert.equal(request.ip, '127.0.0.1');
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue