forked from apostrophecms/express-cache-on-demand
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
134 lines (118 loc) · 3.6 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
var _ = require('lodash');
var cacheOnDemand = require('cache-on-demand');
module.exports = expressCacheOnDemand;
function expressCacheOnDemand(hasher) {
hasher = hasher || expressHasher;
function worker(req, res, next, callback) {
// Patch the response object so that it doesn't respond
// directly, it builds a description of the response that
// can be replayed by each pending res object
var _res = { headers: {} };
var originals = {};
// We're the first in, we get to do the real work.
// Patch our response object to collect data for
// replay into many response objects
patch(res, {
redirect: function(url) {
var status = 302;
var url = url;
if (typeof arguments[0] === 'number') {
status = arguments[0];
url = arguments[1];
}
_res.redirectStatus = status;
_res.redirect = url;
return finish();
},
send: function(data) {
_res.body = data;
return finish();
},
end: function(raw) {
_res.raw = raw;
return finish();
},
setHeader: function(key, val) {
_res.headers[key] = val;
}
});
function finish() {
// Folks tend to write to this one directly
_res.statusCode = res.statusCode;
// Undo the patching so we can replay into this
// response object, as well as others
restore(res);
// Great, we're done
return callback(_res);
}
// All set to continue the middleware chain
return next();
function patch(obj, overrides) {
_.assign(originals, _.pick(obj, _.keys(overrides)));
_.assign(obj, overrides);
}
function restore(obj) {
_.assign(obj, originals);
}
}
var codForMiddleware = cacheOnDemand(worker, hasher);
return function(req, res, next) {
return codForMiddleware(req, res, next, function(_res) {
// Replay the captured response
if (_res.statusCode) {
res.statusCode = _res.statusCode;
}
_.each(_res.headers || {}, function(val, key) {
res.setHeader(key, val);
});
if (_res.redirect) {
return res.redirect(_res.redirectStatus, _res.redirect);
}
if (_res.body) {
return res.send(_res.body);
}
if (_res.raw) {
return res.end(_res.raw);
}
// We know about ending a request with one of
// the above three methods. Anything else doesn't
// make sense with this middleware
console.log('Attempted Request URL: ' + req.url);
throw 'cacheOnDemand.middleware does not know how to deliver this response, use the middleware only with routes that end with res.redirect, res.send or res.end';
});
};
}
function expressHasher(req) {
if ((req.method !== 'GET') && (req.method !== 'HEAD')) {
return false;
}
if (req.user) {
return false;
}
// Examine the session
var safe = true;
_.each(req.session || {}, function(val, key) {
if (key === 'cookie') {
// The mere existence of a session cookie
// doesn't mean we can't cache. There has
// to actually be something in the session
return;
}
if ((key === 'flash') || (key === 'passport')) {
// These two are often empty objects, which
// are safe to cache
if (!_.isEmpty(val)) {
safe = false;
return false;
}
} else {
// Other session properties must be assumed to
// be specific to this user, with a possible
// impact on the response, and thus mean
// this request must not be cached
safe = false;
return false;
}
});
return req.url;
}