@@ -183,8 +183,21 @@ func GetContextKeyAndKContext(w http.ResponseWriter,
183183 return ctx , span , contextKey , kContext , nil
184184}
185185
186- // handleCacheRequest processes a request with caching logic.
187- func handleCacheRequest (c * HeadlampConfig , next http.Handler , w http.ResponseWriter , r * http.Request ) {
186+ // CacheMiddleWare is middleware for caching purposes. It generates a key for each request,
187+ // authorizes the user, stores resource data in cache, and returns cached data when available.
188+ func CacheMiddleWare (c * HeadlampConfig ) mux.MiddlewareFunc {
189+ return func (next http.Handler ) http.Handler {
190+ if ! c .CacheEnabled {
191+ return next
192+ }
193+
194+ return http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
195+ cacheMiddlewareHandler (c , next , w , r )
196+ })
197+ }
198+ }
199+
200+ func cacheMiddlewareHandler (c * HeadlampConfig , next http.Handler , w http.ResponseWriter , r * http.Request ) {
188201 if k8cache .SkipWebSocket (r , next , w ) {
189202 return
190203 }
@@ -195,14 +208,8 @@ func handleCacheRequest(c *HeadlampConfig, next http.Handler, w http.ResponseWri
195208 }
196209
197210 if err := k8cache .HandleNonGETCacheInvalidation (k8sResponseCache , w , r , next , contextKey ); err != nil {
198- // ErrHandled is a sentinel error indicating the request was fully
199- // processed during cache invalidation. For non-GET requests
200- // (POST/PUT/DELETE), HandleNonGETCacheInvalidation invalidates the
201- // cache, makes a fresh request to K8s, stores the response, and
202- // writes the response to the client. When ErrHandled is returned,
203- // the request has already been handled and we must return early to
204- // avoid processing the request again or writing duplicate responses.
205211 if errors .Is (err , k8cache .ErrHandled ) {
212+ // Request was already handled (response written), return early
206213 return
207214 }
208215
@@ -215,55 +222,65 @@ func handleCacheRequest(c *HeadlampConfig, next http.Handler, w http.ResponseWri
215222
216223 key , err := k8cache .GenerateKey (r .URL , contextKey )
217224 if err != nil {
218- c .handleError (w , ctx , span , err , "failed to generate key " , http .StatusBadRequest )
225+ c .handleError (w , ctx , span , err , "failed to generate key" , http .StatusBadRequest )
219226 return
220227 }
221228
229+ handled := handleCacheAuthorization (c , next , w , r , rcw , ctx , span , contextKey , kContext , key )
230+ if handled {
231+ return
232+ }
233+
234+ k8cache .CheckForChanges (k8sResponseCache , contextKey , * kContext )
235+
236+ next .ServeHTTP (rcw , r )
237+
238+ if err := k8cache .StoreK8sResponseInCache (k8sResponseCache , r .URL , rcw , r , key ); err != nil {
239+ // Response was already written to client via rcw; just log the cache storage error
240+ logger .Log (logger .LevelError , nil , err , "failed to store response in cache" )
241+ }
242+ }
243+
244+ func handleCacheAuthorization (
245+ c * HeadlampConfig ,
246+ next http.Handler ,
247+ w http.ResponseWriter ,
248+ r * http.Request ,
249+ rcw * k8cache.ResponseCapture ,
250+ ctx context.Context ,
251+ span trace.Span ,
252+ contextKey string ,
253+ kContext * kubeconfig.Context ,
254+ key string ,
255+ ) bool {
222256 isAllowed , authErr := k8cache .IsAllowed (kContext , r )
223257 if authErr != nil {
224258 k8cache .ServeFromCacheOrForwardToK8s (k8sResponseCache , isAllowed , next , key , w , r , rcw )
225259
226- return
227- } else if ! isAllowed && k8cache .IsAuthBypassURL (r .URL .Path ) {
228- _ = k8cache .ReturnAuthErrorResponse (w , r , contextKey )
260+ return true
261+ }
229262
230- return
263+ if ! isAllowed && k8cache .IsAuthBypassURL (r .URL .Path ) {
264+ if err := k8cache .ReturnAuthErrorResponse (w , r , contextKey ); err != nil {
265+ c .handleError (w , ctx , span , err , "failed to return auth error response" , http .StatusInternalServerError )
266+ }
267+
268+ return true
231269 }
232270
233271 served , err := k8cache .LoadFromCache (k8sResponseCache , isAllowed , key , w , r )
234272 if err != nil {
235- c .handleError (w , ctx , span , errors .New (kContext .Error ), "failed to load from cache" , http .StatusServiceUnavailable )
236- return
273+ // Cache read failed; log error and fall back to K8s instead of failing the request
274+ logger .Log (logger .LevelError , nil , err , "failed to load from cache" )
275+ return false
237276 }
238277
239278 if served {
240279 c .TelemetryHandler .RecordEvent (span , "Served from cache" )
241- return
280+ return true
242281 }
243282
244- k8cache .CheckForChanges (k8sResponseCache , contextKey , * kContext )
245-
246- next .ServeHTTP (rcw , r )
247-
248- err = k8cache .StoreK8sResponseInCache (k8sResponseCache , r .URL , rcw , r , key )
249- if err != nil {
250- c .handleError (w , ctx , span , errors .New (kContext .Error ), "error while storing into cache" , http .StatusBadRequest )
251- return
252- }
253- }
254-
255- // CacheMiddleWare is Middleware for Caching purpose. It involves generating key for a request,
256- // authorizing user , store resource data in cache and returns data if key is present.
257- func CacheMiddleWare (c * HeadlampConfig ) mux.MiddlewareFunc {
258- return func (next http.Handler ) http.Handler {
259- if ! c .CacheEnabled {
260- return next
261- }
262-
263- return http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
264- handleCacheRequest (c , next , w , r )
265- })
266- }
283+ return false
267284}
268285
269286func runListPlugins () {
0 commit comments