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  

Пункт 214. Примечания к API Apache

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

Это некоторые заметки об Apache API и структурах данных, с которыми вам приходится иметь дело, и т. д. Они еще не почти завершены, но, надеюсь, они помогут вам сориентироваться. Имейте в виду, что API все еще может быть изменен по мере того, как мы приобретаем опыт работы с ним. (См. файл TODO, чтобы узнать, что может произойти). Однако будет легко адаптировать модули к любым внесенным изменениям. (У нас больше модулей для адаптации, чем у вас).

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

Наконец, вот схема, чтобы дать вам некоторое представление о том, что будет дальше и в каком порядке:

  • Базовые концепты.
    • Обработчики, модули и запросы
    • Краткий обзор модуля
  • Как работают обработчики
    • Краткая экскурсия по request_rec
    • Откуда берутся структуры request_rec
    • Обработка запросов, отклонение и возврат кодов ошибок
    • Особые замечания для обработчиков ответов
    • Особые соображения для обработчиков аутентификации
    • Особые соображения для обработчиков ведения журналов
  • Распределение ресурсов и пулы ресурсов
  • Конфигурация, команды и тому подобное
    • Структуры конфигурации для каждого каталога
    • Обработка команд
    • Дополнительные примечания --- конфигурация для каждого сервера, виртуальные серверы и т. д .

Базовые концепты

Мы начнем с обзора основных концепций, лежащих в основе API, и того, как они проявляются в коде.

Обработчики, модули и запросы

Apache разбивает обработку запросов на ряд шагов, более или менее так же, как это делает серверный API Netscape (хотя этот API имеет несколько больше этапов, чем NetSite, в качестве перехватчиков для вещей, которые, как я думал, могут быть полезны в будущем). Это:

  • URI -> Перевод имени файла
  • Проверка идентификатора аутентификации [является ли пользователь тем, за кого себя выдает?]
  • Проверка доступа авторизации [авторизован ли пользователь здесь ?]
  • Проверка доступа кроме аутентификации
  • Определение MIME-типа запрошенного объекта
  • «Исправления» — их пока нет, но фаза предназначена для использования в качестве крючка для возможных расширений, таких как SetEnv , которые на самом деле не очень хорошо подходят для других целей.
  • Фактически отправка ответа обратно клиенту.
  • Регистрация запроса

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

  • Обработайте запрос и укажите, что он это сделал, вернув магическую константу OK .
  • Отказаться от обработки запроса, вернув волшебную целочисленную константу DECLINED . В этом случае сервер во всех отношениях ведет себя так, как будто обработчика просто не было.
  • Сообщите об ошибке, вернув один из кодов ошибки HTTP. Это завершает нормальную обработку запроса, хотя может быть вызван ErrorDocument, чтобы попытаться очистить его, и он будет зарегистрирован в любом случае.

Большинство фаз завершается первым модулем, который их обрабатывает; тем не менее, для ведения журнала, исправления и проверки подлинности без доступа все обработчики выполняются всегда (за исключением ошибок). Кроме того, фаза ответа уникальна тем, что модули могут объявить для нее несколько обработчиков через таблицу отправки, указанную в MIME-типе запрошенного объекта. Модули могут объявить обработчик фазы ответа, который может обрабатывать любой запрос, предоставив ему ключ */* ( т. е . спецификацию типа MIME с подстановочными знаками). Однако обработчики подстановочных знаков вызываются только в том случае, если сервер уже пытался и не смог найти более конкретный обработчик ответов для MIME-типа запрошенного объекта (либо их не существовало, либо все они были отклонены).

Сами обработчики являются функциями одного аргумента ( request_rec структура, см. ниже), которая возвращает целое число, как указано выше.

Краткий обзор модуля

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

Начнем с обработчиков. Для обработки CGI-скриптов модуль объявляет для них обработчик ответа. Из-за ScriptAlias , у него также есть обработчики для фазы преобразования имени (для распознавания ScriptAlias URI ed), фазы проверки типа (любой ScriptAlias запрос ed печатается как сценарий CGI).

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

Наконец, этот модуль содержит код для обработки ScriptAlias самой команды. Этот конкретный модуль объявляет только одну команду, но их может быть больше, поэтому в модулях есть таблицы команд , в которых объявляются их команды и описывается, где они разрешены и как их вызывать.

Последнее замечание относительно объявленных типов аргументов некоторых из этих команд: a pool — указатель на структуру пула ресурсов ; они используются сервером для отслеживания выделенной памяти, открытых файлов и т. д . либо для обслуживания определенного запроса, либо для обработки процесса настройки самого себя. Таким образом, когда запрос завершен (или, для конфигурационного пула, когда сервер перезапускается), память может быть освобождена, а файлы закрыты в массовом порядке , и никому не нужно писать явный код, чтобы отслеживать их все и распоряжаться ими. Кроме того, cmd_parms структура содержит различную информацию о считываемом файле конфигурации и другую информацию о состоянии, которая иногда используется функцией, обрабатывающей команду файла конфигурации (например, ScriptAlias ). Без лишних слов, сам модуль:

/* Declarations of handlers. */

