Как узнать, когда отправить ответ 304 Not Modified

Я пишу метод обработки ресурсов, где я контролирую доступ к различным файлам, и я хотел бы иметь возможность использовать кэш браузера. У меня вопрос двоякий:

  1. Какие окончательные заголовки HTTP мне нужно проверить, чтобы точно знать, должен ли я отправлять ответ 304, и что я ищу, когда проверяю их?

  2. Кроме того, есть ли какие-либо заголовки, которые мне нужно отправить при первоначальной отправке файла (например, «Last-Modified») как ответ 200?

Некоторый псевдо-код, вероятно, будет самым полезным ответом.


Как насчет заголовка контроля кеша? Могут ли различные возможные значения, которые влияют на то, что вы отправляете клиенту (а именно, max-age), или должны соблюдаться только в случае изменения?

7.08.2008 07:54:37
Я просто хотел бы добавить, что при отправке ответа 304, вы должны отправлять только заголовок, а не содержание.
GateKiller 7.08.2008 08:12:57
5 ОТВЕТОВ
РЕШЕНИЕ

Вот как я это реализовал. Код работал чуть больше года и с несколькими браузерами, поэтому я думаю, что он довольно надежный. Это основано на RFC 2616 и на наблюдении того, что и когда отправляли различные браузеры.

Вот псевдокод:

server_etag = gen_etag_for_this_file (myfile)
etag_from_browser = get_header ("Etag")

если etag_from_browser не существует:
    etag_from_browser = get_header («Если совпадение отсутствует»)
если браузер цитирует etag:
    удалите кавычки (например, "foo" -> foo)

установить server_etag в заголовок http

если etag_from_browser соответствует server_etag
    отправить код возврата 304 в браузер

Вот фрагмент моей серверной логики, которая обрабатывает это.

/ * клиент должен установить Etag или If-None-Match * /
/ * некоторые клиенты цитируют parm, если да, удаляют кавычки * /
mketag (etag, & sb);

etagin = apr_table_get (r-> headers_in, "Etag");
if (etagin == NULL)
    etagin = apr_table_get (r-> headers_in, "If-None-Match");
if (etag! = NULL && etag [0] == '"') {
    int sl; 
    sl = strlen (etag);
    memmove (etag, etag + 1, sl + 1);
    etag [sl-2] = 0;
    логит (2, "= ETag:% s:", ETag);
}   
... 
apr_table_add (r-> headers_out, "ETag", etag);
... 
if (etagin! = NULL && strcmp (etagin, etag) == 0) {
    / * если etag совпадает, мы возвращаем 304 * /
    rc = HTTP_NOT_MODIFIED;
}   

Если вам нужна помощь с генерацией etag, напишите другой вопрос, и я выкопаю некоторый код, который это делает. НТН!

8
7.08.2008 09:01:58
Вы действительно сталкивались с клиентами, которые отправляют ETagзаголовок в запросе? Предполагается, что он будет использоваться только в ответах. Также это всегда должно быть указано в соответствии со спецификацией.
Matt Kantor 17.09.2012 00:08:00
Мэтт, клиент отправляет обратно etag, который он получил ранее, поэтому сервер может решить, является ли HTTP_NOT_MODIFIED подходящим ответом. Моя основная работа в то время, когда я делал это, была с Firefox и Safari; они оба будут включать хранимый etag, если предыдущий запрос для данного ресурса дал etag клиенту. Смотрите раздел «Типичное использование» здесь: en.wikipedia.org/wiki/HTTP_ETag
Mark Harrison 17.09.2012 01:39:30
Клиент включает свои etag в заголовки If-None-Match/ If-Match. Я никогда не видел, чтобы UA послал ETag: "..."заголовок в запросе, не так ли?
Matt Kantor 17.09.2012 03:22:34
Хм, мне трудно вспомнить точный код, который был написан около 4-5 лет назад. В соответствии с моим псевдокодом выше, похоже, что я проверял ETag и If-None-Match. Возможно, чек ETag бесполезен?
Mark Harrison 17.09.2012 03:38:30

Вы должны отправить 304, если клиент явно заявил, что у него уже может быть страница в его кеше. Это называется условным GET, который должен включать в запрос заголовок if-Modified- Since.

