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  

Пункт 216. Разработка модулей для Apache HTTPD 2.4

В этом документе объясняется, как разрабатывать модули для Apache HTTP Server 2.4.

Введение

Что мы будем обсуждать в этом документе

В этом документе обсуждается, как вы можете создавать модули для Apache HTTP Server 2.4, исследуя пример модуля с именем mod_example . В первой части этого документа целью этого модуля будет вычисление и распечатка различных значений дайджеста для существующих файлов на вашем веб-сервере всякий раз, когда мы обращаемся к URL-адресу http://hostname/filename.sum . Например, если мы хотим узнать значение дайджеста MD5 файла, расположенного по адресу http://www.example.com/index.html , мы должны посетить http://www.example.com/index.html.sum .

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

Предпосылки

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

Наконец, вам необходимо иметь общее представление о том, как модули загружаются и настраиваются на HTTP-сервере Apache, а также о том, как получить заголовки для Apache, если у вас их еще нет, поскольку они необходимы для компиляции новых модулей.

Компиляция вашего модуля

Чтобы скомпилировать исходный код, который мы создаем в этом документе, мы будем использовать APXS. Предполагая, что ваш исходный файл называется mod_example.c, скомпилировать, установить и активировать модуль так же просто, как:

 apxs -i -a -c mod_example.c 

Определение модуля

Теги имени модуля
Каждый модуль начинается с одного и того же объявления или тега имени, если хотите, который определяет модуль как отдельный объект в Apache :

 модуль AP_MODULE_DECLARE_DATA пример_модуля =
{
 STANDARD20_MODULE_STUFF,
 create_dir_conf, /* Обработчик конфигурации для каждого каталога */
 merge_dir_conf, /* Обработчик слияния для конфигураций для каждого каталога */
 create_svr_conf, /* Обработчик конфигурации для каждого сервера */
 merge_svr_conf, /* Обработчик слияния для серверных конфигураций */
 директивы, /* Любые директивы, которые могут быть у нас для httpd */
 register_hooks /* Наша функция регистрации хука */
}; 

Этот фрагмент кода сообщает серверу, что мы зарегистрировали новый модуль в системе и что его имя example_module . Имя модуля используется в основном для двух целей:

  • Сообщите серверу, как загрузить модуль, используя LoadModule
  • Настройка пространства имен для модуля, который будет использоваться в конфигурациях

На данный момент нас интересует только первая цель имени модуля, которая вступает в игру, когда нам нужно загрузить модуль:

 LoadModule example_module modules/mod_example.so 

По сути, это говорит серверу открыть mod_example.so и искать модуль с именем example_module .

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

Начало работы: подключение к серверу

Знакомство с хуками

При обработке запросов в Apache HTTP Server 2.4 первое, что вам нужно сделать, — это создать хук в процессе обработки запросов. Хук — это, по сути, сообщение, сообщающее серверу, что вы готовы либо обслужить, либо, по крайней мере, взглянуть на определенные запросы, данные клиентами. Все обработчики, будь то mod_rewrite, mod_authn_*, mod_proxy и так далее, подключаются к определенным частям процесса запроса. Как вы, наверное, знаете, модули служат разным целям; Некоторые из них являются обработчиками аутентификации/авторизации, другие — обработчиками файлов или сценариев, а третьи модули переписывают URI или прокси-контент. Кроме того, в конце концов, от пользователя сервера зависит, как и когда каждый модуль будет установлен на свое место. Таким образом, сам сервер не предполагает знать, какой модуль отвечает за обработку конкретного запроса, и будет спрашивать каждый модуль, заинтересованы ли они в данном запросе или нет. Затем каждый модуль должен либо мягко отклонить запрос, либо принять его, либо категорически отказать в обслуживании запроса, как это делают модули аутентификации/авторизации
Обработка хуков в httpd
: клиент запрашивает контент, который мы должны обрабатывать или нет, сервер имеет директивы для подсказки модулям, нужна ли их помощь или нет. Два из них AddHandler и SetHandler . Давайте рассмотрим пример с использованием AddHandler . В нашем примере мы хотим, чтобы каждый запрос, оканчивающийся на .sum, обслуживался mod_example , поэтому мы добавим директиву конфигурации, которая говорит серверу делать именно это:

 AddHandler пример-обработчик .sum 

Это сообщает серверу следующее: всякий раз, когда мы получаем запрос на URI, оканчивающийся на .sum, мы должны сообщить всем модулям, что мы ищем того, кто идет под именем «example-handler» . Таким образом, когда обслуживается запрос, оканчивающийся на .sum, сервер сообщает всем модулям, что этот запрос должен обслуживаться "примером-обработчиком". Как вы увидите позже, когда мы начнем собирать mod_example, мы проверим этот тег обработчика, переданный сервером, AddHandler и ответим серверу на основе значения этого тега.

Подключение к httpd

Для начала мы хотим создать простой обработчик, который отвечает клиентскому браузеру при запросе определенного URL-адреса, поэтому мы пока не будем настраивать обработчики конфигурации и директивы. Наше начальное определение модуля будет выглядеть так:

 модуль AP_MODULE_DECLARE_DATA пример_модуля =
{
 STANDARD20_MODULE_STUFF,
 НУЛЕВОЙ,
 НУЛЕВОЙ,
 НУЛЕВОЙ,
 НУЛЕВОЙ,
 НУЛЕВОЙ,
 register_hooks /* Наша функция регистрации хука */
}; 

Это позволяет серверу понять, что нас не интересует ничего необычного, мы просто хотим перехватить запросы и, возможно, обработать некоторые из них.

Ссылка в объявлении нашего примера — register_hooks это имя функции, которую мы создадим для управления тем, как мы подключаемся к процессу запроса. В этом примерном модуле функция имеет только одну цель; Чтобы создать простой хук, который вызывается после всех перезаписей, управления доступом и т.д. Таким образом, мы сообщим серверу, что хотим подключиться к его процессу одним из последних модулей:

 статическая пустота register_hooks (apr_pool_t *pool)
{
 /* Создаем хук в обработчике запроса, чтобы нас вызывали, когда приходит запрос */
 ap_hook_handler (example_handler, NULL, NULL, APR_HOOK_LAST);
} 

Ссылка example_handler — это функция, которая будет обрабатывать запрос. Мы обсудим, как создать обработчик в следующей главе.

