Added CouchDB storage for sessions.

This commit is contained in:
mde
2010-04-03 07:12:49 +00:00
parent 83d4296ed1
commit 089f400a26
8 changed files with 212 additions and 86 deletions

View File

@@ -1,6 +1,7 @@
var http = require('http');
var sys = require('sys');
var fs = require('fs');
var fleegix = require('geddy/lib/fleegix');
var errors = require('geddy/lib/errors');
var session = require('geddy/lib/session');
@@ -12,54 +13,52 @@ var Controller = require('./controller').Controller;
var App = function (initData) {
var _this = this;
this.initData = initData;
this.router = initData.router;
this.controllers = initData.controllers;
this.templates = initData.templates;
this.run = function (req, resp) {
var url = req.url;
var base = fleegix.url.getBase(url);
var route = this.router.parse(base, req.method);
var route = router.parse(base, req.method);
try {
// If the route is a match, run the matching controller/action
if (route) {
var cook = new cookies.CookieCollection(req);
var sess = new session.Session({
app: this,
request: req,
cookies: cook
});
var qs = fleegix.url.getQS(url);
var qsParams = fleegix.url.qsToObject(qs);
var params = fleegix.mixin(route.params, qsParams);
sess.init(function () {
// Instantiate the matching controller from the registry
var constructor = this.controllers[route.controller];
// Give it all the base Controller fu
constructor.prototype = new Controller({
app: this,
request: req,
response: resp,
name: route.controller,
params: params,
cookies: cook,
session: sess
var qs = fleegix.url.getQS(url);
var qsParams = fleegix.url.qsToObject(qs);
var params = fleegix.mixin(route.params, qsParams);
// Instantiate the matching controller from the registry
var constructor = controllerRegistry[route.controller];
// Give it all the base Controller fu
constructor.prototype = new Controller({
request: req,
response: resp,
name: route.controller,
params: params,
cookies: cook,
session: sess
});
var controller = new constructor();
// Mix in any user-defined Application methods
var mixin = new controllerRegistry.Application();
controller[route.action].call(controller, params);
});
var controller = new constructor();
// Mix in any user-defined Application methods
var mixin = new this.controllers.Application();
controller[route.action].call(controller, params);
}
else {
// In dev mode, also serve static files
if (initData.environment = 'development') {
var path = initData.staticFilePath + req.url;
if (config.environment = 'development') {
var path = config.staticFilePath + req.url;
fs.stat(path, function (err, stats) {
// File not found, hand back the 404
if (err) {
@@ -79,7 +78,7 @@ var App = function (initData) {
}
}
}
// Catch all errors, respond with error page & HTTP error code
// Catch all errors, respond with error page & HTTP error code
catch (e) {
var r = new response.Response(resp);
r.send(e.message, e.statusCode, {'Content-Type': 'text/html'});

View File

@@ -1,12 +1,22 @@
var Config = function (dirname) {
this.environment = 'development';
this.port = 8000;
this.dirname = dirname;
this.staticFilePath = this.dirname + '/public';
this.sessionStore = 'memory';
this.staticFilePath = dirname + '/public';
this.sessionStore = 'couchdb';
this.sessionIdKey = 'sid';
this.sessionExpiry = 14 * 24 * 60 * 60;
this.dbHostname = 'localhost';
this.dbPort = 5984;
this.dbName = 'geddy';
// Override with app-level opts
var opts = require(dirname + '/config/config');
for (var p in opts) {
this[p] = opts[p];
}
};
exports.Config = Config;

View File

@@ -7,8 +7,6 @@ var fleegix = require('./fleegix');
var Controller = function (obj) {
//var Controller = function (app, name, params, req, resp) {
// The top-level app
this.app = null;
// The http.ServerRequest passed to the 'request' event
// callback function
this.request = null;
@@ -82,7 +80,10 @@ Controller.prototype = new function () {
var r = new response.Response(this.response);
var headers = {'Content-Type': this.contentType};
headers['Set-Cookie'] = this.cookies.serialize();
r.send(this.content, 200, headers);
var content = this.content;
this.session.close(function () {
r.send(content, 200, headers);
});
};
this.negotiateContent = function (frmt) {
@@ -192,13 +193,13 @@ Controller.prototype = new function () {
if (!url) {
key = parentNode.dirname + '/' + partial + '.html.ejs';
if (this.app.templates[key]) {
if (templateRegistry[key]) {
url = key;
}
}
if (!url) {
key = 'app/views/' + partial + '.html.ejs';
if (this.app.config.templates[key]) {
if (templateRegistry[key]) {
url = key;
}
}

View File

@@ -1,24 +1,22 @@
var fs = require('fs');
var sys = require('sys');
var child_process = require('child_process');
var fleegix = require('geddy/lib/fleegix');
var async = require('geddy/lib/async');
var session = require('geddy/lib/session');
var Init = function (config, callback) {
// Copy all the values from the config
for (var p in config) {
this[p] = config[p];
}
var _this = this;
this.callback = callback;
this.router = require(this.dirname + '/config/router').router;
this.controllers = {};
this.templates = {};
var _callback = callback;
this.createControllers = function (err, dirList) {
GLOBAL.controllerRegistry = {};
GLOBAL.templateRegistry = {};
GLOBAL.config = config;
GLOBAL.router = require(config.dirname + '/config/router').router;
this.registerControllers = function (err, dirList) {
var fileName, controllerName;
var controllers = {};
var jsPat = /\.js$/;
@@ -36,14 +34,14 @@ var Init = function (config, callback) {
controllerName = fleegix.string.capitalize(controllerName);
// Registers as a controller, e.g., controllers.NeilPearts =
// require('/path/to/geddy_app/app/controllers/neil_pearts').NeilPearts
controllers[controllerName] = require(_this.dirname +
controllers[controllerName] = require(config.dirname +
'/app/controllers/' + fileName)[controllerName];
}
}
_this.controllers = controllers;
GLOBAL.controllerRegistry = controllers;
};
this.createTemplates = function (err, stdin, stderr) {
this.registerTemplates = function (err, stdin, stderr) {
if (err) {
sys.puts('Error: ' + JSON.stringify(err));
}
@@ -58,32 +56,32 @@ var Init = function (config, callback) {
for (var i = 0; i < files.length; i++) {
file = files[i];
if (pat.test(file)) {
file = file.replace(_this.dirname + '/', '');
file = file.replace(config.dirname + '/', '');
templates[file] = true;
}
}
_this.templates = templates;
GLOBAL.templateRegistry = templates;
}
};
session.createStore(this.sessionStore);
session.createStore(config.sessionStore);
var cmdList = [
{
func: fs.readdir,
args: [this.dirname + '/app/controllers'],
callback: this.createControllers
args: [config.dirname + '/app/controllers'],
callback: this.registerControllers
},
{
func: child_process.exec,
args: ['find ' + this.dirname + '/app/views'],
callback: this.createTemplates
args: ['find ' + config.dirname + '/app/views'],
callback: this.registerTemplates
}
];
var chain = new async.AsyncChain(cmdList);
chain.last = function () {
_this.callback(_this);
_callback();
};
chain.run();
}

View File

@@ -21,54 +21,54 @@ var session = new function () {
this.createStore = function (type) {
var key = fleegix.string.capitalize(type);
session.store = new session[key + 'SessionStore']();
var constructor = require('geddy/lib/session_stores/' + type)[key];
session.store = new constructor();
};
}();
session.MemorySessionStore = function () {
var _sessions = {};
this.get = function (sid, key, val) {
if (!_sessions[sid]) {
_sessions[sid] = {};
}
return _sessions[sid][key];
};
this.set = function (sid, key, val) {
if (!_sessions[sid]) {
_sessions[sid] = {};
}
_sessions[sid][key] = val;
};
};
session.Session = function (obj) {
this.store = null;
this.sid = '';
// Copy all props passed in from the app
for (var p in obj) {
this[p] = obj[p];
}
var keyName = this.app.initData.sessionIdKey;
var keyName = config.sessionIdKey;
var sid = this.cookies.get(keyName);
if (!sid) {
sid = session.generateSessionId()
var dt = new Date();
dt.setTime(dt.getTime() + (this.app.initData.sessionExpiry * 1000));
dt.setTime(dt.getTime() + (config.sessionExpiry * 1000));
this.cookies.set(keyName, sid, {expires: dt.toGMTString()});
}
this.sid = sid;
};
session.Session.prototype = new function () {
this.init = function(appCallback) {
var _this = this;
var localCallback = function (result) {
_this.store = result;
appCallback();
};
session.store.read(this.sid, localCallback);
};
this.get = function (key) {
return session.store.get(this.id, key);
sys.puts(JSON.stringify(this.store));
return this.store[key];
};
this.set = function (key, val) {
return session.store.set(this.id, key, val);
this.store[key] = val;
sys.puts(JSON.stringify(this.store));
};
this.close = function (appCallback) {
session.store.write(this.sid, this.store, appCallback);
};
}();
for (var p in session) { this[p] = session[p]; }

View File

@@ -0,0 +1,98 @@
var sys = require("sys");
var http = require("http");
var Couchdb = function () {
var _this = this;
var _sessions = {};
var _readCallback;
var _writeCallback;
this.read = function (sid, callback) {
_readCallback = callback;
this.request({url: '/' + config.dbName +
'/' + sid, method: 'GET'}, this.ensureRead);
};
this.ensureRead = function (response) {
if (response.statusCode == 404) {
_this.create(response);
}
else {
var url = response.url;
var sid = url.substr(url.lastIndexOf('/') + 1);
var data = JSON.parse(response.body);
_sessions[sid] = data;
_readCallback(_sessions[sid]);
}
};
this.create = function (response) {
var url = response.url;
var sid = url.substr(url.lastIndexOf('/') + 1);
this.request({url: '/' + config.dbName +
'/' + sid, method: 'PUT', data: {}}, this.ensureRead);
};
this.ensureCreate = function (response) {
if (response.statusCode == 201) {
var url = response.url;
var sid = url.substr(url.lastIndexOf('/') + 1);
var data = JSON.parse(response.body);
_sessions[sid] = data;
_readCallback(_sessions[sid]);
}
else {
throw new Error('could not create CouchDB session.');
}
};
this.write = function (sid, store, callback) {
this.request({url: '/' + config.dbName +
'/' + sid, method: 'PUT', data: store}, this.ensureUpdate);
_writeCallback = callback;
};
this.ensureUpdate = function (response) {
_writeCallback();
};
this.request = function (params, callback) {
var req = {};
req.url = params.url;
req.method = params.method || 'GET';
req.data = JSON.stringify(params.data) || null;
var headers = {host: config.dbHostname};
if (req.data) {
headers['content-length'] = req.data.length;
}
var client = http.createClient(config.dbPort, config.dbHostname);
var request = client.request(req.method, req.url, headers);
request.addListener('response', function (response) {
//sys.puts("STATUS: " + response.statusCode);
//sys.puts("HEADERS: " + JSON.stringify(response.headers));
response.setBodyEncoding("utf8");
response.addListener("data", function (chunk) {
//sys.puts("BODY: " + chunk);
callback({
url: req.url,
statusCode: response.statusCode,
body: chunk
});
});
});
if (req.data) {
request.write(req.data);
}
request.close();
};
// TODO: Create the correctly named DB on app startup if it's not there
//this.request({url: '/' + config.dbName, method: 'PUT'}, this.ensureRead);
};
exports.Couchdb = Couchdb;

View File

@@ -0,0 +1,18 @@
var Memory = function () {
var _sessions = {};
this.read = function (sid, callback) {
if (!_sessions[sid]) {
_sessions[sid] = {};
}
callback(_sessions[sid]);
};
this.write = function (sid, store, callback) {
callback();
};
};
exports.Memory = Memory;

View File

@@ -2,21 +2,23 @@ var appDirname = process.argv[2];
var sys = require('sys');
var http = require('http');
var fleegix = require('geddy/lib/fleegix');
var Config = require('geddy/lib/config').Config;
var Init = require('geddy/lib/init').Init;
var App = require('geddy/lib/app').App;
config = new Config(appDirname);
config = fleegix.mixin(config, require(config.dirname + '/config/config'));
var runServ = function (initData) {
var runServ = function () {
http.createServer(function (req, resp) {
new App(initData).run(req, resp);
new App().run(req, resp);
}).listen(config.port);
sys.puts('Server running at http://127.0.0.1:' + config.port + '/');
};
var config = new Config(appDirname);
// Initialize the app, passing in the config, and runServ at its callback
new Init(config, runServ);