int translate_scriptalias (request_rec *);
int type_scriptalias (request_rec *);
int cgi_handler (request_rec *);

/* Subsidiary dispatch table for response-phase
 * handlers, by MIME type */

handler_rec cgi_handlers[] = {
{ "application/x-httpd-cgi", cgi_handler },
{ NULL }
};

/* Declarations of routines to manipulate the
 * module's configuration info. Note that these are
 * returned, and passed in, as void *'s; the server
 * core keeps track of them, but it doesn't, and can't,
 * know their internal structure.
 */

void *make_cgi_server_config (pool *);
void *merge_cgi_server_config (pool *, void *, void *);

/* Declarations of routines to handle config-file commands */

extern char *script_alias(cmd_parms *, void *per_dir_config, char *fake, char *real);

command_rec cgi_cmds[] = {
{ "ScriptAlias", script_alias, NULL, RSRC_CONF, TAKE2,
"a fakename and a realname"},
{ NULL }
};

module cgi_module = {

 STANDARD_MODULE_STUFF,
 NULL, /* инициализатор */
 NULL, /* создатель конфигурации каталога */
 NULL, /* слияние каталогов */
 make_cgi_server_config, /* конфигурация сервера */
 merge_cgi_server_config, /* конфигурация сервера слияния */
 cgi_cmds, /* таблица команд */
 cgi_handlers, /* обработчики */
 translate_scriptalias, /* перевод имени файла */
 NULL, /* check_user_id */
 NULL, /* проверка авторизации */
 NULL, /* проверить доступ */
 type_scriptalias, /* type_checker */
 NULL, /* исправления */
 NULL, /* регистратор */
 NULL /* парсер заголовков */
}; 

Как работают обработчики

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

Краткий обзор request_rec

Содержит request_rec указатели на пул ресурсов, который будет очищен, когда сервер завершит обработку запроса; к структурам, содержащим информацию о каждом сервере и соединении, и, что наиболее важно, информацию о самом запросе.

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

Другими часто используемыми элементами данных являются таблицы, содержащие заголовки MIME в исходном запросе клиента, заголовки MIME, которые должны быть отправлены обратно с ответом (которые модули могут добавлять по желанию), и переменные среды для любых подпроцессов, которые порождаются в ходе выполнения. обслуживание запроса. Эти таблицы обрабатываются с помощью подпрограмм ap_table_get и ap_table_set .

Обратите внимание, что Content-type значение заголовка не может быть установлено обработчиками содержимого модуля с помощью ap_table_*() подпрограмм. Скорее, он устанавливается путем указания content_type поля в request_rec структуре на соответствующую строку. например ,

r->content_type = "text/html";

Наконец, есть указатели на две структуры данных, которые, в свою очередь, указывают на структуры конфигурации каждого модуля. В частности, они содержат указатели на структуры данных, созданные модулем для описания того, как он был сконфигурирован для работы в заданном каталоге (через .htaccess файлы или <Directory> разделы), для частных данных, которые он построил в ходе обслуживания запроса (т.е. обработчики модулей для одной фазы могут передавать «примечания» своим обработчикам для других фаз). Существует еще один такой вектор конфигурации в server_rec структуре данных, на которую указывает request_rec , которая содержит данные конфигурации для каждого (виртуального) сервера.

Вот сокращенная декларация с указанием наиболее часто используемых полей:

struct request_rec {

pool *pool;
conn_rec *connection;
server_rec *server;

/* What object is being requested */

char *uri;
char *filename;
char *path_info;

