Обробка HTTP запитів це основне завдання в більшості мережевих програм. У цій статті ми розповімо про обробку запитів в Apache, і про те, як модулі можуть додавати власні хуки в обробку запиту при створенні різних прикладних програм і компонентів. Стаття повинна допомогти розробникам в освоєнні роботи з модулями Apache, і надати вам необхідні знання для кращої роботи з документованою API і кодом Apache.

В архітектуру Apache входить: просте ядро, платформо-залежна рівень (APR), і модулі. Будь-який додаток для Apache — навіть найпростіше, обсуживающее «дефолтну» сторінку Apache «It worked» — використовує кілька модулів. Користувачі Apache не потребують знання цього, а для розробника програм, розуміння модулів і API модуля Apache є ключем до роботи з Apache. Більшість, але не всі модулі, пов’язані з різними аспектами обробки НТТР запиту. Досить рідко зустрічається, що модулю необхідно працювати з кожним аспектом HTTP: як це робить httpd (Apache). Перевага модульного підходу полягає в тому, що він дозволяє фокусувати модуль на специфічну задачу, ігноруючи при цьому інші аспекти НТТР, що не стосуються даної задачі.

У цій статті ми розповімо про архітектуру обробки запиту в Apache, і покажемо, як модуль може перехопити контроль над різними частинами циклу обробки запиту.

Найпростіша формулювання веб-сервер — це програма, яка очікує HTTP запити і повертає відповіді, при отриманні запиту. Це основне завдання в Apache, так зване ядро веб-сервера. Для кожного HTTP запиту повинен запускатися генератор контенту. Деякі модулі можуть реєструвати контенту генератори, визначаючи функції, що посилаються на обробник, який може бути налаштований директивами SetHandler або AddHandler в httpd.conf. Ті запити, для яких не надається генератор певного модуля, обробляються стандартним генератором, який просто повертає запитаний файл безпосередньо з файлової системи. Модулі, які реалізують один або більше генераторів контенту, відомі як генератори контенту або обробляють модулі.

В принципі, генератор контенту може використовувати всі функції веб-сервера: наприклад, CGI програма отримує запит і генерує відповідь, і може отримати повний контроль над тим, що статися за цей час. Але, за аналогією з іншими веб-серверів Apache розбиває запит на різні фази. Так, наприклад, він перевіряє, авторизований користувач для роботи перед тим, як генератор контенту видасть результат.

Ось кілька фаз обробки запиту до моменту генерації контенту. Вони використовуються для перевірки і можливо змін заголовків запиту, і визначають, що робити із запитом. Наприклад:

URL запиту потрібно порівняти з даними конфігурації, щоб визначити, який генератор контенту повинен використовуватися.

Треба визначити файл, на який посилається URL запиту. URL може звертатися як до статистичного файлу, так і до CGI скрипту, або до чого-небудь ще, що може використовуватися для генерації контенту.

Якщо контент доступний, mod_negotiation знайде ту версію ресурсу, яка краще підходить до налаштувань браузера. Наприклад, сторінки довідки Apache виводяться на тій мові, на якому надійшов запит від браузера.

Правила доступу та ідентифікації модулів перевіряються на відповідність правилам доступу сервера, і визначається, чи має право користувач отримати те, що він запросив.

mod_alias або mod_rewrite можуть змінити URL в запиті.
На додаток, є ще фаза логування запиту, яка виконується після того, як генератор контенту пошле браузеру відповідь.

Модуль може впровадити свої власні обробники в будь-який з цих хуків. Модулі, які обробляють дані на фазах до генерації контенту, відомі як модулі метаданих. Ті, які працюють з логированием, відомі як модулі логування.

Що ми описали вище — це по суті архітектура будь-якого веб-сервера. Різниця лише в деталях, але фази обробки запиту: метадані->генератор контенту->логування є загальними.

Головне нововведення в Apache 2 це трансформація його з простого веб-сервер (Apache 1.3 та інші) в потужну платформу, що представляє собою ланцюжок фільтрів. Вона може бути зображено як вісь даних, перпендикулярна осі обробки запиту. Запитані дані можуть обробитися вхідними фільтрами до генератора контенту, а відповідь сервера може обробитися вихідними фільтрами до відправки клієнтові. Фільтри дозволяють зробити попередню фільтрацію і більш ефективно представити дані обробки, відокремлюючи ці фази від генерації контенту. Приклад фільтрів: включення на стороні сервера (SSI), XML і XSLT обробка, gzip компресія і шифрування (SSL).

Перед початком обговорення того, як модуль впроваджується в будь стадії обробки запиту/даних, давайте зупинимося на одному моменті, який часто стає причиною плутанини серед початківців розробників модулів Apache: а саме -порядок обробки.

Вісь обробки запиту лінійна: фази відбуваються в строгому порядку. Але плутанина виникне на осі даних. Для максимальної ефективності — порядок змінюється, тому генератор і фільтри не виконуються в строго заданої послідовності. Так, наприклад, в загальному, ви не зможете передати що-небудь у вхідний фільтр і чекати отримання цього в генераторі або у вихідних фільтрах.

В центрі обробки знаходиться генератор контенту, який відповідає за одержання даних з стека вхідних фільтрів і поміщає дані в стек вихідних фільтрів. Коли генератор або фільтр потребують обробці запиту цілком, вони повинні зробити це до передачі даних вниз по ланцюжку (генератора або вихідним фільтрів), або до повернення даних назад вхідного фільтру.

