diff --git a/src/Util/Network.php b/src/Util/Network.php index 6c7fd731c..6b73369d3 100644 --- a/src/Util/Network.php +++ b/src/Util/Network.php @@ -910,4 +910,46 @@ class Network return self::unparseURL($parsed); } + + /** + * Generates ETag and Last-Modified response headers and checks them against + * If-None-Match and If-Modified-Since request headers if present. + * + * Blocking function, sends 304 headers and exits if check passes. + * + * @param string $etag The page etag + * @param string $last_modified The page last modification UTC date + * @throws \Exception + */ + public static function checkEtagModified(string $etag, string $last_modified) + { + $last_modified = DateTimeFormat::utc($last_modified, 'D, d M Y H:i:s') . ' GMT'; + + /** + * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.26 + */ + $if_none_match = filter_input(INPUT_SERVER, 'HTTP_IF_NONE_MATCH'); + $if_modified_since = filter_input(INPUT_SERVER, 'HTTP_IF_MODIFIED_SINCE'); + $flag_not_modified = null; + if ($if_none_match) { + $result = []; + preg_match('/^(?:W\/")?([^"]+)"?$/i', $etag, $result); + $etagTrimmed = $result[1]; + // Lazy exact ETag match, could check weak/strong ETags + $flag_not_modified = $if_none_match == '*' || strpos($if_none_match, $etagTrimmed) !== false; + } + + if ($if_modified_since && (!$if_none_match || $flag_not_modified)) { + // Lazy exact Last-Modified match, could check If-Modified-Since validity + $flag_not_modified = $if_modified_since == $last_modified; + } + + header('Etag: ' . $etag); + header('Last-Modified: ' . $last_modified); + + if ($flag_not_modified) { + header("HTTP/1.1 304 Not Modified"); + exit; + } + } }