Другие полезные крючки

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

  • ap_hook_child_init : Разместите хук, который выполняется при порождении дочернего процесса (обычно используется для инициализации модулей после разветвления сервера).
  • ap_hook_pre_config : Разместите хук, который выполняется до того, как будут прочитаны какие-либо данные конфигурации (очень ранний хук).
  • ap_hook_post_config : Поместите хук, который выполняется после анализа конфигурации, но до того, как сервер разветвится.
  • ap_hook_translate_name : Поместите хук, который выполняется, когда URI необходимо преобразовать в имя файла на сервере (думаю mod_rewrite )
  • ap_hook_quick_handler : аналогично ap_hook_handler , за исключением того, что он запускается перед любыми другими перехватчиками запросов (перевод, аутентификация, исправления и т. д.)
  • ap_hook_log_transaction : Поместите ловушку, которая выполняется, когда сервер собирается добавить запись в журнал текущего запроса.

Создание обработчика

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

Простое «Привет, мир!» обработчик

Давайте начнем с создания очень простого обработчика запросов, который делает следующее:

  1. Убедитесь, что это запрос, который должен обслуживаться «примером-обработчиком».
  2. Установите тип содержимого нашего вывода на text/html
  3. Пишите "Привет, мир!" вернуться в клиентский браузер
  4. Сообщите серверу, что мы позаботились об этом запросе и все прошло нормально

В коде C наш пример обработчика теперь будет выглядеть так:

 статический интервал example_handler (request_rec * r)
{
 /* Во-первых, нам нужно проверить, не является ли это вызовом обработчика «example-handler».
 * Если есть - принимаем и делаем свои дела, если нет - просто возвращаем DECLINED,
 * и сервер попробует в другом месте.
 */
 if (!r->handler || strcmp(r->handler, "пример-обработчик")) return (DECLINED);
 
 /* Теперь, когда мы обработали этот запрос, мы напишем «Hello, world!» клиенту.
 * Для этого мы должны сначала установить соответствующий тип контента, а затем наш вывод.
 */
 ap_set_content_type(r, "текст/html");
 ap_rprintf(r, "Привет, мир!");
 
 /* Наконец, мы должны сообщить серверу, что мы позаботились об этом запросе и все прошло нормально.
 * Мы делаем это, просто возвращая серверу значение OK.
 */
 вернуть ОК;
} 

Теперь мы объединим все, что узнали, и в итоге получим программу, которая выглядит как mod_example_1.c. Функции, используемые в этом примере, будут объяснены позже в разделе «Некоторые полезные функции, которые вам следует знать».

Структура request_rec

Наиболее важной частью любого запроса является запись запроса . При вызове функции-обработчика это представляется структурой, request_rec* передаваемой вместе с каждым сделанным вызовом. Эта структура, обычно просто называемая r модулями, содержит всю информацию, необходимую вашему модулю для полной обработки любого HTTP-запроса и соответствующего ответа.

Некоторые ключевые элементы структуры request_rec :

  • r->handler (char*): Содержит имя обработчика, который сервер в настоящее время запрашивает для обработки этого запроса.
  • r->method (char*): Содержит используемый метод HTTP, например, GET или POST.
  • r->filename (char*): Содержит переведенное имя файла, которое запрашивает клиент.
  • r->args (char*): Содержит строку запроса запроса, если таковая имеется
  • r->headers_in (apr_table_t*): Содержит все заголовки, отправленные клиентом
  • r->connection (conn_rec*): Запись, содержащая информацию о текущем соединении
  • r->user (char*): Если URI требует аутентификации, это устанавливается на указанное имя пользователя.
  • r->useragent_ip (char*): IP-адрес клиента, который к нам подключается
  • r->pool (apr_pool_t*) : Пул памяти этого запроса. Мы обсудим это в главе «Управление памятью».

Полный список всех значений, содержащихся в request_rec структуре, можно найти в httpd.h заголовочном файле или по адресу http://ci.apache.org/projects/httpd/trunk/doxygen/structrequest__rec.html.

Давайте попробуем некоторые из этих переменных в другом примере обработчика:

 статический интервал example_handler (request_rec * r)
{
 /* Установите соответствующий тип содержимого */
 ap_set_content_type(r, "текст/html");
 /* Выводим IP-адрес клиента, который к нам подключается: */
 ap_rprintf(r, "<h2>Здравствуйте, %s!</h2>", r->useragent_ip);
 
 /* Если нас связали с помощью запроса GET или POST, радуйтесь, иначе грустите. */
 if ( !strcmp(r->метод, "POST") || !strcmp(r->метод, "GET") ) {
 ap_rputs("Вы использовали метод GET или POST, это нас очень радует!<br/>", r);
 }
 еще {
 ap_rputs("Вы не использовали POST или GET, это нас огорчает :(<br/>", r);
 }
 /* Наконец, если была строка запроса, давайте напечатаем и ее! */
 если (г->аргументы) {
 ap_rprintf(r, "Ваша строка запроса: %s", r->args);
 }
 вернуть ОК;
} 

Возвращаемые значения

Apache полагается на возвращаемые обработчиками значения, чтобы указать, был ли обработан запрос или нет, и если да, то успешно ли он прошел. Если модуль не заинтересован в обработке конкретного запроса, он всегда должен возвращать значение DECLINED . Если он обрабатывает запрос, он должен либо вернуть общее значение OK , либо конкретный код состояния HTTP, например:

 статический интервал example_handler (request_rec * r)
{
 /* Возврат 404: Не найдено */
 вернуть HTTP_NOT_FOUND;
} 

Возврат OK или код состояния HTTP не обязательно означает, что запрос завершится. На сервере все еще могут быть другие обработчики, заинтересованные в этом запросе, например, модули ведения журнала, которые после успешного запроса запишут сводку того, что было запрошено и как оно прошло. Чтобы сделать полную остановку и предотвратить дальнейшую обработку после завершения вашего модуля, вы можете вернуть значение, DONE чтобы сообщить серверу, что он должен прекратить все действия по этому запросу и продолжить со следующим, не информируя других обработчиков.
Общие коды ответов:

  • DECLINED : мы не обрабатываем этот запрос
  • OK : Мы обработали этот запрос, и все прошло хорошо
  • DONE : мы обработали этот запрос, и сервер должен просто закрыть этот поток без дальнейшей обработки.

Специальные коды возврата HTTP (выдержка):

  • HTTP_OK (200) : запрос прошел нормально
  • HTTP_MOVED_PERMANENTLY (301) : Ресурс переместился на новый URL
  • HTTP_UNAUTHORIZED (401) : Клиент не авторизован для посещения этой страницы
  • HTTP_FORBIDDEN (403) : Доступ запрещен
  • HTTP_NOT_FOUND (404) : Файл не найден
  • HTTP_INTERNAL_SERVER_ERROR (500) : Внутренняя ошибка сервера (не требует пояснений)

