Как создать HTTP-заголовок ETag для файла ресурсов?





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;
}
В моем случае это потому, что это был вычисленный путь из программы CGI. Вы правы, что в случае прямого пути, вероятно, хватит mtime. Поскольку стоимость в основном будет указана в stat (), дополнительная плата за включение inode и размера не взимается, что может защитить от (что, конечно, маловероятно), когда злоумышленник может обновить файл и вернуть его обратно в оригинальный mtime.
@MarkHarrison, зачем вам двойные кавычки вокруг etag? Это обязательная часть синтаксиса?
От http://developer.yahoo.com/performance/rules.html#etags:
By default, both Apache and IIS embed data in the ETag that dramatically reduces the odds of the validity test succeeding on web sites with multiple servers.
...
If you're not taking advantage of the flexible validation model that ETags provide, it's better to just remove the ETag altogether.
Поскольку он изменяется всякий раз, когда изменяется представление ресурса, то как вы его создаете, полностью зависит от вас.
Вы должны попытаться создать его таким образом, чтобы дополнительно:
Использование хэшей содержимого может привести к сбою в # 1, если вы не сохраните вычисленные хэши вместе с файлами.
Использование номеров inode может привести к сбою в # 2, если вы измените свою файловую систему или обслуживаете контент с нескольких серверов.
Один из механизмов, который может работать, - это использовать что-то полностью зависящее от содержимого, например хэш SHA-1 или строку версии, вычисляемую и сохраняемую один раз при изменении содержимого вашего ресурса.
Я бы рекомендовал не использовать их и вместо этого использовать заголовки, измененные последними.
У Askapache есть полезная статья по этому поводу. (поскольку они делают почти все, что кажется!)
http://www.askapache.com/htaccess/apache-speed-etags.html
Хм, это позор, надеюсь, они скоро вернутся, так как сайт стал кладезем советов!
@RichBradshaw просто вопрос, как мы можем узнать последнюю измененную временную метку ресурса? Это полностью ручная операция после просмотра истории изменений БД? Или есть какие-то автоматические способы?
Как сгенерировать apache etag по умолчанию в 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]
Пример кода Марка Харрисона аналогичен тому, что использовался в 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) + '"';
для C#
// 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. См. сравнение ETags с разных серверов
Если mtime - это время последнего изменения файла, то для чего нужен размер и индексный дескриптор?