Permalink
Browse files

Hacking in basic cookie and session support.

  • Loading branch information...
1 parent c6ef836 commit 531d4d4b7488e1edb37240d2ed3c402cadbac4c3 mde committed Apr 1, 2010
Showing with 197 additions and 47 deletions.
  1. +1 −1 README.markdown
  2. +25 −13 lib/app.js
  3. +3 −0 lib/config.js
  4. +31 −18 lib/controller.js
  5. +83 −0 lib/cookies.js
  6. +6 −3 lib/init.js
  7. +8 −9 lib/response.js
  8. +36 −0 lib/session.js
  9. +4 −3 scripts/gen/config.js
View
@@ -57,7 +57,7 @@ create an app. Run `geddy` to start the server.
Go to http://localhost:8000/, and you should see:
-"Attention all planets of the Solar Federation"
+Attention all planets of the Solar Federation
### Adding resources
View
@@ -1,9 +1,11 @@
var http = require('http');
var sys = require('sys');
var fs = require('fs');
-var fleegix = require('./fleegix');
-var errors = require('./errors');
-var response = require('./response');
+var fleegix = require('geddy/lib/fleegix');
+var errors = require('geddy/lib/errors');
+var session = require('geddy/lib/session');
+var response = require('geddy/lib/response');
+var cookies = require('geddy/lib/cookies');
var Controller = require('./controller').Controller;
@@ -14,34 +16,44 @@ var App = function (initData) {
this.router = initData.router;
this.controllers = initData.controllers;
this.templates = initData.templates;
- this.req = null;
- this.resp = null;
this.run = function (req, resp) {
- this.req = req;
- this.resp = resp;
-
var url = req.url;
var base = fleegix.url.getBase(url);
var route = this.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);
// Instantiate the matching controller from the registry
var constructor = this.controllers[route.controller];
// Give it all the base Controller fu
- constructor.prototype = new Controller(this, route.controller, params, req, resp);
+ constructor.prototype = new Controller({
+ app: this,
+ request: req,
+ response: resp,
+ name: route.controller,
+ params: params,
+ cookies: cook,
+ session: session
+ });
var controller = new constructor();
// Mix in any user-defined Application methods
var mixin = new this.controllers.Application();
-
controller[route.action].call(controller, params);
}
else {
@@ -53,7 +65,7 @@ var App = function (initData) {
if (err) {
var e = new errors.NotFoundError('Page ' + req.url + ' not found.');
var r = new response.Response(resp);
- r.send(e.message, 'text/html', e.statusCode);
+ r.send(e.message, e.statusCode, {'Content-Type': 'text/html'});
}
else {
var r = new response.Response(resp);
@@ -69,8 +81,8 @@ var App = function (initData) {
}
// Catch all errors, respond with error page & HTTP error code
catch (e) {
- var r = new response.Response(this.resp);
- r.send(e.message, 'text/html', e.statusCode);
+ var r = new response.Response(resp);
+ r.send(e.message, e.statusCode, {'Content-Type': 'text/html'});
}
}
};
View
@@ -4,6 +4,9 @@ var Config = function (dirname) {
this.port = 8000;
this.dirname = dirname;
this.staticFilePath = this.dirname + '/public';
+ this.sessionStore = 'memory';
+ this.sessionIdKey = 'sid';
+ this.sessionExpiry = 14 * 24 * 60 * 60;
};
exports.Config = Config;
View
@@ -5,28 +5,31 @@ var response = require('./response');
var templates = require('./templates');
var fleegix = require('./fleegix');
-var Controller = function (app, name, params, req, resp) {
+var Controller = function (obj) {
+//var Controller = function (app, name, params, req, resp) {
// The top-level app
- this.app = app;
+ this.app = null;
+ // The http.ServerRequest passed to the 'request' event
+ // callback function
+ this.request = null;
+ // The http.ServerResponse passed to the 'request' event
+ // callback function
+ this.response = null;
+ // The action gets passed these as an argument, but we keep
+ // them here too to have access to the file extension, so
+ // we can do content-negotiation
+ this.params = null;
+
+ this.cookies = null;
+ // The name of the controller constructor function,
+ // in CamelCase with uppercase initial letter
+ this.name = null;
// Content-type the controller can respond with -- assume
// minimum of plaintext
this.respondsWith = ['text'];
- // The name of the controller constructor function,
- // in CamelCase with uppercase initial letter
- this.name = name;
// The name, in lowercase_with_underscores, used for
// picking the template if any to attempt to render
- this.nameDeCamelized = fleegix.string.deCamelize(name);
- // The action gets passed these as an argument, but we keep
- // them here too to have access to the file extension, so
- // we can do content-negotiation
- this.params = params;
- // The http.ServerRequest passed to the 'request' event
- // callback function
- this.request = req;
- // The http.ServerResponse passed to the 'request' event
- // callback function
- this.response = resp;
+ this.nameDeCamelized = null;
// Content to respond with -- can be an Object or String
this.content = '';
// High-level set of options which can represent multiple
@@ -40,6 +43,14 @@ var Controller = function (app, name, params, req, resp) {
this.currentPartialId = 0;
this.baseTemplateNode = null;
+
+ // Copy all props passed in from the app
+ for (var p in obj) {
+ this[p] = obj[p];
+ }
+
+ this.nameDeCamelized = fleegix.string.deCamelize(this.name);
+
};
Controller.prototype = new function () {
@@ -69,7 +80,9 @@ Controller.prototype = new function () {
this.finish = function () {
var r = new response.Response(this.response);
- r.send(this.content, this.contentType);
+ var headers = {'Content-Type': this.contentType};
+ headers['Set-Cookie'] = this.cookies.serialize();
+ r.send(this.content, 200, headers);
};
this.negotiateContent = function (frmt) {
@@ -192,7 +205,7 @@ Controller.prototype = new function () {
if (!url) {
var e = new errors.InternalServerError('Template path "' + key + '" not found');
var r = new response.Response(this.response);
- r.send(e.message, 'text/html', e.statusCode);
+ r.send(e.message, e.statusCode, {'Content-Type': 'text/html'});
return;
}
View
@@ -0,0 +1,83 @@
+var sys = require('sys');
+var fleegix = require('geddy/lib/fleegix');
+
+var cookies = new function () {}();
+
+cookies.CookieCollection = function (req) {
+ this.collection = {};
+ if (req.headers.cookie) {
+ this.parse(req.headers.cookie || {});
+ }
+};
+
+cookies.CookieCollection.prototype = new function () {
+ this.parse = function (header) {
+ var c = header.split(';');
+ var item;
+ var parsed, name, value;
+ for (var i = 0; i < c.length; i++) {
+ item = fleegix.string.trim(c[i]);
+ parsed = item.split('=');
+ name = parsed[0];
+ value = parsed[1];
+ this.collection[name] = new cookies.Cookie(name, value);
+ }
+ };
+
+ this.serialize = function (headers) {
+ var send = [];
+ var c;
+ for (var p in this.collection) {
+ c = this.collection[p];
+ if (c.send) {
+ send.push(c.toString());
+ }
+ }
+ return send.join("\r\nSet-Cookie: ");
+ };
+
+ this.get = function (name) {
+ var c = this.collection[name] || {};
+ return c.value;
+ };
+
+ this.set = function (name, value, opts) {
+ var c = new cookies.Cookie(name, value, opts);
+ c.send = true;
+ this.collection[name] = c;
+ };
+
+}();
+
+cookies.Cookie = function (name, value, o) {
+ var opts = o || {};
+ this.name = name;
+ this.value = value;
+ this.path = opts.path || null;
+ this.expires = opts.expires || null;
+ this.domain = opts.domain || null;
+ this.httpOnly = opts.httpOnly || false;
+ this.send = false;
+};
+
+cookies.Cookie.prototype.toString = function () {
+ var res = [this.name + '=' + this.value];
+ var keys = ['path', 'expires', 'domain'];
+ var key;
+ var str;
+ for (var i = 0; i < keys.length; i++) {
+ key = keys[i];
+ if (this[key]) {
+ res.push(key + '=' + this[key]);
+ }
+ }
+ if (this.httpOnly) {
+ res.push('HttpOnly');
+ }
+ str = res.join('; ');
+ return str;
+};
+
+for (var p in cookies) { this[p] = cookies[p]; }
+
+
View
@@ -5,12 +5,15 @@ var async = require('geddy/lib/async');
var sys = require('sys');
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.dirname = config.dirname;
- this.staticFilePath = config.staticFilePath;
this.router = require(this.dirname + '/config/router').router;
- this.environment = config.environment || 'development';
this.controllers = {};
this.templates = {};
View
@@ -172,9 +172,10 @@ var Response = function (resp) {
};
Response.prototype = new function () {
- this.send = function (content, contentType, statusCode) {
- this.writeHeaders({statusCode: statusCode || 200,
- contentType: contentType});
+ this.send = function (content, statusCode, headers) {
+ var s = statusCode || 200;
+ var h = headers || {};
+ this.writeHeaders(s, h);
this.writeBody(content);
this.finish();
};
@@ -184,9 +185,8 @@ Response.prototype = new function () {
var ext = fleegix.url.getFileExtension(filepath);
var contentType = response.contentTypes[ext] || 'application/octet-stream';
var encoding = 'binary';
-
- this.writeHeaders({contentType: contentType});
+ this.writeHeaders(200, {'Content-Type': contentType});
// From Paperboy, http://github.com/felixge/node-paperboy
fs.open(filepath, process.O_RDONLY, 0666, function (err, fd) {
@@ -211,14 +211,13 @@ Response.prototype = new function () {
};
- this.writeHeaders = function (opts) {
- var statusCode = opts.statusCode || 200;
- var contentType = opts.contentType;
+ this.writeHeaders = function (statusCode, headers) {
+ var contentType = headers['Content-Type'];
var charset = response.charsets[contentType];
if (charset) {
contentType += '; charset: ' + charset;
}
- this.resp.writeHead(statusCode, {'Content-Type': opts.contentType});
+ this.resp.writeHead(statusCode, headers);
};
this.writeBody = function (content) {
View
@@ -0,0 +1,36 @@
+var sys = require('sys');
+
+var session = new function () {
+ var KEY_LENGTH = 32;
+
+ this.generateSessionKey = function () {
+ var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz';
+ var len = KEY_LENGTH;
+ var str = '';
+ var mls = new Date().getTime();
+ for (var i = 0; i < len; i++) {
+ var rnum = (Math.random() * chars.length);
+ rnum = Math.floor(rnum);
+ str += chars.substring(rnum, rnum + 1);
+ }
+ return str;
+ }
+}();
+
+session.Session = function (obj) {
+ // Copy all props passed in from the app
+ for (var p in obj) {
+ this[p] = obj[p];
+ }
+ var keyName = this.app.initData.sessionIdKey;
+ var sessionKey = this.cookies.get(keyName);
+ if (!sessionKey) {
+ var dt = new Date();
+ dt.setTime(dt.getTime() + (this.app.initData.sessionExpiry * 1000));
+ this.cookies.set(keyName, session.generateSessionKey(),
+ {expires: dt.toGMTString()});
+ }
+};
+
+for (var p in session) { this[p] = session[p]; }
+
@@ -1,6 +1,7 @@
-var config = {
- //port: 8000
-};
+var config = new function () {
+ //this.environment = 'development';
+ //this.port = 8000;
+}();
for (var p in config) { this[p] = config[p]; }

0 comments on commit 531d4d4

Please sign in to comment.