 символ *аргументы; /* QUERY_ARGS, если есть */
структура статистики; /* Устанавливается ядром сервера;
 * st_mode обнуляется, если такого файла нет */ 

char *content_type;
char *content_encoding;

/* MIME header environments, in and out. Also,
 * an array containing environment variables to
 * be passed to subprocesses, so people can write
 * modules to add to that environment.
 *
 * The difference between headers_out and
 * err_headers_out is that the latter are printed
 * even on error, and persist across internal
 * redirects (so the headers printed for
 * ErrorDocument handlers will have them).
 */

table *headers_in;
table *headers_out;
table *err_headers_out;
table *subprocess_env;

/* Info about the request itself... */

 интервал заголовок_только; /* Запрос HEAD вместо GET */
символ *протокол; /* Протокол, который нам дан, или HTTP/0.9 */
символ *метод; /* GET, HEAD, POST и т. д. */
интервал_метод_номер; /* M_GET, M_POST и т. д. */ 

/* Info for logging */

char *the_request;
int bytes_sent;

/* A flag which modules can set, to indicate that
 * the data being returned is volatile, and clients
 * should be told not to cache it.
 */

int no_cache;

/* Various other config info which may change
 * with .htaccess files
 * These are config vectors, with one void*
 * pointer for each module (the thing pointed
 * to being the module's business).
 */

 недействительным *per_dir_config; /* Параметры, заданные в конфигурационных файлах и т. д. */
недействительным *request_config; /* Примечания к *этому* запросу */ 

};

Откуда берутся структуры request_rec

Большинство request_rec структур создаются путем чтения HTTP-запроса от клиента и заполнения полей. Однако есть несколько исключений:

  • Если запрос относится к карте изображений, карте типов ( т. е . к *.var файлу) или сценарию CGI, который вернул локальное «Местоположение:», то ресурс, запрошенный пользователем, будет в конечном счете расположен по некоторому URI, отличному от того, который изначально предоставил клиент. В этом случае сервер выполняет внутреннее перенаправление , создавая новый request_rec для нового URI и обрабатывая его почти так же, как если бы клиент запросил новый URI напрямую.
  • Если какой-то обработчик сообщил об ошибке и объект ErrorDocument находится в области действия, в игру вступает тот же внутренний механизм перенаправления.
  • Наконец, обработчику иногда нужно выяснить, «что произойдет, если» будет запущен какой-либо другой запрос. Например, модуль индексации каталогов должен знать, какой тип MIME будет присвоен запросу для каждой записи каталога, чтобы выяснить, какой значок использовать.

    Такие обработчики могут создать подзапрос , используя функции ap_sub_req_lookup_file , ap_sub_req_lookup_uri и ap_sub_req_method_uri ; они создают новую request_rec структуру и обрабатывают ее, как и следовало ожидать, вплоть до фактической отправки ответа, но не включая ее. (Эти функции пропускают проверки доступа, если подзапрос относится к файлу в том же каталоге, что и исходный запрос).

    (На стороне сервера выполняется работа по созданию подзапросов и последующему фактическому вызову обработчика ответов для них с помощью функции ap_run_sub_req ).

Обработка запросов, отклонение и возврат кодов ошибок

Как обсуждалось выше, каждый обработчик, когда он вызывается для обработки определенного request_rec , должен возвращать , int чтобы указать, что произошло. Это может быть либо

  • OK -- запрос был успешно обработан. Это может или не может закончить фазу.
  • DECLINED -- ошибочного состояния нет, но модуль отказывается обрабатывать фазу; сервер пытается найти другой.
  • код ошибки HTTP, который прерывает обработку запроса.

Обратите внимание, что если возвращается код ошибки REDIRECT , то модуль должен поместить Location в запросе , чтобы указать, куда следует перенаправить headers_out клиента .

Особые замечания для обработчиков ответов

Обработчики для большинства фаз выполняют свою работу, просто устанавливая несколько полей в request_rec структуре (или, в случае средств проверки доступа, просто возвращая правильный код ошибки). Однако обработчики ответов должны фактически отправить запрос обратно клиенту.

Они должны начать с отправки заголовка ответа HTTP с помощью функции ap_send_http_header . (Вам не нужно делать ничего особенного, чтобы пропустить отправку заголовка для запросов HTTP/0.9; функция сама определяет, что она не должна ничего делать). Если запрос помечен header_only , это все, что они должны сделать; после этого они должны возвращаться, не предпринимая дальнейших попыток вывода.

