Заголовки кеширования динамического контента

Yurik

/dev/null
Заголовки кеширования динамического контента

Здесь есть два закрытых топика
http://phpclub.net/talk/showthread.php?threadid=29835
http://phpclub.net/talk/showthread.php?threadid=31929

В первом частично был затронут вопрос о кешировании (в частности о посылке Expire хедера), но это совсем не то что делает Апач при запросе картинок с файловой системы, о чем я некорректно написал во втором топике (304 Not modified). Вопрос остается открытым, по крайней мере решений я пока не видел.
Решение этой проблемы сведет на нет преимущетсва хранения файлов в filesystem. Посему прошу обсудить данный вопрос и прийти к какому-нибудь выводу.

P.S. Expire заголовок не подходит т.к. не известно сколько картинка ещё будет действительной: год или 5 минут.
 

Yurik

/dev/null
Изучил показания снифера, RFC, исходники апача, PEAR класс.

Апач посылает заголовки ответа
ETag: entity
Last-Modified: GMT datetime
Entity генерится из трех частей: last-modified date + physical file length + inode number.

На что согласно РФЦ при повторном запрашивании документа броузер должен послать заголовки запроса:

If-None-Match: entity
If-Modified-Since: GMT datetime
Если "entity" совпадает с текущим (точнее входит) то в ответ идет только:
PHP:
if (strstr(if_nonematch, etag)) {
   return HTTP_NOT_MODIFIED;
Если не было заголовка If-None-Match, то Апач переходит к проверке If-Modified-Since:
PHP:
  time_t ims = ap_parseHTTPdate(if_modified_since);
  if ((ims >= mtime) && (ims <= r->request_time)) {
    return HTTP_NOT_MODIFIED;
  }
Если обе проверки не проходят, посылается новый файл.

В Pear::Cache_OutputCompression реализован только механизм Etag:If-None-Match

Оба механизма реализовываются в ПХП. Причем для варианта с ETag в БД даже не обязательно хранить timestamp модификации содержимого, т.к. уникальный ETag можно генерировать прямо из содержимого.

Но RFC2068 говорит что нужно посылать оба заголовка: Etag+Last-Modified

Как быть? Какой механизм будет иметь меньше потенциальных глюков?

Вот реализация второго варианта (проверял начиная с NN4.7, только Опера не посылает If-Modified-Since если жать на Refresh, а так все работает)
PHP:
<?php

$id=(int) @$HTTP_GET_VARS['id'];
if ($id<1) die('No document to display');
mysql_connect($host, $user, $pass);
mysql_select_db($db);
$sql="SELECT * FROM tbldocs WHERE id=".$id;
$result=mysql_query($sql) or die('Query failure');
$row=mysql_fetch_array($result);
mysql_free_result($result);

// часовой пояс
$timeshift=-2;
//TIMESTAMP модификации содержимого
$timestamp=$row['modified'];
//То же в GMT
$timestampc=$timestamp+$timeshift*3600;

// получить данные o дате файла который уже имеется в кеше броузера
if (isset($HTTP_SERVER_VARS["HTTP_IF_MODIFIED_SINCE"])){
	$cached_time=$HTTP_SERVER_VARS["HTTP_IF_MODIFIED_SINCE"];
// NN еще посылает доп. параметр length который нужно обрезать
	$cached_time=substr($cached_time, 0, strpos($cached_time, "GMT"));
//преобразовать GMT в TIMESTAMP
	$cached_time=strtotime($cached_time);
} else {
	$cached_time=0;
}
// сравниваем даты в GMT
if ($cached_time<$timestampc){
	header("Last-Modified: " . gmdate("D, d M Y H:i:s",$timestamp) . " GMT");
	header("Content-length: ".strlen($row['content']));
	header("Content-type: ".$row['mime']);
	echo $row['content'];
	exit;
} else {
	header("HTTP/1.1 304 Not Modified");
	exit;
}
?>
 
Сверху