Некоторые полезные функции, которые вы должны знать

  • ap_rputs(const char *string, request_rec *r) :
    отправляет строку текста клиенту. Это сокращенная версия ap_rwrite.
     ap_rputs("Привет, мир!", г); 
  • ap_rprintf :
    эта функция работает так же , как printf и , за исключением того, что она отправляет результат клиенту.
     ap_rprintf(r, "Привет, %s!", r->useragent_ip); 
  • ap_set_content_type(request_rec *r, const char *type) :
    устанавливает тип содержимого отправляемого вывода.
     ap_set_content_type(r, "текст/обычный"); /* принудительно вывести необработанный текст */ 

Управление памятью

Управлять вашими ресурсами в Apache HTTP Server 2.4 довольно просто благодаря системе пула памяти. По сути, каждый сервер, соединение и запрос имеют свой собственный пул памяти, который очищается, когда его область действия заканчивается, например, когда выполняется запрос или когда серверный процесс завершает работу. Все, что нужно сделать вашему модулю, — это зафиксироваться в этом пуле памяти, и вам не придется беспокоиться о необходимости убирать за собой — довольно аккуратно, да?

В нашем модуле мы в первую очередь будем выделять память для каждого запроса, поэтому уместно использовать ссылку r->pool при создании новых объектов. Вот несколько функций для выделения памяти в пуле:

  • void* apr_palloc( apr_pool_t *p, apr_size_t size) : Выделяет size количество байтов в пуле для вас
  • void* apr_pcalloc( apr_pool_t *p, apr_size_t size) : Выделяет size количество байтов в пуле для вас и устанавливает все байты в 0
  • char* apr_pstrdup( apr_pool_t *p, const char *s) : Создает дубликат строки s . Это полезно для копирования постоянных значений, чтобы вы могли их редактировать.
  • char* apr_psprintf( apr_pool_t *p, const char *fmt, ...) : аналогично sprintf , за исключением того, что сервер предоставляет вам соответствующим образом выделенную целевую переменную

Давайте поместим эти функции в пример обработчика:

 статический интервал example_handler (request_rec * r)
{
 const char *original = "Вы не можете редактировать это!";
 символ *копировать;
 int *целые числа;
 
 /* Выделить место для 10 целочисленных значений и установить их все равными нулю. */
 целые числа = apr_pcalloc (r-> pool, sizeof (int) * 10);
 
 /* Создаем копию «исходной» переменной, которую мы можем редактировать. */
 копия = apr_pstrdup(r->pool, original);
 вернуть ОК;
} 

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

 статическая пустота register_hooks (apr_pool_t *pool)
{
 /* Вызов функции, которая инициализирует некоторые вещи */
 example_init_function (пул);
 /* Создаем хук в обработчике запроса, чтобы нас вызывали, когда приходит запрос */
 ap_hook_handler (example_handler, NULL, NULL, APR_HOOK_LAST);
} 

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

Парсинг данных запроса

В нашем примере модуля мы хотели бы добавить функцию, которая проверяет, какой тип дайджеста, MD5 или SHA1, хотел бы видеть клиент. Эту проблему можно решить, добавив в запрос строку запроса. Строка запроса обычно состоит из нескольких ключей и значений, объединенных в строку, например valueA=yes&valueB=no&valueC=maybe . Сам модуль должен анализировать их и получать необходимые данные. В нашем примере мы будем искать ключ с именем digest , и если установлено значение md5 , мы создадим дайджест MD5, в противном случае мы создадим дайджест SHA1.

С момента появления Apache HTTP Server 2.4 синтаксический анализ данных запросов GET и POST никогда не был таким простым. Все, что нам нужно для анализа данных GET и POST, — это четыре простые строки:

 апр_таблица_t *GET; 
апрель_array_header_t*POST; 


ap_args_to_table(r, &GET); 

ap_parse_form_data(r, NULL, &POST, -1, 8192); 

В нашем конкретном примере модуля мы ищем digest значение из строки запроса, которая теперь находится внутри таблицы с именем GET . Чтобы извлечь это значение, нам нужно всего лишь выполнить простую операцию:

 /* Получить ключ "дайджеста" из строки запроса, если он есть. */
const char *digestType = apr_table_get(GET, "дайджест");
/* Если ключ не был возвращен, вместо него мы установим значение по умолчанию. */
если (!digestType) дайджестТип = "sha1"; 

Структуры, используемые для данных POST и GET, не совсем одинаковы, поэтому, если бы нам нужно было получить значение из данных POST вместо строки запроса, нам пришлось бы прибегнуть к еще нескольким строкам, как показано в этом примере в последнюю главу этого документа.

Создание расширенного обработчика

