Apache. Документация на русском


Разделы:   1    2    3    4    5    6    7    8    9    10      11      12    13    14    15    16  

Раздел 11. Документация для разработчиков

Пункты:   214    215    216    217    218    219    220      221      222    223  

 <         > 
  RU            EN  

Пункт 221. Как работают фильтры в версии 2.x

Это задание вырезано и вставлено из электронного письма (<022501c1c529$f63a9550$7f00000a@KOJ>) и переформатировано только для лучшей читабельности. Он не актуален, но может стать хорошим началом для дальнейших исследований.

Типы фильтров

Существует три основных типа фильтров (каждый из них на самом деле разбит на две категории, но об этом позже).

CONNECTION
Фильтры этого типа действуют в течение всего времени жизни этого соединения. ( AP_FTYPE_CONNECTION , AP_FTYPE_NETWORK )
PROTOCOL
Фильтры этого типа действительны в течение всего времени жизни этого запроса с точки зрения клиента, это означает, что запрос действителен с момента отправки запроса до момента получения ответа. ( AP_FTYPE_PROTOCOL , AP_FTYPE_TRANSCODE )
RESOURCE
Фильтры этого типа действительны в течение времени, пока этот контент используется для удовлетворения запроса. Для простых запросов это идентично PROTOCOL , но внутренние перенаправления и подзапросы могут изменять содержимое без завершения запроса. ( AP_FTYPE_RESOURCE , AP_FTYPE_CONTENT_SET )

Важно различать протокол и фильтр ресурсов. Фильтр ресурсов привязан к конкретному ресурсу, также может быть привязан к информации заголовка, но основная привязка — к ресурсу. Если вы пишете фильтр и хотите знать, является ли он ресурсом или протоколом, правильный вопрос будет следующим: «Можно ли удалить этот фильтр, если запрос перенаправляется на другой ресурс?» Если ответ да, то это фильтр ресурсов. Если нет, то, скорее всего, дело в протоколе или фильтре соединения. Я не буду вдаваться в фильтры соединений, потому что они кажутся хорошо понятными. С этим определением могут помочь несколько примеров:

Диапазон байтов
Мы закодировали его так, чтобы он вставлялся для всех запросов и удалялся, если не использовался. Поскольку этот фильтр активен в начале всех запросов, его нельзя удалить, если он перенаправлен, так что это фильтр протокола.
http_header
Этот фильтр фактически записывает заголовки в сеть. Очевидно, что это обязательный фильтр (за исключением особого случая asis, который будет рассмотрен ниже), и, следовательно, это фильтр протокола.
Выкачать
Администратор настраивает этот фильтр в зависимости от того, какой файл был запрошен. Если мы делаем внутреннее перенаправление со страницы автоиндекса на страницу index.html, фильтр deflate может быть добавлен или удален в зависимости от конфигурации, так что это фильтр ресурсов.

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

Как вставляются фильтры?

На самом деле это довольно просто в теории, но код сложен. Прежде всего, важно, чтобы все понимали, что для каждого запроса есть три списка фильтров, но все они объединены вместе:

  • r->output_filters (соответствует РЕСУРСУ)
  • r->proto_output_filters (соответствует ПРОТОКОЛУ)
  • r->connection->output_filters (соответствует ПОДКЛЮЧЕНИЮ)

Раньше проблема заключалась в том, что мы использовали односвязный список для создания стека фильтров и начинали с «правильного» места. Это означает, что если бы у меня был RESOURCE фильтр в стеке, и я добавил бы CONNECTION фильтр, CONNECTION фильтр был бы проигнорирован. Это должно иметь смысл, потому что мы бы вставили фильтр подключения вверху списка c->output_filters , но конец r->output_filters указывал на фильтр, который раньше был перед c->output_filters . Это явно неправильно. В новом коде вставки используется двусвязный список. Это имеет то преимущество, что мы никогда не теряем вставленный фильтр. К сожалению, это идет с отдельным набором головных болей.

