В порядке ли моя реализация HTTP условного получения ответов в PHP? - PullRequest
8 голосов
/ 07 января 2010

После долгих поисков, прочитав все учебники, которые я нашел, и задав несколько вопросов здесь, мне наконец удалось правильно ответить (по крайней мере, я так думаю) на HTTP-запросы if-none-match и if-updated-Since.

Чтобы сделать быстрое резюме, вот что я делаю на каждые кешируемые страницы:

session_cache_limiter('public'); //Cache on clients and proxies
session_cache_expire(180); //3 hours
header('Content-Type: ' . $documentMimeType . '; charset=' . $charset);
header('ETag: "' . $eTag . '"'); //$eTag is a MD5 of $currentLanguage + $lastModified
if ($isXML)
    header('Vary: Accept'); //$documentMimeType can be either application/xhtml+xml or text/html for XHTML (based on $_SERVER['HTTP_ACCEPT'])
header('Last-Modified: ' . $lastModified);
header('Content-Language: ' . $currentLanguage);

Кроме того, каждая страница имеет свой собственный URL (для всех языков). Например, «index.php» будет обслуживаться по URL-адресу «/ en / home» на английском языке и «/ fr / accueil» на французском.

Моя большая проблема заключалась в том, чтобы ответить «304 Not Modified» на HTTP-запросы if-none-match и if-Modified-начиная с только при необходимости .

Лучший документ, который я нашел: http://rithiur.anthd.com/tutorials/conditionalget.php

И это реализация, которую я сделал (этот фрагмент кода называется как можно скорее на страницах, которые можно кэшировать):

$ifNoneMatch = array_key_exists('HTTP_IF_NONE_MATCH', $_SERVER) ? $_SERVER['HTTP_IF_NONE_MATCH'] : false;
$ifModifiedSince = array_key_exists('HTTP_IF_MODIFIED_SINCE', $_SERVER) ? $_SERVER['HTTP_IF_MODIFIED_SINCE'] : false;

if ($ifNoneMatch !== false && $ifModifiedSince !== false)
{
    //Both if-none-match and if-modified-since were received.
    //They must match the document values in order to send a HTTP 304 answer.
    if ($ifNoneMatch == $eTag && $ifModifiedSince == $lastModified)
    {
        header('Not Modified', true, 304);
        exit();
    }
}
else
{
    //Only one header received, it it match the document value, send a HTTP 304 answer.
    if (($ifNoneMatch !== false && $ifNoneMatch == $eTag) || ($ifModifiedSince !== false && $ifModifiedSince == $lastModified))
    {
        header('Not Modified', true, 304);
        exit();
    }
}

У меня вопрос в два раза:

  • Это правильный способ сделать это? Я имею в виду, что если отправлены if-none-match и if-Modified-Since, оба должны совпадать, чтобы ответить на 304, и если отправляется только один из двух, то только для совпадения можно отправить 304
  • При использовании в контексте, описанном здесь, эти 2 фрагмента являются общедоступными для кеша (я имею в виду кеш для прокси и веб-браузеров)?

Кстати, я использую только PHP 5.1.0+ (я не поддерживаю версии ниже этой).

Редактировать: Добавлена ​​награда ... Я ожидаю качественного ответа. Не отвечайте / голосуйте, если вы что-то угадываете!

1 Ответ

22 голосов
/ 12 января 2010
  • Это не совсем правильно. Пожалуйста, взгляните на алгоритм: альтернативный текст http://img532.imageshack.us/img532/1017/cache.png
  • Решение является дружественным к прокси-серверу, вы можете использовать Cache-control: proxy-revalidate, чтобы заставить кеши подчиняться любой информации о свежести, которую вы предоставляете им о ресурсе (относится только к общим | прокси-кэшам)

Вот функция, которая может помочь:

function isModified($mtime, $etag) {
    return !( (
        isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])
        && 
        strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) >= $mtime
    ) || (
        isset($_SERVER['HTTP_IF_NONE_MATCH'])
        && 
        $_SERVER['HTTP_IF_NONE_MATCH'] == $etag
    ) ) ;
}

Я предлагаю вам взглянуть на следующую статью: http://www.peej.co.uk/articles/http-caching.html

Обновление:

[AlexV] Возможно ли одновременно получать if-none-match И if-Modified-Since?

Вы можете определенно установить оба. Однако:

Если ни один из тегов объекта не совпадает, то сервер МОЖЕТ выполнить запрошенный метод, как если бы поле заголовка If-None-Match не существовало, но ДОЛЖНО также игнорировать любое поле (поля) заголовка If-Modified-Since в запрос. То есть, если никакие теги сущностей не совпадают, сервер НЕ ДОЛЖЕН возвращать ответ 304 (не изменен).

RFC2616 # 14.26

Пример значений (W означает «слабый»; подробнее см. RFC2616 # 13.3.3 ):

If-None-Match: "xyzzy", "r2d2xxxx", "c3piozzzz"
If-None-Match: W/"xyzzy", W/"r2d2xxxx", W/"c3piozzzz"
If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT
If-None-Match: *

В особом случае значение "*" соответствует любой текущей сущности ресурса.

...