Теперь, когда мы научились анализировать данные формы и управлять нашими ресурсами, мы можем перейти к созданию расширенной версии нашего модуля, который выдает дайджест файлов MD5 или SHA1:

 статический интервал example_handler (request_rec * r)
{
 int rc, существует;
 апрель_финфо_т инфо;
 апрель_файл_т *файл;
 символ *имя файла;
 символьный буфер[256];
 апрель_размер_t байтов чтения;
 инт н;
 апр_таблица_t *GET;
 апрель_array_header_t *POST;
 const char *digestType;
 
 
 /* Проверяем, вызывается ли обработчик "example-handler". */
 if (!r->handler || strcmp(r->handler, "пример-обработчик")) return (DECLINED);
 
 /* Выясните, какой файл запрашивается, удалив из него .sum */
 имя_файла = apr_pstrdup(r->пул, r->имя файла);
 имя_файла[strlen(имя_файла)-4] = 0; /* Отрезать последние 4 символа. */
 
 /* Выясняем, существует ли файл, для которого мы запрашиваем сумму, и не является ли он каталогом */
 rc = apr_stat(&finfo, имя файла, APR_FINFO_MIN, r->pool);
 если (rc == APR_SUCCESS) {
 существует =
 (
 (finfo.filetype != APR_NOFILE)
 && !(finfo.filetype и APR_DIR)
 );
 если (! существует) вернуть HTTP_NOT_FOUND; /* Возвращаем 404, если не найдено. */
 }
 /* Если apr_stat не удалось, нам, вероятно, не разрешено проверять этот файл. */
 иначе вернуть HTTP_FORBIDDEN;
 
 /* Разбираем данные GET и, возможно, POST, отправленные нам */
 
 ap_args_to_table(r, &GET);
 ap_parse_form_data(r, NULL, &POST, -1, 8192);
 
 /* Установите соответствующий тип содержимого */
 ap_set_content_type(r, "текст/html");
 
 /* Вывести название и некоторую общую информацию */
 ap_rprintf(r, "<h2>Информация о %s:</h2>", имя файла);
 ap_rprintf(r, "<b>Размер:</b> %u байт<br/>", finfo.size);
 
 /* Получить тип дайджеста, который хочет видеть клиент */
 дайджестТип = apr_table_get(GET, "дайджест");
 если (!digestType) дайджестТип = "MD5";
 
 
 rc = apr_file_open(&file, имя файла, APR_READ, APR_OS_DEFAULT, r->пул);
 если (rc == APR_SUCCESS) {
 
 /* Пытаемся ли мы вычислить дайджест MD5 или SHA1? */
 если (!strcasecmp(digestType, "md5")) {
 /* Вычислить сумму MD5 файла */
 союз {
 символ хр[16];
 uint32_t число[4];
 } дайджест;
 апр_md5_ctx_t md5;
 апр_md5_init(&md5);
 прочитанные байты = 256;
 в то время как ( apr_file_read (файл, буфер и байты чтения) == APR_SUCCESS ) {
 apr_md5_update(&md5, буфер, readBytes);
 }
 apr_md5_final(digest.chr, &md5);
 
 /* Распечатайте дайджест MD5 */
 ap_rputs("<b>MD5: </b><code>", r);
 для (n = 0; n < APR_MD5_DIGESTSIZE/4; n++) {
 ap_rprintf(r, "%08x", дайджест.num[n]);
 }
 ap_rputs("</code>", г);
 /* Вывести ссылку на версию SHA1 */
 ap_rputs("<br/><a href='?digest=sha1'>Просмотреть вместо этого хэш SHA1</a>", r);
 }
 еще {
 /* Вычислить сумму SHA1 файла */
 союз {
 символ хр[20];
 uint32_t число[5];
 } дайджест;
 апр_ша1_ctx_t ша1;
 апрель_sha1_init(&sha1);
 прочитанные байты = 256;
 в то время как ( apr_file_read (файл, буфер и байты чтения) == APR_SUCCESS ) {
 apr_sha1_update(&sha1, буфер, readBytes);
 }
 apr_sha1_final(digest.chr, &sha1);
 
 /* Распечатываем дайджест SHA1 */
 ap_rputs("<b>SHA1: </b><code>", r);
 для (n = 0; n < APR_SHA1_DIGESTSIZE/4; n++) {
 ap_rprintf(r, "%08x", дайджест.num[n]);
 }
 ap_rputs("</code>", г);
 
 /* Вывести ссылку на версию MD5 */
 ap_rputs("<br/><a href='?digest=md5'>Просмотреть вместо этого хэш MD5</a>", r);
 }
 апр_файл_закрыть (файл);
 
 }
 /* Сообщаем серверу, что мы ответили на этот запрос. */
 вернуть ОК;
} 

Полностью эту версию можно найти здесь: mod_example_2.c.

Добавление параметров конфигурации

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

Введение в директивы конфигурации

Если вы читаете это, то вы, вероятно, уже знаете, что такое директива конфигурации. Проще говоря, директива — это способ сообщить отдельному модулю (или набору модулей), как себя вести, например, эти директивы управляют тем, как mod_rewrite работает:

 RewriteEngine включен
RewriteCond "%{REQUEST_URI}" "^/foo/bar"
RewriteRule "^/foo/bar/(.*)$" "/foobar?page=$1" 

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

Делаем пример конфигурации

Для начала создадим базовую конфигурацию в C-space:

 структура typedef {
 инт включен; /* Включаем или отключаем наш модуль */
 константный символ *путь; /* Какой-то путь к... чему-то */
 интервал типа действия; /* 1 означает действие A, 2 означает действие B и так далее */
} пример_конфигурации; 

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

 структура typedef {
 инт включен; /* Включаем или отключаем наш модуль */
 константный символ *путь; /* Какой-то путь к... чему-то */
 интервал типа действия; /* 1 означает действие A, 2 означает действие B и так далее */
} пример_конфигурации;
статический конфиг example_config;
статический интервал example_handler (request_rec * r)
{
 if (!r->handler || strcmp(r->handler, "пример-обработчик")) return(DECLINED);
 ap_set_content_type(r, "текст/обычный");
 ap_rprintf(r, "Включено: %u\n", config.enabled);
 ap_rprintf(r, "Путь: %s\n", config.path);
 ap_rprintf(r, "TypeOfAction: %x\n", config.typeOfAction);
 вернуть ОК;
}
статическая пустота register_hooks (apr_pool_t *pool)
{
 конфиг.включено = 1;
 config.path = "/foo/bar";
 config.typeOfAction = 0x00;
 ap_hook_handler (example_handler, NULL, NULL, APR_HOOK_LAST);
}
/* Определяем наш модуль как сущность и назначаем функцию для регистрации хуков */
модуль AP_MODULE_DECLARE_DATA пример_модуля =
{
 STANDARD20_MODULE_STUFF,
 NULL, /* Обработчик конфигурации для каждого каталога */
 NULL, /* Обработчик слияния для конфигураций для каждого каталога */
 NULL, /* Обработчик конфигурации для каждого сервера */
 NULL, /* Обработчик слияния для серверных конфигураций */
 NULL, /* Любые директивы, которые могут быть у нас для httpd */
 register_hooks /* Наша функция регистрации хука */
}; 

Все идет нормально. Чтобы получить доступ к нашему новому обработчику, мы можем добавить в нашу конфигурацию следующее:

 <Расположение "/пример">
 SetHandler пример-обработчик
</местоположение> 

При посещении мы увидим нашу текущую конфигурацию, выдаваемую нашим модулем.

Регистрация директив на сервере

Что, если мы захотим изменить нашу конфигурацию не путем жесткого ввода новых значений в модуль, а с помощью файла apache2.conf или, возможно, файла .htaccess? Пришло время сообщить серверу, что мы хотим, чтобы это было возможно. Для этого мы должны сначала изменить наш тег имени , чтобы включить ссылку на директивы конфигурации, которые мы хотим зарегистрировать на сервере:

 модуль AP_MODULE_DECLARE_DATA пример_модуля =
{
 STANDARD20_MODULE_STUFF,
 NULL, /* Обработчик конфигурации для каждого каталога */
 NULL, /* Обработчик слияния для конфигураций для каждого каталога */
 NULL, /* Обработчик конфигурации для каждого сервера */
 NULL, /* Обработчик слияния для серверных конфигураций */
 example_directives, /* Любые директивы, которые могут быть у нас для httpd */
 register_hooks /* Наша функция регистрации хука */
}; 

