Как это работает
В этом документе описан алгоритм, по которому
Apache-RUS определяет в какой кодировке документ должен быть отдан клиенту.
В каком-то смысле это повторение
описания директив конфигурации, но порядок
изложения отвечает именно поведению сервера, при этом подробное описание
директив опущено. В документе описана текущая версия
(PL20-PL24). Конфигурация (в т.ч. и названия директив) старых версий
(PL16 и более старых) отличается и описана в
отдельном документе. В тех местах, где
поведение новых и старых версий сервера отличается принципиально сделаны
специальные оговорки.
В дальнейшем тексте термины charset и кодировка используются практически
как синонимы.
Предварительные замечания
Основное назначение модуля, отвечающего за перекодировки, - произвести
корректное преобразование из "charset на диске"
(кодировка хранения) в "charset у клиента" (кодировка передачи)
при передаче документа клиенту и произвести обратное преобразование при
приеме информации от
клиента (submit формы и т.п.). Все возможные способы таких преобразований
должно быть описано в конфигурации
сервера директивами CharsetDecl
(объявление серверу о существовании кодовой таблицы) и
CharsetRecodeTable
(описание преобразования из одной кодировки в другую). Все имеющиеся
кодировки и преобразования могут быть описаны только в конфигурации
сервера/виртуального сервера. Описание директив CharsetDecl и
CharsetRecodeTable в .htaccess/<Directory> не допускается по
очевидной причине - такое описание требует, чтобы сервер переинициализировал
таблицы перекодировки при каждом обращении к такой директории, что ведет
к очень большому количеству лишних действий. Все прочие директивы
Charset... могут быть указаны где угодно.
Кодировка хранения (т.е. та, в которой файлы хранятся на диске) должна быть
указана (возможно, отдельно для каждой директории) директивой
CharsetSourceEnc
(описывает все файлы в директории) или директивой
CharsetByExtension. Директива
CharsetByExtension имеет больший приоритет.
Определение кодировки клиента по Accept-Charset:/Accept
В случае, если в HTTP-заголовках присутствует заголовок
Accept-Charset: SomeCharset или Accept: text/x-cyrillic-SomeCharset, и как
минимум один из запрошенных charset известен
серверу (т.е. описан в директивах
CharsetDecl
или CharsetAlias), то сервер выдает
документ в соответствии с запрошенным charset. Если серверу известны
несколько из запрошенных charset, то будет выбран имеющий наибольший
приоритет. Если несколько charset в запросе имеют одинаковый наибольший
приоритет, то будет выбран тот из них, который раньше описан в директиве
CharsetPriority.
Если при этом отсутствует и директива CharsetPriority, то
результат выбора из этих charset с наибольшим приоритетом не определен.
Если в заголовке Accept-Charset (Accept) указаны только charset неизвестные
серверу и не указан wildcard (*), то поведение сервера зависит от флага
CharsetErrReject. Если этот
флаг включен (On), то клиенту вернут сообщение об ошибке. Если выключен -
сервер попробует определить charset клиента по прочим параметрам.
Полностью отключить распознавание кодировки клиента по заголовку
Accept-Charset невозможно - это нарушало бы стандарт HTTP.
Однако существуют частные случаи, когда действие Accept-Charset нужно
выключить, например Netscape Communicator 4.x настроенный по-умолчанию
шлет заголовок "Accept-Charset: iso-8859-1,*,utf-8",
соответственно если у вас описан Charset iso-8859-1, то пользователю
с NC 4.x будет всегда показан именно iso-8859-1. Для отключения
распознавания Accept-Charset в таких конкретных случаях может
быть использована директива
CharsetBrokenAccept.
Определение кодировки клиента по прочим параметрам
Если заголовки AcceptCharset/Accept в запросе отсутствуют, либо по ним не
удалось произвести выбор кодировки, то сервер попробует определить
кодировку пользователя по трем параметрам:
- По номеру порта (работает начиная с версии PL20.2). В случае, когда
TCP-port к которому идет обращение совпадает с одним из описанных
директивой CharsetByPort,
то будет выбрана эта кодировка.
- По Hostname сервера. В случае, если hostname сервера (виртуального
сервера) начинается с названия charset
(CharsetDecl) или с алиаса
Charset
(CharsetAlias),
то в качестве клиентской кодировки будет выбрана эта кодировка.
- По префиксу URL. Если URL начинается с /charset-name/path/to/file.html
или с /~user/charset-name/path/to/file.html, то будет выбрана эта
кодировка.
- По программному обеспечению пользователя (HTTP-заголовку User-Agent).
Если в заголовке User-Agent есть подстрока, описанная в директиве
CharsetAgent, то будет выбрана
соответствующая кодировка. Если совпадающих подстрок несколько, то
сработает самая длинная. Если совпадающих подстрок несколько и они имеют
одинаковую длину, то выбор из них не определен.
Порядок срабатывания этих способов определяется директивой
CharsetSelectionOrder
(В версиях до PL16 можно было менять местами только порядок срабатывания
Directory Prefix/UserAgent и выключать (по отдельности) Hostname prefix и
Directory Prefix).
Необходимая "строгость" совпадения Hostname сервера/префикса
filename с именем/алиасом какой-то кодировки может быть отрегулирована
директивой
CharsetStrictURIMatch.
В режиме "Off" (умолчание) выбор кодировки по
Hostname/Directory производится сервером в случае совпадения начала
имени сервера/имени директории с названием/алиасом какого-то charset.
В режиме "On" производится более строгая проверка - с именем
charset должны совпасть полное имя сервера или его host part (для выбора
по hostname) и, соответственно, полное название директории (для выбора
по имени директории).
Если у сервера ничего не получилось
Если сервер не сумел определить кодировку клиента, то документ будет отдан в
кодировке, определенной директивой
CharsetDefault.
Если CharsetDefault не указана, то будет использован charset,
описанный первым в директиве
CharsetPriority, а если нет и
этой директивы, то charset, описанный первой директивой
CharsetDecl.
SSI
Т.к. все директивы (кроме CharsetDecl и CharsetRecodeTable)
могут быть указаны где угодно, то это позволяет хранить документы в любой
смеси кодировок. Некоторые осложнения могут вызвать ServerSideIncludes.
Правило простое - на файл (в том числе включаемый через SSI) действуют
правила той директории, где он физически находится.
HTTP-заголовок Content-Type: text/html; charset=...
На выдачу сервером ; charset=CharsetName в заголовке
Content-Type: влияет установлена ли директива
CharsetUseMultiViews.
Если она установлена в On, то charest=... выдается при соблюдении
таких трех условий одновременно:
- Броузер клиента не является
"Bad Agent"
- Опция MultiViews (поддержка многоязыковой выдачи) включена (On)
- Язык документа, описанный через директиву AddLanguage, совпадает
с языком Charset, описанным директивой
CharsetDecl.
Если опция CharsetUseMultiViews выключена (Off), то charset=....
выдается для всех документов.