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;
}

Я использовал Adler-32 как сокращатель HTML-ссылок. Я не уверен, хорошая ли это идея, но пока не заметил никаких дубликатов. Может работать как генератор etag. И это должно быть быстрее, чем попытки хеширования с использованием схемы шифрования, такой как sha, но я не проверял это. Я использую следующий код:

 shortlink = str(hex(zlib.adler32(link)+(2**32-1)/2))[2:-1]

Из http://developer.yahoo.com/performance/rules.html#etags :

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

...

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

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

Вы должны попытаться создать его таким образом, чтобы дополнительно:

  1. не требует, чтобы вы повторно вычисляли его при каждом условном GET, и
  2. не меняется, если содержимое ресурса не изменилось

Использование хэшей содержимого может привести к сбою в # 1, если вы не сохраните вычисленные хэши вместе с файлами.

Использование номеров inode может привести к сбою в # 2, если вы переставляете файловую систему или обслуживаете контент с нескольких серверов.

Один из механизмов, который может работать, - это использовать что-то полностью зависящее от содержимого, например хэш SHA-1 или строку версии, вычисляемую и сохраняемую один раз при изменении содержимого вашего ресурса.

Я бы рекомендовал не использовать их и вместо этого использовать заголовки, измененные последними.

У Askapache есть полезная статья по этому поводу. (поскольку они делают почти все, что кажется!)

http://www.askapache.com/htaccess/apache-speed-etags.html

Как сгенерировать 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 ).

Пример кода Марка Харрисона аналогичен тому, что использовался в 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 с разных серверов