11<?php namespace App \Http \Middleware ;
2- /**
3- * Copyright 2015 OpenStack Foundation
4- * Licensed under the Apache License, Version 2.0 (the "License");
5- * you may not use this file except in compliance with the License.
6- * You may obtain a copy of the License at
7- * http://www.apache.org/licenses/LICENSE-2.0
8- * Unless required by applicable law or agreed to in writing, software
9- * distributed under the License is distributed on an "AS IS" BASIS,
10- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11- * See the License for the specific language governing permissions and
12- * limitations under the License.
13- **/
142
153use Closure ;
16- use Illuminate \Support \Facades \Config ;
174use Illuminate \Http \JsonResponse ;
18- use libs \ utils \ CacheRegions ;
19- use libs \ utils \ ICacheService ;
5+ use Illuminate \ Support \ Facades \ Cache ;
6+ use Illuminate \ Support \ Facades \ Config ;
207use Illuminate \Support \Facades \Log ;
8+ use libs \utils \CacheRegions ;
219use models \oauth2 \IResourceServerContext ;
2210
23- /**
24- * Class CacheMiddleware
25- * @package App\Http\Middleware
26- */
2711final class CacheMiddleware
2812{
29- /**
30- * @var ICacheService
31- */
32- private $ cache_service ;
13+ private IResourceServerContext $ context ;
3314
34- /**
35- * @var IResourceServerContext
36- */
37- private $ context ;
38-
39- public function __construct (IResourceServerContext $ context , ICacheService $ cache_service )
15+ public function __construct (IResourceServerContext $ context )
4016 {
4117 $ this ->context = $ context ;
42- $ this ->cache_service = $ cache_service ;
4318 }
4419
45-
4620 /**
47- * @param $request
48- * @param Closure $next
49- * @param $cache_lifetime
50- * @param null $cache_region
51- * @param null $param_id
21+ * @param \Illuminate\Http\Request $request
22+ * @param Closure $next
23+ * @param int $cache_lifetime seconds
24+ * @param string| null $cache_region one of CacheRegions::*
25+ * @param string| null $param_id route parameter name (e.g. "event_id")
5226 * @return JsonResponse|mixed
5327 */
5428 public function handle ($ request , Closure $ next , $ cache_lifetime , $ cache_region = null , $ param_id = null )
5529 {
5630 Log::debug ('CacheMiddleware::handle ' );
57- $ cache_lifetime = intval ($ cache_lifetime );
58- if ($ request ->getMethod () !== 'GET ' ) {
59- // short circuit
60- Log::debug ('CacheMiddleware::handle method is not GET ' );
61- return $ next ($ request );
62- }
6331
64- $ key = $ request ->getPathInfo ();
65- $ query = $ request ->getQueryString ();
66- $ current_time = time ();
67- $ evict_cache = false ;
68- if (!empty ($ query )) {
69- Log::debug (sprintf ('CacheMiddleware::handle query %s ' , $ query ));
70- $ query = explode ('& ' , $ query );
71- foreach ($ query as $ q ) {
72- $ q = explode ('= ' , $ q );
73- /*
74- if(strtolower($q[0]) === "evict_cache"){
75- if(strtolower($q[1]) === '1') {
76- Log::debug('CacheMiddleware::handle cache will be evicted');
77- $evict_cache = true;
78- }
79- continue;
80- }
81- */
82- if (in_array (strtolower ($ q [0 ]), ['access_token ' , 'token_type ' , 'q ' , 't ' ,'evict_cache ' ])) continue ;
83- $ key .= ". " . implode ("= " , $ q );
84- }
32+ // Only cache GETs:
33+ if ($ request ->method () !== 'GET ' ) {
34+ Log::debug ('CacheMiddleware::handle skipping non-GET ' );
35+ return $ next ($ request );
8536 }
8637
87- if (str_contains ($ request ->getPathInfo (), '/me ' )) {
88- $ current_member = $ this ->context ->getCurrentUser ();
89- if (!is_null ($ current_member ))
90- $ key .= ': ' . $ current_member ->getId ();
91- }
38+ $ cache_lifetime = intval ($ cache_lifetime );
39+ $ key = $ this ->buildKey ($ request );
40+ $ regionTag = null ;
9241
93- $ data = $ this ->cache_service ->getSingleValue ($ key );
94- $ time = $ this ->cache_service ->getSingleValue ($ key . ".generated " );
95- $ region = [];
96- $ cache_region_key = null ;
97- if (!empty ($ cache_region ) && !empty ($ param_id )){
42+ // If we have a region (e.g. summits/69 → CacheRegionEvents:69):
43+ if ($ cache_region && $ param_id ) {
9844 $ id = $ request ->route ($ param_id );
99- $ cache_region_key = CacheRegions::getCacheRegionFor ($ cache_region , $ id );
100- if (!empty ($ cache_region_key ) && $ this ->cache_service ->exists ($ cache_region_key )) {
101- //
102- Log::debug (sprintf ("CacheMiddleware::handle trying to get region %s data ... " , $ cache_region_key ));
103- $ region_data = $ this ->cache_service ->getSingleValue ($ cache_region_key );
104- if (!empty ($ region_data )){
105- $ region = json_decode (gzinflate ($ region_data ), true );
106- Log::debug (sprintf ("CacheMiddleware::handle got payload %s for region %s " , json_encode ($ region ), $ cache_region_key ));
107- }
45+ if ($ id ) {
46+ $ regionTag = CacheRegions::getCacheRegionFor ($ cache_region , $ id );
10847 }
10948 }
110- if (empty ($ data ) || empty ($ time ) || $ evict_cache ) {
111- $ time = $ current_time ;
112- Log::debug (sprintf ("CacheMiddleware::handle cache value not found for key %s , getting from api... " , $ key ));
113- // normal flow ...
114- $ response = $ next ($ request );
115- if ($ response instanceof JsonResponse && $ response ->getStatusCode () === 200 ) {
116- // and if its json, store it on cache ..).
117- $ data = $ response ->getData (true );
118- Log::debug (sprintf ("CacheMiddleware::handle storing data for key %s " , $ key ));
119- $ this ->cache_service ->setSingleValue ($ key , gzdeflate (json_encode ($ data ), 9 ), $ cache_lifetime );
120- $ this ->cache_service ->setSingleValue ($ key . ".generated " , $ time , $ cache_lifetime );
121- if (!empty ($ cache_region_key )){
122- $ region [$ key ] = $ key ;
123- Log::debug (sprintf ("CacheMiddleware::handle storing data for region %s " , $ cache_region_key ));
124- $ this ->cache_service ->setSingleValue ($ cache_region_key , gzdeflate (json_encode ($ region ), 9 ));
125- }
126- }
49+
50+ if ($ regionTag ) {
51+ Log::debug ("CacheMiddleware: using region tag {$ regionTag }" );
52+ $ data = Cache::tags ($ regionTag )
53+ ->remember ($ key , $ cache_lifetime , function () use ($ next , $ request , $ regionTag , $ key , $ cache_lifetime ) {
54+ Log::debug ("CacheMiddleware: cache miss for {$ key } in tag {$ regionTag }" );
55+ $ resp = $ next ($ request );
56+ if ($ resp instanceof JsonResponse && $ resp ->getStatusCode () === 200 ) {
57+ return $ resp ->getData (true );
58+ }
59+ // don’t cache non-200 or non-JSON
60+ return Cache::get ($ key );
61+ });
12762 } else {
128- $ ttl = $ this ->cache_service ->ttl ($ key );
129- // cache hit ...
130- Log::debug (sprintf ("CacheMiddleware::handle cache hit for %s - ttl %s ... " , $ key , $ ttl ));
131- $ response = new JsonResponse (json_decode (gzinflate ($ data ), true ), 200 , [
132- 'content-type ' => 'application/json ' ,
133- ]
134- );
63+ Log::debug ("CacheMiddleware: using global cache " );
64+ $ data = Cache::remember ($ key , $ cache_lifetime , function () use ($ next , $ request , $ key ) {
65+ Log::debug ("CacheMiddleware: cache miss for {$ key }" );
66+ $ resp = $ next ($ request );
67+ if ($ resp instanceof JsonResponse && $ resp ->getStatusCode () === 200 ) {
68+ return $ resp ->getData (true );
69+ }
70+ return Cache::get ($ key );
71+ });
13572 }
13673
74+ // Build the JsonResponse (either from cache or fresh)
75+ $ response = new JsonResponse ($ data , 200 , ['Content-Type ' => 'application/json ' ]);
76+
77+ // Mark for revalidation so your ETag middleware can return 304 when unchanged
78+ $ response ->setPublic ();
79+ $ response ->setMaxAge (0 );
80+ $ response ->headers ->addCacheControlDirective ('must-revalidate ' , true );
81+ $ response ->headers ->addCacheControlDirective ('proxy-revalidate ' , true );
82+
13783 return $ response ;
13884 }
85+
86+ /**
87+ * Build a cache key based on path + sorted query params
88+ */
89+ private function buildKey ($ request ): string
90+ {
91+ $ path = $ request ->getPathInfo ();
92+ $ params = collect ($ request ->query ())
93+ ->except (['access_token ' ,'token_type ' ,'q ' ,'t ' ,'evict_cache ' ])
94+ ->sortKeys ()
95+ ->map (function ($ v ) {
96+ return is_array ($ v ) ? implode (', ' , $ v ) : $ v ;
97+ })
98+ ->all ();
99+
100+ if (str_contains ($ path , '/me ' ) && $ user = $ this ->context ->getCurrentUser ()) {
101+ // per-user cache on /me routes
102+ $ path .= ": {$ user ->getId ()}" ;
103+ }
104+
105+ if (empty ($ params )) {
106+ return $ path ;
107+ }
108+
109+ // build a normalized query string
110+ $ qs = http_build_query ($ params , '' , '& ' , PHP_QUERY_RFC3986 );
111+ return "{$ path }. {$ qs }" ;
112+ }
139113}
0 commit comments