Это сообщит серверу, что теперь мы принимаем директивы из файлов конфигурации и что вызываемая структура example_directives содержит информацию о том, что представляют собой наши директивы и как они работают. Поскольку в конфигурации нашего модуля есть три разные переменные, мы добавим структуру с тремя директивами и NULL в конце:

 статическая константа command_rec example_directives[] =
{
 AP_INIT_TAKE1("exampleEnabled", example_set_enabled, NULL, RSRC_CONF, "Включить или отключить mod_example"),
 AP_INIT_TAKE1("examplePath", example_set_path, NULL, RSRC_CONF, "Путь к чему бы то ни было"),
 AP_INIT_TAKE2("exampleAction", example_set_action, NULL, RSRC_CONF, "Специальное значение действия!"),
 { НУЛЕВОЙ }
}; 

Структура директив
Как видите, для каждой директивы нужно задать как минимум 5 параметров:

  1. AP_INIT_TAKE1 : это макрос, который сообщает серверу, что эта директива принимает один и только один аргумент. Если бы нам требовалось два аргумента, мы могли бы использовать макрос AP_INIT_TAKE2 и т. д. (дополнительные макросы см. в httpd_conf.h).
  2. exampleEnabled : Это название нашей директивы. Точнее, это то, что пользователь должен указать в своей конфигурации, чтобы вызвать изменение конфигурации в нашем модуле.
  3. example_set_enabled : Это ссылка на функцию C, которая анализирует директиву и соответствующим образом устанавливает конфигурацию. Мы обсудим, как это сделать, в следующем параграфе.
  4. RSRC_CONF : Это сообщает серверу, где разрешена директива. Мы подробно рассмотрим это значение в следующих главах, а пока это RSRC_CONF означает, что сервер будет принимать эти директивы только в контексте сервера.
  5. "Enable or disable...." : Это просто краткое описание того, что делает директива.

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

Функция обработчика директив

Теперь, когда мы сказали серверу ожидать некоторые директивы для нашего модуля, пришло время создать несколько функций для их обработки. То, что сервер читает в файле (файлах) конфигурации, является текстом, и поэтому, естественно, то, что он передает нашему обработчику директив, — это одна или несколько строк, которые нам самим нужно распознать и действовать. Вы заметите, что, поскольку мы настроили нашу exampleAction директиву на прием двух аргументов, ее C-функция также имеет определенный дополнительный параметр:

 /* Обработчик директивы "exampleEnabled" */
const char *example_set_enabled(cmd_parms *cmd, void *cfg, const char *arg)
{
 if(!strcasecmp(arg, "on")) config.enabled = 1;
 иначе config.enabled = 0;
 вернуть НУЛЬ;
}
/* Обработчик директивы "examplePath" */
const char *example_set_path(cmd_parms *cmd, void *cfg, const char *arg)
{
 config.path = аргумент;
 вернуть НУЛЬ;
}
/* Обработчик директивы "exampleAction" */
/* Предположим, что этот аргумент принимает один аргумент (файл или БД) и второй (запретить или разрешить), */
/* и мы храним его побитно. */
const char *example_set_action(cmd_parms *cmd, void *cfg, const char *arg1, const char *arg2)
{
 if(!strcasecmp(arg1, "файл")) config.typeOfAction = 0x01;
 иначе config.typeOfAction = 0x02;
 
 if(!strcasecmp(arg2, "deny")) config.typeOfAction += 0x10;
 иначе config.typeOfAction += 0x20;
 вернуть НУЛЬ;
} 

Собираем все вместе

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

 /* mod_example_config_simple.c: */
#include <stdio.h>
#include "apr_hash.h"
#include "ap_config.h"
#include "ap_provider.h"
#include "httpd.h"
#include "http_core.h"
#include "http_config.h"
#include "http_log.h"
#include "http_protocol.h"
#include "http_request.h"
/*
 ================================================== =============================
 Наш прототип конфигурации и объявление:
 ================================================== =============================
 */
структура typedef {
 инт включен; /* Включаем или отключаем наш модуль */
 константный символ *путь; /* Какой-то путь к... чему-то */
 интервал типа действия; /* 1 означает действие A, 2 означает действие B и так далее */
} пример_конфигурации;
статический конфиг example_config;
/*
 ================================================== =============================
 Наши обработчики директив:
 ================================================== =============================
 */
/* Обработчик директивы "exampleEnabled" */
const char *example_set_enabled(cmd_parms *cmd, void *cfg, const char *arg)
{
 if(!strcasecmp(arg, "on")) config.enabled = 1;
 иначе config.enabled = 0;
 вернуть НУЛЬ;
}
/* Обработчик директивы "examplePath" */
const char *example_set_path(cmd_parms *cmd, void *cfg, const char *arg)
{
 config.path = аргумент;
 вернуть НУЛЬ;
}
/* Обработчик директивы "exampleAction" */
/* Предположим, что этот аргумент принимает один аргумент (файл или БД) и второй (запретить или разрешить), */
/* и мы храним его побитно. */
const char *example_set_action(cmd_parms *cmd, void *cfg, const char *arg1, const char *arg2)
{
 if(!strcasecmp(arg1, "файл")) config.typeOfAction = 0x01;
 иначе config.typeOfAction = 0x02;
 
 if(!strcasecmp(arg2, "deny")) config.typeOfAction += 0x10;
 иначе config.typeOfAction += 0x20;
 вернуть НУЛЬ;
}
/*
 ================================================== =============================
 Структура директив для нашего тега имени:
 ================================================== =============================
 */
статическая константа command_rec example_directives[] =
{
 AP_INIT_TAKE1("exampleEnabled", example_set_enabled, NULL, RSRC_CONF, "Включить или отключить mod_example"),
 AP_INIT_TAKE1("examplePath", example_set_path, NULL, RSRC_CONF, "Путь к чему бы то ни было"),
 AP_INIT_TAKE2("exampleAction", example_set_action, NULL, RSRC_CONF, "Специальное значение действия!"),
 { НУЛЕВОЙ }
};
/*
 ================================================== =============================
 Обработчик нашего модуля:
 ================================================== =============================
 */
статический интервал example_handler (request_rec * r)
{
 if(!r->handler || strcmp(r->handler, "example-handler")) return(DECLINED);
 ap_set_content_type(r, "текст/обычный");
 ap_rprintf(r, "Включено: %u\n", config.enabled);
 ap_rprintf(r, "Путь: %s\n", config.path);
 ap_rprintf(r, "TypeOfAction: %x\n", config.typeOfAction);
 вернуть ОК;
}
/*
 ================================================== =============================
 Функция регистрации хука (также инициализирует значения конфигурации по умолчанию):
 ================================================== =============================
 */
