Переговоры о содержании
Apache HTTPD поддерживает переговоры о содержании, как описано в спецификации HTTP/1.1. Он может выбрать наилучшее представление ресурса, основываясь на предпочтениях браузера по типу носителя, языку, набору символов и кодировке. Он также реализует несколько функций для более разумной обработки запросов от браузеров, которые отправляют неполную информацию о переговорах.
Переговоры о содержании предоставляются модулем mod_negotiation, который компилируется по умолчанию.
О переговорах о содержании
Ресурс может быть доступен в нескольких различных представлениях. Например, он может быть доступен на разных языках или с разными типами носителей, или в сочетании. Один из способов выбора наиболее подходящего варианта — предоставить пользователю страницу индекса и позволить ему выбрать. Однако часто сервер может выбрать автоматически. Это работает, потому что браузеры могут отправлять в рамках каждого запроса информацию о предпочитаемых представлениях. Например, браузер может указать, что хотел бы увидеть информацию на французском языке, если это возможно, иначе на английском. Браузеры указывают свои предпочтения с помощью заголовков в запросе. Чтобы запросить только французские представления, браузер отправит
Accept-Language: fr
Обратите внимание, что это предпочтение будет применяться только тогда, когда есть выбор представлений, и они отличаются по языку.
В качестве примера более сложного запроса, этот браузер настроен на принятие французского и английского языков, но с предпочтением французского, и на принятие различных типов носителей, отдавая предпочтение HTML перед обычным текстом или другими текстовыми типами, а также GIF или JPEG перед другими типами носителей, но также разрешая и любой другой тип носителя в качестве последнего средства:
Accept-Language: fr; q=1.0, en; q=0.5 Accept: text/html; q=1.0, text/*; q=0.8, image/gif; q=0.6, image/jpeg; q=0.6, image/*; q=0.5, */*; q=0.1
httpd поддерживает «управляемые сервером» переговоры о содержании, как определено в спецификации HTTP/1.1. Он полностью поддерживает заголовки запросов Accept, Accept-Language, Accept-Charset и Accept-Encoding. httpd также поддерживает «прозрачные» переговоры о содержании, которые представляют собой экспериментальный протокол переговоров, определенный в RFC 2295 и RFC 2296. Он не поддерживает «переговоры о функциях», как определено в этих RFC.
Ресурс — это концептуальная сущность, идентифицируемая URI (RFC 2396). HTTP-сервер, например Apache HTTP Server, предоставляет доступ к представлениям ресурса(ов) в его пространстве имен, причем каждое представление имеет вид последовательности байтов с определенным типом носителя, набором символов, кодировкой и т. д. Каждый ресурс может быть связан с нулевым, одним или более чем одним представлением в любой момент времени. Если доступно несколько представлений, ресурс называется переговорным, а каждое из его представлений называется вариантом. Способы, которыми различаются варианты переговорного ресурса, называются измерениями переговоров.
Переговоры в httpd
Для переговоров о ресурсе серверу необходимо предоставить информацию о каждом из вариантов. Это делается двумя способами:
- Используя карту типов (т.е., файл
*.var), в котором явно указываются имена файлов, содержащие варианты, или - Используя поиск «MultiViews», где сервер выполняет неявное сопоставление шаблонов имен файлов и выбирает из результатов.
Использование файла карты типов
Карта типов — это документ, связанный с обработчиком, названным type-map (или, для обратной совместимости со старыми конфигурациями httpd, тип MIME application/x-type-map). Обратите внимание, что для использования этой функции вам необходимо установить обработчик в конфигурации, определяющий расширение файла как type-map; это лучше всего сделать с помощью
AddHandler type-map .var
в файле конфигурации сервера.
Файлы карты типов должны иметь то же имя, что и ресурс, который они описывают, с расширением .var. В показанных ниже примерах ресурс имеет имя foo, поэтому файл карты типов имеет имя foo.var.
В этом файле должна быть запись для каждого доступного варианта; эти записи состоят из смежных строк заголовков в формате HTTP. Записи для разных вариантов разделены пустыми строками. Пустые строки внутри записи недопустимы. Обычно файл карты начинается с записи для всего объединенного сущности (хотя это не обязательно, и если присутствует, будет проигнорировано). Ниже показан пример файла карты.
URI в этом файле относительны к расположению файла карты типов. Обычно эти файлы будут находиться в той же директории, что и файл карты типов, но это не обязательно. Вы можете предоставить абсолютные или относительные URI для любого файла, расположенного на том же сервере, что и файл карты.
URI: foo URI: foo.en.html Content-type: text/html Content-language: en URI: foo.fr.de.html Content-type: text/html;charset=iso-8859-2 Content-language: fr, de
Обратите также внимание, что файл карты типов будет иметь приоритет над расширением имени файла, даже при включенном Multiviews. Если у вариантов разная исходная сортировка, это можно указать с помощью параметра «qs» к типу носителя, как в этом изображении (доступно как JPEG, GIF или ASCII-art):
URI: foo URI: foo.jpeg Content-type: image/jpeg; qs=0.8 URI: foo.gif Content-type: image/gif; qs=0.5 URI: foo.txt Content-type: text/plain; qs=0.01
Значения qs могут изменяться в диапазоне от 0,000 до 1,000. Обратите внимание, что любой вариант со значением qs 0,000 никогда не будет выбран. Варианты без значения параметра «qs» получают коэффициент qs 1,0. Параметр qs указывает относительную «качество» этого варианта по сравнению с другими доступными вариантами, независимо от возможностей клиента. Например, файл JPEG обычно имеет более высокое исходное качество, чем файл ASCII, если он пытается отобразить фотографию. Однако, если представляемый ресурс является исходным ASCII-искусством, то ASCII-представление будет иметь более высокое исходное качество, чем JPEG-представление. Таким образом, значение qs специфично для данного варианта в зависимости от характера представляемого им ресурса.
Полный список распознаваемых заголовков доступен в документации по картам типов mod_negotiation.
Multiviews
MultiViews — это параметр на уровне каталога, что означает, что он может быть задан с помощью директивы Options в разделе <Directory>, <Location> или <Files> в httpd.conf, или (если AllowOverride правильно задан) в файлах .htaccess. Обратите внимание, что Options All не устанавливает MultiViews; вам нужно запросить его по имени.
Эффект MultiViews таков: если сервер получает запрос на /some/dir/foo, если /some/dir имеет MultiViews включенным, и /some/dir/foo не существует, то сервер считывает каталог, ищет файлы с именем foo.*, и фактически создает карту типов, которая назначает всем этим файлам те же типы носителей и кодировки, которые были бы, если бы клиент запросил один из них по имени. Затем он выбирает наилучшее соответствие требованиям клиента.
MultiViews также может применяться к поиску файла по имени, указанному в директиве DirectoryIndex, если сервер пытается проиндексировать каталог. Если в файлах конфигурации указано
DirectoryIndex index
то сервер будет арбитрировать между index.html и index.html3, если оба присутствуют. Если ни то, ни другое не присутствует, а index.cgi присутствует, сервер выполнит его.
Если один из файлов, найденных при чтении каталога, не имеет расширения, распознанного mod_mime для обозначения его набора символов, типа носителя, языка или кодировки, то результат зависит от настройки директивы MultiViewsMatch. Эта директива определяет, могут ли обработчики, фильтры и другие типы расширений участвовать в переговорах MultiViews.
Методы переговоров
После того, как httpd получил список вариантов для данного ресурса, либо из файла карты типов, либо из имен файлов в каталоге, он вызывает один из двух методов, чтобы принять решение о «лучшем» варианте для возврата, если таковой имеется. Для использования функций переговоров о содержании httpd не требуется знать подробности о том, как фактически происходят переговоры. Однако остальная часть этого документа объясняет методы, используемые для заинтересованных лиц.
Существует два метода переговоров:
- Переговоры, управляемые сервером, с алгоритмом httpd используются в обычном случае. Алгоритм httpd подробно описан ниже. При использовании этого алгоритма httpd иногда может «изменять» коэффициент качества определенного измерения для достижения лучшего результата. Способы, которыми httpd может изменять коэффициенты качества, подробно описаны ниже.
- Прозрачные переговоры о содержании используются, когда браузер специально запрашивает это с помощью механизма, определенного в RFC 2295. Этот метод переговоров предоставляет браузеру полный контроль над принятием решения о «лучшем» варианте, а результат, следовательно, зависит от конкретных алгоритмов, используемых браузером. В рамках прозрачных переговоров браузер может попросить httpd запустить «алгоритм выбора удаленного варианта», определенный в RFC 2296.
Измерения переговоров
| Измерение | Примечания |
|---|---|
| Тип носителя | Браузер указывает предпочтения с помощью поля заголовка Accept. Каждый элемент может иметь связанный коэффициент качества. Описание варианта также может иметь коэффициент качества (параметр «qs»). |
| Язык | Браузер указывает предпочтения с помощью поля заголовка Accept-Language. Каждый элемент может иметь коэффициент качества. Варианты могут быть связаны с нулевым, одним или более языками. |
| Кодировка | Браузер указывает предпочтения с помощью поля заголовка Accept-Encoding . Каждый элемент может иметь коэффициент качества. |
| Набор символов | Браузер указывает предпочтения с помощью поля заголовка Accept-Charset . Каждый элемент может иметь коэффициент качества. Варианты могут указывать набор символов как параметр типа носителя. |
Алгоритм переговоров httpd
httpd может использовать следующий алгоритм для выбора «лучшего» варианта (если таковой имеется) для возврата браузеру. Этот алгоритм не настраивается. Он работает следующим образом:
- Сначала, для каждой размерности переговоров, проверьте соответствующее поле заголовка Accept* и назначьте качество каждому варианту. Если заголовок Accept* для какой-либо размерности подразумевает, что этот вариант неприемлем, исключите его. Если вариантов не останется, перейдите к шагу 4.
- Выберите «лучший» вариант путём исключения. Каждый из следующих тестов применяется в порядке. Любые варианты, не выбранные на каждом тесте, исключаются. После каждого теста, если останется только один вариант, выберите его как наилучшее соответствие и перейдите к шагу 3. Если останется более одного варианта, перейдите к следующему тесту.
- Умножьте коэффициент качества из заголовка
Acceptна коэффициент качества источника для типа медиа этого варианта и выберите варианты с наибольшим значением. - Выберите варианты с наибольшим коэффициентом качества языка.
- Выберите варианты с наилучшим соответствием языка, используя либо порядок языков в заголовке
Accept-Language(если он присутствует), либо порядок языков в директивеLanguagePriority(если она присутствует). - Выберите варианты с наилучшим параметром «уровень» медиа (используется для указания версии типов медиа text/html).
- Выберите варианты с лучшими параметрами кодировки символов, как указано в строке заголовка
Accept-Charset. Кодировка символов ISO-8859-1 приемлема, если она не исключена явно. Варианты с типом медиаtext/*без явного указания определённой кодировки символов предполагаются в кодировке ISO-8859-1. - Выберите те варианты, которые имеют связанные параметры кодировки символов, которые не являются ISO-8859-1. Если таких вариантов нет, выберите все варианты.
- Выберите варианты с лучшей кодировкой. Если есть варианты с кодировкой, которая приемлема для пользовательского агента, выберите только эти варианты. В противном случае, если есть смесь кодированных и некодированных вариантов, выберите только некодированные варианты. Если все варианты закодированы или все варианты не закодированы, выберите все варианты.
- Выберите варианты с наименьшим размером содержимого.
- Выберите первый вариант из оставшихся. Это будет либо первый в списке в файле типа-карты, либо, когда варианты считываются из каталога, тот, чьё имя файла находится первым при сортировке по порядку кодов ASCII.
- Умножьте коэффициент качества из заголовка
- Теперь алгоритм выбрал один «лучший» вариант, поэтому верните его в качестве ответа. Заголовок HTTP-ответа
Varyустанавливается для указания размерностей переговоров (браузеры и кэши могут использовать эту информацию при кэшировании ресурса). Конец. - Для того, чтобы добраться до этого момента, означает, что ни один вариант не был выбран (потому что ни один не приемлем для браузера). Верните статус 406 (означающий «Нет приемлемого представления») с телом ответа, состоящим из HTML-документа, перечисляющего доступные варианты. Также установите HTTP-заголовок
Varyдля указания размерностей вариаций.
Работа с значениями качества
Иногда httpd изменяет значения качества по сравнению с тем, что ожидалось бы при строгом толковании алгоритма переговоров httpd выше. Это делается для получения лучшего результата алгоритмом для браузеров, которые не отправляют полную или точную информацию. Некоторые из самых популярных браузеров отправляют информацию в заголовке Accept, что в противном случае привело бы к выбору неправильного варианта во многих случаях. Если браузер отправляет полную и правильную информацию, эти изменения не будут применяться.
Типы медиа и подстановочные знаки
Заголовок запроса Accept: указывает предпочтения для типов медиа. Он также может включать «подстановочные» типы медиа, такие как «image/*» или «*/*», где * соответствует любой строке. Поэтому запрос, включающий:
Accept: image/*, */*
указывает, что любой тип, начинающийся с «image/», приемлем, как и любой другой тип. Некоторые браузеры регулярно отправляют подстановочные знаки в дополнение к явным типам, которые они могут обрабатывать. Например:
Accept: text/html, text/plain, image/gif, image/jpeg, */*
Цель этого — указать, что явно указанные типы предпочтительнее, но если доступно другое представление, это тоже нормально. Используя явные значения качества, браузер на самом деле хочет что-то вроде:
Accept: text/html, text/plain, image/gif, image/jpeg, */*; q=0.01
Явно указанные типы не имеют коэффициента качества, поэтому по умолчанию они имеют предпочтение 1,0 (наивысшее). Подстановочный знак */* имеет низкое предпочтение 0,01, поэтому другие типы будут возвращены только в том случае, если ни один вариант не соответствует явно указанному типу.
Если заголовок Accept: вообще не содержит q-факторов, httpd устанавливает q-значение */*, если оно есть, в 0,01, чтобы смоделировать желаемое поведение. Он также устанавливает q-значение подстановочных знаков формата «type/*» в 0,02 (чтобы они имели предпочтение перед соответствиями по */*). Если какой-либо тип медиа в заголовке Accept: содержит q-фактор, эти специальные значения не применяются, поэтому запросы от браузеров, которые отправляют явную информацию с самого начала, работают так, как ожидается.
Исключения в переговорах по языкам
В httpd 2.0 были добавлены некоторые исключения в алгоритм переговоров, чтобы позволить плавную отмену, когда переговоры по языку не находят соответствия.
Когда клиент запрашивает страницу на вашем сервере, но сервер не может найти ни одной страницы, соответствующей значению Accept-language, отправленному браузером, сервер вернёт клиенту ответ «Нет приемлемых вариантов» или «Несколько вариантов». Чтобы избежать этих сообщений об ошибке, можно настроить httpd так, чтобы он игнорировал Accept-language в этих случаях и предоставлял документ, который не соответствует запросу клиента. Директива ForceLanguagePriority может быть использована для переопределения одного или обоих этих сообщений об ошибке и замены суждения сервера в виде директивы LanguagePriority.
Сервер также попытается сопоставить языковые подмножества, когда не может быть найдено другое соответствие. Например, если клиент запрашивает документы на языке en-GB для британского английского языка, сервер по общему правилу, в соответствии со стандартом HTTP/1.1, не разрешается сопоставить это с документом, помеченным просто как en. (Обратите внимание, что, скорее всего, ошибка конфигурации включает en-GB и не включает en в заголовке Accept-Language, так как маловероятно, что читатель понимает британский английский, но не понимает английский в целом. К сожалению, многие современные клиенты имеют настройки по умолчанию, которые напоминают это.) Однако, если невозможно найти соответствие по другому языку, и сервер собирается вернуть ошибку «Нет приемлемых вариантов» или вернуться к LanguagePriority, сервер проигнорирует спецификацию подмножества и сопоставит en-GB с документами en. В неявном виде httpd добавит родительский язык в список приемлемых языков клиента с очень низким значением качества. Но обратите внимание, что если клиент запрашивает «en-GB; q=0.9, fr; q=0.8», а сервер имеет документы, обозначенные как «en» и «fr», то документ «fr» будет возвращён. Это необходимо для соблюдения спецификации HTTP/1.1 и для эффективной работы с правильно настроенными клиентами.
Для поддержки расширенных методов (таких как cookies или специальные URL-пути) определения предпочтительного языка пользователя, начиная с httpd 2.0.47, mod_negotiation распознаёт переменную окружения prefer-language. Если она существует и содержит соответствующий тег языка, mod_negotiation попытается выбрать соответствующий вариант. Если такого варианта нет, применяется обычный процесс переговоров.
Пример
SetEnvIf Cookie "language=(.+)" prefer-language=$1 Header append Vary cookie
Расширения прозрачных переговоров по контенту
httpd расширяет протокол прозрачных переговоров по контенту (RFC 2295) следующим образом. Новый элемент {encoding ..} используется в списках вариантов для маркировки вариантов, которые доступны только со специфической кодировкой содержимого. Реализация алгоритма RVSA/1.0 (RFC 2296) расширена, чтобы распознавать закодированные варианты в списке и использовать их как кандидатные варианты, когда их кодировки приемлемы в соответствии с заголовком запроса Accept-Encoding. Реализация RVSA/1.0 не округляет вычисленные коэффициенты качества до 5 десятичных знаков перед выбором лучшего варианта.
Замечание по гиперссылкам и соглашениям об именовании
Если вы используете переговоры по языкам, вы можете выбрать между различными соглашениями об именовании, потому что файлы могут иметь более чем одно расширение, и порядок расширений обычно не имеет значения (подробнее см. документацию mod_mime).
Типичный файл имеет расширение MIME-типа (например, html), возможно, расширение кодировки (например, gz), и, конечно, расширение языка (например, en) когда у нас есть различные языковые варианты этого файла.
Примеры:
- foo.en.html
- foo.html.en
- foo.en.html.gz
Ниже приведены дополнительные примеры имён файлов вместе с допустимыми и недопустимыми гиперссылками:
| Имя файла | Допустимая гиперссылка | Недопустимая гиперссылка |
|---|---|---|
| foo.html.en | foo foo.html | - |
| foo.en.html | foo | foo.html |
| foo.html.en.gz | foo foo.html | foo.gz foo.html.gz |
| foo.en.html.gz | foo | foo.html foo.html.gz foo.gz |
| foo.gz.html.en | foo foo.gz foo.gz.html | foo.html |
| foo.html.gz.en | foo foo.html foo.html.gz | foo.gz |
Глядя на таблицу выше, вы заметите, что всегда можно использовать имя без каких-либо расширений в гиперссылке (например, foo). Преимущество состоит в том, что вы можете скрыть фактический тип документа соответственно файла и можете изменить его позже, например, из html в shtml или cgi без изменения каких-либо ссылок.
Если вы хотите продолжать использовать MIME-тип в ваших гиперссылках (например foo.html), расширение языка (включая расширение кодировки, если оно есть) должно быть справа от расширения MIME-типа (например, foo.html.en).
Примечания по кэшированию
Когда кэш сохраняет представление, он связывает его с URL запроса. В следующий раз, когда этот URL запрашивается, кэш может использовать сохранённое представление. Однако, если ресурс подлежит переговорам на сервере, это может привести к кэшированию только первого запрошенного варианта, а последующие попадания в кэш могут вернуть неверный ответ. Чтобы предотвратить это, httpd обычно маркирует все ответы, возвращаемые после переговоров по контенту, как некэшируемые для клиентов HTTP/1.0. httpd также поддерживает функции протокола HTTP/1.1, чтобы разрешить кэширование результата переговоров.
Для запросов, которые поступают от клиента, совместимого с HTTP/1.0 (это может быть браузер или кэш), директива CacheNegotiatedDocs может быть использована для разрешения кэширования ответов, которые были предметом переговоров. Эта директива может быть указана в конфигурации сервера или виртуального хоста и не принимает аргументов. Она не влияет на запросы от клиентов HTTP/1.1.
Для клиентов HTTP/1.1, httpd отправляет заголовок HTTP-ответа Vary для указания размеров согласования для ответа. Кэши могут использовать эту информацию, чтобы определить, может ли последующий запрос быть обработан из локальной копии. Чтобы побудить кэш использовать локальную копию независимо от размеров согласования, установите переменную окружения force-no-vary переменную среды.
© 2018 The Apache Software Foundation
Licensed under the Apache License, Version 2.0.
https://httpd.apache.org/docs/2.4/en/content-negotiation.html