По сути, этот заголовок запроса содержит дату, с которой клиент утверждает, что имеет кэшированную копию. Вы должны проверить, изменился ли контент после этой даты, и отправить 304, если это не так.

См. Http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.25 для соответствующего раздела в RFC.

3
7.08.2008 08:01:54

по поводу кеш-контроля:

Вам не нужно беспокоиться о контроле за кешем при раздаче, кроме того, чтобы установить его на разумное значение. Это в основном говорит браузеру и другим нижестоящим объектам (таким как прокси) максимальное время, которое должно пройти до тайм-аута кэша.

1
7.08.2008 08:47:09

Ответ 304 Not Modified может быть результатом запроса GET или HEAD с заголовком If-Modified-Since ("IMS") или If-Not-Match ("INM").

Чтобы решить, что делать, когда вы получаете эти заголовки, представьте, что вы обрабатываете запрос GET без этих условных заголовков. Определите, какие значения ваших заголовков ETag и Last-Modified будут в этом ответе, и используйте их для принятия решения. Надеюсь, вы построили свою систему так, что ее определение обходится дешевле, чем построение полного ответа.

Если есть INM и значение этого заголовка совпадает со значением, которое вы бы поместили в ETag, ответьте 304.

Если есть IMS, и значение даты в этом заголовке позже, чем то, которое вы бы поместили в Last-Modified, то ответьте 304.

Иначе, действуйте так, как если бы запрос не содержал этих заголовков.

Для подхода с наименьшими усилиями ко второй части вашего вопроса выясните, какие заголовки (Expires, ETag и Last-Modified) вы можете легко и правильно создать в своем веб-приложении.

Для предлагаемых материалов для чтения:

http://www.w3.org/Protocols/rfc2616/rfc2616.html

http://www.mnot.net/cache_docs/

4
9.08.2008 13:27:59

Мы также обрабатываем кэшированные, но защищенные ресурсы. Если вы отправляете / генерируете заголовок ETAg (который рекомендуется в разделе 13.3 RFC 2616, СЛЕДУЕТ), тогда клиент ДОЛЖЕН использовать его в условном запросе (обычно в заголовке If-None-Match - HTTP_IF_NONE_MATCH -). Если вы отправляете заголовок Last-Modified (снова вы ДОЛЖНЫ), то вам следует проверить заголовок If-Modified-Since - HTTP_IF_MODIFIED_SINCE -. Если вы отправляете оба, клиент ДОЛЖЕН отправить оба, но он ДОЛЖЕН отправить ETag. Также обратите внимание, что валидация просто определяется как проверка условных заголовков на строгое равенство с теми, которые вы отправляете. Кроме того, только сильный валидатор (такой как ETag) будет использоваться для ранжированных запросов (когда запрашивается только часть ресурса).

На практике, поскольку ресурсы, которые мы защищаем, довольно статичны, а время задержки в одну секунду приемлемо, мы делаем следующее:

  1.  Проверьте, авторизован ли пользователь для доступа к запрошенному ресурсу.

         Если это не так, перенаправьте их или отправьте ответ 4xx в зависимости от ситуации. Мы сгенерируем 404 ответа на запросы, которые выглядят как попытки взлома или явные попытки выполнить защитный запуск.

  2.  Сравните заголовок If-Modified-Since с заголовком Last-Modified, который мы отправим (см. Ниже) для строгого равенства

         Если они совпадают, отправьте ответ 304 Not Modified и выйдите из обработки страницы

  3.  Создать заголовок Last-Modified, используя время модификации запрошенного ресурса

        Посмотрите формат даты HTTP в RFC 2616

  4.  Отправьте заголовок и контент ресурса вместе с соответствующим Content-Type

Мы решили отказаться от заголовка ETag, так как он излишний для наших целей. Я полагаю, мы могли бы также использовать метку даты в качестве ETag. Если мы перейдем к настоящей системе ETag, мы, вероятно, будем хранить вычисленные хэши для ресурсов и использовать их в качестве ETag.

Если ваши ресурсы генерируются динамически, например, из содержимого базы данных, то ETag может быть лучше для ваших нужд, поскольку они представляют собой просто текст, который нужно заполнять, как вы считаете нужным.

2
13.10.2009 14:16:47