Написание входных фильтров Apache 2.0


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

Второе отличие заключается в API фильтров. Функций, аналогичных ap_f*, которые мы рассмотрели в прошлой статье, для входных фильтров нет. Это означает, что разработчикам, которые пишут входные фильтры, необходимо обрабатывать блоки и бригады напрямую.
Последнее отличие заключается в порядке изменения данных фильтрами. Для выходных фильтров первым отрабатывал генератор контента, создавая основные данные и передавая эти данные вниз по стеку фильтров. В конце стека фильтров данные отсылались в сеть и пустая бригада возвращалась обратно в стек.
Входные фильтры работают наоборот. После получения данных из сети вызывается функция ap_get_brigade() с пустой бригадой. Затем фильтр передает бригаду следующему фильтру, и так до тех пор, пока пустую бригаду не получит последний фильтр в стеке. После этого последний фильтр заполняет бригаду данными и возвращает ее предыдущему фильтру. Предыдущий фильтр изменяет эти данные и возвращает их, и так до тех пор, пока первоначальная функция не вернет заполненную бригаду.
Осталась одна функция, которую нам нужно рассмотреть - это ap_get_brigade(). Эта функция вызывает следующий фильтр в стеке. Она имеет 4 аргумента:
apr_status_t ap_get_brigade(ap_filter_t *filter, apr_bucket_brigade *bucket, ap_input_mode_t mode, apr_size_t *readbytes);
Первый аргумент - это следующий фильтр в стеке. Второй - это бригада, используемая для хранения данных, полученных из сети. Помните, что при первом вызове функции этот аргумент всегда пустой, и он наполняется только после выполнения ap_get_brigade. Третий аргумент - это режим, определяющий способ чтения данных из фильтра. Есть три значения этого параметра: AP_MODE_BLOCKING, AP_MODE_NONBLOCKING и AP_MODE_PEEK. Два первых понятны по смыслу - чтение из сети происходит либо блоками, либо нет. Третий режим более сложный. После поступления первого запроса серверу необходимо определить, поступит ли следующий запрос от того же сокета. Если запрос поступит, то Apache не отсылает завершающий блок ответа, а ждет пока и второй запрос будет полностью обработан. Это делается для оптимального использования пропускной способности сети. Большинство фильтров могут игнорировать этот параметр, и если он установлен в AP_MODE_PEEK, то сервер возьмет на себя заботу о корректной передаче данных. Последний параметр readbytes, это количество байт, запрошенных из сети на входе, и количество байт переданных в сеть на выходе. Он используется для передачи информации о том, сколько данных доступно для обработки. Если значение равно 0, то входные фильтры вернут одну строку данных.
Теперь, после того как мы изучили основы входных фильтров, давайте взглянем на детали. Как и в предыдущей статье, здесь продемонстрирован модуль, который реализует, ниже описанный, входной фильтр. Этот модуль был создан после Лондонской конференции ApacheCon. На этом мероприятии посетителям раздавались диски. Но появилась одна проблема: дело в том, что диски были созданы под Windows, поэтому все HTML файлы использовали обратные слеши и пробелы в URL вместо переднего слеша и %20. Это сделало диски непригодными для любых платформ кроме Windows. Так как большинство посетителей не использовали Windows, следовательно, они не могли ознакомиться с содержанием диска. Для решения этой проблемы, я создал небольшой модуль Apache 2.0, который корректировал поступающие запросы.
Итак, давайте рассмотрим входной фильтр ApacheCon:
static apr_status_t apcon_filter_in(ap_filter_t *f, apr_bucket_brigade *b,
ap_input_mode_t mode, apr_size_t *readbytes)
{
const char *str;
const char *begin;
int length;
apr_bucket *e;
apr_bucket *d;
char data[256];
int i,j;
Мы начали с объявления всех необходимых фильтру переменных. Смысл каждой из этих переменных станет очевидным, когда мы полностью рассмотрим фильтр.
ap_get_brigade(f->next, b, mode, readbytes);
Я повторю снова, что самый первый вызов в каждом входном фильтре должен быть ap_get_brigade.
e = APR_BRIGADE_FIRST(b);
if (e->type == NULL) {
return APR_SUCCESS;
}
После того как мы получили бригаду, следующая вещь, которую мы должны сделать, это получить первый контейнер в этой бригаде. Если значение type этого контейнера равно NULL, что означает, что данная бригада пустая, то нам остается только вернуть APR_SUCCESS остальным фильтрам.
apr_bucket_read(e, &str, &length, 1);
if (strncmp(“GET “, str, strlen(“GET “))) {
return APR_SUCCESS;
}
apr_bucket_split(e, strlen(“GET “));
e = APR_BUCKET_NEXT(e);
Теперь у нас есть бригада, и она содержит данные. Следующий шаг, который мы должны сделать, это прочитать из контейнера строку данных. В нашем случае фильтр очень простой и обрабатывает только GET запросы. Итак, мы знаем, что у нас есть GET запрос, далее мы разбиваем контейнер таким образом, чтобы только отделить URL и версию HTTP.
apr_bucket_read(e, &str, &length, 1);
/* это должно работать, потому что мы работает только с HTTP/1.0 или HTTP/1.1 */
begin = str + (strlen(str) - 3);
do {
begin--;
} while (strncmp(“HTTP”, begin, 4) && (begin > str));
apr_bucket_split(e, begin - str - 1);
Данный код отделяет URL от HTTP версии. Нас не интересует версия протокола HTTP, но фильтру необходимо отделить URL от всего остального.
apr_bucket_read(e, &str, &length, 1);
i = 0;
j = 0;
while (i < length) {
if (str[i] == ‘ ‘) {
data[j++] = ‘%’;
data[j++] = ‘2′;
data[j++] = ‘0′;
i++;
}
else if (str[i] == ‘\\’) {
data[j++] = ‘/’;
i++;
} else {
data[j++] = str[i++];
}
}
Тут мы изменяем URL и копируем его в новую строку, заменяя пробелы на “%20″ и “\” на “/”.
d = apr_bucket_transient_create(data, j);
apr_bucket_setaside(d, f->c->pool);
APR_BUCKET_INSERT_AFTER(e, d);
APR_BUCKET_REMOVE(e);
apr_bucket_destroy(e);
return APR_SUCCESS;
}
В завершение мы записываем новый URL в контейнер и вставляем этот контейнер в бригаду. Этот фильтр немного неточен, но это простительно, потому что главной его задачей было обучение. В нем мы используем временный (transient) контейнер, а затем вызываем apr_bucket_setaside. Это сделано для того, чтобы показать механизм работы apr_bucket_setaside. Вставка контейнера производится после вставки первоначального контейнера, который затем удаляется.
Для тестирования этого модуля необходимо сконфигурировать сервер с опцией -with-module=filters:/path/to/mod_apachecon. Это скопирует файлы модуля в дерево исходников сервера и добавит его при сборке системы. Этот фильтр запускается автоматически и обрабатывает каждый запрос. Для проверки его работы можно подключиться по telnet к серверу и отправить некоторый запрос:
GET \foo.html HTTP/1.0
Теперь убедитесь, что вы получили файл foo.html в вашу DocumentRoot директорию.
В следующий раз мы побеседуем о том как писать модули, которые могут быть расширены другими модулями.
Автор: Райн Блум (Ryan Bloom)
Перевод: Сипягин Максим
Оригинал документа (en)

