Comparing version 0.0.1 to 0.0.2-1
var http = require("http"), | ||
sessions = require("../lib/sessions"), | ||
handler = new sessions(); // defaults to memory store | ||
handler = new sessions(); | ||
http.createServer(function (req, res) { | ||
var session = handler.httpRequest(req, res); | ||
handler.httpRequest(req, res, function (err, session) { | ||
if (err) { | ||
return res.end("session error"); | ||
} | ||
console.log("[%s] > %s", session.uid(), req.url); | ||
console.log("[%s] > %s", session.uid(), req.url); | ||
res.end(req.url); | ||
res.end(req.url); | ||
}); | ||
@@ -12,0 +16,0 @@ }).listen(1337, function () { |
@@ -19,13 +19,23 @@ /** | ||
var sessions = require("../lib/sessions"), | ||
Sessions = new sessions(sessions.stores.memory, { expires: 5 }); | ||
var session1 = Sessions.create("session1"), | ||
session2 = Sessions.create("session2"), | ||
session3 = Sessions.create(/* name automatically generated */).refresh(8), | ||
Sessions = new sessions(sessions.stores.memory, { expires: 5 }), | ||
intervalId = null; | ||
console.log("* created session '%s'", session1.uid()); | ||
console.log("* created session '%s'", session2.uid()); | ||
console.log("* created session '%s'", session3.uid()); | ||
Sessions.create("session1", function (_, session1) { | ||
console.log("* created session '%s'", session1.uid()); | ||
intervalId = setInterval(function () { | ||
session1.refresh(); | ||
}, 1000); | ||
Sessions.create("session2", function (_, session2) { | ||
console.log("* created session '%s'", session2.uid()); | ||
Sessions.create(/* name automatically generated */ function (_, session3) { | ||
console.log("* created session '%s'", session3.uid()); | ||
session3.refresh(8); | ||
}); | ||
}); | ||
}); | ||
Sessions.on("expired", function (uid) { | ||
@@ -40,6 +50,2 @@ console.log("! session expired:", uid); | ||
console.log("* checking session expiration.."); | ||
}); | ||
intervalId = setInterval(function () { | ||
session1.refresh(); | ||
}, 1000); | ||
}); |
@@ -12,3 +12,3 @@ var util = require("util"), | ||
this.opts = opts || {}; | ||
this.sessions = {}; | ||
this._currentSessions = 0; | ||
@@ -30,9 +30,15 @@ this._checkTimeoutId = null; | ||
Sessions.prototype.total = function () { | ||
return Object.keys(this.sessions).length; | ||
return this._currentSessions; | ||
}; | ||
Sessions.prototype.get = function (uid) { | ||
if (this.sessions.hasOwnProperty(uid)) { | ||
return this.sessions[uid]; | ||
} | ||
return null; | ||
Sessions.prototype.get = function (uid, cb) { | ||
var sessions = this; | ||
this.store.get(uid, function (err, meta, data) { | ||
if (err) { | ||
return cb(err); | ||
} | ||
return cb(null, buildSessionUtils(uid, meta, data, sessions.store, sessions.opts.expires, sessions), sessions); | ||
}); | ||
return this; | ||
}; | ||
@@ -42,3 +48,5 @@ Sessions.prototype.create = function () { | ||
uid = null, | ||
data = {}; | ||
data = {}, | ||
meta = { expires: Date.now() + (this.opts.expires * 1000) }, | ||
cb = null; | ||
@@ -53,2 +61,5 @@ for (var i = 0; i < arguments.length; i++) { | ||
break; | ||
case "function": | ||
cb = arguments[i]; | ||
break; | ||
} | ||
@@ -61,61 +72,15 @@ } | ||
if (this.store.add(uid, { | ||
data: data, | ||
expires: Date.now() + (this.opts.expires * 1000) | ||
}) === false) { | ||
return null; | ||
} | ||
this.store.add(uid, meta, data, function (err, data) { | ||
if (err) { | ||
return cb(err); | ||
} | ||
this.sessions[uid] = { | ||
get: function (key) { | ||
if (typeof key == "undefined") { | ||
return sessions.store.get(uid).data; | ||
} | ||
return sessions.store.get(uid).data[key] || null; | ||
}, | ||
set: function (key, value) { | ||
if (typeof key == "object") { | ||
sessions.store.set(uid, key); | ||
} else { | ||
var o = {}; | ||
o[key] = value; | ||
sessions.store.set(uid, o); | ||
} | ||
return this; | ||
}, | ||
refresh: function (expires) { | ||
sessions.store.set(uid, { | ||
expires: Date.now() + ((expires || sessions.opts.expires) * 1000) | ||
}); | ||
return this; | ||
}, | ||
expire: function () { | ||
sessions.store.remove(uid); | ||
delete sessions.sessions[uid]; | ||
sessions.emit("expired", uid, true); | ||
return this; | ||
}, | ||
uid: function () { | ||
return uid; | ||
}, | ||
expires: function () { | ||
var sess = sessions.store.get(uid); | ||
return (sess ? sess.expires : null); | ||
if (sessions._checkTimeoutId === null) { | ||
sessions._checkTimeoutId = setTimeout(function () { | ||
sessions.checkExpiration(); | ||
}, (sessions.opts.expires + 1) * 1000); | ||
} | ||
}; | ||
if (this._checkTimeoutId === null) { | ||
this._checkTimeoutId = setTimeout((function (me) { | ||
return function () { | ||
me.checkExpiration(); | ||
}; | ||
})(this), (this.opts.expires + 1) * 1000); | ||
} | ||
return this.sessions[uid]; | ||
return cb(null, buildSessionUtils(uid, meta, data, sessions.store, sessions.opts.expires, sessions), sessions); | ||
}); | ||
}; | ||
@@ -125,62 +90,64 @@ Sessions.prototype.uid = function () { | ||
}; | ||
Sessions.prototype.httpRequest = function (req, res) { | ||
var cookies = {}, session; | ||
Sessions.prototype.httpRequest = function (req, res, cb) { | ||
var cookies = parseCookies(req), | ||
sessions = this; | ||
req.headers.cookie && req.headers.cookie.split(";").forEach(function (param) { | ||
var part = param.split("=", 2); | ||
cookies[part[0].toLowerCase()] = part[1] || true; | ||
}); | ||
if (!cookies.hasOwnProperty("uid")) { | ||
session = this.create(); | ||
return this.create(function (err, session) { | ||
if (err) { | ||
return cb(err); | ||
} | ||
setHttpCookie(res, "uid", session.uid(), session.expires(), this.opts.path, this.opts.domain); | ||
setHttpCookie(res, "uid", session.uid(), session.expires(), sessions.opts.path, sessions.opts.domain); | ||
return session; | ||
return cb(null, session, sessions); | ||
}); | ||
} | ||
session = this.get(cookies.uid); | ||
if (session === null) { | ||
session = this.create(); | ||
console.log("creating new session.."); | ||
this.get(cookies.uid, function (err, session) { | ||
if (err) { | ||
return sessions.create(function (err, session) { | ||
if (err) { | ||
return cb(err); | ||
} | ||
setHttpCookie(res, "uid", session.uid(), session.expires(), this.opts.path, this.opts.domain); | ||
} | ||
setHttpCookie(res, "uid", session.uid(), session.expires(), sessions.opts.path, sessions.opts.domain); | ||
return session; | ||
return cb(null, session); | ||
}); | ||
} else { | ||
return cb(null, session, sessions); | ||
} | ||
}); | ||
return this; | ||
}; | ||
Sessions.prototype.checkExpiration = function () { | ||
var uids = this.store.uids(), sess, | ||
i = 0, l = uids.length, dt = Date.now(), | ||
next_expires = null; | ||
var sessions = this, | ||
conf = { dt: Date.now(), next: null }; | ||
this.emit("expirecheck"); | ||
this.store.uids(function (err, uids) { | ||
if (err) { | ||
return; | ||
} | ||
for (; i < l; i++) { | ||
sess = this.store.get(uids[i]); | ||
var sess, i = 0, l = uids.length, missing = l; | ||
if (sess.expires <= dt) { | ||
this.emit("expired", uids[i], false); | ||
this.store.remove(uids[i]); | ||
continue; | ||
} | ||
if (next_expires === null) { | ||
next_expires = sess.expires; | ||
continue; | ||
} | ||
if (sess.expires < next_expires) { | ||
next_expires = sess.expires; | ||
} | ||
} | ||
sessions.emit("expirecheck"); | ||
if (next_expires !== null) { | ||
i = (next_expires - Date.now()) / 1000; | ||
for (; i < l; i++) { | ||
checkSessionExpiration(sessions, uids[i], conf, function () { | ||
missing -= 1; | ||
if (missing == 0) { | ||
if (conf.next !== null) { | ||
i = (conf.next - Date.now()) / 1000; | ||
this._checkTimeoutId = setTimeout((function (me) { | ||
return function () { | ||
me.checkExpiration(); | ||
}; | ||
})(this), (i > 0 ? (i + 1) : 10) * 1000); | ||
} | ||
sessions._checkTimeoutId = setTimeout(function () { | ||
sessions.checkExpiration(); | ||
}, (i > 0 ? (i + 1) : 10) * 1000); | ||
} | ||
} | ||
}); | ||
} | ||
}); | ||
}; | ||
@@ -192,2 +159,3 @@ | ||
module.exports.stores = sessionStores; | ||
module.exports.parseCookies = parseCookies; | ||
@@ -208,8 +176,45 @@ function initStores() { | ||
function parseCookies(req) { | ||
var cookies = {}; | ||
req.headers.cookie && req.headers.cookie.split(";").forEach(function (param) { | ||
var part = param.split("=", 2); | ||
cookies[part[0].toLowerCase()] = part[1] || true; | ||
}); | ||
return cookies; | ||
} | ||
function checkSessionExpiration(sessions, uid, conf, cb) { | ||
sessions.store.get(uid, function (err, sess) { | ||
if (err) return cb(); | ||
if (sess.expires <= conf.dt) { | ||
sessions.emit("expired", uid, false); | ||
sessions.store.remove(uid); | ||
return cb(); | ||
} | ||
if (conf.next === null) { | ||
conf.next = sess.expires; | ||
return cb(); | ||
} | ||
if (sess.expires < conf.next) { | ||
conf.next = sess.expires; | ||
} | ||
return cb(); | ||
}); | ||
} | ||
function setHttpCookie(res, key, value, expires, path, domain) { | ||
var cookie = [], dt; | ||
cookie.push(key + "=" + value); | ||
if (domain !== null) { | ||
cookie.push("domain=" + domain); | ||
} | ||
if (path !== null) { | ||
cookie.push("path=" + path); | ||
} | ||
@@ -229,1 +234,42 @@ if (expires !== null) { | ||
} | ||
function buildSessionUtils(uid, meta, data, store, expires, sessions) { | ||
return { | ||
get: function (key) { | ||
if (typeof key == "undefined") { | ||
return data; | ||
} | ||
return data[key] || null; | ||
}, | ||
set: function (key, value, cb) { | ||
if (typeof key == "object") { | ||
store.set(uid, key, cb); | ||
} else { | ||
var o = {}; | ||
o[key] = value; | ||
store.set(uid, o, cb); | ||
} | ||
return this; | ||
}, | ||
refresh: function (expire) { | ||
meta.expires = Date.now() + ((expire || expires) * 1000); | ||
store.set(uid, meta, {}); | ||
return this; | ||
}, | ||
expire: function () { | ||
store.remove(uid); | ||
sessions.emit("expired", uid, true); | ||
return this; | ||
}, | ||
uid: function () { | ||
return uid; | ||
}, | ||
expires: function () { | ||
return meta.expires; | ||
} | ||
}; | ||
} |
function MemoryStore() { | ||
this.store = {}; | ||
} | ||
MemoryStore.prototype.add = function (uid, data) { | ||
if (this.store.hasOwnProperty(uid)) { | ||
return false; | ||
MemoryStore.prototype.add = function (uid) { | ||
var meta = null, data = {}, cb = null, success = false; | ||
for (var i = 1; i < arguments.length; i++) { | ||
switch (typeof arguments[i]) { | ||
case "function": | ||
cb = arguments[i]; | ||
break; | ||
case "object": | ||
if (meta === null) { | ||
meta = arguments[i]; | ||
} else { | ||
data = arguments[i]; | ||
} | ||
break; | ||
} | ||
} | ||
this.store[uid] = data || {}; | ||
if (meta === null) { | ||
meta = {}; | ||
} | ||
return true; | ||
if (!this.store.hasOwnProperty(uid)) { | ||
this.store[uid] = { meta: meta, data: data }; | ||
success = true; | ||
} | ||
if (cb !== null) { | ||
if (success) { | ||
cb(null, meta, data); | ||
} else { | ||
cb(new Error("could not add uid")); | ||
} | ||
} | ||
return this; | ||
}; | ||
MemoryStore.prototype.uids = function () { | ||
return Object.keys(this.store); | ||
MemoryStore.prototype.uids = function (cb) { | ||
cb(null, Object.keys(this.store)); | ||
return this; | ||
}; | ||
MemoryStore.prototype.set = function (uid, data) { | ||
MemoryStore.prototype.set = function (uid, meta, data, cb) { | ||
if (!this.store.hasOwnProperty(uid)) { | ||
return false; | ||
typeof cb == "function" && cb(new Error("uid not found")); | ||
return this; | ||
} | ||
for (k in meta) { | ||
this.store[uid].meta[k] = meta[k]; | ||
} | ||
for (k in data) { | ||
this.store[uid][k] = data[k]; | ||
this.store[uid].data[k] = data[k]; | ||
} | ||
return true; | ||
typeof cb == "function" && cb(null); | ||
return this; | ||
}; | ||
MemoryStore.prototype.get = function (uid, key) { | ||
MemoryStore.prototype.get = function (uid) { | ||
var key = null, cb = null; | ||
for (var i = 1; i < arguments.length; i++) { | ||
switch (typeof arguments[i]) { | ||
case "string": | ||
key = arguments[i]; | ||
break; | ||
case "function": | ||
cb = arguments[i]; | ||
break; | ||
} | ||
} | ||
if (cb === null) { | ||
throw new Error("missing callback"); | ||
} | ||
if (this.store.hasOwnProperty(uid)) { | ||
if (typeof key == "undefined") { | ||
return this.store[uid]; | ||
if (key === null) { | ||
cb(null, this.store[uid].meta, this.store[uid].data); | ||
} else { | ||
cb(null, this.store[uid].data.hasOwnProperty(key) ? this.store[uid].data[key] : null); | ||
} | ||
return this.store[uid].hasOwnProperty(key) ? this.store[uid][key] : null; | ||
} else { | ||
cb(new Error("uid not found")); | ||
} | ||
return null; | ||
return this; | ||
}; | ||
MemoryStore.prototype.remove = function () { | ||
var items = Array.prototype.slice.apply(arguments), | ||
uid = items.shift(); | ||
uid = items.shift(), cb = items.pop(); | ||
if (!this.store.hasOwnProperty(uid)) { | ||
return false; | ||
typeof cb == "function" && cb(new Error("uid not found")); | ||
return this; | ||
} | ||
if (items.length == 0) { | ||
@@ -50,5 +111,8 @@ delete this.store[uid]; | ||
} | ||
return true; | ||
typeof cb == "function" && cb(null); | ||
return this; | ||
}; | ||
module.exports = MemoryStore; |
{ | ||
"name" : "sessions", | ||
"version" : "0.0.1", | ||
"version" : "0.0.2-1", | ||
"description" : "NodeJS session management", | ||
@@ -29,2 +29,2 @@ "keywords" : [ "session", "cookie" ], | ||
} | ||
} | ||
} |
var vows = require("vows"), | ||
assert = require("assert"), | ||
memoryStore = require("../lib/sessions").stores.memory, | ||
memoryStore = require("../lib/store/memory"), | ||
store = new memoryStore(), | ||
testMeta = { meta: "test" }, | ||
dup_id = "dupid"; | ||
vows.describe("memory store").addBatch({ | ||
"a clean memory store": { | ||
topic: new memoryStore(), | ||
"should have no uids initially": function (topic) { | ||
assert.isArray(topic.uids()); | ||
assert.equal(topic.uids().length, 0); | ||
"getting initial uids": { | ||
topic: function () { | ||
store.uids(this.callback); | ||
}, | ||
"add duplicated uids should not be possible": function (topic) { | ||
assert.isTrue(topic.add(dup_id, { dup: false })); | ||
assert.isFalse(topic.add(dup_id, { dup: true })); | ||
"should come empty": function (uids) { | ||
assert.isArray(uids); | ||
assert.equal(uids.length, 0); | ||
} | ||
} | ||
}).addBatch({ | ||
"adding an uid": { | ||
topic: function () { | ||
store.add(dup_id, testMeta, { dup: false }, this.callback); | ||
}, | ||
"changing current uid data is possible": function (topic) { | ||
assert.isTrue(topic.set(dup_id, { dup: true })); | ||
"should be ok": function (err, meta, data) { | ||
assert.isNull(err); | ||
assert.deepEqual(meta, testMeta); | ||
assert.deepEqual(data, { dup: false }); | ||
} | ||
} | ||
}).addBatch({ | ||
"but adding again": { | ||
topic: function () { | ||
store.add(dup_id, testMeta, { dup: true }, this.callback); | ||
}, | ||
"changing unknown uid data is impossible": function (topic) { | ||
assert.isFalse(topic.set(dup_id + "-unknown", { dup: true })); | ||
"should not be ok": function (err, data) { | ||
assert.isNotNull(err); | ||
} | ||
} | ||
}).addBatch({ | ||
"changing current uid data": { | ||
topic: function () { | ||
store.set(dup_id, {}, { dup: true }, this.callback); | ||
}, | ||
"previous uid data should be ok": function (topic) { | ||
assert.isTrue(topic.get(dup_id, "dup")); | ||
assert.deepEqual(topic.get(dup_id), { dup: true }); | ||
"should be ok": function (err, _) { | ||
assert.isNull(err); | ||
} | ||
} | ||
}).addBatch({ | ||
"changing unknown uid data": { | ||
topic: function () { | ||
store.set(dup_id + "-unknown", {}, { dup: true }, this.callback); | ||
}, | ||
"previous uid unknown data should return null": function (topic) { | ||
assert.isNull(topic.get(dup_id, "unknown key")); | ||
"should not be ok": function (err, _) { | ||
assert.isNotNull(err); | ||
} | ||
} | ||
}).addBatch({ | ||
"previous uid data": { | ||
topic: function () { | ||
store.get(dup_id, this.callback); | ||
}, | ||
"only one uid should be present now": function (topic) { | ||
assert.isArray(topic.uids()); | ||
assert.equal(topic.uids().length, 1); | ||
"should be ok": function (err, meta, data) { | ||
assert.isNull(err); | ||
assert.deepEqual(meta, testMeta); | ||
assert.deepEqual(data, { dup: true }); | ||
} | ||
} | ||
}).addBatch({ | ||
"previous uid unknown data": { | ||
topic: function () { | ||
store.get(dup_id + "-unknown", this.callback); | ||
}, | ||
"removing a property should be ok": function (topic) { | ||
assert.isTrue(topic.remove(dup_id, "dup")); | ||
assert.isNull(topic.get(dup_id, "dup")); | ||
assert.isEmpty(topic.get(dup_id)); | ||
"should return error": function (err, _) { | ||
assert.isNotNull(err); | ||
} | ||
} | ||
}).addBatch({ | ||
"the total uids saved": { | ||
topic: function () { | ||
store.uids(this.callback); | ||
}, | ||
"removing an uid should be ok": function (topic) { | ||
assert.isTrue(topic.remove(dup_id)); | ||
assert.isNull(topic.get(dup_id, "dup")); | ||
assert.isNull(topic.get(dup_id)); | ||
"should now be one": function (uids) { | ||
assert.isArray(uids); | ||
assert.equal(uids.length, 1); | ||
} | ||
} | ||
}).addBatch({ | ||
"removing a property": { | ||
topic: function () { | ||
store.remove(dup_id, "dup", this.callback); | ||
}, | ||
"no uids should be in store now": function (topic) { | ||
assert.isArray(topic.uids()); | ||
assert.equal(topic.uids().length, 0); | ||
"should be ok": function (err, _) { | ||
assert.isNull(err); | ||
} | ||
} | ||
}).addBatch({ | ||
"removing an uid": { | ||
topic: function () { | ||
store.remove(dup_id, this.callback); | ||
}, | ||
"should be ok": function (err, _) { | ||
assert.isNull(err); | ||
} | ||
} | ||
}).addBatch({ | ||
"the total uids saved": { | ||
topic: function () { | ||
store.uids(this.callback); | ||
}, | ||
"should now be zero": function (uids) { | ||
assert.isArray(uids); | ||
assert.equal(uids.length, 0); | ||
} | ||
} | ||
}).export(module); |
@@ -8,3 +8,3 @@ var vows = require("vows"), | ||
vows.describe("sessions").addBatch({ | ||
"a clean session handler": { | ||
"a session handler": { | ||
topic: new sessions(), | ||
@@ -14,19 +14,19 @@ "should have no sessions initially": function (topic) { | ||
}, | ||
"should be able to create a new session": function (topic) { | ||
session = topic.create(sessid); | ||
assert.isObject(session); | ||
assert.isNotNull(session); | ||
}, | ||
"new session should have no data": function (topic) { | ||
assert.isObject(session.get()); | ||
assert.isNull(session.get("undefined")); | ||
}, | ||
"new session should have defined uid": function (topic) { | ||
assert.strictEqual(session.uid(), sessid); | ||
}, | ||
"expiring session should clean it": function (topic) { | ||
session.expire(); | ||
assert.strictEqual(topic.total(), 0); | ||
"creating a new session": { | ||
topic: function (sessions) { | ||
sessions.create(sessid, this.callback); | ||
}, | ||
"should be ok": function (err, session) { | ||
assert.isNull(err); | ||
assert.isObject(session); | ||
assert.isFunction(session.get); | ||
assert.isFunction(session.set); | ||
assert.isFunction(session.refresh); | ||
assert.isFunction(session.expire); | ||
assert.isFunction(session.expires); | ||
assert.isFunction(session.uid); | ||
}, | ||
"uid should be the same used": function (err, session) { | ||
assert.equal(session.uid(), sessid); | ||
} | ||
} | ||
@@ -33,0 +33,0 @@ }, |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No README
QualityPackage does not have a README. This may indicate a failed publish or a low quality package.
Found 1 instance in 1 package
16491
11
527
1
28
2