@@ -11,6 +11,7 @@ const X_CACHE_HIT = 'x-cache-hit'
11
11
const CACHE_ETAG = 'etag'
12
12
const CACHE_CONTROL = 'cache-control'
13
13
const CACHE_IF_NONE_MATCH = 'if-none-match'
14
+ const DATA_POSTFIX = '-d'
14
15
15
16
const middleware = ( opts ) => async ( req , res , next ) => {
16
17
try {
@@ -30,43 +31,50 @@ const middleware = (opts) => async (req, res, next) => {
30
31
// ref cache key on req object
31
32
req . cacheKey = key
32
33
33
- // try to retrieve cached response
34
- const cached = await get ( mcache , key )
34
+ // try to retrieve cached response metadata
35
+ const metadata = await get ( mcache , key )
35
36
36
- if ( cached ) {
37
+ if ( metadata ) {
37
38
// respond from cache if there is a hit
38
- let { status, headers, data } = JSON . parse ( cached )
39
+ const { status, headers, encoding } = JSON . parse ( metadata )
39
40
40
41
// pre-checking If-None-Match header
41
42
if ( req . headers [ CACHE_IF_NONE_MATCH ] && req . headers [ CACHE_IF_NONE_MATCH ] === headers [ CACHE_ETAG ] ) {
42
43
res . setHeader ( 'content-length' , '0' )
43
44
res . statusCode = 304
44
45
res . end ( )
45
46
46
- return // exit because client cache state matches
47
- }
48
-
49
- if ( typeof data === 'object' && data . type === 'Buffer' ) {
50
- data = Buffer . from ( data . data )
51
- }
52
- headers [ X_CACHE_HIT ] = '1'
47
+ return
48
+ } else {
49
+ // try to retrieve cached response data
50
+ const payload = await get ( mcache , key + DATA_POSTFIX )
51
+ if ( payload ) {
52
+ let { data } = JSON . parse ( payload )
53
+ if ( typeof data === 'object' && data . type === 'Buffer' ) {
54
+ data = Buffer . from ( data . data )
55
+ }
56
+ headers [ X_CACHE_HIT ] = '1'
53
57
54
- // set cached response headers
55
- Object . keys ( headers ) . forEach ( header => res . setHeader ( header , headers [ header ] ) )
58
+ // set cached response headers
59
+ Object . keys ( headers ) . forEach ( header => res . setHeader ( header , headers [ header ] ) )
56
60
57
- // send cached payload
58
- req . cacheHit = true
59
- res . statusCode = status
60
- res . end ( data )
61
+ // send cached payload
62
+ req . cacheHit = true
63
+ res . statusCode = status
64
+ res . end ( data , encoding )
61
65
62
- return
66
+ return
67
+ }
68
+ }
63
69
}
64
70
65
71
onEnd ( res , ( payload ) => {
66
72
if ( payload . headers [ X_CACHE_EXPIRE ] ) {
67
73
// support service level expiration
68
74
const keysPattern = payload . headers [ X_CACHE_EXPIRE ] . replace ( / \s / g, '' )
69
- const patterns = keysPattern . split ( ',' )
75
+ const patterns = keysPattern . split ( ',' ) . map ( pattern =>
76
+ pattern . endsWith ( '*' ) ? pattern : [ pattern , pattern + DATA_POSTFIX ] )
77
+ . flat ( )
70
78
// delete keys on all cache tiers
71
79
patterns . forEach ( pattern => opts . stores . forEach ( store => getKeys ( store , pattern ) . then ( keys => mcache . del ( keys ) ) ) )
72
80
} else if ( payload . headers [ X_CACHE_TIMEOUT ] || payload . headers [ CACHE_CONTROL ] ) {
@@ -92,7 +100,10 @@ const middleware = (opts) => async (req, res, next) => {
92
100
payload . headers [ CACHE_ETAG ] = Math . random ( ) . toString ( 36 ) . substring ( 2 , 16 )
93
101
}
94
102
95
- // cache response
103
+ // cache response data
104
+ mcache . set ( req . cacheKey + DATA_POSTFIX , JSON . stringify ( { data : payload . data } ) , { ttl } )
105
+ delete payload . data
106
+ // cache response metadata
96
107
mcache . set ( req . cacheKey , JSON . stringify ( payload ) , { ttl } )
97
108
}
98
109
} )
0 commit comments