@@ -5,128 +5,117 @@ const iu = require('middleware-if-unless')()
55const { parse : cacheControl } = require ( '@tusbar/cache-control' )
66const ms = require ( 'ms' )
77const onEnd = require ( 'on-http-end' )
8- const getKeys = require ( './get-keys ' )
8+ const { get , deleteKeys , DATA_POSTFIX } = require ( './utils ' )
99
1010const X_CACHE_EXPIRE = 'x-cache-expire'
1111const X_CACHE_TIMEOUT = 'x-cache-timeout'
1212const X_CACHE_HIT = 'x-cache-hit'
1313const CACHE_ETAG = 'etag'
1414const CACHE_CONTROL = 'cache-control'
1515const CACHE_IF_NONE_MATCH = 'if-none-match'
16- const DATA_POSTFIX = '-d'
1716
18- const middleware = ( opts ) => async ( req , res , next ) => {
19- try {
20- opts = Object . assign ( {
21- stores : [ CacheManager . caching ( { store : 'memory' , max : 1000 , ttl : 30 } ) ]
22- } , opts )
17+ const middleware = ( opts ) => {
18+ opts = Object . assign ( {
19+ stores : [ CacheManager . caching ( { store : 'memory' , max : 1000 , ttl : 30 } ) ]
20+ } , opts )
21+ const mcache = CacheManager . multiCaching ( opts . stores )
2322
24- // creating multi-cache instance
25- const mcache = CacheManager . multiCaching ( opts . stores )
23+ return async ( req , res , next ) => {
24+ try {
25+ if ( req . cacheDisabled ) return next ( )
2626
27- if ( req . cacheDisabled ) return next ( )
27+ if ( typeof req . cacheKey !== 'string' ) {
28+ let { url, cacheAppendKey = req => '' } = req
29+ cacheAppendKey = await cacheAppendKey ( req )
2830
29- let { url, cacheAppendKey = req => '' } = req
30- cacheAppendKey = await cacheAppendKey ( req )
31-
32- const key = req . method + url + cacheAppendKey
33- // ref cache key on req object
34- req . cacheKey = key
31+ const key = req . method + url + cacheAppendKey
32+ // ref cache key on req object
33+ req . cacheKey = key
34+ }
3535
36- // try to retrieve cached response metadata
37- const metadata = await get ( mcache , key )
36+ // try to retrieve cached response metadata
37+ const metadata = await get ( mcache , req . cacheKey )
3838
39- if ( metadata ) {
39+ if ( metadata ) {
4040 // respond from cache if there is a hit
41- const { status, headers, encoding } = JSON . parse ( metadata )
42-
43- // pre-checking If-None-Match header
44- if ( req . headers [ CACHE_IF_NONE_MATCH ] && req . headers [ CACHE_IF_NONE_MATCH ] === headers [ CACHE_ETAG ] ) {
45- res . setHeader ( 'content-length' , '0' )
46- res . statusCode = 304
47- res . end ( )
48-
49- return
50- } else {
51- // try to retrieve cached response data
52- const payload = await get ( mcache , key + DATA_POSTFIX )
53- if ( payload ) {
54- let { data } = JSON . parse ( payload )
55- if ( typeof data === 'object' && data . type === 'Buffer' ) {
56- data = Buffer . from ( data . data )
57- }
58- headers [ X_CACHE_HIT ] = '1'
59-
60- // set cached response headers
61- Object . keys ( headers ) . forEach ( header => res . setHeader ( header , headers [ header ] ) )
41+ const { status, headers, encoding } = JSON . parse ( metadata )
6242
63- // send cached payload
64- req . cacheHit = true
65- res . statusCode = status
66- res . end ( data , encoding )
43+ // pre-checking If-None-Match header
44+ if ( req . headers [ CACHE_IF_NONE_MATCH ] && req . headers [ CACHE_IF_NONE_MATCH ] === headers [ CACHE_ETAG ] ) {
45+ res . setHeader ( 'content-length' , '0' )
46+ res . statusCode = 304
47+ res . end ( )
6748
6849 return
50+ } else {
51+ // try to retrieve cached response data
52+ const payload = await get ( mcache , req . cacheKey + DATA_POSTFIX )
53+ if ( payload ) {
54+ let { data } = JSON . parse ( payload )
55+ if ( typeof data === 'object' && data . type === 'Buffer' ) {
56+ data = Buffer . from ( data . data )
57+ }
58+ headers [ X_CACHE_HIT ] = '1'
59+
60+ // set cached response headers
61+ Object . keys ( headers ) . forEach ( header => res . setHeader ( header , headers [ header ] ) )
62+
63+ // send cached payload
64+ req . cacheHit = true
65+ res . statusCode = status
66+ res . end ( data , encoding )
67+
68+ return
69+ }
6970 }
7071 }
71- }
7272
73- onEnd ( res , async ( payload ) => {
74- if ( payload . status === 304 ) return
75-
76- if ( payload . headers [ X_CACHE_EXPIRE ] ) {
77- // support service level expiration
78- const keysPattern = payload . headers [ X_CACHE_EXPIRE ] . replace ( / \s / g, '' )
79- const patterns = keysPattern . split ( ',' ) . map ( pattern => pattern . endsWith ( '*' )
80- ? pattern
81- : [ pattern , pattern + DATA_POSTFIX ]
82- ) . reduce ( ( acc , item ) => {
83- if ( Array . isArray ( item ) ) {
84- acc . push ( ...item )
85- } else {
86- acc . push ( item )
73+ onEnd ( res , async ( payload ) => {
74+ if ( payload . status === 304 ) return
75+
76+ if ( payload . headers [ X_CACHE_EXPIRE ] ) {
77+ // support service level expiration
78+ const keysPattern = payload . headers [ X_CACHE_EXPIRE ] . replace ( / \s / g, '' )
79+ const patterns = keysPattern . split ( ',' )
80+ // delete keys on all cache tiers
81+ deleteKeys ( opts . stores , patterns )
82+ } else if ( payload . headers [ X_CACHE_TIMEOUT ] || payload . headers [ CACHE_CONTROL ] ) {
83+ // extract cache ttl
84+ let ttl = 0
85+ if ( payload . headers [ CACHE_CONTROL ] ) {
86+ ttl = cacheControl ( payload . headers [ CACHE_CONTROL ] ) . maxAge
87+ }
88+ if ( ! ttl ) {
89+ if ( payload . headers [ X_CACHE_TIMEOUT ] ) {
90+ ttl = Math . max ( ms ( payload . headers [ X_CACHE_TIMEOUT ] ) , 1000 ) / 1000 // min value: 1 second
91+ } else {
92+ return // no TTL found, we don't cache
93+ }
8794 }
8895
89- return acc
90- } , [ ] )
91- // delete keys on all cache tiers
92- patterns . forEach ( pattern => opts . stores . forEach ( store => getKeys ( store , pattern ) . then ( keys => keys . length > 0 ? mcache . del ( keys ) : null ) ) )
93- } else if ( payload . headers [ X_CACHE_TIMEOUT ] || payload . headers [ CACHE_CONTROL ] ) {
94- // extract cache ttl
95- let ttl = 0
96- if ( payload . headers [ CACHE_CONTROL ] ) {
97- ttl = cacheControl ( payload . headers [ CACHE_CONTROL ] ) . maxAge
98- }
99- if ( ! ttl ) {
100- if ( payload . headers [ X_CACHE_TIMEOUT ] ) {
101- ttl = Math . max ( ms ( payload . headers [ X_CACHE_TIMEOUT ] ) , 1000 ) / 1000 // min value: 1 second
102- } else {
103- return // no TTL found, we don't cache
96+ // setting cache-control header if absent
97+ if ( ! payload . headers [ CACHE_CONTROL ] ) {
98+ payload . headers [ CACHE_CONTROL ] = `private, no-cache, max-age=${ ttl } `
99+ }
100+ // setting ETag if absent
101+ if ( ! payload . headers [ CACHE_ETAG ] ) {
102+ payload . headers [ CACHE_ETAG ] = Math . random ( ) . toString ( 36 ) . substring ( 2 , 16 )
104103 }
105- }
106104
107- // setting cache-control header if absent
108- if ( ! payload . headers [ CACHE_CONTROL ] ) {
109- payload . headers [ CACHE_CONTROL ] = `private, no-cache, max-age=${ ttl } `
110- }
111- // setting ETag if absent
112- if ( ! payload . headers [ CACHE_ETAG ] ) {
113- payload . headers [ CACHE_ETAG ] = Math . random ( ) . toString ( 36 ) . substring ( 2 , 16 )
105+ // cache response data
106+ await mcache . set ( req . cacheKey + DATA_POSTFIX , JSON . stringify ( { data : payload . data } ) , { ttl } )
107+ delete payload . data
108+ // cache response metadata
109+ await mcache . set ( req . cacheKey , JSON . stringify ( payload ) , { ttl } )
114110 }
111+ } )
115112
116- // cache response data
117- await mcache . set ( req . cacheKey + DATA_POSTFIX , JSON . stringify ( { data : payload . data } ) , { ttl } )
118- delete payload . data
119- // cache response metadata
120- await mcache . set ( req . cacheKey , JSON . stringify ( payload ) , { ttl } )
121- }
122- } )
123-
124- return next ( )
125- } catch ( err ) {
126- return next ( err )
113+ return next ( )
114+ } catch ( err ) {
115+ return next ( err )
116+ }
127117 }
128118}
129119
130- const get = ( cache , key ) => cache . getAndPassUp ( key )
131-
132120module . exports = iu ( middleware )
121+ module . exports . deleteKeys = deleteKeys
0 commit comments