1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 | 'use strict'
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 | var debug = require('debug')('cookie-session');
|
16 | var Cookies = require('cookies');
|
17 | var onHeaders = require('on-headers');
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 | module.exports = cookieSession
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 | function cookieSession(options) {
|
41 | var opts = options || {}
|
42 |
|
43 |
|
44 | var name = opts.name || opts.key || 'express:sess';
|
45 |
|
46 |
|
47 | var keys = opts.keys;
|
48 | if (!keys && opts.secret) keys = [opts.secret];
|
49 |
|
50 |
|
51 | if (null == opts.overwrite) opts.overwrite = true;
|
52 | if (null == opts.httpOnly) opts.httpOnly = true;
|
53 | if (null == opts.signed) opts.signed = true;
|
54 |
|
55 | if (!keys && opts.signed) throw new Error('.keys required.');
|
56 |
|
57 | debug('session options %j', opts);
|
58 |
|
59 | return function _cookieSession(req, res, next) {
|
60 | var cookies = req.sessionCookies = new Cookies(req, res, {
|
61 | keys: keys
|
62 | });
|
63 | var sess, json;
|
64 |
|
65 |
|
66 | req.sessionOptions = Object.create(opts)
|
67 | req.sessionKey = name
|
68 |
|
69 | req.__defineGetter__('session', function(){
|
70 |
|
71 | if (sess) return sess;
|
72 |
|
73 |
|
74 | if (false === sess) return null;
|
75 |
|
76 | json = cookies.get(name, req.sessionOptions)
|
77 |
|
78 | if (json) {
|
79 | debug('parse %s', json);
|
80 | try {
|
81 | sess = new Session(req, decode(json));
|
82 | } catch (err) {
|
83 |
|
84 |
|
85 |
|
86 |
|
87 |
|
88 | if (!(err instanceof SyntaxError)) throw err;
|
89 | sess = new Session(req);
|
90 | }
|
91 | } else {
|
92 | debug('new session');
|
93 | sess = new Session(req);
|
94 | }
|
95 |
|
96 | return sess;
|
97 | });
|
98 |
|
99 | req.__defineSetter__('session', function(val){
|
100 | if (null == val) return sess = false;
|
101 | if ('object' == typeof val) return sess = new Session(req, val);
|
102 | throw new Error('req.session can only be set as null or an object.');
|
103 | });
|
104 |
|
105 | onHeaders(res, function setHeaders() {
|
106 | if (sess === undefined) {
|
107 |
|
108 | return;
|
109 | }
|
110 |
|
111 | try {
|
112 | if (sess === false) {
|
113 |
|
114 | cookies.set(name, '', req.sessionOptions)
|
115 | } else if (!json && !sess.length) {
|
116 |
|
117 | } else if (sess.changed(json)) {
|
118 |
|
119 | sess.save();
|
120 | }
|
121 | } catch (e) {
|
122 | debug('error saving session %s', e.message);
|
123 | }
|
124 | });
|
125 |
|
126 | next();
|
127 | }
|
128 | };
|
129 |
|
130 |
|
131 |
|
132 |
|
133 |
|
134 |
|
135 |
|
136 |
|
137 |
|
138 | function Session(ctx, obj) {
|
139 | this._ctx = ctx
|
140 |
|
141 | Object.defineProperty(this, 'isNew', {
|
142 | value: !obj
|
143 | })
|
144 |
|
145 | if (obj) {
|
146 | for (var key in obj) {
|
147 | this[key] = obj[key]
|
148 | }
|
149 | }
|
150 | }
|
151 |
|
152 |
|
153 |
|
154 |
|
155 |
|
156 |
|
157 |
|
158 |
|
159 | Session.prototype.inspect =
|
160 | Session.prototype.toJSON = function toJSON() {
|
161 | var keys = Object.keys(this)
|
162 | var obj = {}
|
163 |
|
164 | for (var i = 0; i < keys.length; i++) {
|
165 | var key = keys[i]
|
166 |
|
167 | if (key[0] !== '_') {
|
168 | obj[key] = this[key]
|
169 | }
|
170 | }
|
171 |
|
172 | return obj
|
173 | }
|
174 |
|
175 |
|
176 |
|
177 |
|
178 |
|
179 |
|
180 |
|
181 |
|
182 |
|
183 |
|
184 | Session.prototype.changed = function(prev){
|
185 | if (!prev) return true;
|
186 | this._json = encode(this);
|
187 | return this._json != prev;
|
188 | };
|
189 |
|
190 |
|
191 |
|
192 |
|
193 |
|
194 |
|
195 |
|
196 |
|
197 |
|
198 | Session.prototype.__defineGetter__('length', function(){
|
199 | return Object.keys(this.toJSON()).length;
|
200 | });
|
201 |
|
202 |
|
203 |
|
204 |
|
205 |
|
206 |
|
207 |
|
208 |
|
209 | Session.prototype.__defineGetter__('populated', function(){
|
210 | return !!this.length;
|
211 | });
|
212 |
|
213 |
|
214 |
|
215 |
|
216 |
|
217 |
|
218 |
|
219 | Session.prototype.save = function(){
|
220 | var ctx = this._ctx;
|
221 | var json = this._json || encode(this);
|
222 | var opts = ctx.sessionOptions;
|
223 | var name = ctx.sessionKey;
|
224 |
|
225 | debug('save %s', json);
|
226 | ctx.sessionCookies.set(name, json, opts);
|
227 | };
|
228 |
|
229 |
|
230 |
|
231 |
|
232 |
|
233 |
|
234 |
|
235 |
|
236 |
|
237 | function decode(string) {
|
238 | var body = new Buffer(string, 'base64').toString('utf8');
|
239 | return JSON.parse(body);
|
240 | }
|
241 |
|
242 |
|
243 |
|
244 |
|
245 |
|
246 |
|
247 |
|
248 |
|
249 |
|
250 | function encode(body) {
|
251 | var str = JSON.stringify(body)
|
252 | return new Buffer(str).toString('base64')
|
253 | }
|