1313 **/
1414
1515use Closure ;
16+ use Illuminate \Contracts \Cache \LockTimeoutException ;
1617use Illuminate \Http \JsonResponse ;
1718use Illuminate \Support \Facades \Cache ;
1819use Illuminate \Support \Facades \Log ;
@@ -112,11 +113,7 @@ public function handle($request, Closure $next, $cache_lifetime, $cache_region =
112113
113114 if ($ encoded !== null ) {
114115 Log::debug ("CacheMiddleware: cache HIT " , $ logCtx );
115-
116- $ data = $ this ->decode ($ encoded );
117- if ($ data === null ) $ data = is_array ($ encoded ) ? $ encoded : [];
118-
119- $ response = new JsonResponse ($ data , 200 , ['Content-Type ' => 'application/json ' ]);
116+ $ response = $ this ->buildCachedResponse ($ encoded );
120117 $ wasHit = true ;
121118 } else {
122119 // Phase 2: cache miss — acquire lock so only one request executes the handler
@@ -125,65 +122,65 @@ public function handle($request, Closure $next, $cache_lifetime, $cache_region =
125122 $ wasHit = false ;
126123
127124 try {
128- if ($ lock ->block (self ::LOCK_WAIT )) {
129- // Won the lock — double-check: another request may have populated the cache
130- $ encoded = $ cache ->get ($ key );
131-
132- if ($ encoded !== null ) {
133- Log::debug ("CacheMiddleware: cache HIT (after lock) " , $ logCtx );
134-
135- $ data = $ this ->decode ($ encoded );
136- if ($ data === null ) $ data = is_array ($ encoded ) ? $ encoded : [];
137-
138- $ response = new JsonResponse ($ data , 200 , ['Content-Type ' => 'application/json ' ]);
139- $ wasHit = true ;
140- } else {
141- Log::debug ("CacheMiddleware: cache MISS (executing handler) " , $ logCtx );
142-
143- $ resp = $ next ($ request );
125+ $ lock ->block (self ::LOCK_WAIT );
144126
145- // Only cache 200 JSON responses; let everything else pass through as-is
146- if ($ resp instanceof JsonResponse && $ resp ->getStatusCode () === 200 ) {
147- $ cache ->put ($ key , $ this ->encode ($ resp ->getData (true )), $ cache_lifetime );
148- } else {
149- return $ resp ;
150- }
127+ // Won the lock — double-check: another request may have populated the cache
128+ $ encoded = $ cache ->get ($ key );
151129
152- $ response = $ resp ;
153- }
130+ if ($ encoded !== null ) {
131+ Log::debug ("CacheMiddleware: cache HIT (after lock) " , $ logCtx );
132+ $ response = $ this ->buildCachedResponse ($ encoded );
133+ $ wasHit = true ;
154134 } else {
155- // Could not acquire lock within LOCK_WAIT seconds — fall through without lock
156- Log::warning ("CacheMiddleware: lock timeout, executing handler without lock " , $ logCtx );
157-
158- $ resp = $ next ($ request );
159-
160- if ($ resp instanceof JsonResponse && $ resp ->getStatusCode () === 200 ) {
161- $ cache ->put ($ key , $ this ->encode ($ resp ->getData (true )), $ cache_lifetime );
162- } else {
163- return $ resp ;
164- }
165-
166- $ response = $ resp ;
135+ Log::debug ("CacheMiddleware: cache MISS (executing handler) " , $ logCtx );
136+ $ response = $ this ->executeAndCache ($ next , $ request , $ cache , $ key , $ cache_lifetime );
167137 }
138+ } catch (LockTimeoutException $ e ) {
139+ Log::warning ("CacheMiddleware: lock timeout, executing handler without lock " , $ logCtx );
140+ $ response = $ this ->executeAndCache ($ next , $ request , $ cache , $ key , $ cache_lifetime );
168141 } finally {
169142 $ lock ->release ();
170143 }
171144 }
172145
173- // Mark for revalidation so ETag middleware can return 304 when unchanged
174- $ response ->setPublic ();
175- $ response ->setMaxAge (0 );
176- $ response ->headers ->addCacheControlDirective ('must-revalidate ' , true );
177- $ response ->headers ->addCacheControlDirective ('proxy-revalidate ' , true );
178- $ response ->headers ->add ([
179- 'X-Cache-Result ' => $ wasHit ? 'HIT ' : 'MISS ' ,
180- ]);
146+ // Cache headers only for cacheable (200 JSON) responses
147+ if ($ response instanceof JsonResponse && $ response ->getStatusCode () === 200 ) {
148+ $ response ->setPublic ();
149+ $ response ->setMaxAge (0 );
150+ $ response ->headers ->addCacheControlDirective ('must-revalidate ' , true );
151+ $ response ->headers ->addCacheControlDirective ('proxy-revalidate ' , true );
152+ $ response ->headers ->add ([
153+ 'X-Cache-Result ' => $ wasHit ? 'HIT ' : 'MISS ' ,
154+ ]);
155+ }
181156
182157 Log::debug ("CacheMiddleware: returning response " , $ logCtx );
183158
184159 return $ response ;
185160 }
186161
162+ /**
163+ * Decode a cached value and wrap it in a JsonResponse.
164+ */
165+ private function buildCachedResponse ($ encoded ): JsonResponse
166+ {
167+ $ data = $ this ->decode ($ encoded );
168+ if ($ data === null ) $ data = is_array ($ encoded ) ? $ encoded : [];
169+ return new JsonResponse ($ data , 200 , ['Content-Type ' => 'application/json ' ]);
170+ }
171+
172+ /**
173+ * Execute the next handler and cache the response if it is a 200 JSON response.
174+ */
175+ private function executeAndCache (Closure $ next , $ request , $ cache , string $ key , int $ cache_lifetime )
176+ {
177+ $ resp = $ next ($ request );
178+ if ($ resp instanceof JsonResponse && $ resp ->getStatusCode () === 200 ) {
179+ $ cache ->put ($ key , $ this ->encode ($ resp ->getData (true )), $ cache_lifetime );
180+ }
181+ return $ resp ;
182+ }
183+
187184 /**
188185 * Build a cache key based on path + sorted query params
189186 */
0 commit comments