статическая пустота register_hooks (apr_pool_t *pool)
{
 конфиг.включено = 1;
 config.path = "/foo/bar";
 config.typeOfAction = 3;
 ap_hook_handler (example_handler, NULL, NULL, APR_HOOK_LAST);
}
/*
 ================================================== =============================
 Тег имени нашего модуля:
 ================================================== =============================
 */
модуль AP_MODULE_DECLARE_DATA пример_модуля =
{
 STANDARD20_MODULE_STUFF,
 NULL, /* Обработчик конфигурации для каждого каталога */
 NULL, /* Обработчик слияния для конфигураций для каждого каталога */
 NULL, /* Обработчик конфигурации для каждого сервера */
 NULL, /* Обработчик слияния для серверных конфигураций */
 example_directives, /* Любые директивы, которые могут быть у нас для httpd */
 register_hooks /* Наша функция регистрации хука */
}; 

В нашем файле apache2.conf теперь мы можем изменить жестко закодированную конфигурацию, добавив несколько строк:

 ПримерВключен
ПримерПуть "/usr/bin/foo"
Пример файла действия разрешить 

Таким образом, мы применяем конфигурацию, заходим /example на наш веб-сайт и видим, что конфигурация адаптирована к тому, что мы написали в нашем файле конфигурации.

Контекстно-зависимые конфигурации

Введение в контекстно-зависимые конфигурации

В Apache HTTP Server 2.4 разные URL-адреса, виртуальные хосты, каталоги и т. д. могут иметь очень разное значение для пользователя сервера и, следовательно, разные контексты, в которых должны работать модули. Например, предположим, что у вас настроена эта конфигурация для mod_rewrite:

 <Каталог "/var/www">
 RewriteCond "%{HTTP_HOST}" "^example.com$"
 RewriteRule "(.*)" "http://www.example.com/$1"
</Каталог>
<Каталог "/var/www/sub">
 RewriteRule "^foobar$" "index.php?foobar=true"
</Каталог> 

В этом примере вы настроите два разных контекста для mod_rewrite:

  1. Внутри /var/www все запросы http://example.com должны идти на http://www.example.com
  2. Внутри /var/www/sub все запросы foobar должны идти на index.php?foobar=true

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

Так как же модуль получает конкретную конфигурацию для рассматриваемого сервера, каталога или местоположения? Это делается с помощью одного простого вызова:

 example_config *config = (example_config*) ap_get_module_config(r->per_dir_config, &example_module); 

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

Наша базовая настройка конфигурации

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

 структура typedef {
 контекст char[256];
 путь символа[256];
 интервал типа действия;
 инт включен;
} пример_конфигурации; 

Наш обработчик запросов также будет изменен, но все еще очень прост:

 статический интервал example_handler (request_rec * r)
{
 if(!r->handler || strcmp(r->handler, "example-handler")) return(DECLINED);
 example_config *config = (example_config*) ap_get_module_config(r->per_dir_config, &example_module);
 ap_set_content_type(r, "текст/обычный");
 ap_rprintf("Включено: %u\n", config->включено);
 ap_rprintf("Путь: %s\n", config->путь);
 ap_rprintf("TypeOfAction: %x\n", config->typeOfAction);
 ap_rprintf("Контекст: %s\n", config->context);
 вернуть ОК;
} 

Выбор контекста

Прежде чем мы сможем начать делать наш модуль контекстно-зависимым, мы должны сначала определить, какие контексты мы будем принимать. Как мы видели в предыдущей главе, для определения директивы необходимо установить пять элементов:

 AP_INIT_TAKE1("exampleEnabled", example_set_enabled, NULL, RSRC_CONF, "Включить или отключить mod_example"), 

Определение RSRC_CONF сообщило серверу, что мы разрешим эту директиву только в глобальном контексте сервера, но, поскольку сейчас мы тестируем контекстно-зависимую версию нашего модуля, мы должны установить это на что-то более мягкое, а именно на значение, которое позволяет нам ACCESS_CONF использовать директива внутри блоков <Directory> и <Location>. Для большего контроля над размещением ваших директив вы можете объединить следующие ограничения вместе, чтобы сформировать конкретное правило:

  • RSRC_CONF : Разрешить в файлах .conf (не .htaccess) вне <Directory> или <Location>
  • ACCESS_CONF : Разрешить файлы .conf (не .htaccess) внутри <Directory> или <Location>
  • OR_OPTIONS : Разрешить в файлах .conf и .htaccess, если AllowOverride Options установлено
  • OR_FILEINFO : Разрешить в файлах .conf и .htaccess, если AllowOverride FileInfo установлено
  • OR_AUTHCFG : Разрешить в файлах .conf и .htaccess, если AllowOverride AuthConfig установлено
  • OR_INDEXES : Разрешить в файлах .conf и .htaccess, если AllowOverride Indexes установлено
  • OR_ALL : Разрешить в любом месте в файлах .conf и .htaccess.

Использование сервера для выделения слотов конфигурации

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

 модуль AP_MODULE_DECLARE_DATA пример_модуля =
{
 STANDARD20_MODULE_STUFF,
 create_dir_conf, /* Обработчик конфигурации для каждого каталога */
 merge_dir_conf, /* Обработчик слияния для конфигураций для каждого каталога */
 NULL, /* Обработчик конфигурации для каждого сервера */
 NULL, /* Обработчик слияния для серверных конфигураций */
 директивы, /* Любые директивы, которые могут быть у нас для httpd */
 register_hooks /* Наша функция регистрации хука */
}; 

Создание новых конфигураций контекста

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

 void *create_dir_conf(apr_pool_t *pool, char *context) {
 контекст = контекст? контекст : "(неопределенный контекст)";
 example_config *cfg = apr_pcalloc(pool, sizeof(example_config));
 если (конфиг.) {
 /* Установить некоторые значения по умолчанию */
 strcpy(cfg->контекст, контекст);
 cfg->включено = 0;
 cfg->path = "/foo/bar";
 cfg->typeOfAction = 0x11;
 }
 вернуть конфиг;
} 

Объединение конфигураций

Наш следующий шаг в создании контекстно-зависимой конфигурации — объединение конфигураций. Эта часть процесса особенно применима к сценариям, в которых у вас есть родительская конфигурация и дочерняя, например:

 <Каталог "/var/www">
 ПримерВключен
 ПримерПуть "/foo/bar"
 Пример файла действия разрешить
</Каталог>
<Каталог "/var/www/subdir">
 Пример действия по отказу в доступе к файлу
</Каталог> 