Тепер у нас є загальне уявлення про обробці запиту в Apache, і ми можемо продовжити говорити про те, як модулі стають частиною цієї обробки. Структура модуля apache описується декількома (необов’язково) полями даних і функціями:

module AP_MODULE_DECLARE_DATA my_module = {
STANDARD20_MODULE_STUFF,
my_dir_conf,
my_dir_merge,
my_server_conf,
my_server_merge,
my_cmds,
my_hooks
};

Функція модуля, яка створює хуки обробки запитів, остання в цій структурі:

static void my_hooks(apr_pool_t* pool) {
/* створення необхідних хуків обробки запиту */
}

Залежно від того, в яких частинах запиту зацікавлений наш модуль, нам потрібно створити відповідні хуки. Наприклад, модуль, який реалізує генератор контенту (обробника), потребує хука обробки (handler hook), на зразок:

ap_hook_handler(my_handler, NULL, NULL, APR_HOOK_MIDDLE);

Тепер my_handler буде викликаний, коли обробка запиту дійде до фази генерації контенту. Хуки інших фаз запиту схожі; Також іноді використовують один із:

ap_hook_post_read_request Перший шанс для обробки запиту після його прийняття.
ap_hook_fixups Останній шанс обробки запиту перед генерацією контенту.
ap_hook_log_transaction Хук логування.
Між хуками post_read_request і fixups є кілька інших хуков, створених для певних цілей: наприклад модулі доступу та ідентифікації мають хуки перевірки доступу. Всі хуки мають точно такий же вигляд, як і хук обробки. Для детальної інформації, дивіться http_config.h

Прототип обробника будь фази:

static int my_handler(request_rec* r) {
/* обробка запиту */
}

request_rec це головна структура даних apache, що зберігає всі дані НТТР запиту.

Обчислене значення my_handler може бути наступним:

OK
my_handler обробив запит успішно. Фаза обробки завершена.
DECLINED
my_handler не зацікавлений у запиті. Дозволимо іншим обробників розібратися з ним.
Деякий НТТР код
Сталася помилка, поки оброблявся запит. Це змінює шлях обробки запиту: нормальна обробка припинена, і сервер натомість повертає ErrorDocument.

Фільтри також реєструються у функціях my_hooks, але API трохи інший:

ap_register_output_filter(«my-output-filter-name», my_output_filter,
NULL, AP_FTYPE_RESOURCE) ;

ap_register_input_filter(«my-input-filter-name», my_input_filter,
NULL, AP_FTYPE_RESOURCE) ;

з наступними прототипами функцій фільтрів

static apr_status_t my_output_filter(ap_filter_t* f, apr_bucket_brigade* bb) {
/* Читання блоку даних, обробка і передача наступного фільтру*/
return APR_SUCCESS;
}

static apr_status_t my_input_filter(ap_filter_t* f, apr_bucket_brigade* bb,
ap_input_mode_t mode, apr_read_type_e block, apr_off_t nbytes) {
/* отримання від блоку фільтра, обробка, повернення блоку в bb*/
return APR_SUCCESS;
}

Опції фільтрів повертають APR_SUCCESS якщо все нормально, або явно, подібно перерахованим вище, або як код повернення від фільтра через виклик ap_pass_brigade або ap_get_brigade. Документація API знаходиться в util_filter.h.

Центральна структура, яка описує НТТР запит — це request_rec. Вона створюється, коли Apache приймає запит, і вона надається всіх функцій обробки запиту, як показано вище в прототипі my_handler. У фільтрі контенту, request_rec доступний як f->r.

request_rec це величезна структура, яка, прямо або побічно, всі дані, необхідні у процесі обробки запиту. Будь обробник метаданих працює через отримання і зміна полів у request_rec; так надходять генератор контенту і фільтри, також поводяться додаткові процеси I/Про і обробник логування. Для повного опису, дивіться заголовковий файл httpd.h.

Ми закінчуємо цю статтю, коротко розповівши про використання request_rec. Вам необхідно також подивитися на API або інші статті, де описані деталі використання цієї структури.

Ось невеликий список відповідей на часті питання:

Пул запиту r->pool доступний для всіх створених ресурсів і має час життя запиту.

Заголовки запиту і відповіді доступні в r->headers_in і r->headers_out відповідно. Вони мають тип apr_table_t і обробляються apr_table функціями, такі як apr_table_get і apr_table_set.

Поле обробника визначає, який обробник зараз в дії. Генератори контенту повинні перевіряти його і негайно повертати DECLINED, якщо він не підходить.

Поля input_filter і output_filter можуть бути використані як I/O дескриптори, тільки в модулі-фільтрі. Високо-рівневі I/O (перенесено з apache 1.x) доступні для генераторів контенту, але не для фільтрів.

Директиви конфігурації надаються в поле per_dir_config, і можуть бути отримані через використання ap_get_module_config (також ap_set_module_config, хоча це має бути дуже недоречно ст час обробки запиту).

Інші структури даних ядра доступні як r->connection (структура сполуки), r->server (структура сервера), і т. п. Їх конфігурації доступні за запитом.

Додаткове поле конфігурації request_config оновлюється для кожного запиту, і зберігає ці дані запиту між фазами обробки поточного запиту.