В противном случае они должны создать тело запроса, которое отвечает клиенту соответствующим образом. Примитивами для этого являются ap_rputc и ap_rprintf , для генерируемого внутри вывода, и ap_send_fd , чтобы скопировать содержимое некоторого FILE * прямо на клиент.

К этому моменту вы должны более или менее понять следующий фрагмент кода, который является обработчиком, который обрабатывает GET запросы, у которых нет более конкретного обработчика; он также показывает, как GET можно обрабатывать условные s, если это желательно сделать в конкретном обработчике ответов - ap_set_last_modified проверяет If-modified-since значение, предоставленное клиентом, если оно есть, и возвращает соответствующий код (который, если он не равен нулю, будет USE_LOCAL_COPY) . Для , подобные соображения не применяются ap_set_content_length , но он возвращает код ошибки для симметрии.

int default_handler (request_rec *r)
{
int errstatus;
FILE *f;

if (r->method_number != M_GET) return DECLINED;
if (r->finfo.st_mode == 0) return NOT_FOUND;

if ((errstatus = ap_set_content_length (r, r->finfo.st_size))
    || (errstatus = ap_set_last_modified (r, r->finfo.st_mtime)))
return errstatus;

f = fopen (r->filename, "r");

if (f == NULL) {
log_reason("file permissions deny server access", r->filename, r);
return FORBIDDEN;
}

register_timeout ("send", r);
ap_send_http_header (r);

if (!r->header_only) send_fd (f, r);
ap_pfclose (r->pool, f);
return OK;
}

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

(Вызов ap_internal_redirect из обработчиков, которые не являются обработчиками ответов, приведет к серьезной путанице).

Особые соображения для обработчиков аутентификации

Вещи, которые следует обсудить здесь подробно:

  • Обработчики фазы аутентификации не вызываются, если для каталога не настроена аутентификация.
  • Общая конфигурация аутентификации хранится в конфигурации ядра для каждого каталога; он имеет средства доступа ap_auth_type , ap_auth_name и ap_requires .
  • Общие подпрограммы для обработки конца протокола, по крайней мере, для базовой аутентификации HTTP ( ap_get_basic_auth_pw , которая автоматически устанавливает connection->user поле структуры, и ap_note_basic_auth_failure , которая обеспечивает WWW-Authenticate: отправку надлежащего заголовка).

Особые соображения для обработчиков ведения журналов

Когда запрос имеет внутреннюю переадресацию, возникает вопрос, что регистрировать. Apache справляется с этим, объединяя всю цепочку перенаправлений в список request_rec структур, которые проходят через указатели r->prev и r->next . request_rec Обработчику ведения журнала в таких случаях передается тот, который изначально был создан для исходного запроса от клиента ; обратите внимание, что bytes_sent поле будет правильным только в последнем запросе в цепочке (тот, на который фактически был отправлен ответ).

Распределение ресурсов и пулы ресурсов

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

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

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

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

Следует отметить, что использование механизма пула, как правило, не является обязательным, за исключением таких ситуаций, как обработчики ведения журнала, где вам действительно необходимо зарегистрировать очистку, чтобы убедиться, что файл журнала закрывается при перезапуске сервера (это проще всего сделать с помощью используя функцию ap_pfopen , которая также обеспечивает закрытие базового файлового дескриптора до редактирования любых дочерних процессов, таких как сценарии CGI exec ), или в случае, если вы используете механизм тайм-аута (который еще даже не задокументирован здесь). Однако у его использования есть два преимущества: ресурсы, выделенные для пула, никогда не утекают (даже если вы выделяете временную строку и просто забываете об этом); Кроме того, для выделения памяти, ap_palloc как правило, быстрее, чем malloc .

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

Распределение памяти в пулах

Память выделяется в пулы путем вызова функции ap_palloc , которая принимает два аргумента, один из которых является указателем на структуру пула ресурсов, а другой — объемом выделяемой памяти (в char с). В обработчиках для обработки запросов наиболее распространенным способом получения структуры пула ресурсов является просмотр слота pool соответствующего request_rec ; отсюда повторяющееся появление следующей идиомы в коде модуля:

int my_handler(request_rec *r)
{
struct my_structure *foo;
...

foo = (foo *)ap_palloc (r->pool, sizeof(my_structure));
}