В этом примере естественно предположить, что каталог /var/www/subdir должен наследовать значения, установленные для /var/www каталога, так как мы не указывали ни ExampleEnabled , ни ExamplePath для этого каталога. Сервер не претендует знать, правда ли это, но ловко делает следующее:

  1. Создает новую конфигурацию для /var/www
  2. Устанавливает значения конфигурации в соответствии с директивами, данными для /var/www
  3. Создает новую конфигурацию для /var/www/subdir
  4. Устанавливает значения конфигурации в соответствии с директивами, данными для /var/www/subdir
  5. Предлагает объединить две конфигурации в новую конфигурацию для /var/www/subdir

Это предложение обрабатывается функцией, на merge_dir_conf которую мы ссылаемся в нашем теге имени. Цель этой функции — оценить две конфигурации и решить, как их объединить:

 void *merge_dir_conf(apr_pool_t *pool, void *BASE, void *ADD) {
 example_config *base = (example_config *) BASE ; /* Это то, что было установлено в родительском контексте */
 example_config *add = (example_config *) ADD ; /* Это то, что установлено в новом контексте */
 example_config *conf = (example_config *) create_dir_conf(pool, "Объединенная конфигурация"); /* Это будет объединенная конфигурация */
 
 /* Объединение конфигураций */
 conf->enabled = (add->enabled == 0)? base->enabled : add->enabled ;
 conf->typeOfAction = add->typeOfAction ? add->typeOfAction : base->typeOfAction;
 strcpy(conf->path, strlen(add->path) ? add->path : base->path);
 
 вернуть конфиг;
} 

Пробуем наши новые контекстно-зависимые конфигурации

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

 <Расположение "/а">
 SetHandler пример-обработчик
 ПримерВключено
 ПримерПуть "/foo/bar"
 Пример файла действия разрешить
</местоположение>
<Расположение "/a/b">
 Пример действия по отказу в доступе к файлу
 ПримерВключено выключено
</местоположение>
<Расположение "/a/b/c">
 Пример действия db deny
 ПримерПуть "/foo/bar/baz"
 ПримерВключено
</местоположение> 

Затем мы соберем код нашего модуля. Обратите внимание: поскольку теперь мы используем наш тег имени в качестве ссылки при выборке конфигураций в нашем обработчике, я добавил несколько прототипов, чтобы компилятор был доволен:

 /*$6
 ++++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++
 * mod_example_config.c
 ++++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++
 */
#include <stdio.h>
#include "apr_hash.h"
#include "ap_config.h"
#include "ap_provider.h"
#include "httpd.h"
#include "http_core.h"
#include "http_config.h"
#include "http_log.h"
#include "http_protocol.h"
#include "http_request.h"
/*$1
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~
 Структура конфигурации
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~
 */
структура typedef
{
 контекст char[256];
 путь символа[256];
 интервал типа действия;
 инт включен;
} пример_конфигурации;
/*$1
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~
 Прототипы
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~
 */
статический интервал example_handler (request_rec * r);
const char *example_set_enabled(cmd_parms *cmd, void *cfg, const char *arg);
const char *example_set_path(cmd_parms *cmd, void *cfg, const char *arg);
const char *example_set_action(cmd_parms *cmd, void *cfg, const char *arg1, const char *arg2);
void *create_dir_conf(apr_pool_t *pool, char *context);
void *merge_dir_conf(apr_pool_t *pool, void *BASE, void *ADD);
статическая пустота register_hooks (apr_pool_t *pool);
/*$1
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~
 Директивы конфигурации
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~
 */
директивы static const command_rec[] =
{
 AP_INIT_TAKE1("exampleEnabled", example_set_enabled, NULL, ACCESS_CONF, "Включить или отключить mod_example"),
 AP_INIT_TAKE1("examplePath", example_set_path, NULL, ACCESS_CONF, "Путь к чему бы то ни было"),
 AP_INIT_TAKE2("exampleAction", example_set_action, NULL, ACCESS_CONF, "Специальное значение действия!"),
 { НУЛЕВОЙ }
};
/*$1
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~
 Наша табличка с именем
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~
 */
модуль AP_MODULE_DECLARE_DATA пример_модуля =
{
 STANDARD20_MODULE_STUFF,
 create_dir_conf, /* Обработчик конфигурации для каждого каталога */
 merge_dir_conf, /* Обработчик слияния для конфигураций для каждого каталога */
 NULL, /* Обработчик конфигурации для каждого сервера */
 NULL, /* Обработчик слияния для серверных конфигураций */
 директивы, /* Любые директивы, которые могут быть у нас для httpd */
 register_hooks /* Наша функция регистрации хука */
};
/*
 ================================================== ================================================== ====================
 Функция регистрации хука
 ================================================== ================================================== ====================
 */
статическая пустота register_hooks (apr_pool_t *pool)
{
 ap_hook_handler (example_handler, NULL, NULL, APR_HOOK_LAST);
}
/*
 ================================================== ================================================== ====================
 Наш пример обработчика веб-сервиса
 ================================================== ================================================== ====================
 */
статический интервал example_handler (request_rec * r)
{
 if(!r->handler || strcmp(r->handler, "example-handler")) return(DECLINED);
 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 example_config *config = (example_config *) ap_get_module_config(r->per_dir_config, &example_module);
 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 ap_set_content_type(r, "текст/обычный");
 ap_rprintf(r, "Включено: %u\n", config->включено);
 ap_rprintf(r, "Путь: %s\n", config->путь);
 ap_rprintf(r, "TypeOfAction: %x\n", config->typeOfAction);
 ap_rprintf(r, "Контекст: %s\n", config->context);
 вернуть ОК;
}
/*
 ================================================== ================================================== ====================
 Обработчик директивы «exampleEnabled»
 ================================================== ================================================== ====================
 */
const char *example_set_enabled(cmd_parms *cmd, void *cfg, const char *arg)
{
 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
 example_config *conf = (example_config *) cfg;
 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
 если (конф.)
 {
 если(!strcasecmp(аргумент, "вкл"))
 конф->включено = 1;
 еще
 конф->включено = 0;
 }
 вернуть НУЛЬ;
}
/*
 ================================================== ================================================== ====================
 Обработчик директивы "examplePath"
 ================================================== ================================================== ====================
 */
const char *example_set_path(cmd_parms *cmd, void *cfg, const char *arg)
{
 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
 example_config *conf = (example_config *) cfg;
 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
 если (конф.)
 {
 strcpy(conf->путь, аргумент);
 }
 вернуть НУЛЬ;
}
/*
 ================================================== ================================================== ====================
 Обработчик директивы "exampleAction" ;
 Предположим, что этот аргумент принимает один аргумент (файл или БД) и второй (запретить или разрешить), ;
 и мы храним его побитовым способом.
 ================================================== ================================================== ====================
 */
