koa-cash 4.0.4 → 4.1.1
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/README.md +62 -34
- package/index.js +12 -2
- package/package.json +5 -9
- package/.editorconfig +0 -9
- package/.lintstagedrc.js +0 -7
- package/.remarkignore +0 -1
- package/.travis.yml +0 -9
- package/test/when-cached.js +0 -86
- package/test/when-not-cached.js +0 -376
package/README.md
CHANGED
|
@@ -10,20 +10,29 @@
|
|
|
10
10
|
|
|
11
11
|
> HTTP response caching for Koa. Supports Redis, in-memory store, and more!
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
* [Features](#features)
|
|
17
|
-
* [Install](#install)
|
|
18
|
-
* [Usage](#usage)
|
|
19
|
-
* [API](#api)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
* [
|
|
23
|
-
* [
|
|
24
|
-
* [
|
|
25
|
-
* [
|
|
26
|
-
|
|
13
|
+
Table of Contents
|
|
14
|
+
|
|
15
|
+
* [koa-cash](#koa-cash)
|
|
16
|
+
* [Features](#features)
|
|
17
|
+
* [Install](#install)
|
|
18
|
+
* [Usage](#usage)
|
|
19
|
+
* [API](#api)
|
|
20
|
+
* [app.use(koaCash(options))](#appusekoacashoptions)
|
|
21
|
+
* [`maxAge`](#maxage)
|
|
22
|
+
* [`threshold`](#threshold)
|
|
23
|
+
* [`compression`](#compression)
|
|
24
|
+
* [`setCachedHeader`](#setcachedheader)
|
|
25
|
+
* [`methods`](#methods)
|
|
26
|
+
* [`hash()`](#hash)
|
|
27
|
+
* [`get()`](#get)
|
|
28
|
+
* [`set()`](#set)
|
|
29
|
+
* [Example](#example)
|
|
30
|
+
* [Max age (optional)](#max-age-optional)
|
|
31
|
+
* [CashClear](#cashclear)
|
|
32
|
+
* [Notes](#notes)
|
|
33
|
+
* [Contributors](#contributors)
|
|
34
|
+
* [License](#license)
|
|
35
|
+
* [Links](#links)
|
|
27
36
|
|
|
28
37
|
## Features
|
|
29
38
|
|
|
@@ -35,30 +44,36 @@ Caches the response based on any arbitrary store you'd like.
|
|
|
35
44
|
|
|
36
45
|
:tada: **Pairs great with [@ladjs/koa-cache-responses](https://github.com/ladjs/koa-cache-responses)** :tada:
|
|
37
46
|
|
|
38
|
-
|
|
39
47
|
## Install
|
|
40
48
|
|
|
41
|
-
[
|
|
49
|
+
[NPM](https://www.npmjs.com/)
|
|
42
50
|
|
|
43
51
|
```sh
|
|
44
52
|
npm install koa-cash
|
|
45
53
|
```
|
|
46
54
|
|
|
47
|
-
[
|
|
55
|
+
[Yarn](https://yarnpkg.com/)
|
|
48
56
|
|
|
49
57
|
```sh
|
|
50
58
|
yarn add koa-cash
|
|
51
59
|
```
|
|
52
60
|
|
|
53
|
-
|
|
54
61
|
## Usage
|
|
55
62
|
|
|
56
63
|
```js
|
|
57
|
-
|
|
64
|
+
import LRU from 'lru-cache';
|
|
65
|
+
import koaCash from 'koa-cash';
|
|
58
66
|
|
|
59
67
|
// ...
|
|
60
|
-
|
|
61
|
-
app.use(koaCash(
|
|
68
|
+
const cache = new LRU();
|
|
69
|
+
app.use(koaCash({
|
|
70
|
+
get: (key) => {
|
|
71
|
+
return cache.get(key);
|
|
72
|
+
},
|
|
73
|
+
set(key, value) {
|
|
74
|
+
return cache.set(key, value);
|
|
75
|
+
},
|
|
76
|
+
}))
|
|
62
77
|
|
|
63
78
|
app.use(async ctx => {
|
|
64
79
|
// this response is already cashed if `true` is returned,
|
|
@@ -71,7 +86,6 @@ app.use(async ctx => {
|
|
|
71
86
|
});
|
|
72
87
|
```
|
|
73
88
|
|
|
74
|
-
|
|
75
89
|
## API
|
|
76
90
|
|
|
77
91
|
### app.use(koaCash(options))
|
|
@@ -90,6 +104,16 @@ Minimum byte size to compress response bodies. Default `1kb`.
|
|
|
90
104
|
|
|
91
105
|
If a truthy value is passed, then compression will be enabled. This value is `false` by default.
|
|
92
106
|
|
|
107
|
+
#### `setCachedHeader`
|
|
108
|
+
|
|
109
|
+
If a truthy value is passed, then `X-Cached-Response` header will be set as `HIT` when response is served from the cache. This value is `false` by default.
|
|
110
|
+
|
|
111
|
+
#### `methods`
|
|
112
|
+
|
|
113
|
+
If an object is passed, then add extra HTTP method caching. This value is empty by default. But `GET` and `HEAD` are enabled.
|
|
114
|
+
|
|
115
|
+
Eg: `{ POST: true }`
|
|
116
|
+
|
|
93
117
|
#### `hash()`
|
|
94
118
|
|
|
95
119
|
A hashing function. By default, it's:
|
|
@@ -150,7 +174,11 @@ app.use(koaCash({
|
|
|
150
174
|
|
|
151
175
|
See [@ladjs/koa-cache-responses](https://github.com/ladjs/koa-cache-responses) test folder more examples (e.g. Redis with `ioredis`).
|
|
152
176
|
|
|
153
|
-
###
|
|
177
|
+
### Max age (optional)
|
|
178
|
+
|
|
179
|
+
```js
|
|
180
|
+
const cached = await ctx.cashed(maxAge) // maxAge is passed to your caching strategy
|
|
181
|
+
```
|
|
154
182
|
|
|
155
183
|
This is how you enable a route to be cached. If you don't call `await ctx.cashed()`, then this route will not be cached nor will it attempt to serve the request from the cache.
|
|
156
184
|
|
|
@@ -158,17 +186,20 @@ This is how you enable a route to be cached. If you don't call `await ctx.cashed
|
|
|
158
186
|
|
|
159
187
|
If `cached` is `true`, then the current request has been served from cache and **you should early `return`**. Otherwise, continue setting `ctx.body=` and this will cache the response.
|
|
160
188
|
|
|
189
|
+
### CashClear
|
|
190
|
+
|
|
191
|
+
```js
|
|
192
|
+
ctx.cashClear('/')
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
This is a special method available on the ctx that you can use to clear the cache for a specific key.
|
|
161
196
|
|
|
162
197
|
## Notes
|
|
163
198
|
|
|
164
|
-
* Only `GET` and `HEAD` requests are cached.
|
|
199
|
+
* Only `GET` and `HEAD` requests are cached. (Unless overridden)
|
|
165
200
|
* Only `200` responses are cached. Don't set `304` status codes on these routes - this middleware will handle it for you
|
|
166
201
|
* The underlying store should be able to handle `Date` objects as well as `Buffer` objects. Otherwise, you may have to serialize/deserialize yourself.
|
|
167
202
|
|
|
168
|
-
|
|
169
|
-
## Usage
|
|
170
|
-
|
|
171
|
-
|
|
172
203
|
## Contributors
|
|
173
204
|
|
|
174
205
|
| Name | Website |
|
|
@@ -176,14 +207,11 @@ If `cached` is `true`, then the current request has been served from cache and *
|
|
|
176
207
|
| **Jonathan Ong** | <http://jongleberry.com> |
|
|
177
208
|
| **Nick Baugh** | <http://niftylettuce.com> |
|
|
178
209
|
|
|
179
|
-
|
|
180
210
|
## License
|
|
181
211
|
|
|
182
212
|
[MIT](LICENSE) © [Jonathan Ong](http://jongleberry.com)
|
|
183
213
|
|
|
214
|
+
## Links
|
|
184
215
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
[npm]: https://www.npmjs.com/
|
|
188
|
-
|
|
189
|
-
[yarn]: https://yarnpkg.com/
|
|
216
|
+
* [NPM](https://www.npmjs.com/)
|
|
217
|
+
* [Yarn](https://yarnpkg.com/)
|
package/index.js
CHANGED
|
@@ -11,13 +11,15 @@ const safeStringify = require('fast-safe-stringify');
|
|
|
11
11
|
const compress = promisify(gzip);
|
|
12
12
|
|
|
13
13
|
// methods we cache
|
|
14
|
-
const
|
|
14
|
+
const defaultMethods = {
|
|
15
15
|
HEAD: true,
|
|
16
16
|
GET: true
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
module.exports = function(options) {
|
|
20
|
-
options = options || { compression: false };
|
|
20
|
+
options = options || { compression: false, setCachedHeader: false };
|
|
21
|
+
|
|
22
|
+
const methods = Object.assign(defaultMethods, options.methods);
|
|
21
23
|
|
|
22
24
|
const hash =
|
|
23
25
|
options.hash ||
|
|
@@ -34,6 +36,12 @@ module.exports = function(options) {
|
|
|
34
36
|
if (!get) throw new Error('.get not defined');
|
|
35
37
|
if (!set) throw new Error('.set not defined');
|
|
36
38
|
|
|
39
|
+
// allow for manual cache clearing
|
|
40
|
+
function cashClear(key) {
|
|
41
|
+
// console.log(`Removing cache key: ${key}`);
|
|
42
|
+
set(key, false);
|
|
43
|
+
}
|
|
44
|
+
|
|
37
45
|
// ctx.cashed(maxAge) => boolean
|
|
38
46
|
async function cashed(maxAge) {
|
|
39
47
|
// uncacheable request method
|
|
@@ -53,6 +61,7 @@ module.exports = function(options) {
|
|
|
53
61
|
this.response.type = obj.type;
|
|
54
62
|
if (obj.lastModified) this.response.lastModified = obj.lastModified;
|
|
55
63
|
if (obj.etag) this.response.etag = obj.etag;
|
|
64
|
+
if (options.setCachedHeader) this.response.set('X-Cached-Response', 'HIT');
|
|
56
65
|
if (this.request.fresh) {
|
|
57
66
|
this.response.status = 304;
|
|
58
67
|
return true;
|
|
@@ -81,6 +90,7 @@ module.exports = function(options) {
|
|
|
81
90
|
async function middleware(ctx, next) {
|
|
82
91
|
ctx.vary('Accept-Encoding');
|
|
83
92
|
ctx.cashed = cashed.bind(ctx);
|
|
93
|
+
ctx.cashClear = cashClear.bind(ctx);
|
|
84
94
|
|
|
85
95
|
await next();
|
|
86
96
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koa-cash",
|
|
3
3
|
"description": "HTTP response caching for Koa. HTTP response caching for Koa. Supports Redis, in-memory store, and more!",
|
|
4
|
-
"version": "4.
|
|
4
|
+
"version": "4.1.1",
|
|
5
5
|
"author": "Jonathan Ong <me@jongleberry.com> (http://jongleberry.com)",
|
|
6
6
|
"ava": {
|
|
7
7
|
"verbose": true,
|
|
@@ -38,7 +38,6 @@
|
|
|
38
38
|
"eslint": "6.x",
|
|
39
39
|
"eslint-config-xo-lass": "latest",
|
|
40
40
|
"fixpack": "latest",
|
|
41
|
-
"husky": "latest",
|
|
42
41
|
"into-stream": "^5.1.1",
|
|
43
42
|
"koa": "^2.12.0",
|
|
44
43
|
"lint-staged": "latest",
|
|
@@ -46,19 +45,16 @@
|
|
|
46
45
|
"nyc": "latest",
|
|
47
46
|
"remark-cli": "latest",
|
|
48
47
|
"remark-preset-github": "latest",
|
|
49
|
-
"supertest": "
|
|
48
|
+
"supertest": "latest",
|
|
50
49
|
"xo": "0.25"
|
|
51
50
|
},
|
|
52
51
|
"engines": {
|
|
53
52
|
"node": ">=8.3"
|
|
54
53
|
},
|
|
54
|
+
"files": [
|
|
55
|
+
"index.js"
|
|
56
|
+
],
|
|
55
57
|
"homepage": "https://github.com/koajs/cash",
|
|
56
|
-
"husky": {
|
|
57
|
-
"hooks": {
|
|
58
|
-
"pre-commit": "lint-staged && npm test",
|
|
59
|
-
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
|
|
60
|
-
}
|
|
61
|
-
},
|
|
62
58
|
"keywords": [
|
|
63
59
|
"alternative",
|
|
64
60
|
"amazon",
|
package/.editorconfig
DELETED
package/.lintstagedrc.js
DELETED
package/.remarkignore
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
test/snapshots/**/*.md
|
package/.travis.yml
DELETED
package/test/when-cached.js
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
const LRU = require('lru-cache');
|
|
2
|
-
const Koa = require('koa');
|
|
3
|
-
const request = require('supertest');
|
|
4
|
-
const test = require('ava');
|
|
5
|
-
|
|
6
|
-
const cash = require('..');
|
|
7
|
-
|
|
8
|
-
const createApp = function(c, opts) {
|
|
9
|
-
const app = new Koa();
|
|
10
|
-
app.use(
|
|
11
|
-
cash(
|
|
12
|
-
opts || {
|
|
13
|
-
get(key) {
|
|
14
|
-
return c.get(key);
|
|
15
|
-
},
|
|
16
|
-
set(key, value) {
|
|
17
|
-
return c.set(key, value);
|
|
18
|
-
},
|
|
19
|
-
compression: true
|
|
20
|
-
}
|
|
21
|
-
)
|
|
22
|
-
);
|
|
23
|
-
return app;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
const c = new LRU();
|
|
27
|
-
const date = Math.round(Date.now() / 1000);
|
|
28
|
-
|
|
29
|
-
test.before.cb(t => {
|
|
30
|
-
const app = createApp(c);
|
|
31
|
-
app.use(async function(ctx) {
|
|
32
|
-
if (await ctx.cashed()) return;
|
|
33
|
-
ctx.body = 'lol';
|
|
34
|
-
ctx.etag = 'lol';
|
|
35
|
-
ctx.type = 'text/lol; charset=utf-8';
|
|
36
|
-
ctx.lastModified = new Date(date * 1000);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
request(app.listen())
|
|
40
|
-
.get('/')
|
|
41
|
-
.expect(200, t.end);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
test.cb('when cached when the method is GET it should serve from cache', t => {
|
|
45
|
-
const app = createApp(c);
|
|
46
|
-
app.use(async function(ctx) {
|
|
47
|
-
if (await ctx.cashed()) return;
|
|
48
|
-
throw new Error('wtf');
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
request(app.listen())
|
|
52
|
-
.get('/')
|
|
53
|
-
.expect(200)
|
|
54
|
-
.expect('Content-Type', 'text/lol; charset=utf-8')
|
|
55
|
-
.expect('Content-Encoding', 'identity')
|
|
56
|
-
.expect('ETag', '"lol"')
|
|
57
|
-
.expect('lol', t.end);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
test.cb(
|
|
61
|
-
'when cached when the method is POST it should not serve from cache',
|
|
62
|
-
t => {
|
|
63
|
-
const app = createApp(c);
|
|
64
|
-
app.use(async function(ctx) {
|
|
65
|
-
if (await ctx.cashed()) throw new Error('wtf');
|
|
66
|
-
ctx.body = 'lol';
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
request(app.listen())
|
|
70
|
-
.post('/')
|
|
71
|
-
.expect(200, t.end);
|
|
72
|
-
}
|
|
73
|
-
);
|
|
74
|
-
|
|
75
|
-
test.cb('when cached when the response is fresh it should 304', t => {
|
|
76
|
-
const app = createApp(c);
|
|
77
|
-
app.use(async function(ctx) {
|
|
78
|
-
if (await ctx.cashed()) return;
|
|
79
|
-
throw new Error('wtf');
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
request(app.listen())
|
|
83
|
-
.get('/')
|
|
84
|
-
.set('If-None-Match', '"lol"')
|
|
85
|
-
.expect(304, t.end);
|
|
86
|
-
});
|
package/test/when-not-cached.js
DELETED
|
@@ -1,376 +0,0 @@
|
|
|
1
|
-
const Koa = require('koa');
|
|
2
|
-
const LRU = require('lru-cache');
|
|
3
|
-
const intoStream = require('into-stream');
|
|
4
|
-
const request = require('supertest');
|
|
5
|
-
const test = require('ava');
|
|
6
|
-
|
|
7
|
-
const cash = require('..');
|
|
8
|
-
|
|
9
|
-
const createApp = function(c, opts) {
|
|
10
|
-
const app = new Koa();
|
|
11
|
-
app.use(
|
|
12
|
-
cash(
|
|
13
|
-
opts || {
|
|
14
|
-
get(key) {
|
|
15
|
-
return c.get(key);
|
|
16
|
-
},
|
|
17
|
-
set(key, value) {
|
|
18
|
-
return c.set(key, value);
|
|
19
|
-
},
|
|
20
|
-
compression: true
|
|
21
|
-
}
|
|
22
|
-
)
|
|
23
|
-
);
|
|
24
|
-
return app;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
test.cb('should pass the maxAge through ctx.cash=', t => {
|
|
28
|
-
let set = false;
|
|
29
|
-
|
|
30
|
-
const c = new LRU();
|
|
31
|
-
const app = createApp(c, {
|
|
32
|
-
get(key) {
|
|
33
|
-
return c.get(key);
|
|
34
|
-
},
|
|
35
|
-
set(key, value, maxAge) {
|
|
36
|
-
set = true;
|
|
37
|
-
t.is(maxAge, 300);
|
|
38
|
-
return c.set(key, value);
|
|
39
|
-
},
|
|
40
|
-
compression: true
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
app.use(async function(ctx) {
|
|
44
|
-
if (await ctx.cashed()) return;
|
|
45
|
-
ctx.cash = {
|
|
46
|
-
maxAge: 300
|
|
47
|
-
};
|
|
48
|
-
ctx.body = 'lol';
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
request(app.listen())
|
|
52
|
-
.get('/')
|
|
53
|
-
.expect(200)
|
|
54
|
-
.expect('lol', err => {
|
|
55
|
-
if (err) return t.end(err);
|
|
56
|
-
|
|
57
|
-
t.truthy(set);
|
|
58
|
-
t.is(c.get('/').body, 'lol');
|
|
59
|
-
t.end();
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
test.cb('when body is a string it should cache the response', t => {
|
|
64
|
-
const c = new LRU();
|
|
65
|
-
const app = createApp(c);
|
|
66
|
-
app.use(async function(ctx) {
|
|
67
|
-
if (await ctx.cashed()) return;
|
|
68
|
-
ctx.body = 'lol';
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
request(app.listen())
|
|
72
|
-
.get('/')
|
|
73
|
-
.expect(200)
|
|
74
|
-
.expect('lol', err => {
|
|
75
|
-
if (err) return t.end(err);
|
|
76
|
-
|
|
77
|
-
t.is(c.get('/').body, 'lol');
|
|
78
|
-
t.end();
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
test.cb('when the body is a buffer it should cache the response', t => {
|
|
83
|
-
const c = new LRU();
|
|
84
|
-
const app = createApp(c);
|
|
85
|
-
app.use(async function(ctx) {
|
|
86
|
-
if (await ctx.cashed()) return;
|
|
87
|
-
ctx.body = Buffer.from('lol');
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
request(app.listen())
|
|
91
|
-
.get('/')
|
|
92
|
-
.expect(200)
|
|
93
|
-
.expect('lol', err => {
|
|
94
|
-
if (err) return t.end(err);
|
|
95
|
-
|
|
96
|
-
t.is(c.get('/').body.toString('utf8'), 'lol');
|
|
97
|
-
t.end();
|
|
98
|
-
});
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
test.cb('when the body is JSON it should cache the response', t => {
|
|
102
|
-
const c = new LRU();
|
|
103
|
-
const app = createApp(c);
|
|
104
|
-
app.use(async function(ctx) {
|
|
105
|
-
if (await ctx.cashed()) return;
|
|
106
|
-
ctx.body = {
|
|
107
|
-
message: 'hi'
|
|
108
|
-
};
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
request(app.listen())
|
|
112
|
-
.get('/')
|
|
113
|
-
.expect(200)
|
|
114
|
-
.expect('{"message":"hi"}', err => {
|
|
115
|
-
if (err) return t.end(err);
|
|
116
|
-
|
|
117
|
-
t.is(c.get('/').body, '{"message":"hi"}');
|
|
118
|
-
t.end();
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
test.cb('when the body is a stream it should cache the response', t => {
|
|
123
|
-
const c = new LRU();
|
|
124
|
-
const app = createApp(c);
|
|
125
|
-
app.use(async function(ctx) {
|
|
126
|
-
if (await ctx.cashed()) return;
|
|
127
|
-
ctx.body = intoStream('lol');
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
request(app.listen())
|
|
131
|
-
.get('/')
|
|
132
|
-
.expect(200)
|
|
133
|
-
.expect('lol', err => {
|
|
134
|
-
if (err) return t.end(err);
|
|
135
|
-
|
|
136
|
-
t.is(c.get('/').body.toString('utf8'), 'lol');
|
|
137
|
-
t.end();
|
|
138
|
-
});
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
test.cb('when the type is compressible it should compress the body', t => {
|
|
142
|
-
const c = new LRU();
|
|
143
|
-
const app = createApp(c);
|
|
144
|
-
app.use(async function(ctx) {
|
|
145
|
-
if (await ctx.cashed()) return;
|
|
146
|
-
ctx.response.type = 'text/plain';
|
|
147
|
-
ctx.body = Buffer.alloc(2048);
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
request(app.listen())
|
|
151
|
-
.get('/')
|
|
152
|
-
.expect('Content-Encoding', 'gzip')
|
|
153
|
-
.expect(200, err => {
|
|
154
|
-
if (err) return t.end(err);
|
|
155
|
-
|
|
156
|
-
t.truthy(c.get('/').body);
|
|
157
|
-
t.truthy(c.get('/').gzip);
|
|
158
|
-
t.is(c.get('/').type, 'text/plain; charset=utf-8');
|
|
159
|
-
t.end();
|
|
160
|
-
});
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
test.cb(
|
|
164
|
-
'when the type is compressible it should handle possible data serialisation and deserialisation',
|
|
165
|
-
t => {
|
|
166
|
-
const c = new LRU();
|
|
167
|
-
const app = createApp(c, {
|
|
168
|
-
get(key) {
|
|
169
|
-
const value = c.get(key);
|
|
170
|
-
return value && JSON.parse(value);
|
|
171
|
-
},
|
|
172
|
-
set(key, value) {
|
|
173
|
-
return c.set(key, JSON.stringify(value));
|
|
174
|
-
},
|
|
175
|
-
compression: true
|
|
176
|
-
});
|
|
177
|
-
app.use(async function(ctx) {
|
|
178
|
-
if (await ctx.cashed()) return;
|
|
179
|
-
ctx.body = new Array(1024).join('42');
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
const server = app.listen();
|
|
183
|
-
request(server)
|
|
184
|
-
.get('/')
|
|
185
|
-
.expect('Content-Encoding', 'gzip')
|
|
186
|
-
.expect(200, (err, res1) => {
|
|
187
|
-
if (err) return t.end(err);
|
|
188
|
-
|
|
189
|
-
request(server)
|
|
190
|
-
.get('/')
|
|
191
|
-
.expect('Content-Encoding', 'gzip')
|
|
192
|
-
.expect(200, (err, res2) => {
|
|
193
|
-
if (err) return t.end(err);
|
|
194
|
-
|
|
195
|
-
t.is(res1.text, res2.text);
|
|
196
|
-
t.end();
|
|
197
|
-
});
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
);
|
|
201
|
-
|
|
202
|
-
test.cb(
|
|
203
|
-
'when the type is not compressible it should not compress the body',
|
|
204
|
-
t => {
|
|
205
|
-
const c = new LRU();
|
|
206
|
-
const app = createApp(c);
|
|
207
|
-
app.use(async function(ctx) {
|
|
208
|
-
if (await ctx.cashed()) return;
|
|
209
|
-
ctx.response.type = 'image/png';
|
|
210
|
-
ctx.body = Buffer.alloc(2048);
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
request(app.listen())
|
|
214
|
-
.get('/')
|
|
215
|
-
.expect('Content-Encoding', 'identity')
|
|
216
|
-
.expect(200, err => {
|
|
217
|
-
if (err) return t.end(err);
|
|
218
|
-
|
|
219
|
-
t.truthy(c.get('/').body);
|
|
220
|
-
t.true(!c.get('/').gzip);
|
|
221
|
-
t.is(c.get('/').type, 'image/png');
|
|
222
|
-
t.end();
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
);
|
|
226
|
-
|
|
227
|
-
test.cb(
|
|
228
|
-
'when the body is below the threshold it should not compress the body',
|
|
229
|
-
t => {
|
|
230
|
-
const c = new LRU();
|
|
231
|
-
const app = createApp(c);
|
|
232
|
-
app.use(async function(ctx) {
|
|
233
|
-
if (await ctx.cashed()) return;
|
|
234
|
-
ctx.body = 'lol';
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
request(app.listen())
|
|
238
|
-
.get('/')
|
|
239
|
-
.expect('Content-Encoding', 'identity')
|
|
240
|
-
.expect('lol')
|
|
241
|
-
.expect(200, err => {
|
|
242
|
-
if (err) return t.end(err);
|
|
243
|
-
|
|
244
|
-
t.truthy(c.get('/').body);
|
|
245
|
-
t.true(!c.get('/').gzip);
|
|
246
|
-
t.is(c.get('/').type, 'text/plain; charset=utf-8');
|
|
247
|
-
t.end();
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
|
-
);
|
|
251
|
-
|
|
252
|
-
test.cb('when the method is HEAD it should cache the response', t => {
|
|
253
|
-
const c = new LRU();
|
|
254
|
-
const app = createApp(c);
|
|
255
|
-
app.use(async function(ctx) {
|
|
256
|
-
if (await ctx.cashed()) return;
|
|
257
|
-
ctx.body = 'lol';
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
request(app.listen())
|
|
261
|
-
.head('/')
|
|
262
|
-
.expect('')
|
|
263
|
-
.expect(200, err => {
|
|
264
|
-
if (err) return t.end(err);
|
|
265
|
-
|
|
266
|
-
t.is(c.get('/').body, 'lol');
|
|
267
|
-
t.is(c.get('/').type, 'text/plain; charset=utf-8');
|
|
268
|
-
t.end();
|
|
269
|
-
});
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
test.cb('when the method is POST it should not cache the response', t => {
|
|
273
|
-
const c = new LRU();
|
|
274
|
-
const app = createApp(c);
|
|
275
|
-
app.use(async function(ctx) {
|
|
276
|
-
if (await ctx.cashed()) return;
|
|
277
|
-
ctx.body = 'lol';
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
request(app.listen())
|
|
281
|
-
.post('/')
|
|
282
|
-
.expect('lol')
|
|
283
|
-
.expect(200, err => {
|
|
284
|
-
if (err) return t.end(err);
|
|
285
|
-
|
|
286
|
-
t.true(!c.get('/'));
|
|
287
|
-
t.end();
|
|
288
|
-
});
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
test.cb(
|
|
292
|
-
'when the response code is not 200 it should not cache the response',
|
|
293
|
-
t => {
|
|
294
|
-
const c = new LRU();
|
|
295
|
-
const app = createApp(c);
|
|
296
|
-
app.use(async function(ctx) {
|
|
297
|
-
if (await ctx.cashed()) return;
|
|
298
|
-
ctx.body = 'lol';
|
|
299
|
-
ctx.status = 201;
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
request(app.listen())
|
|
303
|
-
.post('/')
|
|
304
|
-
.expect('lol')
|
|
305
|
-
.expect(201, err => {
|
|
306
|
-
if (err) return t.end(err);
|
|
307
|
-
|
|
308
|
-
t.true(!c.get('/'));
|
|
309
|
-
t.end();
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
|
-
);
|
|
313
|
-
|
|
314
|
-
test.cb(
|
|
315
|
-
'when etag and last-modified headers are set it should cache those values',
|
|
316
|
-
t => {
|
|
317
|
-
const c = new LRU();
|
|
318
|
-
const app = createApp(c);
|
|
319
|
-
const date = Math.round(Date.now() / 1000);
|
|
320
|
-
app.use(async function(ctx) {
|
|
321
|
-
if (await ctx.cashed()) return;
|
|
322
|
-
ctx.body = 'lol';
|
|
323
|
-
ctx.etag = 'lol';
|
|
324
|
-
ctx.type = 'text/lol; charset=utf-8';
|
|
325
|
-
ctx.lastModified = new Date(date * 1000);
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
request(app.listen())
|
|
329
|
-
.get('/')
|
|
330
|
-
.expect('lol')
|
|
331
|
-
.expect(200, err => {
|
|
332
|
-
if (err) return t.end(err);
|
|
333
|
-
|
|
334
|
-
const obj = c.get('/');
|
|
335
|
-
t.truthy(obj);
|
|
336
|
-
t.is(obj.body, 'lol');
|
|
337
|
-
t.is(obj.etag, '"lol"');
|
|
338
|
-
t.is(obj.type, 'text/lol; charset=utf-8');
|
|
339
|
-
t.is(obj.lastModified.getTime(), new Date(date * 1000).getTime());
|
|
340
|
-
t.end();
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
);
|
|
344
|
-
|
|
345
|
-
test.cb(
|
|
346
|
-
'when the response is fresh it should return a 304 and cache the response',
|
|
347
|
-
t => {
|
|
348
|
-
const c = new LRU();
|
|
349
|
-
const app = createApp(c);
|
|
350
|
-
const date = Math.round(Date.now() / 1000);
|
|
351
|
-
app.use(async function(ctx) {
|
|
352
|
-
if (await ctx.cashed()) return;
|
|
353
|
-
ctx.body = 'lol';
|
|
354
|
-
ctx.etag = 'lol';
|
|
355
|
-
ctx.type = 'text/lol; charset=utf-8';
|
|
356
|
-
ctx.lastModified = new Date(date * 1000);
|
|
357
|
-
});
|
|
358
|
-
|
|
359
|
-
const server = app.listen();
|
|
360
|
-
request(server)
|
|
361
|
-
.get('/')
|
|
362
|
-
.set('If-None-Match', '"lol"')
|
|
363
|
-
.expect('')
|
|
364
|
-
.expect(304, err => {
|
|
365
|
-
if (err) return t.end(err);
|
|
366
|
-
|
|
367
|
-
const obj = c.get('/');
|
|
368
|
-
t.truthy(obj);
|
|
369
|
-
t.is(obj.body, 'lol');
|
|
370
|
-
t.is(obj.etag, '"lol"');
|
|
371
|
-
t.is(obj.type, 'text/lol; charset=utf-8');
|
|
372
|
-
t.is(obj.lastModified.getTime(), new Date(date * 1000).getTime());
|
|
373
|
-
t.end();
|
|
374
|
-
});
|
|
375
|
-
}
|
|
376
|
-
);
|