Обратите внимание, что память не ap_pfree освобождается -- ap_palloc только при очистке связанного с ней пула ресурсов. Это означает, что ap_palloc не нужно делать столько учета, сколько malloc() ; все, что он делает в типичном случае, это округляет размер, нажимает на указатель и выполняет проверку диапазона.

(Это также повышает вероятность того, что интенсивное использование ap_palloc может привести к тому, что серверный процесс станет чрезмерно большим. Есть два способа справиться с этим, которые рассматриваются ниже; вкратце, вы можете использовать malloc и попытаться убедиться, что все память становится явно free d, или вы можете выделить подпул основного пула, выделить свою память в подпуле и периодически очищать его Последний метод обсуждается в разделе о подпулах ниже и используется в коде индексации каталогов, чтобы избежать чрезмерного выделения памяти при перечислении каталогов с тысячами файлов).

Выделение инициализированной памяти

Есть функции, которые выделяют инициализированную память и часто бывают полезны. Функция ap_pcalloc имеет тот же интерфейс, что и ap_palloc , но очищает выделенную ею память, прежде чем вернуть ее. Функция ap_pstrdup принимает пул ресурсов и a char * в качестве аргументов и выделяет память для копии строки, на которую указывает указатель, возвращая указатель на копию. Наконец, ap_pstrcat это функция в стиле varargs, которая принимает указатель на пул ресурсов и как минимум два char * аргумента, последний из которых должен быть NULL . Он выделяет достаточно памяти для размещения копий каждой из строк как единой единицы; например:

ap_pstrcat (r->pool, "foo", "/", "bar", NULL);

возвращает указатель на 8-байтовую память, инициализированную значением "foo/bar" .

Часто используемые пулы на веб-сервере Apache

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

permanent_pool
никогда не переходил ни к чему другому, это родоначальник всех пулов
pconf
  • подпул постоянного_пула
  • создается в начале "цикла" конфига; существует до тех пор, пока сервер не будет остановлен или перезапущен; передается всем подпрограммам времени конфигурации либо через cmd->pool, либо в качестве аргумента «pool *p» для тех, которые не принимают пулы
  • передается в модуль функций init()
ptemp
  • извините, я вру, этот пул в настоящее время не называется так в версии 1.3, я переименовал его в это при разработке pthreads. Я имею в виду использование ptrans в родительском элементе... сравните это с более поздним определением ptrans в дочернем элементе.
  • подпул постоянного_пула
  • создается в начале "цикла" конфига; существует до конца разбора конфига; передается подпрограммам времени конфигурации через cmd->temp_pool. Что-то вроде «незаконнорожденного ребенка», потому что он доступен не везде. Используется для временного рабочего пространства, которое может понадобиться некоторым процедурам конфигурации, но которое удаляется в конце конфигурации.
pchild
  • подпул постоянного_пула
  • создается при порождении дочернего элемента (или создании потока); живет до тех пор, пока этот ребенок (поток) не будет уничтожен
  • переданы в модуль функций child_init
  • уничтожение происходит сразу после вызова функций child_exit... (что может объяснить, почему я считаю, что child_exit избыточен и не нужен)
ptrans
  • должен быть подпулом pchild, но в настоящее время является подпулом Permanent_pool, см. выше
  • очищается дочерним элементом перед переходом в цикл accept() для получения соединения
  • используется как соединение-> пул
r->pool
  • для основного запроса это подпул connection->pool; для подзапросов это подпул родительского пула запросов.
  • существует до конца запроса ( например , ap_destroy_sub_req или в child_main после завершения process_request)
  • обратите внимание, что сам r выделяется из r->pool; т. е . сначала создается r->pool, а затем r является первым, что из него palloc()d

Практически все, что делают люди, r->pool — это использование бассейна. Но вы можете видеть, как другие времена жизни, такие как pchild, полезны для некоторых модулей... таких как модули, которые должны открывать соединение с базой данных один раз для каждого дочернего элемента и хотят очистить его, когда дочерний элемент умирает.

Вы также можете увидеть, как проявляются некоторые ошибки, такие как установка connection->user значения from r->pool -- в этом случае соединение существует в течение времени жизни ptrans , которое больше, чем r->pool (особенно если r->pool это подзапрос!). Так что правильнее всего выделить из connection->pool .