const char *example_set_action(cmd_parms *cmd, void *cfg, const char *arg1, const char *arg2)
{
 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
 example_config *conf = (example_config *) cfg;
 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
 если (конф.)
 {
 {
 если(!strcasecmp(arg1, "файл"))
 conf->typeOfAction = 0x01;
 еще
 conf->typeOfAction = 0x02;
 если(!strcasecmp(arg2, "запретить"))
 conf->typeOfAction += 0x10;
 еще
 conf->typeOfAction += 0x20;
 }
 }
 вернуть НУЛЬ;
}
/*
 ================================================== ================================================== ====================
 Функция для создания новых конфигураций для контекстов каталогов
 ================================================== ================================================== ====================
 */
void *create_dir_conf(apr_pool_t *pool, char *context)
{
 контекст = контекст? context : "Вновь созданная конфигурация";
 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~*/
 example_config *cfg = apr_pcalloc(pool, sizeof(example_config));
 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~*/
 если (конфиг.)
 {
 {
 /* Установить некоторые значения по умолчанию */
 strcpy(cfg->контекст, контекст);
 cfg->включено = 0;
 memset(cfg->путь, 0, 256);
 cfg->typeOfAction = 0x00;
 }
 }
 вернуть конфиг;
}
/*
 ================================================== ================================================== ====================
 Функция объединения конфигураций
 ================================================== ================================================== ====================
 */
void *merge_dir_conf(apr_pool_t *pool, void *BASE, void *ADD)
{
 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~*/
 example_config *base = (example_config *) BASE;
 example_config *add = (example_config *) ADD;
 example_config *conf = (example_config *) create_dir_conf(pool, "Объединенная конфигурация");
 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~*/
 conf->enabled = (добавить->enabled == 0)? base->enabled : add->enabled;
 conf->typeOfAction = add->typeOfAction ? add->typeOfAction : base->typeOfAction;
 strcpy(conf->path, strlen(add->path) ? add->path : base->path);
 вернуть конфиг;
} 

Подведение итогов

Теперь мы рассмотрели, как создавать простые модули для Apache HTTP Server 2.4 и настраивать их. Что вы будете делать дальше, полностью зависит от вас, но я надеюсь, что чтение этой документации принесло вам что-то ценное. Если у вас есть вопросы о дальнейшей разработке модулей, присоединяйтесь к нашим спискам рассылки или ознакомьтесь с остальной частью нашей документации для получения дополнительных советов.

Некоторые полезные фрагменты кода

Получить переменные из данных формы POST

 структура typedef {
 константный символ *ключ;
 константный символ *значение;
} пара значений ключей;
keyValuePair *readPost(request_rec *r) {
 apr_array_header_t *pairs = NULL;
 апр_офф_т лен;
 размер apr_size_t;
 целое разрешение;
 интервал я = 0;
 символ *буфер;
 keyValuePair *kvp;
 res = ap_parse_form_data(r, NULL, &pairs, -1, HUGE_STRING_LEN);
 if (res != OK || !pairs) вернуть NULL; /* Возвращаем NULL, если мы потерпели неудачу или если нет данных POST */
 kvp = apr_pcalloc(r->pool, sizeof(keyValuePair) * (pairs->nelts + 1));
 в то время как (пары && !apr_is_empty_array(пары)) {
 ap_form_pair_t *pair = (ap_form_pair_t *) apr_array_pop(pairs);
 apr_brigade_length(пара->значение, 1, &len);
 размер = (apr_size_t) длина;
 буфер = apr_palloc (r-> пул, размер + 1);
 apr_brigade_flatten (пара-> значение, буфер и размер);
 буфер[длина] = 0;
 kvp[i].key = apr_pstrdup(r->пул, пара->имя);
 kvp[i].value = буфер;
 я++;
 }
 возврат квп;
}
статический интервал example_handler (request_rec * r)
{
 /*~~~~~~~~~~~~~~~~~~~~~~*/
 keyValuePair *formData;
 /*~~~~~~~~~~~~~~~~~~~~~~*/
 данные формы = чтение сообщения (г);
 если (данные формы) {
 инт я;
 для (i = 0; &formData[i]; i++) {
 если (formData[i].key && formData[i].value) {
 ap_rprintf(r, "%s = %s\n", formData[i].key, formData[i].value);
 } иначе если (formData[i].key) {
 ap_rprintf(r, "%s\n", formData[i].key);
 } иначе если (formData[i].value) {
 ap_rprintf(r, "= %s\n", formData[i].value);
 } еще {
 перерыв;
 }
 }
 }
 вернуть ОК;
} 

Распечатка каждого полученного заголовка HTTP

 статический интервал example_handler (request_rec * r)
{
 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
 const apr_array_header_t *поля;
 инт я;
 apr_table_entry_t *e = 0;
 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
 fields = apr_table_elts(r->headers_in);
 e = (apr_table_entry_t *) fields->elts;
 for(i = 0; i < fields->nelts; i++) {
 ap_rprintf(r, "%s: %s\n", e[i].key, e[i].val);
 }
 вернуть ОК;
} 

Чтение тела запроса в память

 static int util_read (request_rec *r, const char **rbuf, apr_off_t *size)
{
 /*~~~~~~~~*/
 интервал rc = ОК;
 /*~~~~~~~~*/
 if((rc = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR))) {
 возврат (рс);
 }
 если (ap_should_client_block (r)) {
 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
 char argsbuffer[HUGE_STRING_LEN];
 apr_off_t rsize, len_read, rpos = 0;
 длина apr_off_t = r->оставшаяся;
 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
 *rbuf = (const char *) apr_pcalloc(r->pool, (apr_size_t) (длина + 1));
 *размер = длина;
 while((len_read = ap_get_client_block(r, argsbuffer, sizeof(argsbuffer))) > 0) {
 если ((rpos + len_read) > длина) {
 rsize = длина - rpos;
 }
 еще {
 rsize = len_read;
 }
 memcpy((char *) *rbuf + rpos, argsbuffer, (size_t) rsize);
 rpos += rsize;
 }
 }
 возврат (рс);
}
статический интервал example_handler (request_rec * r)
{
 /*~~~~~~~~~~~~~~~~*/
 размер апр_офф_т;
 константный символ *буфер;
 /*~~~~~~~~~~~~~~~~*/
 if(util_read(r, &buffer, &size) == OK) {
 ap_rprintf(r, "Мы прочитали тело запроса длиной %" APR_OFF_T_FMT " байт", размер);
 }
 вернуть ОК;
} 


 <         > 

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

Рейтинг@Mail.ru