express-cache-ctrl 1.0.0 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/node.js.yml +30 -0
- package/LICENSE +20 -20
- package/README.md +2 -2
- package/package.json +11 -13
- package/src/cache.js +122 -128
- package/test/cache.js +182 -182
- package/test/mocks/response.js +39 -41
- package/.jscsrc +0 -71
- package/.jshintrc +0 -60
- package/.npmignore +0 -25
- package/.travis.yml +0 -13
- package/appveyor.yml +0 -29
- package/gulpfile.js +0 -30
- package/travis.yml +0 -21
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node
|
|
2
|
+
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
|
3
|
+
|
|
4
|
+
name: Node.js CI
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
branches: [ master ]
|
|
9
|
+
pull_request:
|
|
10
|
+
branches: [ master ]
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
build:
|
|
14
|
+
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
|
|
17
|
+
strategy:
|
|
18
|
+
matrix:
|
|
19
|
+
node-version: [10.x, 12.x, 14.x, 16.x]
|
|
20
|
+
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
|
21
|
+
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v2
|
|
24
|
+
- name: Use Node.js ${{ matrix.node-version }}
|
|
25
|
+
uses: actions/setup-node@v2
|
|
26
|
+
with:
|
|
27
|
+
node-version: ${{ matrix.node-version }}
|
|
28
|
+
cache: 'npm'
|
|
29
|
+
- run: npm ci
|
|
30
|
+
- run: npm test
|
package/LICENSE
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
The MIT License (MIT)
|
|
2
|
-
Copyright (c) 2016 Carlos Luis Castro Márquez
|
|
3
|
-
|
|
4
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
6
|
-
in the Software without restriction, including without limitation the rights
|
|
7
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
9
|
-
furnished to do so, subject to the following conditions:
|
|
10
|
-
|
|
11
|
-
The above copyright notice and this permission notice shall be included in all
|
|
12
|
-
copies or substantial portions of the Software.
|
|
13
|
-
|
|
14
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
-
SOFTWARE.
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
Copyright (c) 2016 Carlos Luis Castro Márquez
|
|
3
|
+
|
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
in the Software without restriction, including without limitation the rights
|
|
7
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
furnished to do so, subject to the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -39,7 +39,7 @@ Returns the cache middleware using the given `options` and `ttl`. This middlewar
|
|
|
39
39
|
|
|
40
40
|
#### ttl
|
|
41
41
|
|
|
42
|
-
Cache Time-To-Live in seconds or in ms() notation.
|
|
42
|
+
Cache Time-To-Live in seconds or in ms() notation. e.g. `1d`
|
|
43
43
|
|
|
44
44
|
#### Options
|
|
45
45
|
|
|
@@ -71,7 +71,7 @@ Returns the cache middleware using the given `options` and `ttl`. This middlewar
|
|
|
71
71
|
|
|
72
72
|
#### ttl
|
|
73
73
|
|
|
74
|
-
Cache Time-To-Live in seconds or in ms() notation.
|
|
74
|
+
Cache Time-To-Live in seconds or in ms() notation. e.g. `1d`
|
|
75
75
|
|
|
76
76
|
#### Options
|
|
77
77
|
|
package/package.json
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "express-cache-ctrl",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Express middleware to handle content expiration using Cache-Control header.",
|
|
5
5
|
"main": "src/cache.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"test": "mocha test/"
|
|
8
8
|
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/clcastro87/express-cache-ctrl.git"
|
|
12
|
+
},
|
|
9
13
|
"keywords": [
|
|
10
14
|
"express",
|
|
11
15
|
"cache",
|
|
@@ -13,24 +17,18 @@
|
|
|
13
17
|
"http"
|
|
14
18
|
],
|
|
15
19
|
"engines": {
|
|
16
|
-
"node": ">=
|
|
20
|
+
"node": ">= 10.0"
|
|
17
21
|
},
|
|
18
22
|
"author": "Carlos Luis Castro Márquez <carlosluiscastro@gmail.com>",
|
|
19
23
|
"license": "MIT",
|
|
20
24
|
"dependencies": {
|
|
21
|
-
"
|
|
22
|
-
"
|
|
25
|
+
"express": "^4.9.3",
|
|
26
|
+
"ms": "^0.7.0"
|
|
23
27
|
},
|
|
24
28
|
"devDependencies": {
|
|
25
|
-
"morgan": "^1.1.1",
|
|
26
|
-
"gulp": "^3.8.10",
|
|
27
|
-
"gulp-jscs": "^1.1.2",
|
|
28
|
-
"gulp-jshint": "^1.7.1",
|
|
29
|
-
"gulp-load-plugins": "^0.7.0",
|
|
30
|
-
"gulp-util": "^3.0.1",
|
|
31
|
-
"jshint-stylish": "^1.0.0",
|
|
32
|
-
"mocha": "^1.21.5",
|
|
33
29
|
"chai": "^1.10.0",
|
|
34
|
-
"
|
|
30
|
+
"mocha": "^9.1.3",
|
|
31
|
+
"mock-res": "^0.1.0",
|
|
32
|
+
"morgan": "^1.1.1"
|
|
35
33
|
}
|
|
36
34
|
}
|
package/src/cache.js
CHANGED
|
@@ -1,128 +1,122 @@
|
|
|
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
|
-
return cacheControl;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
})(module.exports);
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// Module requires
|
|
4
|
+
const ms = require("ms");
|
|
5
|
+
|
|
6
|
+
// Defaults
|
|
7
|
+
const defaultTTL = "1h";
|
|
8
|
+
|
|
9
|
+
// Public functions
|
|
10
|
+
function cacheCustom(opts) {
|
|
11
|
+
// Avoid undefined exception
|
|
12
|
+
opts = opts || {};
|
|
13
|
+
// Generate header according to options
|
|
14
|
+
var cacheControl = generateHeader(opts);
|
|
15
|
+
|
|
16
|
+
// Save values for performance optimization.
|
|
17
|
+
var cacheControlHeader = cacheControl.join(", ");
|
|
18
|
+
var setPragma = !!opts.noCache;
|
|
19
|
+
|
|
20
|
+
// Return middleware.
|
|
21
|
+
return function (req, res, next) {
|
|
22
|
+
var fnSet = res.setHeader || res.set;
|
|
23
|
+
var fnUnset = res.removeHeader;
|
|
24
|
+
fnSet.call(res, "Cache-Control", cacheControlHeader);
|
|
25
|
+
if (setPragma) {
|
|
26
|
+
fnSet.call(res, "Pragma", "no-cache");
|
|
27
|
+
} else {
|
|
28
|
+
fnUnset.call(res, "Pragma");
|
|
29
|
+
}
|
|
30
|
+
next();
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function cacheDisable() {
|
|
35
|
+
return cacheCustom({
|
|
36
|
+
noCache: true,
|
|
37
|
+
mustRevalidate: true,
|
|
38
|
+
proxyRevalidate: true,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function cacheSecure() {
|
|
43
|
+
return cacheCustom({
|
|
44
|
+
scope: "private",
|
|
45
|
+
noCache: true,
|
|
46
|
+
mustRevalidate: true,
|
|
47
|
+
noTransform: true,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function cachePublic(ttl, opts) {
|
|
52
|
+
opts = opts || {};
|
|
53
|
+
opts.scope = "public";
|
|
54
|
+
opts.sttl = opts.ttl = ttl || defaultTTL;
|
|
55
|
+
return cacheCustom(opts);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function cachePrivate(ttl, opts) {
|
|
59
|
+
opts = opts || {};
|
|
60
|
+
opts.scope = "private";
|
|
61
|
+
opts.ttl = ttl || defaultTTL;
|
|
62
|
+
return cacheCustom(opts);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Private functions
|
|
66
|
+
function toTimespan(input) {
|
|
67
|
+
if (!isNaN(input)) {
|
|
68
|
+
return parseInt(input);
|
|
69
|
+
}
|
|
70
|
+
// Is in seconds, not milliseconds
|
|
71
|
+
return parseInt(ms(input) / 1000);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function generateHeader(opts) {
|
|
75
|
+
var cacheControl = [];
|
|
76
|
+
|
|
77
|
+
// Set corresponding caching scope.
|
|
78
|
+
if (opts.scope && opts.scope === "public") {
|
|
79
|
+
cacheControl.push("public");
|
|
80
|
+
} else if (opts.scope && opts.scope === "private") {
|
|
81
|
+
cacheControl.push("private");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (!!opts.noCache) {
|
|
85
|
+
// Set no cache headers to both navigator engines.
|
|
86
|
+
cacheControl.push("no-cache");
|
|
87
|
+
cacheControl.push("no-store");
|
|
88
|
+
} else {
|
|
89
|
+
// Verify scope
|
|
90
|
+
if (cacheControl.length === 0) {
|
|
91
|
+
cacheControl.push("private");
|
|
92
|
+
}
|
|
93
|
+
// Set max-age
|
|
94
|
+
cacheControl.push("max-age=" + toTimespan(opts.ttl || defaultTTL));
|
|
95
|
+
if (opts.sttl) {
|
|
96
|
+
cacheControl.push("s-maxage=" + toTimespan(opts.sttl));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (!!opts.mustRevalidate) {
|
|
100
|
+
// Set endpoint content revalidation
|
|
101
|
+
cacheControl.push("must-revalidate");
|
|
102
|
+
}
|
|
103
|
+
if (!!opts.proxyRevalidate) {
|
|
104
|
+
// Set proxy content revalidation
|
|
105
|
+
cacheControl.push("proxy-revalidate");
|
|
106
|
+
}
|
|
107
|
+
if (!!opts.noTransform) {
|
|
108
|
+
// Don't transform response.
|
|
109
|
+
cacheControl.push("no-transform");
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return cacheControl;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Module exports
|
|
116
|
+
exports.defaultTTL = defaultTTL;
|
|
117
|
+
exports.custom = cacheCustom;
|
|
118
|
+
exports.disable = cacheDisable;
|
|
119
|
+
exports.secure = cacheSecure;
|
|
120
|
+
exports.public = cachePublic;
|
|
121
|
+
exports.private = cachePrivate;
|
|
122
|
+
exports.toTimespan = toTimespan;
|
package/test/cache.js
CHANGED
|
@@ -1,182 +1,182 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
describe(
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
expect(middleware).to.
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
expect(controls).to.property(
|
|
19
|
-
expect(controls).to.property(
|
|
20
|
-
expect(controls).to.property(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
expect(pragma).to.
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
expect(middleware).to.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
expect(cacheControl).to.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
expect(controls).to.property(
|
|
41
|
-
expect(controls).to.property(
|
|
42
|
-
expect(controls).to.property(
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
expect(pragma).to.
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
expect(middleware).to.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
.and.to.equal(cache.toTimespan(cache.defaultTTL));
|
|
63
|
-
done();
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it(
|
|
68
|
-
|
|
69
|
-
expect(middleware).to.not.null();
|
|
70
|
-
expect(middleware).to.be.a(
|
|
71
|
-
runMiddleware(middleware, function (res) {
|
|
72
|
-
|
|
73
|
-
expect(cacheControl).to.not.null();
|
|
74
|
-
expect(cacheControl.indexOf(
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
expect(controls).to.property(
|
|
78
|
-
done();
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it(
|
|
83
|
-
|
|
84
|
-
expect(middleware).to.not.null();
|
|
85
|
-
expect(middleware).to.be.a(
|
|
86
|
-
runMiddleware(middleware, function (res) {
|
|
87
|
-
|
|
88
|
-
expect(cacheControl).to.not.null();
|
|
89
|
-
expect(cacheControl.indexOf(
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
expect(controls).to.property(
|
|
93
|
-
expect(controls).to.property(
|
|
94
|
-
done();
|
|
95
|
-
});
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it(
|
|
99
|
-
|
|
100
|
-
expect(middleware).to.not.null();
|
|
101
|
-
expect(middleware).to.be.a(
|
|
102
|
-
runMiddleware(middleware, function (res) {
|
|
103
|
-
|
|
104
|
-
expect(cacheControl).to.not.null();
|
|
105
|
-
expect(cacheControl.indexOf(
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
expect(controls).to.property(
|
|
109
|
-
expect(controls).to.property(
|
|
110
|
-
done();
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it(
|
|
115
|
-
|
|
116
|
-
expect(middleware).to.not.null();
|
|
117
|
-
expect(middleware).to.be.a(
|
|
118
|
-
runMiddleware(middleware, function (res) {
|
|
119
|
-
|
|
120
|
-
expect(cacheControl).to.not.null();
|
|
121
|
-
expect(cacheControl.indexOf(
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
expect(controls).to.property(
|
|
125
|
-
expect(controls).to.property(
|
|
126
|
-
expect(controls).to.property(
|
|
127
|
-
done();
|
|
128
|
-
});
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
it(
|
|
132
|
-
|
|
133
|
-
ttl: ttl,
|
|
134
|
-
sttl: ttl,
|
|
135
|
-
scope:
|
|
136
|
-
mustRevalidate: true,
|
|
137
|
-
proxyRevalidate: true,
|
|
138
|
-
noTransform: true
|
|
139
|
-
});
|
|
140
|
-
expect(middleware).to.not.null();
|
|
141
|
-
expect(middleware).to.be.a(
|
|
142
|
-
runMiddleware(middleware, function (res) {
|
|
143
|
-
|
|
144
|
-
expect(cacheControl).to.not.null();
|
|
145
|
-
expect(cacheControl.indexOf(
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
expect(controls).to.property(
|
|
149
|
-
expect(controls).to.property(
|
|
150
|
-
expect(controls).to.property(
|
|
151
|
-
expect(controls).to.property(
|
|
152
|
-
expect(controls).to.property(
|
|
153
|
-
done();
|
|
154
|
-
});
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
function runMiddleware(middleware, callback) {
|
|
158
|
-
|
|
159
|
-
middleware.call(middleware, {}, res, function () {
|
|
160
|
-
callback(res);
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function parseCacheControl(input) {
|
|
165
|
-
//console.log('Cache-Control:', input);
|
|
166
|
-
|
|
167
|
-
return parts.reduce(function (prev, current) {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
if (current.indexOf(
|
|
171
|
-
|
|
172
|
-
key = keyAndValue[0] && keyAndValue[0].trim();
|
|
173
|
-
value = keyAndValue[1] && keyAndValue[1].trim();
|
|
174
|
-
}
|
|
175
|
-
if (!isNaN(value)) {
|
|
176
|
-
value = parseInt(value);
|
|
177
|
-
}
|
|
178
|
-
prev[key] = value;
|
|
179
|
-
return prev;
|
|
180
|
-
}, {});
|
|
181
|
-
}
|
|
182
|
-
});
|
|
1
|
+
const expect = require("chai").expect;
|
|
2
|
+
const ms = require("ms");
|
|
3
|
+
const cache = require("../src/cache");
|
|
4
|
+
const Response = require("./mocks/response");
|
|
5
|
+
|
|
6
|
+
describe("Cache Middleware", function () {
|
|
7
|
+
const ttl = 3600;
|
|
8
|
+
|
|
9
|
+
it("cache disabled", function (done) {
|
|
10
|
+
const middleware = cache.disable();
|
|
11
|
+
expect(middleware).to.not.null();
|
|
12
|
+
expect(middleware).to.be.a("function");
|
|
13
|
+
runMiddleware(middleware, function (res) {
|
|
14
|
+
const cacheControl = res.get("Cache-Control");
|
|
15
|
+
expect(cacheControl).to.not.null();
|
|
16
|
+
const controls = parseCacheControl(cacheControl);
|
|
17
|
+
expect(controls).to.property("no-store");
|
|
18
|
+
expect(controls).to.property("no-cache");
|
|
19
|
+
expect(controls).to.property("must-revalidate");
|
|
20
|
+
expect(controls).to.property("proxy-revalidate");
|
|
21
|
+
|
|
22
|
+
const pragma = res.get("Pragma");
|
|
23
|
+
expect(pragma).to.not.null();
|
|
24
|
+
expect(pragma).to.equal("no-cache");
|
|
25
|
+
done();
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("cache secure", function (done) {
|
|
30
|
+
const middleware = cache.secure();
|
|
31
|
+
expect(middleware).to.not.null();
|
|
32
|
+
expect(middleware).to.be.a("function");
|
|
33
|
+
runMiddleware(middleware, function (res) {
|
|
34
|
+
const cacheControl = res.get("Cache-Control");
|
|
35
|
+
expect(cacheControl).to.not.null();
|
|
36
|
+
expect(cacheControl.indexOf("private")).to.equal(0);
|
|
37
|
+
|
|
38
|
+
const controls = parseCacheControl(cacheControl);
|
|
39
|
+
expect(controls).to.property("no-store");
|
|
40
|
+
expect(controls).to.property("no-cache");
|
|
41
|
+
expect(controls).to.property("must-revalidate");
|
|
42
|
+
expect(controls).to.property("no-transform");
|
|
43
|
+
|
|
44
|
+
const pragma = res.get("Pragma");
|
|
45
|
+
expect(pragma).to.not.null();
|
|
46
|
+
expect(pragma).to.equal("no-cache");
|
|
47
|
+
done();
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("cache with no TTL", function (done) {
|
|
52
|
+
const middleware = cache.private();
|
|
53
|
+
expect(middleware).to.not.null();
|
|
54
|
+
expect(middleware).to.be.a("function");
|
|
55
|
+
runMiddleware(middleware, function (res) {
|
|
56
|
+
const cacheControl = res.get("Cache-Control");
|
|
57
|
+
expect(cacheControl).to.not.null();
|
|
58
|
+
|
|
59
|
+
const controls = parseCacheControl(cacheControl);
|
|
60
|
+
expect(controls)
|
|
61
|
+
.to.property("max-age")
|
|
62
|
+
.and.to.equal(cache.toTimespan(cache.defaultTTL));
|
|
63
|
+
done();
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("cache private", function (done) {
|
|
68
|
+
const middleware = cache.private(ttl);
|
|
69
|
+
expect(middleware).to.not.null();
|
|
70
|
+
expect(middleware).to.be.a("function");
|
|
71
|
+
runMiddleware(middleware, function (res) {
|
|
72
|
+
const cacheControl = res.get("Cache-Control");
|
|
73
|
+
expect(cacheControl).to.not.null();
|
|
74
|
+
expect(cacheControl.indexOf("private")).to.equal(0);
|
|
75
|
+
|
|
76
|
+
const controls = parseCacheControl(cacheControl);
|
|
77
|
+
expect(controls).to.property("max-age").and.to.equal(ttl);
|
|
78
|
+
done();
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("cache private, with custom options", function (done) {
|
|
83
|
+
const middleware = cache.private(ttl, { mustRevalidate: true });
|
|
84
|
+
expect(middleware).to.not.null();
|
|
85
|
+
expect(middleware).to.be.a("function");
|
|
86
|
+
runMiddleware(middleware, function (res) {
|
|
87
|
+
const cacheControl = res.get("Cache-Control");
|
|
88
|
+
expect(cacheControl).to.not.null();
|
|
89
|
+
expect(cacheControl.indexOf("private")).to.equal(0);
|
|
90
|
+
|
|
91
|
+
const controls = parseCacheControl(cacheControl);
|
|
92
|
+
expect(controls).to.property("max-age").and.to.equal(ttl);
|
|
93
|
+
expect(controls).to.property("must-revalidate");
|
|
94
|
+
done();
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("cache public", function (done) {
|
|
99
|
+
const middleware = cache.public(ttl);
|
|
100
|
+
expect(middleware).to.not.null();
|
|
101
|
+
expect(middleware).to.be.a("function");
|
|
102
|
+
runMiddleware(middleware, function (res) {
|
|
103
|
+
const cacheControl = res.get("Cache-Control");
|
|
104
|
+
expect(cacheControl).to.not.null();
|
|
105
|
+
expect(cacheControl.indexOf("public")).to.equal(0);
|
|
106
|
+
|
|
107
|
+
const controls = parseCacheControl(cacheControl);
|
|
108
|
+
expect(controls).to.property("max-age").and.to.equal(ttl);
|
|
109
|
+
expect(controls).to.property("s-maxage").and.to.equal(ttl);
|
|
110
|
+
done();
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("cache public, with custom options", function (done) {
|
|
115
|
+
const middleware = cache.public(ttl, { mustRevalidate: true });
|
|
116
|
+
expect(middleware).to.not.null();
|
|
117
|
+
expect(middleware).to.be.a("function");
|
|
118
|
+
runMiddleware(middleware, function (res) {
|
|
119
|
+
const cacheControl = res.get("Cache-Control");
|
|
120
|
+
expect(cacheControl).to.not.null();
|
|
121
|
+
expect(cacheControl.indexOf("public")).to.equal(0);
|
|
122
|
+
|
|
123
|
+
const controls = parseCacheControl(cacheControl);
|
|
124
|
+
expect(controls).to.property("max-age").and.to.equal(ttl);
|
|
125
|
+
expect(controls).to.property("s-maxage").and.to.equal(ttl);
|
|
126
|
+
expect(controls).to.property("must-revalidate");
|
|
127
|
+
done();
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("cache custom", function (done) {
|
|
132
|
+
const middleware = cache.custom({
|
|
133
|
+
ttl: ttl,
|
|
134
|
+
sttl: ttl,
|
|
135
|
+
scope: "public",
|
|
136
|
+
mustRevalidate: true,
|
|
137
|
+
proxyRevalidate: true,
|
|
138
|
+
noTransform: true,
|
|
139
|
+
});
|
|
140
|
+
expect(middleware).to.not.null();
|
|
141
|
+
expect(middleware).to.be.a("function");
|
|
142
|
+
runMiddleware(middleware, function (res) {
|
|
143
|
+
const cacheControl = res.get("Cache-Control");
|
|
144
|
+
expect(cacheControl).to.not.null();
|
|
145
|
+
expect(cacheControl.indexOf("public")).to.equal(0);
|
|
146
|
+
|
|
147
|
+
const controls = parseCacheControl(cacheControl);
|
|
148
|
+
expect(controls).to.property("max-age").and.to.equal(ttl);
|
|
149
|
+
expect(controls).to.property("s-maxage").and.to.equal(ttl);
|
|
150
|
+
expect(controls).to.property("must-revalidate");
|
|
151
|
+
expect(controls).to.property("no-transform");
|
|
152
|
+
expect(controls).to.property("proxy-revalidate");
|
|
153
|
+
done();
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
function runMiddleware(middleware, callback) {
|
|
158
|
+
const res = new Response();
|
|
159
|
+
middleware.call(middleware, {}, res, function () {
|
|
160
|
+
callback(res);
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function parseCacheControl(input) {
|
|
165
|
+
//console.log('Cache-Control:', input);
|
|
166
|
+
const parts = input.split(",");
|
|
167
|
+
return parts.reduce(function (prev, current) {
|
|
168
|
+
let value = true;
|
|
169
|
+
let key = current.trim();
|
|
170
|
+
if (current.indexOf("=") !== -1) {
|
|
171
|
+
let keyAndValue = current.split("=");
|
|
172
|
+
key = keyAndValue[0] && keyAndValue[0].trim();
|
|
173
|
+
value = keyAndValue[1] && keyAndValue[1].trim();
|
|
174
|
+
}
|
|
175
|
+
if (!isNaN(value)) {
|
|
176
|
+
value = parseInt(value);
|
|
177
|
+
}
|
|
178
|
+
prev[key] = value;
|
|
179
|
+
return prev;
|
|
180
|
+
}, {});
|
|
181
|
+
}
|
|
182
|
+
});
|
package/test/mocks/response.js
CHANGED
|
@@ -1,41 +1,39 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Response prototype.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
function Response() {
|
|
9
|
-
MockRes.call(this);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
util.inherits(Response, MockRes);
|
|
13
|
-
|
|
14
|
-
Response.prototype.set = function (field, val) {
|
|
15
|
-
if (arguments.length === 2) {
|
|
16
|
-
var value = Array.isArray(val)
|
|
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
|
-
module.exports = Response;
|
|
1
|
+
const MockRes = require("mock-res");
|
|
2
|
+
const util = require("util");
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Response prototype.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
function Response() {
|
|
9
|
+
MockRes.call(this);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
util.inherits(Response, MockRes);
|
|
13
|
+
|
|
14
|
+
Response.prototype.set = function (field, val) {
|
|
15
|
+
if (arguments.length === 2) {
|
|
16
|
+
var value = Array.isArray(val) ? val.map(String) : String(val);
|
|
17
|
+
|
|
18
|
+
this.setHeader(field, value);
|
|
19
|
+
} else {
|
|
20
|
+
for (let key in field) {
|
|
21
|
+
this.set(key, field[key]);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return this;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Get value for header `field`.
|
|
29
|
+
*
|
|
30
|
+
* @param {String} field
|
|
31
|
+
* @return {String}
|
|
32
|
+
* @public
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
Response.prototype.get = function (field) {
|
|
36
|
+
return this.getHeader(field);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
module.exports = Response;
|
package/.jscsrc
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"excludeFiles": ["node_modules/**", "bower_components/**"],
|
|
3
|
-
|
|
4
|
-
"requireCurlyBraces": [
|
|
5
|
-
"if",
|
|
6
|
-
"else",
|
|
7
|
-
"for",
|
|
8
|
-
"while",
|
|
9
|
-
"do",
|
|
10
|
-
"try",
|
|
11
|
-
"catch"
|
|
12
|
-
],
|
|
13
|
-
"requireOperatorBeforeLineBreak": true,
|
|
14
|
-
"requireCamelCaseOrUpperCaseIdentifiers": true,
|
|
15
|
-
"maximumLineLength": {
|
|
16
|
-
"value": 100,
|
|
17
|
-
"allowComments": true,
|
|
18
|
-
"allowRegex": true
|
|
19
|
-
},
|
|
20
|
-
"validateIndentation": 4,
|
|
21
|
-
"validateQuoteMarks": "'",
|
|
22
|
-
|
|
23
|
-
"disallowMixedSpacesAndTabs": true,
|
|
24
|
-
"disallowTrailingWhitespace": true,
|
|
25
|
-
"disallowSpaceAfterPrefixUnaryOperators": true,
|
|
26
|
-
"disallowMultipleVarDecl": null,
|
|
27
|
-
|
|
28
|
-
"requireSpaceAfterKeywords": [
|
|
29
|
-
"if",
|
|
30
|
-
"else",
|
|
31
|
-
"for",
|
|
32
|
-
"while",
|
|
33
|
-
"do",
|
|
34
|
-
"switch",
|
|
35
|
-
"return",
|
|
36
|
-
"try",
|
|
37
|
-
"catch"
|
|
38
|
-
],
|
|
39
|
-
"requireSpaceBeforeBinaryOperators": [
|
|
40
|
-
"=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=",
|
|
41
|
-
"&=", "|=", "^=", "+=",
|
|
42
|
-
|
|
43
|
-
"+", "-", "*", "/", "%", "<<", ">>", ">>>", "&",
|
|
44
|
-
"|", "^", "&&", "||", "===", "==", ">=",
|
|
45
|
-
"<=", "<", ">", "!=", "!=="
|
|
46
|
-
],
|
|
47
|
-
"requireSpaceAfterBinaryOperators": true,
|
|
48
|
-
"requireSpacesInConditionalExpression": true,
|
|
49
|
-
"requireSpaceBeforeBlockStatements": true,
|
|
50
|
-
"requireLineFeedAtFileEnd": true,
|
|
51
|
-
"disallowSpacesInsideObjectBrackets": "all",
|
|
52
|
-
"disallowSpacesInsideArrayBrackets": "all",
|
|
53
|
-
"disallowSpacesInsideParentheses": true,
|
|
54
|
-
|
|
55
|
-
"validateJSDoc": {
|
|
56
|
-
"checkParamNames": true,
|
|
57
|
-
"requireParamTypes": true
|
|
58
|
-
},
|
|
59
|
-
|
|
60
|
-
"disallowMultipleLineBreaks": true,
|
|
61
|
-
|
|
62
|
-
"disallowCommaBeforeLineBreak": null,
|
|
63
|
-
"disallowDanglingUnderscores": null,
|
|
64
|
-
"disallowEmptyBlocks": null,
|
|
65
|
-
"disallowMultipleLineStrings": null,
|
|
66
|
-
"disallowTrailingComma": null,
|
|
67
|
-
"requireCommaBeforeLineBreak": null,
|
|
68
|
-
"requireDotNotation": null,
|
|
69
|
-
"requireMultipleVarDecl": null,
|
|
70
|
-
"requireParenthesesAroundIIFE": true
|
|
71
|
-
}
|
package/.jshintrc
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"bitwise": true,
|
|
3
|
-
"camelcase": true,
|
|
4
|
-
"curly": true,
|
|
5
|
-
"eqeqeq": true,
|
|
6
|
-
"es3": false,
|
|
7
|
-
"forin": true,
|
|
8
|
-
"freeze": true,
|
|
9
|
-
"immed": true,
|
|
10
|
-
"indent": 4,
|
|
11
|
-
"latedef": "nofunc",
|
|
12
|
-
"newcap": true,
|
|
13
|
-
"noarg": true,
|
|
14
|
-
"noempty": true,
|
|
15
|
-
"nonbsp": true,
|
|
16
|
-
"nonew": true,
|
|
17
|
-
"plusplus": false,
|
|
18
|
-
"quotmark": "single",
|
|
19
|
-
"undef": true,
|
|
20
|
-
"unused": false,
|
|
21
|
-
"strict": false,
|
|
22
|
-
"maxparams": 10,
|
|
23
|
-
"maxdepth": 5,
|
|
24
|
-
"maxstatements": 40,
|
|
25
|
-
"maxcomplexity": 10,
|
|
26
|
-
"maxlen": 120,
|
|
27
|
-
|
|
28
|
-
"asi": false,
|
|
29
|
-
"boss": false,
|
|
30
|
-
"debug": false,
|
|
31
|
-
"eqnull": true,
|
|
32
|
-
"esnext": false,
|
|
33
|
-
"evil": false,
|
|
34
|
-
"expr": false,
|
|
35
|
-
"funcscope": false,
|
|
36
|
-
"globalstrict": false,
|
|
37
|
-
"iterator": false,
|
|
38
|
-
"lastsemic": false,
|
|
39
|
-
"laxbreak": false,
|
|
40
|
-
"laxcomma": false,
|
|
41
|
-
"loopfunc": true,
|
|
42
|
-
"maxerr": 50,
|
|
43
|
-
"moz": false,
|
|
44
|
-
"multistr": false,
|
|
45
|
-
"notypeof": false,
|
|
46
|
-
"proto": false,
|
|
47
|
-
"scripturl": false,
|
|
48
|
-
"shadow": false,
|
|
49
|
-
"sub": true,
|
|
50
|
-
"supernew": false,
|
|
51
|
-
"validthis": false,
|
|
52
|
-
"noyield": false,
|
|
53
|
-
|
|
54
|
-
"browser": true,
|
|
55
|
-
"node": true,
|
|
56
|
-
|
|
57
|
-
"globals": {
|
|
58
|
-
"angular": false
|
|
59
|
-
}
|
|
60
|
-
}
|
package/.npmignore
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
*.pyc
|
|
2
|
-
*.egg-info
|
|
3
|
-
*.db
|
|
4
|
-
*.db.old
|
|
5
|
-
*.swp
|
|
6
|
-
*.db-journal
|
|
7
|
-
|
|
8
|
-
.coverage
|
|
9
|
-
.DS_Store
|
|
10
|
-
.installed.cfg
|
|
11
|
-
_gh_pages/*
|
|
12
|
-
|
|
13
|
-
.idea
|
|
14
|
-
.vscode
|
|
15
|
-
.svn
|
|
16
|
-
|
|
17
|
-
bin
|
|
18
|
-
cfcache
|
|
19
|
-
develop-eggs
|
|
20
|
-
downloads
|
|
21
|
-
eggs
|
|
22
|
-
parts
|
|
23
|
-
tmp
|
|
24
|
-
.sass-cache
|
|
25
|
-
node_modules
|
package/.travis.yml
DELETED
package/appveyor.yml
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
# http://www.appveyor.com/docs/appveyor-yml
|
|
2
|
-
|
|
3
|
-
version: "{build}"
|
|
4
|
-
|
|
5
|
-
clone_depth: 10
|
|
6
|
-
|
|
7
|
-
init:
|
|
8
|
-
- git config --global core.autocrlf true
|
|
9
|
-
|
|
10
|
-
environment:
|
|
11
|
-
matrix:
|
|
12
|
-
- nodejs_version: 4
|
|
13
|
-
- nodejs_version: 5
|
|
14
|
-
|
|
15
|
-
install:
|
|
16
|
-
- ps: Install-Product node $env:nodejs_version
|
|
17
|
-
- npm install
|
|
18
|
-
|
|
19
|
-
build: off
|
|
20
|
-
|
|
21
|
-
test_script:
|
|
22
|
-
- node --version && npm --version
|
|
23
|
-
- npm test
|
|
24
|
-
|
|
25
|
-
matrix:
|
|
26
|
-
fast_finish: true
|
|
27
|
-
|
|
28
|
-
cache:
|
|
29
|
-
- node_modules -> package.json # local npm modules
|
package/gulpfile.js
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
var gulp = require('gulp');
|
|
2
|
-
var $ = require('gulp-load-plugins')({lazy: true});
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* List the available gulp tasks
|
|
6
|
-
*/
|
|
7
|
-
gulp.task('help', $.taskListing);
|
|
8
|
-
gulp.task('default', ['vet']);
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* vet the code and create coverage report
|
|
12
|
-
* @return {Stream}
|
|
13
|
-
*/
|
|
14
|
-
gulp.task('vet', function () {$.jshint();
|
|
15
|
-
log('Analyzing source with JSHint and JSCS');
|
|
16
|
-
|
|
17
|
-
return gulp
|
|
18
|
-
.src('src/cache.js')
|
|
19
|
-
//.pipe($.if(args.verbose, $.print()))
|
|
20
|
-
.pipe($.jshint())
|
|
21
|
-
.pipe($.jshint.reporter('jshint-stylish', {verbose: true}))
|
|
22
|
-
.pipe($.jshint.reporter('fail'))
|
|
23
|
-
.pipe($.jscs());
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
function log(msg) {
|
|
27
|
-
console.log(msg);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
module.exports = gulp;
|
package/travis.yml
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
language: node_js
|
|
2
|
-
cache: npm
|
|
3
|
-
node_js:
|
|
4
|
-
- 5
|
|
5
|
-
- 4
|
|
6
|
-
- "io.js"
|
|
7
|
-
- 0.12
|
|
8
|
-
- 0.10
|
|
9
|
-
install:
|
|
10
|
-
- npm install mocha -g
|
|
11
|
-
- npm install
|
|
12
|
-
matrix:
|
|
13
|
-
fast_finish: true
|
|
14
|
-
env:
|
|
15
|
-
- CXX=g++-4.8
|
|
16
|
-
addons:
|
|
17
|
-
apt:
|
|
18
|
-
sources:
|
|
19
|
-
- ubuntu-toolchain-r-test
|
|
20
|
-
packages:
|
|
21
|
-
- g++-4.8
|