И был еще один интересный баг в mod_include / mod_cgi . Вы увидите, что они проводят этот тест, чтобы решить, следует ли им использовать r->pool или r->main->pool . В этом случае ресурс, который они регистрируют для очистки, является дочерним процессом. Если бы он был зарегистрирован в r->pool , то код был бы wait() для дочернего элемента после завершения подзапроса. При mod_include этом мог быть любой старый #include , а задержка может быть до 3-х секунд... и случалось довольно часто. Вместо этого регистрируется подпроцесс, r->main->pool который вызывает его очистку, когда весь запрос выполнен, т. е . после того, как вывод был отправлен клиенту и произошло ведение журнала.

Отслеживание открытых файлов и т. д.

Как указано выше, пулы ресурсов также используются для отслеживания других видов ресурсов помимо памяти. Наиболее распространены открытые файлы. Обычно для этого используется подпрограмма ap_pfopen , которая принимает пул ресурсов и две строки в качестве аргументов; строки такие же, как типичные аргументы для fopen , например ,

...
FILE *f = ap_pfopen (r->pool, r->filename, "r");

if (f == NULL) { ... } else { ... }

Существует также ap_popenf подпрограмма, аналогичная open системному вызову более низкого уровня. Обе эти процедуры обеспечивают закрытие файла при очистке рассматриваемого пула ресурсов.

В отличие от случая с памятью, есть функции для закрытия файлов, выделенных с помощью ap_pfopen , и ap_popenf , а именно ap_pfclose и ap_pclosef . (Это связано с тем, что во многих системах количество файлов, которые может открыть один процесс, весьма ограничено). Важно использовать эти функции для закрытия файлов, выделенных с помощью ap_pfopen и ap_popenf , поскольку в противном случае это может привести к фатальным ошибкам в таких системах, как Linux, которые плохо реагируют, если одно и то же FILE* закрывается более одного раза.

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

Другие виды ресурсов -- функции очистки

Здесь больше текста. Описать примитивы очистки, с помощью которых реализовано наполнение файла; также, spawn_process .

Очистка пула существует до тех пор, пока clear_pool() не будет вызвана: clear_pool(a) рекурсивно вызывает destroy_pool() все подпулы a ; затем вызывает все очистки для a ; затем освобождает всю память для a . destroy_pool(a) вызывает clear_pool(a) , а затем освобождает саму структуру пула. т. е . clear_pool(a) не удаляет a , а просто освобождает все ресурсы, и вы можете немедленно начать использовать его снова.

Точный контроль — создание и работа с подпулами, с примечанием о подзапросах

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

Примитивом для создания подпула является ap_make_sub_pool , который принимает другой пул (родительский пул) в качестве аргумента. Когда основной пул будет очищен, подпул будет уничтожен. Подпул также можно очистить или уничтожить в любое время, вызвав функции ap_clear_pool и ap_destroy_pool соответственно. (Разница в том, что ap_clear_pool освобождаются ресурсы, связанные с пулом, а ap_destroy_pool также освобождается сам пул. В первом случае вы можете выделить новые ресурсы в пуле, снова очистить его и т. д.; во втором случае это просто ушел).

И последнее замечание: подзапросы имеют свои собственные пулы ресурсов, которые являются подпулами пула ресурсов для основного запроса. Вежливый способ вернуть ресурсы, связанные с подзапросом, который вы выделили (используя функции ap_sub_req_... ), — это ap_destroy_sub_req , который освобождает пул ресурсов. Перед вызовом этой функции обязательно скопируйте все, что вам нужно, что может быть выделено в пуле ресурсов подзапроса, в какое-то менее изменчивое место (например, имя файла в его структуре) request_rec .

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

Конфигурация, команды и тому подобное

Одной из целей разработки этого сервера было обеспечение внешней совместимости с сервером NCSA 1.3 --- то есть чтение тех же файлов конфигурации, корректная обработка всех директив в них и, в целом, замена NCSA. С другой стороны, другой целью разработки было перенести как можно больше функциональных возможностей сервера в модули, которые как можно меньше связаны с монолитным ядром сервера. Единственный способ примирить эти цели — перенести обработку большинства команд с центрального сервера в модули.

