Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

simplified session storage and added test

  • Loading branch information...
commit d099b2d466454e8fbaa4d1072d60d10a2877299a 1 parent 9a3a880
AJ ONeal authored
84  lib/lib/connect-cors-session.js
@@ -2,12 +2,15 @@
2 2
   "use strict";
3 3
 
4 4
   // TODO use one config and then auto camelcase headers
5  
-  var defaultSessionKey = 'userSession'
  5
+  var http = require('http')
  6
+    , UUID = require('node-uuid')
  7
+    , MemoryStore = require('./memory-store')
  8
+    , CorsSession = require('./cors-session')
  9
+    , defaultSessionKey = 'userSession'
6 10
     , defaultSessionAppKey = 'appSession'
7 11
     , defaultSessionHeader = 'X-User-Session'
8  
-    , sessionHeader = defaultSessionHeader
9  
-    , lcSessionHeader = sessionHeader.toLowerCase()
10 12
     , defaultSessionAppHeader = 'X-App-Session'
  13
+    , resProto = http.ServerResponse.prototype
11 14
     ;
12 15
 
13 16
   function random() {
@@ -17,81 +20,72 @@
17 20
   function create(options) {
18 21
     options = options || {};
19 22
 
20  
-    var http = require('http')
21  
-      , resProto = http.ServerResponse.prototype
22  
-      //, sendJsonProto = resProto.json
23  
-      , secret = options.secret || (Math.random() * Date.now()).toString('36').split('').sort(random).join('')
  23
+    var sessionHeader = defaultSessionHeader
  24
+      , lcSessionHeader = sessionHeader.toLowerCase()
24 25
       , sessionKey = options.sessionKey || defaultSessionKey
25 26
       , lcSessionKey = sessionKey.toLowerCase()
  27
+      , appSession = {}
26 28
       , purgeInterval = options.purgeInterval || 10 * 60 * 1000
27 29
       , maxAge = options.maxAge || 60 * 60 * 1000
28  
-      , db = {}
29 30
       ;
30 31
 
31  
-    resProto.sessionSendJson = resProto.json;
  32
+    // don't use the prototype?
  33
+    resProto.corsSessionSendJson = resProto.json;
32 34
     resProto.json = function (data, opts) {
33 35
       this.meta(sessionKey, this.sessionId);
34  
-      this.sessionSendJson(data, opts);
  36
+      this.corsSessionSendJson(data, opts);
35 37
     };
36 38
 
37  
-    // TODO fingerprint to prevent theft by Wireshark sniffers
38  
-    // TODO rolling fingerprint that is different for each request
39  
-    function createSessionId() {
40  
-      return (secret + 
41  
-        Date.now().toString('36') + 
42  
-        (Math.random() * 19860616).toString('36')
43  
-      ).split('').sort(random).join('').replace(/[\W]/g, '').substr(0, 32);
44  
-    }
45  
-
46  
-    function purge() {
  39
+    function purge(appSession) {
47 40
       var now = Date.now()
48 41
         , val
49 42
         ;
50 43
 
51  
-      Object.keys(db).forEach(function (key) {
52  
-        val = db[key];
  44
+      Object.keys(appSession).forEach(function (key) {
  45
+        val = appSession[key];
53 46
         if ((now - val.timestamp) > maxAge) {
54  
-          delete db[key];
  47
+          if (appSession[key]) {
  48
+            delete appSession[key].corsStore
  49
+          }
  50
+          delete appSession[key];
55 51
         }
56 52
       });
57 53
     }
58 54
 
59  
-    function session(req, res, next) {
  55
+    // TODO fingerprint to prevent theft by Wireshark sniffers
  56
+    // TODO rolling fingerprint that is different for each request
  57
+    function connectSession(req, res, next) {
60 58
         var sessionId
61  
-          , timestamp = Date.now()
62 59
           ;
63 60
 
64  
-        // TODO add Cookie support
65  
-        if (sessionId = req.headers[lcSessionHeader]) {
66  
-          req.sessionId = sessionId;
67  
-        } else if (sessionId = req.body && req.body[sessionKey]) {
68  
-          req.sessionId = sessionId;
69  
-        } else if (sessionId = req.query[sessionKey]) {
70  
-          req.sessionId = sessionId;
71  
-        } else {
72  
-          req.sessionId = sessionId = createSessionId();
73  
-        }
  61
+        // TODO add Cookie support?
  62
+        sessionId = req.sessionId = req.headers[lcSessionHeader]
  63
+          || (req.body && req.body[sessionKey])
  64
+          || req.query[sessionKey]
  65
+          || UUID.v4()
  66
+          ;
74 67
 
75  
-        if (!(req.session = db[sessionId])) {
76  
-          req.session = db[sessionId] = {};
77  
-          req.session.virgin = true;
78  
-          req.session.createdAt = timestamp;
  68
+        req.session = appSession[sessionId];
  69
+
  70
+        if (!req.session) {
  71
+          req.session = appSession[sessionId] = CorsSession.create();
79 72
         } else {
80 73
           delete req.session.virgin;
81 74
         }
82 75
 
83 76
         // TODO else if (req.expireSession) { delete a replaced session }
84  
-        res.sessionId = req.sessionId;
85  
-        req.session.touchedAt = timestamp;
  77
+        req.session.touch();
86 78
         res.setHeader(sessionHeader, sessionId);
87  
-
88  
-      next();
  79
+        // used by res.json
  80
+        res.sessionId = sessionId;
  81
+        next();
89 82
     }
90 83
 
91 84
     setInterval(purge, purgeInterval);
92 85
 
93  
-    session.headers = [lcSessionHeader];
94  
-    return session;
  86
+    // to allow headers through CORS
  87
+    connectSession.headers = [lcSessionHeader];
  88
+    return connectSession;
95 89
   }
96 90
 
97 91
   module.exports = create;
26  lib/lib/cors-session.js
... ...
@@ -0,0 +1,26 @@
  1
+(function () {
  2
+  "use strict";
  3
+
  4
+  var MemoryStore = require('./memory-store')
  5
+    ;
  6
+
  7
+  function Session() {
  8
+
  9
+    if (!this) {
  10
+      return new Session();
  11
+    }
  12
+    this.virgin = true;
  13
+    this.createdAt = Date.now();
  14
+    this.corsStore = MemoryStore.create();
  15
+  }
  16
+  Session.prototype.touch = function () {
  17
+    this.touchedAt = Date.now();
  18
+  };
  19
+
  20
+  function create() {
  21
+    return new Session();
  22
+  }
  23
+
  24
+  Session.create = create;
  25
+  module.exports = Session;
  26
+}());
38  lib/lib/memory-store.js
... ...
@@ -0,0 +1,38 @@
  1
+(function () {
  2
+  "use strict";
  3
+
  4
+  function nextTick(fn, val) {
  5
+    process.nextTick(function () {
  6
+      fn(val);
  7
+    });
  8
+  }
  9
+
  10
+  function Store() {
  11
+    if (!this) {
  12
+      return new Store();
  13
+    }
  14
+    this.store = {};
  15
+  }
  16
+  Store.prototype.get = function (key, fn) {
  17
+    var val = this.store[key]
  18
+      ;
  19
+
  20
+    fn && nextTick(fn, val);
  21
+    return val;
  22
+  };
  23
+  Store.prototype.set = function (key, val, fn) {
  24
+    this.store[key] = val;
  25
+    fn && nextTick(fn);
  26
+  };
  27
+  Store.prototype.delete = function (key, fn) {
  28
+    delete store[key];
  29
+    fn && nextTick(fn);
  30
+  };
  31
+
  32
+  function create() {
  33
+    return new Store();
  34
+  }
  35
+
  36
+  Store.create = create;
  37
+  module.exports = Store;
  38
+}());
3  lib/package.json
@@ -2,7 +2,7 @@
2 2
   "author": "AJ ONeal <coolaj86@gmail.com> (http://coolaj86.info)",
3 3
   "name": "steve",
4 4
   "description": "JSON's best friend (a CORS/XHR2 application platform)",
5  
-  "version": "0.5.7",
  5
+  "version": "0.5.8",
6 6
   "repository": {
7 7
     "type": "git",
8 8
     "url": "git://github.com/coolaj86/steve.git"
@@ -16,6 +16,7 @@
16 16
     , "nowww": ">= 1.1.x"
17 17
     , "connect-xcors": ">= 0.0.0"
18 18
     , "express-chromeframe": ">= 0.2.0"
  19
+    , "node-uuid": "1.3.x"
19 20
   },
20 21
   "devDependencies": {}
21 22
 }
22  test.js
@@ -20,6 +20,11 @@
20 20
     ;
21 21
 
22 22
   server = connect.createServer(function (req, res, next) {
  23
+    var count
  24
+      ;
  25
+
  26
+    count = req.session.corsStore.get('foo') || 0;
  27
+
23 28
     res.json({
24 29
         "url": req.url
25 30
       //, "adddress": req.socket.address()
@@ -29,7 +34,11 @@
29 34
       //, "search": req.search
30 35
       , "path": req.path
31 36
       , "headers": req.headers
  37
+      , "count": count
32 38
     });
  39
+
  40
+    count += 1;
  41
+    req.session.corsStore.set('foo', count);
33 42
   });
34 43
 
35 44
   server.listen(port, function () {
@@ -45,7 +54,6 @@
45 54
       var data
46 55
         ;
47 56
 
48  
-      server.close();
49 57
       if (err) {
50 58
         console.error(err);
51 59
         return;
@@ -61,7 +69,17 @@
61 69
       assert.strictEqual(pathname, data.pathname, "pathnames don't match");
62 70
       //assert.strictEqual(search, data.search, "searchs don't match");
63 71
       assert.deepEqual(query, data.query, "queries don't match");
64  
-      console.log('tests pass (ctrl+c to exit)');
  72
+      assert.strictEqual(0, data.count);
  73
+
  74
+      request.get(fullurl, null, { headers: headers }).when(function (err, ahr, resp2) {
  75
+        if (err) {
  76
+          console.error(err);
  77
+          return;
  78
+        }
  79
+        assert.strictEqual(1, resp2.result.count);
  80
+        server.close();
  81
+        console.log('tests pass (ctrl+c to exit)');
  82
+      });
65 83
     });
66 84
   });
67 85
 

0 notes on commit d099b2d

Please sign in to comment.
Something went wrong with that request. Please try again.