Как узнать, когда отправить ответ 304 Not Modified
Я пишу метод обработки ресурсов, где я контролирую доступ к различным файлам, и я хотел бы иметь возможность использовать кэш браузера. У меня вопрос двоякий:
Какие окончательные заголовки HTTP мне нужно проверить, чтобы точно знать, должен ли я отправлять ответ 304, и что я ищу, когда проверяю их?
Кроме того, есть ли какие-либо заголовки, которые мне нужно отправить при первоначальной отправке файла (например, "Last-Modified") как ответ 200?
Некоторый псевдо-код, вероятно, будет самым полезным ответом.
Как насчет заголовка управления кешем? Могут ли различные возможные значения, которые влияют на то, что вы отправляете клиенту (а именно, max-age), или должны соблюдаться только в случае изменения?
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 */ /* некоторые клиенты цитируют парм, лишние кавычки, если так * / 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) { /* если этаг совпадает, мы возвращаем 304 */ rc = HTTP_NOT_MODIFIED; }
Если вам нужна помощь с генерацией etag, напишите другой вопрос, и я найду код, который это делает. НТН!
Ответ 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) вы можете легко и правильно создать в своем веб-приложении.
Для предлагаемых материалов для чтения:
Вы должны отправить 304, если клиент явно заявил, что у него уже может быть страница в кеше. Это называется условным GET, который должен включать в запрос заголовок if-Modified- Since.
По сути, этот заголовок запроса содержит дату, с которой клиент утверждает, что имеет кэшированную копию. Вы должны проверить, изменился ли контент после этой даты, и отправить 304, если это не так.
См. http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html для соответствующего раздела в RFC.
Мы также обрабатываем кэшированные, но защищенные ресурсы. Если вы отправляете / генерируете заголовок ETAg (который рекомендуется в разделе 13.3 RFC 2616, СЛЕДУЕТ), тогда клиент ДОЛЖЕН использовать его в условном запросе (обычно в заголовке If-None-Match - HTTP_IF_NONE_MATCH -). Если вы отправляете заголовок Last-Modified (снова вы ДОЛЖНЫ), то вам следует проверить заголовок If-Modified-Since - HTTP_IF_MODIFIED_SINCE -. Если вы отправляете оба, клиент ДОЛЖЕН отправить оба, но он ДОЛЖЕН отправить ETag. Также обратите внимание, что валидация просто определяется как проверка условных заголовков на строгое равенство с теми, которые вы отправляете. Кроме того, только сильный валидатор (такой как ETag) будет использоваться для ранжированных запросов (когда запрашивается только часть ресурса).
На практике, поскольку ресурсы, которые мы защищаем, довольно статичны, а время задержки в одну секунду приемлемо, мы делаем следующее:
Проверьте, авторизован ли пользователь для доступа к запрошенному ресурсу.
Если это не так, перенаправьте их или отправьте ответ 4xx в зависимости от ситуации. Мы сгенерируем 404 ответа на запросы, которые выглядят как попытки взлома или явные попытки выполнить защитный запуск.
Сравните заголовок If-Modified-Since с заголовком Last-Modified, который мы отправим (см. Ниже) для строгого равенства
Если они совпадают, отправьте ответ 304 Not Modified и выйдите из обработки страницы
Создать заголовок Last-Modified, используя время модификации запрошенного ресурса
Посмотрите формат даты HTTP в RFC 2616
Отправьте заголовок и контент ресурса вместе с соответствующим Content-Type
Мы решили отказаться от заголовка ETag, так как он излишний для наших целей. Я полагаю, мы могли бы также использовать метку даты и времени в качестве ETag. Если мы перейдем к настоящей системе ETag, мы, вероятно, будем хранить вычисленные хэши для ресурсов и использовать их в качестве ETag.
Если ваши ресурсы генерируются динамически, например, из содержимого базы данных, тогда ETag может быть лучше для ваших нужд, поскольку они представляют собой просто текст, который нужно заполнять, как вы считаете нужным.
В отношении контроля кеша:
Вам не нужно беспокоиться о контроле кэша при его выдаче, за исключением того, что вы устанавливаете его на разумное значение. Это в основном говорит браузеру и другим нижестоящим объектам (таким как прокси) максимальное время, которое должно пройти до тайм-аута кеша.