Проблема в том, что у нас есть два разных случая, когда мы используем подзапросы. Первый — вставить больше данных в ответ. Второй — заменить существующий ответ внутренним перенаправлением. Это два разных случая, и рассматривать их нужно соответственно.

В первом случае мы создаем подзапрос из обработчика или фильтра. Это означает, что в функцию должен быть передан следующий фильтр make_sub_request , а последний ресурсный фильтр в подзапросе будет указывать на следующий фильтр в основном запросе. Это имеет смысл, поскольку данные подзапроса должны проходить через тот же набор фильтров, что и основной запрос. Графическое представление может помочь:

 Default_handler --> include_filter --> диапазон байтов --> ... 

Если фильтр вложений создает подзапрос, то мы не хотим, чтобы данные из этого подзапроса проходили через фильтр вложений, потому что это могут быть не данные SSI. Итак, подзапрос добавляет следующее:

 Default_handler --> include_filter --> диапазон байтов --> ...
 /
Default_handler --> sub_request_core 

Что произойдет, если подзапросом будут данные SSI? Ну, это просто, это includes_filter фильтр ресурсов, поэтому он будет добавлен к подзапросу между Default_handler и sub_request_core фильтром.

Второй случай для подзапросов — это когда один подзапрос станет настоящим запросом. Это происходит всякий раз, когда подзапрос создается вне обработчика или фильтра, и NULL передается функции в качестве следующего фильтра make_sub_request .

В этом случае фильтры ресурсов больше не имеют смысла для нового запроса, поскольку ресурс изменился. Таким образом, вместо того, чтобы начинать с нуля, мы просто указываем начало фильтров ресурсов для подзапроса на начало фильтров протокола для старого запроса. Это означает, что мы не потеряем ни один из фильтров протоколов и не попытаемся отправить эти данные через фильтр, который их не должен видеть.

Проблема в том, что сейчас мы используем двусвязный список для наших стеков фильтров. Но вы должны заметить, что в этой модели два списка могут пересекаться. Итак, вы обрабатываете предыдущий указатель? На этот вопрос очень сложно ответить, потому что нет «правильного» ответа, любой метод одинаково действителен. Я посмотрел, почему мы используем предыдущий указатель. Единственная причина этого — облегчить добавление новых серверов. При этом решение, которое я выбрал, состояло в том, чтобы предыдущий указатель всегда оставался на исходном запросе.

Это вызывает более сложную логику, но работает во всех случаях. Меня беспокоит то, что он переместится в подзапрос, заключается в том, что для более распространенного случая (где подзапрос используется для добавления данных к ответу) основная цепочка фильтров будет неправильной. Мне это не показалось хорошей идеей.

Как есть

Финальная тема. :-) Mod_Asis немного хак, но обработчик должен удалить все фильтры, кроме фильтров соединения, и отправить данные. Если вы используете mod_asis , все остальные ставки отключены.

Пояснения

Абсолютно последний момент заключается в том, что причина, по которой этот код было так трудно исправить, заключалась в том, что мы так много взломали, чтобы заставить его работать. Я написал большинство хаков изначально, так что я очень виноват. Однако теперь, когда код правильный, я начал удалять некоторые хаки. Большинство людей должны были видеть, что функции reset_filters и add_required_filters исчезли. Эти вставленные фильтры уровня протокола для условий ошибки, по сути, обе функции делали одно и то же, одна за другой, это было действительно странно. Поскольку мы больше не теряем фильтры протоколов для случаев ошибок, эти взломы исчезли. Все фильтры HTTP_HEADER , Content-length и Byterange добавляются на insert_filters этапе, потому что, если бы они были добавлены раньше, у нас было бы несколько интересных взаимодействий. Теперь все это можно было переместить для вставки с помощью фильтров HTTP_IN , CORE и CORE_IN . Это упростило бы следование коду.



 <         > 

Пункты:   214    215    216    217    218    219    220      221      222    223  

Рейтинг@Mail.ru