Однако просто дать модулям таблицы команд недостаточно, чтобы полностью оторвать их от ядра сервера. Сервер должен запомнить команды, чтобы действовать по ним позже. Это включает в себя сохранение данных, которые являются частными для модулей и могут быть либо для каждого сервера, либо для каждого каталога. Большинство вещей относится к каталогу, включая, в частности, информацию об управлении доступом и авторизации, а также информацию о том, как определять типы файлов по суффиксам, которые могут быть изменены директивами и AddType и ForceType т.д. В общем, основная философия заключается в том, что все, что можно настроить с помощью каталога, должно быть настроено; информация для каждого сервера обычно используется в стандартном наборе модулей для информации, такой как Alias es и Redirect s, которые вступают в действие до того, как запрос будет привязан к определенному месту в базовой файловой системе.

Еще одним требованием для эмуляции сервера NCSA является возможность обработки файлов конфигурации для каждого каталога, обычно называемых .htaccess файлами, хотя даже на сервере NCSA они могут содержать директивы, которые не имеют никакого отношения к управлению доступом. Соответственно, после преобразования URI -> имени файла, но до выполнения любого другого этапа, сервер проходит вниз по иерархии каталогов базовой файловой системы, следуя переведенному имени пути, чтобы прочитать любые файлы, которые могут .htaccess присутствовать. Информация, которая считывается, затем должна быть объединена с применимой информацией из собственных файлов конфигурации сервера (либо из <Directory> разделов в access.conf , либо из значений по умолчанию в srm.conf , что на самом деле ведет себя для большинства целей почти так же, как <Directory /> ).

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

Структуры конфигурации для каждого каталога

Давайте посмотрим, как все это проявляется в mod_mime.c , который определяет обработчик типов файлов, который эмулирует поведение сервера NCSA при определении типов файлов по суффиксам. Здесь мы рассмотрим код, реализующий команды AddType и AddEncoding . Эти команды могут появляться в .htaccess файлах, поэтому они должны обрабатываться в личных данных модуля для каждого каталога, которые фактически состоят из двух отдельных таблиц для типов MIME и информации о кодировании и объявляются следующим образом:

 структура typedef {
 таблица *forced_types; /* Дополнительный материал AddTyped */
 таблица *encoding_types; /* Добавлено с помощью AddEncoding... */
} mime_dir_config; 

