После того, как заглянул в убогий живот системы обработки запросов Apache, оказалось, что документация несколько вводит в заблуждение ... Но прежде чем я углублюсь в объяснение, из того, что я могу сказать, вы зависите от Apache в этомone.
Проблема клиента
Во-первых, имя заголовка не будет добавлено в заголовок ответа Vary , если оно не отправлено клиентом.Это связано с тем, как mod_rewrite
создает значение для этого заголовка внутренне .
Он ищет заголовок по имени, используя apr_table_get()
, таблицу заголовков запросаи имя, которое вы указали:
const char *val = apr_table_get(ctx->r->headers_in, name);
Если name
не является ключом в таблице, эта функция вернет NULL
.Это проблема, потому что сразу после этого проверка по val
:
if (val) {
// Set the structure member ctx->vary_this
}
ctx->vary_this
используется на основе на RewriteCond
для накопления имен заголовков, которые должны быть собраны в финал Варьируется заголовок *.Поскольку при отсутствии значения назначение или добавление не произойдет, ссылочный (но не отправленный) заголовок никогда не появится в Vary
.В документации явно не говорится об этом, поэтому это может быть, а может и не быть тем, что вы ожидали.
* Кроме того, флаг NV
(без изменений) и функция игнорирования при сбое реализованы путем установки ctx->vary_this
в NULL
, предотвращая его добавление в заголовок ответа.
Однако, возможно, вы отправили Accept-Language , но оно было пустым.В этом случае пустая строка пройдет вышеупомянутую проверку, и имя заголовка будет добавлено к Vary на mod_rewrite
из описанного выше.Имея это в виду, я использовал следующий запрос для диагностики происходящего:
User-Agent: Fiddler
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language:
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Host: 129.168.0.123
Это тоже не работает, но почему?mod_rewrite
определенно устанавливает заголовки, когда правило и условие совпадают (ctx->vary
представляет собой совокупность ctx->vary_this
для всех проверенных условий):
if (ctx->vary) {
apr_table_merge(r->headers_out, "Vary", ctx->vary);
}
Это можно проверить с помощью оператора журнала, и r->headers_out
- это переменная, используемая при создании заголовков ответа.Если что-то определенно идет не так, то после выполнения правил могут возникнуть проблемы.
Проблема .htaccess
В настоящее время вы, похоже, определяете свои правила в .htaccess
, или<Directory>
раздел.Это означает, что mod_rewrite
работает в фазе исправления Apache, и механизм, который он использует, чтобы фактически выполнить переписывания здесь, очень запутан.Давайте на секунду предположим, что внешнего перенаправления не существует, поскольку у вас возникла проблема даже без него (и я перейду к проблеме с перенаправлением позже).
После того, как вы выполните перезапись, уже слишком позднообработка запроса для модуля, чтобы фактически отобразить в файл.Вместо этого он назначает себя обработчиком «содержимого» запроса, и когда запрос достигает этой точки, он выполняет вызов ap_internal_redirect()
.Это приводит к созданию нового объекта запроса, который не содержит таблицы headers_out
из оригинала.
При условии, что mod_rewrite
не вызывает дальнейших перенаправлений, ответ генерируется из новый объект запроса, которому никогда не будут назначены соответствующие (оригинальные) заголовки.Можно обойти это, работая в контексте сервера (в основной конфигурации или в <VirtualHost>
), но ...
Проблема перенаправления
К сожалению, этооказывается, что это в значительной степени не имеет значения, так как даже если мы используем mod_rewrite
в контексте сервера, путь, который получает ответ в случае перенаправления, все равно вызывает заголовки, которые отбрасывает установленный модуль.
Когда Apache получает запрос, через цепочку вызовов функций он переходит к ap_process_request()
.Это, в свою очередь, вызывает ap_process_request_internal()
, где происходит основная часть важных шагов анализа запроса (включая вызов mod_rewrite
).Он возвращает целочисленный код состояния, который в случае вашего перенаправления устанавливается на 301.
Большинство запросов возвращают OK
(который имеет значение 0), что сразу приводит к ap_finalize_request_protocol()
.Однако это здесь не так :
if (access_status == OK) {
ap_finalize_request_protocol(r);
}
else {
r->status = HTTP_OK;
ap_die(access_status, r);
}
ap_die()
выполняет некоторые дополнительные манипуляции (например, возвращает код ответа обратно на 301), и в этом конкретном случае заканчивается вызовомдо ap_send_error_response()
.
К счастью, это, наконец, корень проблемы.Хотя может показаться, что это не «задом наперед», и это приводит к разрушению первоначальных заголовков.Есть даже комментарий об этом в источнике :
if (!r->assbackwards) {
apr_table_t *tmp = r->headers_out;
/* For all HTTP/1.x responses for which we generate the message,
* we need to avoid inheriting the "normal status" header fields
* that may have been set by the request handler before the
* error or redirect, except for Location on external redirects.
*/
r->headers_out = r->err_headers_out;
r->err_headers_out = tmp;
apr_table_clear(r->err_headers_out);
if (ap_is_HTTP_REDIRECT(status) || (status == HTTP_CREATED)) {
if ((location != NULL) && *location) {
apr_table_setn(r->headers_out, "Location", location);
}
//...
}
//...
}
Обратите внимание, что r->headers_out
заменено, и исходная таблица очищена.Эта таблица содержала всю информацию, которая должна была появиться в ответе, поэтому теперь она потеряна.
Заключение
Если вы не перенаправляете и определяете правила в-сервер сервера, кажется, все работает правильно.Тем не менее, это не то, что вы хотите.Я вижу возможный обходной путь, но я не уверен, что это будет приемлемо, не говоря уже о необходимости перекомпилировать сервер.
Что касается Vary: Accept-Encoding
, я могу только предположить, что он исходит отдругой модуль, который ведет себя так, что позволяет заголовку пробираться.Я также не уверен, почему у Gumbo не было проблем при его попытке.
Для справки я искал исходный код магистрали 2.2.14 и 2.2 , и я модифицировал и запустил Apache 2.2.15.Похоже, что нет никаких существенных различий между версиями в соответствующих разделах кода.