Как создать HTTP-заголовок ETag для файла ресурсов?
HTTP: создание заголовка ETag
Ответы (7)
etag — это произвольная строка, которую сервер отправляет клиенту, которую клиент отправит обратно на сервер при следующем запросе файла.
etag должен вычисляться на сервере на основе файла. Вроде как контрольная сумма, но вы можете не захотеть проверять контрольную сумму каждого отправляемого файла.
server client
<------------- request file foo
file foo etag: "xyz" -------->
<------------- request file foo
etag: "xyz" (what the server just sent)
(the etag is the same, so the server can send a 304)
Я построил строку в формате метка даты-размер файла-номер индексного дескриптора файла. Таким образом, если файл изменяется на сервере после того, как он был передан клиенту, вновь сгенерированный etag не будет совпадать, если клиент повторно запросит его.
char *mketag(char *s, struct stat *sb)
{
sprintf(s, "%d-%d-%d", sb->st_mtime, sb->st_size, sb->st_ino);
return s;
}
Пока он меняется всякий раз, когда изменяется представление ресурса, то, как вы его создаете, полностью зависит от вас.
Вы должны попытаться произвести его таким образом, чтобы дополнительно:
- не требует повторного вычисления при каждом условном GET, и
- не меняется, если содержимое ресурса не изменилось
Использование хэшей контента может привести к ошибке №1, если вы не сохраните вычисленные хэши вместе с файлами.
Использование номеров инодов может привести к сбою в #2, если вы переупорядочите свою файловую систему или будете обслуживать контент с нескольких серверов.
Один из механизмов, который может работать, заключается в использовании чего-то, полностью зависящего от содержимого, например, хэш SHA-1 или строка версии, вычисляемая и сохраняемая один раз при изменении содержимого вашего ресурса.
Из http://developer.yahoo.com/performance/rules.html#etags:
По умолчанию как Apache, так и IIS встраивают данные в ETag, что значительно снижает шансы на успех теста на достоверность на веб-сайтах с несколькими серверами.
...
Если вы не пользуетесь преимуществами гибкой модели проверки, предоставляемой ETag, лучше вообще удалить ETag.
Как создать тег apache по умолчанию в bash
for file in *; do printf "%x-%x-%x\t$file\n" `stat -c%i $file` `stat -c%s $file` $((`stat -c%Y $file`*1000000)) ; done
Даже когда я искал что-то точно такое же, как etag (браузер запрашивает файл только в том случае, если он изменился на сервере), это никогда не работало, и я закончил использовать трюк GET (добавляя метку времени в качестве аргумента получения к файлам js ).
Я использовал Adler-32 в качестве сокращателя html-ссылок. Я не уверен, что это хорошая идея, но пока я не заметил никаких дубликатов. Он может работать как генератор etag. И это должно быть быстрее, чем попытка хэширования с использованием схемы шифрования, такой как sha, но я этого не проверял. Код, который я использую:
shortlink = str(hex(zlib.adler32(link)+(2**32-1)/2))[2:-1]
Я бы рекомендовал не использовать их и вместо этого использовать заголовки с последними изменениями.
У Аскапача есть полезная статья на эту тему. (поскольку они делают почти все, что кажется!)
http://www.askapache.com/htaccess/apache-speed-etags.html
Пример кода Марка Харрисона аналогичен тому, что используется в Apache 2.2. Но такой алгоритм вызывает проблемы с балансировкой нагрузки, когда у вас есть два сервера с одним и тем же файлом, но файл inode
отличается. Вот почему в Apache 2.4 разработчики упростили схему ETag и удалили часть inode
. Также, чтобы сделать ETag короче, обычно они кодируются в шестнадцатеричном формате:
<inttypes.h>
char *mketag(char *s, struct stat *sb)
{
sprintf(s, "\"%" PRIx64 "-%" PRIx64 "\"", sb->st_mtime, sb->st_size);
return s;
}
или для Java
etag = '"' + Long.toHexString(lastModified) + '-' +
Long.toHexString(contentLength) + '"';
для С#
// Generate ETag from file's size and last modification time as unix timestamp in seconds from 1970
public static string MakeEtag(long lastMod, long size)
{
string etag = '"' + lastMod.ToString("x") + '-' + size.ToString("x") + '"';
return etag;
}
public static void Main(string[] args)
{
long lastMod = 1578315296;
long size = 1047;
string etag = MakeEtag(lastMod, size);
Console.WriteLine("ETag: " + etag);
//=> ETag: "5e132e20-417"
}
Функция возвращает ETag, совместимый с Nginx. См. сравнение ETag с разных серверов