Когда сервер читает файл конфигурации или <Directory> раздел, который включает одну из команд модуля MIME, ему необходимо создать структуру mime_dir_config , чтобы этим командам было над чем воздействовать. Он делает это, вызывая функцию, найденную в модуле `create per-dir config slot', с двумя аргументами: именем каталога, к которому применяется эта информация о конфигурации (или для ), и указателем на пул ресурсов, NULL в srm.conf котором выделение должно произойти.

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

Для модуля MIME функция создания конфигурации для каждого каталога ap_palloc имеет только структуру, описанную выше, и создает пару таблиц для ее заполнения. Это выглядит так:

void *create_mime_dir_config (pool *p, char *dummy)
{
mime_dir_config *new =
(mime_dir_config *) ap_palloc (p, sizeof(mime_dir_config));

new->forced_types = ap_make_table (p, 4);
new->encoding_types = ap_make_table (p, 4);

return new;
}

Теперь предположим, что мы только что прочитали файл .htaccess . У нас уже есть структура конфигурации для каждого каталога для следующего каталога в иерархии. Если в .htaccess файле, который мы только что прочитали, не было команд AddType или AddEncoding , его структура конфигурации для каждого каталога для модуля MIME все еще действительна, и мы можем просто использовать ее. В противном случае нам нужно как-то объединить две структуры.

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

void *merge_mime_dir_configs (pool *p, void *parent_dirv, void *subdirv)
{
mime_dir_config *parent_dir = (mime_dir_config *)parent_dirv;
mime_dir_config *subdir = (mime_dir_config *)subdirv;
mime_dir_config *new =
(mime_dir_config *)ap_palloc (p, sizeof(mime_dir_config));

new->forced_types = ap_overlay_tables (p, subdir->forced_types,
parent_dir->forced_types);
new->encoding_types = ap_overlay_tables (p, subdir->encoding_types,
parent_dir->encoding_types);

return new;
}

В качестве примечания - если нет функции слияния для каждого каталога, сервер просто использует информацию о конфигурации подкаталога и игнорирует родительский. Для некоторых модулей это работает просто отлично ( например , для модуля include, чья информация о конфигурации для каждого каталога состоит исключительно из состояния XBITHACK ), и для этих модулей вы можете просто не объявлять его и оставить соответствующий слот структуры в сам модуль NULL .

Обработка команд

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

char *add_type(cmd_parms *cmd, mime_dir_config *m, char *ct, char *ext)
{
if (*ext == '.') ++ext;
ap_table_set (m->forced_types, ext, ct);
return NULL;
}

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

Другой особенностью этого конкретного обработчика команд является то, что он не может столкнуться с ошибочными условиями. Если бы они были, он мог бы вернуть сообщение об ошибке вместо NULL ; это вызывает распечатку ошибки на сервере stderr с последующим быстрым выходом, если она есть в основных файлах конфигурации; для .htaccess файла синтаксическая ошибка регистрируется в журнале ошибок сервера (вместе с указанием, откуда она пришла), а запрос возвращается с ответом об ошибке сервера (состояние ошибки HTTP, код 500).

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

command_rec mime_cmds[] = {
{ "AddType", add_type, NULL, OR_FILEINFO, TAKE2,
"a mime type followed by a file extension" },
{ "AddEncoding", add_encoding, NULL, OR_FILEINFO, TAKE2,
"an encoding (e.g., gzip), followed by a file extension" },
{ NULL }
};

Записи в этих таблицах следующие:

  • Название команды
  • Функция, которая его обрабатывает
  • указатель (void *) , который передается в cmd_parms структуре обработчику команд --- это полезно, если одна и та же функция обрабатывает много похожих команд.
  • Битовая маска, указывающая, где может появиться команда. Имеются биты маски, соответствующие каждой AllowOverride опции, и дополнительный бит маски, RSRC_CONF указывающий, что команда может появляться в собственных файлах конфигурации сервера, но не в каком-либо другом .htaccess файле.
  • Флаг, указывающий, сколько аргументов требуется обработчику команды для предварительного анализа и как они должны быть переданы, TAKE2 указывает на два предварительно проанализированных аргумента. Другими параметрами являются TAKE1 , указывающий на один предварительно проанализированный аргумент, FLAG , указывающий, что аргумент должен быть On или Off , и передаваемый как логический флаг, RAW_ARGS , который заставляет сервер предоставить команде необработанные, не проанализированные аргументы (все, кроме команды само название). Также есть ITERATE , что означает, что обработчик выглядит так же, как TAKE1 , но при наличии нескольких аргументов его следует вызывать несколько раз, и, наконец ITERATE2 , что указывает на то, что обработчик команды выглядит как TAKE2 , но если присутствует больше аргументов, то его следует вызывать несколько раз, сохраняя первый аргумент постоянным.
  • Наконец, у нас есть строка, описывающая аргументы, которые должны присутствовать. Если аргументы в фактическом файле конфигурации не соответствуют требованиям, эта строка будет использоваться для получения более конкретного сообщения об ошибке. (Можете смело оставить это NULL ).

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

int find_ct(request_rec *r)
{
int i;
char *fn = ap_pstrdup (r->pool, r->filename);
mime_dir_config *conf = (mime_dir_config *)
ap_get_module_config(r->per_dir_config, &mime_module);
char *type;

if (S_ISDIR(r->finfo.st_mode)) {
r->content_type = DIR_MAGIC_TYPE;
return OK;
}

if((i=ap_rind(fn,'.')) < 0) return DECLINED;
++i;

if ((type = ap_table_get (conf->encoding_types, &fn[i])))
{
r->content_encoding = type;

/* go back to previous extension to try to use it as a type */
fn[i-1] = '\0';
if((i=ap_rind(fn,'.')) < 0) return OK;
++i;
}

if ((type = ap_table_get (conf->forced_types, &fn[i])))
{
r->content_type = type;
}

return OK;
}

Дополнительные примечания — конфигурация для каждого сервера, виртуальные серверы и т. д .

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

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

char *add_redirect(cmd_parms *cmd, void *dummy, char *f, char *url)
{
server_rec *s = cmd->server;
alias_server_conf *conf = (alias_server_conf *)
ap_get_module_config(s->module_config,&alias_module);
alias_entry *new = ap_push_array (conf->redirects);

if (!ap_is_url (url)) return "Redirect to non-URL";

new->fake = f; new->real = url;
return NULL;
}



 <         > 

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

Рейтинг@Mail.ru