11'use strict'
22
33const assert = require ( 'node:assert' )
4+ const { Readable } = require ( 'node:stream' )
45const util = require ( '../core/util' )
56const CacheHandler = require ( '../handler/cache-handler' )
67const MemoryCacheStore = require ( '../cache/memory-cache-store' )
@@ -57,95 +58,88 @@ module.exports = (opts = {}) => {
5758 // Where body can be a Buffer, string, stream or blob?
5859 const result = store . get ( cacheKey )
5960 if ( ! result ) {
60- // Request isn't cached
6161 return dispatch ( opts , new CacheHandler ( globalOpts , cacheKey , handler ) )
6262 }
6363
6464 /**
65- * @param {import('node:stream').Readable | undefined } stream
66- * @param {import('../../types/cache-interceptor.d.ts').default.CachedResponse } value
65+ * @param {import('../../types/cache-interceptor.d.ts').default.GetResult } result
6766 */
68- const respondWithCachedValue = ( stream , value ) => {
69- assert ( ! stream || ! stream . destroyed , 'stream should not be destroyed' )
70- assert ( ! stream || ! stream . readableDidRead , 'stream should not be readableDidRead' )
71- try {
72- stream
73- ?. on ( 'error' , function ( err ) {
74- if ( ! this . readableEnded ) {
75- if ( typeof handler . onError === 'function' ) {
76- handler . onError ( err )
77- } else {
78- process . nextTick ( ( ) => {
79- throw err
80- } )
81- }
82- }
83- } )
84- . on ( 'close' , function ( ) {
85- if ( ! this . errored && typeof handler . onComplete === 'function' ) {
86- handler . onComplete ( [ ] )
67+ const respondWithCachedValue = ( { cachedAt, rawHeaders, statusCode, statusMessage, body } ) => {
68+ const stream = util . isStream ( body )
69+ ? body
70+ : Readable . from ( body ?? [ ] )
71+
72+ assert ( ! stream . destroyed , 'stream should not be destroyed' )
73+ assert ( ! stream . readableDidRead , 'stream should not be readableDidRead' )
74+
75+ stream
76+ . on ( 'error' , function ( err ) {
77+ if ( ! this . readableEnded ) {
78+ if ( typeof handler . onError === 'function' ) {
79+ handler . onError ( err )
80+ } else {
81+ throw err
8782 }
88- } )
83+ }
84+ } )
85+ . on ( 'close' , function ( ) {
86+ if ( ! this . errored && typeof handler . onComplete === 'function' ) {
87+ handler . onComplete ( [ ] )
88+ }
89+ } )
8990
90- if ( typeof handler . onConnect === 'function' ) {
91- handler . onConnect ( ( err ) => {
92- stream ? .destroy ( err )
93- } )
91+ if ( typeof handler . onConnect === 'function' ) {
92+ handler . onConnect ( ( err ) => {
93+ stream . destroy ( err )
94+ } )
9495
95- if ( stream ?. destroyed ) {
96- return
97- }
96+ if ( stream . destroyed ) {
97+ return
9898 }
99+ }
99100
100- if ( typeof handler . onHeaders === 'function' ) {
101- // Add the age header
102- // https://www.rfc-editor.org/rfc/rfc9111.html#name-age
103- const age = Math . round ( ( Date . now ( ) - value . cachedAt ) / 1000 )
101+ if ( typeof handler . onHeaders === 'function' ) {
102+ // Add the age header
103+ // https://www.rfc-editor.org/rfc/rfc9111.html#name-age
104+ const age = Math . round ( ( Date . now ( ) - cachedAt ) / 1000 )
104105
105- // TODO (fix): What if rawHeaders already contains age header?
106- const rawHeaders = [ ... value . rawHeaders , AGE_HEADER , Buffer . from ( `${ age } ` ) ]
106+ // TODO (fix): What if rawHeaders already contains age header?
107+ rawHeaders = [ ...rawHeaders , AGE_HEADER , Buffer . from ( `${ age } ` ) ]
107108
108- if ( handler . onHeaders ( value . statusCode , rawHeaders , ( ) => stream ?. resume ( ) , value . statusMessage ) === false ) {
109- stream ?. pause ( )
110- }
109+ if ( handler . onHeaders ( statusCode , rawHeaders , ( ) => stream ?. resume ( ) , statusMessage ) === false ) {
110+ stream . pause ( )
111111 }
112+ }
112113
113- if ( opts . method === 'HEAD' ) {
114- if ( typeof handler . onComplete === 'function' ) {
115- handler . onComplete ( [ ] )
114+ if ( opts . method === 'HEAD' ) {
115+ stream . destroy ( )
116+ } else {
117+ stream . on ( 'data' , function ( chunk ) {
118+ if ( typeof handler . onData === 'function' && ! handler . onData ( chunk ) ) {
119+ stream . pause ( )
116120 }
117-
118- stream ?. destroy ( )
119- } else {
120- stream . on ( 'data' , function ( chunk ) {
121- if ( typeof handler . onData === 'function' && ! handler . onData ( chunk ) ) {
122- stream . pause ( )
123- }
124- } )
125- }
126- } catch ( err ) {
127- stream ?. destroy ( err )
121+ } )
128122 }
129123 }
130124
131125 /**
132126 * @param {import('../../types/cache-interceptor.d.ts').default.GetResult } result
133127 */
134- const handleStream = ( result ) => {
135- const { response : value , body : stream } = result
128+ const handleResult = ( result ) => {
129+ // TODO (perf): Readable.from path can be optimized...
136130
137- if ( ! stream && opts . method !== 'HEAD' ) {
131+ if ( ! result . body && opts . method !== 'HEAD' ) {
138132 throw new Error ( 'stream is undefined but method isn\'t HEAD' )
139133 }
140134
141135 // Check if the response is stale
142136 const now = Date . now ( )
143- if ( now < value . staleAt ) {
137+ if ( now < result . staleAt ) {
144138 // Dump request body.
145139 if ( util . isStream ( opts . body ) ) {
146140 opts . body . on ( 'error' , ( ) => { } ) . destroy ( )
147141 }
148- respondWithCachedValue ( stream , value )
142+ respondWithCachedValue ( result )
149143 } else if ( util . isStream ( opts . body ) && util . bodyLength ( opts . body ) !== 0 ) {
150144 // If body is is stream we can't revalidate...
151145 // TODO (fix): This could be less strict...
@@ -157,15 +151,15 @@ module.exports = (opts = {}) => {
157151 ...opts ,
158152 headers : {
159153 ...opts . headers ,
160- 'if-modified-since' : new Date ( value . cachedAt ) . toUTCString ( )
154+ 'if-modified-since' : new Date ( result . cachedAt ) . toUTCString ( )
161155 }
162156 } ,
163157 new CacheRevalidationHandler (
164158 ( success ) => {
165159 if ( success ) {
166- respondWithCachedValue ( stream , value )
167- } else {
168- stream . on ( 'error' , ( ) => { } ) . destroy ( )
160+ respondWithCachedValue ( result )
161+ } else if ( util . isStream ( result . body ) ) {
162+ result . body . on ( 'error' , ( ) => { } ) . destroy ( )
169163 }
170164 } ,
171165 new CacheHandler ( globalOpts , cacheKey , handler )
@@ -177,14 +171,19 @@ module.exports = (opts = {}) => {
177171 if ( typeof result . then === 'function' ) {
178172 result . then ( ( result ) => {
179173 if ( ! result ) {
180- // Request isn't cached
181- return dispatch ( opts , new CacheHandler ( globalOpts , cacheKey , handler ) )
174+ dispatch ( opts , new CacheHandler ( globalOpts , cacheKey , handler ) )
175+ } else {
176+ handleResult ( result )
182177 }
183-
184- handleStream ( result )
185- } ) . catch ( err => handler . onError ( err ) )
178+ } , err => {
179+ if ( typeof handler . onError === 'function' ) {
180+ handler . onError ( err )
181+ } else {
182+ throw err
183+ }
184+ } )
186185 } else {
187- handleStream ( result )
186+ handleResult ( result )
188187 }
189188
190189 return true
0 commit comments