These icons link to social bookmarking sites where readers can share and discover new web pages.
  • News2.ru
  • NewsLand.ru
  • del.icio.us
  • BobrDobr.ru
  • Ma.gnolia
  • Digg
  • Reddit
  • Technorati
  • Slashdot
  • Netscape
  • DZone
  • ThisNext
  • Furl
  • YahooMyWeb
Опубликовано в: Разработка модулей Февраль 4, 2006

5 Комментариев »

  1. Что за перевод, запутал всех..
    Хорошо что прочитал оригинал:
    Теперь убедитесь, что вы получили файл foo.html в вашу DocumentRoot директорию.
    Just be sure that you have a file named “foo bar” in your DocumentRoot directory.
    Перводится как,
    Только проверьте что у вас есть файл foo bar в DocRoot.
    Ужжссс…. Если и остальное такое же..

    Комментарий от Alex — Январь 14, 2008 @ 6:49 pm

  2. Спасибо огромное зоздателям сайта!!!! Сколько полезного!!! РЕСПЕКТ!!!
    __________________
    http://notfromhere.info/

    Комментарий от андрюха — Август 29, 2008 @ 1:57 pm

  3. oneoneone
    ___
    тем кто “зарабатывает”: писать в комменте сайт ненадо

    Комментарий от Никита — Октябрь 18, 2008 @ 5:51 pm

  4. ИМЕННО!

    Комментарий от Глеб — Октябрь 18, 2008 @ 5:51 pm

  5. +1

    Комментарий от Матвей — Октябрь 18, 2008 @ 5:52 pm

Оставить комментарий

You must be logged in to post a comment.

Работает на WordPress