diff --git a/hwe/j_map_recent.php b/hwe/j_map_recent.php index f5cd93c2..f24d0106 100644 --- a/hwe/j_map_recent.php +++ b/hwe/j_map_recent.php @@ -31,17 +31,12 @@ $now = time(); if($mapInfo && ($now - $mapInfo['timestamp'] < 600)){ $mapEtag = $mapInfo['etag']; $mapModified = $mapInfo['timestamp']; - - header("Last-Modified: ".gmdate("D, d M Y H:i:s", $mapModified)." GMT"); - header("Etag: $mapEtag"); - - if ( - strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']??'2000-01-01') === $mapModified || - trim($_SERVER['HTTP_IF_NONE_MATCH']??'') === $mapEtag - ) { - header("HTTP/1.1 304 Not Modified"); - die(); - } + WebUtil::setCacheHeader(new APICacheResult(TimeUtil::secondsToDateTime($mapModified), $mapEtag)); + $reqMapEtag = WebUtil::parseETag(); + $reqModifiedSince = WebUtil::parseLastModified(); + if($mapEtag === $reqMapEtag || $mapModified === $reqModifiedSince){ + WebUtil::dieWithNotModified(); + } Json::die($mapInfo['data'], 0); } @@ -76,7 +71,6 @@ $map = [ 'data'=>$rawMap, ]; $cache->save("recent_map", $map); -header("Last-Modified: ".gmdate("D, d M Y H:i:s", $now)." GMT"); -header("Etag: $etag"); +WebUtil::setCacheHeader(new APICacheResult(TimeUtil::secondsToDateTime($now), $etag)); Json::die($map['data'], 0); \ No newline at end of file diff --git a/hwe/sammo/API/Global/GetConst.php b/hwe/sammo/API/Global/GetConst.php index fb14c627..6154c581 100644 --- a/hwe/sammo/API/Global/GetConst.php +++ b/hwe/sammo/API/Global/GetConst.php @@ -281,6 +281,13 @@ class GetConst extends \sammo\BaseAPI $storage = new \Nette\Caching\Storages\FileStorage($cacheDir); $cache = new Cache($storage); + $currentCacheKey = $this->tryCache(); + if($modifiedSince !== null && $currentCacheKey->lastModified == $modifiedSince){ + return null; + } + if($reqEtag !== null && $currentCacheKey->etag == $reqEtag){ + return null; + } $constCache = $this->readCache($cache); if ($constCache !== null) { diff --git a/src/sammo/APIHelper.php b/src/sammo/APIHelper.php index 7e30680f..6f90af2e 100644 --- a/src/sammo/APIHelper.php +++ b/src/sammo/APIHelper.php @@ -9,18 +9,6 @@ class APIHelper //static only } - private static function setCacheHeader(){ - header('cache-control: private, max-age=60'); - header("Pragma: cache"); - header_remove('expires'); - } - - private static function DieWithNotModified(): never{ - static::setCacheHeader(); - header("HTTP/1.1 304 Not Modified"); - die(); - } - public static function launch(string $rootPath, string $actionPath, array $eParams = [], bool $loadRawInput = true) { if($loadRawInput){ @@ -73,51 +61,41 @@ class APIHelper } } - $modifiedSince = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) - ? new \DateTimeImmutable($_SERVER['HTTP_IF_MODIFIED_SINCE'], new \DateTimeZone("UTC")) - : null; - $reqEtags = isset($_SERVER['HTTP_IF_NONE_MATCH']) ? trim($_SERVER['HTTP_IF_NONE_MATCH']) : null; + $modifiedSince = WebUtil::parseLastModified(); + $reqEtags = WebUtil::parseETag(); $result = $obj->launch($session, $modifiedSince, $reqEtags); if (is_string($result)) { Json::dieWithReason($result); } - $cacheResult = $obj->tryCache(); + $cache = $obj->tryCache(); $setCache = false; - if ($cacheResult !== null) { - $lastModified = $cacheResult->lastModified; - $etag = $cacheResult->etag; - - if ($lastModified !== null) { - header("Last-Modified: " . gmdate("D, d M Y H:i:s", Util::toInt(TimeUtil::DateTimeToSeconds($lastModified, true))) . " GMT"); - $setCache = true; - } - if ($etag !== null) { - header("Etag: $etag"); + if ($cache !== null) { + if($cache->lastModified !== null || $cache->etag !== null){ $setCache = true; + WebUtil::setCacheHeader($cache); } - if ($modifiedSince !== null && $lastModified !== null && TimeUtil::DateIntervalToSeconds($modifiedSince->diff($lastModified)) == 0) { - static::DieWithNotModified(); + + if ($modifiedSince !== null && $cache->lastModified !== null){ + $lastModifiedUnixTime = Util::toInt(TimeUtil::DateTimeToSeconds($cache->lastModified, true)); + $modifiedSinceUnixTime = Util::toInt(TimeUtil::DateTimeToSeconds($modifiedSince)); + if($lastModifiedUnixTime === $modifiedSinceUnixTime){ + WebUtil::dieWithNotModified(); + } } - if ($reqEtags !== null && $reqEtags === $etag) { - static::DieWithNotModified(); + if ($reqEtags !== null && $reqEtags === $cache->etag) { + WebUtil::dieWithNotModified(); } } if ($result === null) { - if ($setCache) { - static::setCacheHeader(); - } Json::die([ 'result' => true, 'reason' => 'success' ], $setCache ? 0 : Json::NO_CACHE); } - if ($setCache) { - static::setCacheHeader(); - } Json::die($result, $setCache ? 0 : Json::NO_CACHE); } catch (\Throwable $e) { Json::dieWithReason($e->getMessage()); diff --git a/src/sammo/WebUtil.php b/src/sammo/WebUtil.php index 5bb24034..caed96a9 100644 --- a/src/sammo/WebUtil.php +++ b/src/sammo/WebUtil.php @@ -36,6 +36,48 @@ class WebUtil return strtolower($_SERVER['HTTP_X_REQUESTED_WITH'] ?? null) === 'xmlhttprequest'; } + public static function setCacheHeader(?APICacheResult $cache, int $maxAge=60){ + header_remove('expires'); + header("Cache-Control: private, max-age={$maxAge}"); + header("Pragma: cache"); + if($cache->etag !== null){ + header("ETag: \"{$cache->etag}\""); + } + if($cache->lastModified !== null){ + $lastModifiedUnixTime = Util::toInt(TimeUtil::DateTimeToSeconds($cache->lastModified, true)); + $lastModified = gmdate("D, d M Y H:i:s", $lastModifiedUnixTime); + header("Last-Modified: $lastModified GMT"); + } + } + + public static function dieWithNotModified(): never{ + header("HTTP/1.1 304 Not Modified"); + die(); + } + + public static function parseETag(): ?string{ + $etag = $_SERVER['HTTP_IF_NONE_MATCH']??null; + if($etag === null){ + return $etag; + } + $etag = trim($etag); + if(str_starts_with($etag, 'W/"') && str_ends_with($etag, '"')){ + return substr($etag, 3, strlen($etag) - 4); + } + if(str_starts_with($etag, '"') && str_ends_with($etag, '"')){ + return substr($etag, 1, strlen($etag) - 2); + } + return $etag; + } + + public static function parseLastModified(): ?\DateTimeInterface{ + $modifiedSinceStr = $_SERVER['HTTP_IF_MODIFIED_SINCE']??null; + if($modifiedSinceStr === null){ + return null; + } + return new \DateTimeImmutable($modifiedSinceStr, new \DateTimeZone("UTC")); + } + public static function requireAJAX(): void { if (!static::isAJAX()) {