Chapter 3 - Архитектура Apache Module API
Apache API написаны из условия , что Apache - это HTTP-server, который работает как демон .
Поскольку он HTTP-server, он должен слушать входящие TCP/IP-пакеты,распознавть URI ,
парсить их в имена файлов и скриптов и возвращать контент на клиентский броузер .
Модули играют активную роль во всех этих аспектах .
Апач устроен так , что новый запрос может инициировать новый процесс прежде , чем закончится
процесс , обслуживающий предыдущий запрос . Используется мультипроцессорная модель с наличием
множества серверов , каждый из которых может обслуживать множество клиентов .
На Win32-модели в большей степени используется мультитрэдинговая модель .
HTTP Protocol
Пример запроса , который клиент посылает на сервер :
GET /very/important/document.html HTTP/1.1
Host: www.modperl.com
1-я строка состоит из 3-х компонентов :
GET - метод запроса (GET,POST,PUT,HEAD,DELETE)
/very/important/document.html - URI документа
HTTP/1.1 - тип протокола ; 1.1 отличается от 1.0 наличием виртуальных хостов и кэшированием
2-я строка состоит из имени хоста
Дальше идут серия строк , каждая из которых состоит из хидера , двоеточия и расшифровки :
GET /news.html HTTP/1.1
Connection: Keep-Alive
User-Agent: Mozilla/4.05 [en] (X11; I; Linux 2.0.33 i686)
Host: www.modperl.com
Referer: http://www.modperl.com/index.html
If-Modified-Since: Tue, 24 Feb 1998 11:19:03 GMT
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png, */*
Accept-Language: en
Accept-Charset: iso-8859-1,*,utf-8
В этом примере приведены большинство HTTP-хидеров , которые нужно знать .
Например , хидер Connection говорит о том , что TCP-коннект будет открытым до окончания запроса.
В User-Agent указывается тип броузера .
Host - имя хоста , откуда пришел запрос .
Referer - URI ссылаемого документа .
If-Modified-Since - дата кэширования документа на клиенте . В зависимости от него
сервер может не возвращать контент .
Accept - список MIME-типов , которые поддерживаются броузером .
Если тип запроса POST , могут быть другие дополнительные поля .
Apache Life Cycle
Апачевский цикл показан на картинке .
Он стартует , инициализирует , создает несколько копий самого себя любимого ,
после чего запускается цикл , в котором проверяются входящие запросы .
Модули регистрируют хэндлеры , которые вызываются в нужное время .
Порядок вызова зависит от того , в каком порядке загружены модули .
Server Startup
При старте апач парсит командную строку и конфиг файлы .
Конфиги могут включать директивы , реализованные в модулях .
В каждом модуле есть init-процедура , которая передает от сервера информацию
в структуру server_rec .
Ее полями являются ServerName, Port,ServerAdmin.
Модули на C имеют дополнительный указатель на область памяти "resource pool".
В перловом модуле , на этапе инициализации , могут быть применены
директивы PerlRequire и PerlModule.
Далее апач создает несколько процессов , которые будут обслуживать
входящие запросы .
Эти процессы устанавливают собственные user и group IDs для нерутовых пользователей
("nobody" или "guest").
Родительский рутовый процесс обслуживает эту очередь дочерних процессов .
Каждый дочерний процесс , обслужив положенную порцию процессов ,
равную MaxRequestsPerChild ,
прекращает свое существование . При этом в соответствующем модуле будет
вызываться хэндлер child_exit ,
где могут комититься незавершенные транзакции , закрываться файлы и т.д.
Перловые модули при этом могут вызвать хэндлер , который может быть прописан как
PerlChildExitHandler в конфиге .
Основной цикл
Он показан на следующей картинке .
Решения принимает апач , но свою лепту могут вносить модули .
Можно выделить 8 основных моментов :
1. Что это ? (трансляция URI )
URI может ссылаться на физический файл , на документ , генерируемый на лету внешним скриптом ,
или на документ , генерируемый модулем .
2. Откуда пришел запрос ?
3. От кого пришел запрос ?
4. Кому разрешено выполнять этот запрос ?
В конфигурации могут быть перечислены файлы , доступ к которым имеют далеко не все .
5. Тип документа ? (MIME)
6. Кто генерирует контент для этого документа ?
Апач может принять решение о том , что генерация контента отдается внешнему модулю .
7. Кто и куда пишет лог ?
Вообще-то логами занимается сам апч , но может быть принято решение о передаче внешнему
модулю прав на специальное логирование , например в базу данных .
8. Кто двери закрывает ?
После обработки запроса модули могут выполнять различные телодвижения .
Внутренние запросы апача
Они могут быть сгенерированы апачем при обработке ошибок или в других ситуациях .
При этом возвращаемый контент заменяется на другой .
С помощью функции internal_redirect() апач прекращает обработку текущего запроса и передает
управление другому процессу . При возникновении ошибок срабатывает ErrorDocument ,
при этом возвращается спциальный HTML-file или CGI-script .
В некоторых ситуациях вызываются функции lookup_file() или lookup_uri() .
Они генерят запрос , как будто он пришел снаружи .
Некоторые стандартные апач-модули используют т.н. environment variables .
mod_cgi устанавливает их для хранения информации о запросе .
mod_include использует их при генерации HTML .
mod_log_config включает информацию о них в лог-файл .
mod_access принимает решение о разрешении доступа на их основе.
Их можно модифицировать с помощью PassEnv, SetEnv, UnsetEnv директив
которые реализованы в модуле mod_env .
Pass-Env может передавать их процессу .
Эти переменные также можно выставлять в конфиге с помощью директив <Directory> и <Location> :
PerlPassEnv ORGANIZATION
PerlSetEnv TMPDIR /usr/tmp
<Location /stage/upload>
PerlSetEnv TMPDIR /tmp/staging
</Location>
При старте апач копирует environment variables во внутреннюю таблицу .
После чего загружаемые модули читают из нее эти переменные , а не из конфигов .
Perl и C API позволяют модифицировать содержимое этой таблицы .
Handler API
При вызове хэндлера апач передает ему информацию о текущей транзакции и конфигурации сервера ,
который должен быть возвращен апачу назад .
В Perl API определение хэндлера такое :
sub {
my $r = shift;
# do something
return ;
}
Хэндлер может быть вызван в любой момент . Он возвращает единственный аргумент , являющийся
ссылкой на обьект запроса . Этот обьект есть структура и называется request record,
включающая в себя всю необходимую информацию о транзакции .
Этот обьект хранится в перл-переменной , называемой $r.
В особом случае хэндлер может вернуть и 2 аргумента ($$) , где первым аргументом будет
имя пакета . Это позволяет перловым хэндлерам использовать возможности наследования и полиморфизма .
Такие хэндлеры называются "method handlers" :
sub ($$) {
my $class = shift;
my $r = shift;
# do something
return ;
}
Хэндлеры на C API декларируются так :
static int (request_rec* r) {
/* do something */
return ;
}
В отличие от Perl API, где все хэндлеры имеют одинаковую структуру , C API handlers
могут иметь другую форму .Например , child_init()-handler выглядит так :
static void child_init (server_rec *s, pool *p) {
/* do something */
}
Status Codes
Каждый хэндлер должен возвращать статус-код , который ассоциируется с символической констаной .
Для перла они определены в модуле Apache::Constants и для си в httpd.h-include .
Code
|
Constant (Nickname)
|
Описание
|
---|
2XX Codes--Success |
200
|
HTTP_OK
(DOCUMENT_FOLLOWS)
|
URI был найден и контент определен.
|
201
|
HTTP_CREATED
|
URI создан с помощью PUT.
|
202
|
HTTP_ACCEPTED
|
Доступ к запросу
|
203
|
HTTP_NON_AUTHORITATIVE
|
Ошибка авторизации
|
204
|
HTTP_NO_CONTENT
|
Запрос правильный , но контент нулевой
|
206
|
HTTP_PARTIAL_CONTENT
(PARTIAL_CONTENT)
|
Часть контента документа
| 3XX Codes--Multiple Choices Available |
300
|
HTTP_MULTIPLE_CHOICES
(MULTIPLE_CHOICES)
|
Несколько документов
|
301
|
HTTP_MOVED_PERMANENTLY
(MOVED)
|
Документ перемещен на другой URI.
|
302
|
HTTP_MOVED_TEMPORARILY
(REDIRECT)
|
Документ временно перемещен на другой URI.
|
304
|
HTTP_NOT_MODIFIED
(USE_LOCAL_COPY)
|
Документ закэширован , обновлять не надо .
| 4XX Codes--Client-Side Errors |
400
|
HTTP_BAD_REQUEST
(BAD_REQUEST)
|
Запрос с ошибкой
|
401
|
HTTP_UNAUTHORIZED
(AUTH_REQUIRED)
|
Неправильно авторизованный клиент
|
402
|
HTTP_PAYMENT_REQUIRED
|
Деньги вперед
|
403
|
HTTP_FORBIDDEN
(FORBIDDEN)
|
Доступ к документу для клиента запрещен
|
404
|
HTTP_NOT_FOUND
(NOT_FOUND)
|
Запрашиваемый документ не существует
|
405
|
HTTP_METHOD_NOT_ALLOWED
(METHOD_NOT_ALLOWED)
|
Метод (e.g., PUT) не разрешен
|
406
|
HTTP_NOT_ACCEPTABLE
|
Запрос недоступен
|
407
|
HTTP_PROXY_AUTHENTICATION_REQUIRED
|
Proxy authentication.
|
408
|
HTTP_REQUEST_TIME_OUT
|
Клиент ждет
|
410
|
HTTP_GONE
|
Документа нет.
|
412
|
HTTP_PRECONDITION_FAILED
(PRECONDITION_FAILED)
|
Документ не найден
|
413
|
HTTP_REQUEST_ENTITY_TOO_LARGE
|
Слишком большой обьем данных от клиента
|
414
|
HTTP_REQUEST_URI_TOO_LARGE
|
Слишком длинный URI
|
415
|
HTTP_UNSUPPORTED_MEDIA_TYPE
|
MIME type не поддерживается
| 5XX Codes--Server-Side Errors |
500
|
HTTP_INTERNAL_SERVER_ERROR
(SERVER_ERROR)
|
Неопределенная ошибка
|
501
|
HTTP_NOT_IMPLEMENTED
(NOT_IMPLEMENTED)
|
Не реализовано
|
502
|
HTTP_BAD_GATEWAY
(BAD_GATEWAY)
|
Ошибка proxy
|
503
|
HTTP_SERVICE_UNAVAILABLE
|
Сервер в дауне
|
504
|
HTTP_GATEWAY_TIME_OUT
|
proxy - time out
|
505
|
HTTP_VERSION_NOT_SUPPORTED
|
Версия HTTP не поддерживается
|
506
|
HTTP_VARIANT_ALSO_VARIES
(VARIANT_ALSO_VARIES)
|
Документ представлен несколькими вариантами
|
Модуль Apache::Constants экспортирует не все HTTP_* имена ,
и при попытке вызвать некое HTTP_* имя можно получить "Undefined subroutine".
Кроме статус-кодов из этой таблицы , апач может иметь дополнительно свои внутренние :
OK - говорит о том , что хэндлер был успешным (не путать с HTTP_OK)
DECLINED - хэндлер отказывается обрабатывать запрос .
DONE - апач немедленно закрывает коннект с клиентом
Запуск хэндлера
Perl и C API по-разному запускают хэндлеры .
Для C API хэндлер определяется с помошью указателя на его функцию
в таблице внутри откомпилированного модуля .
В Perl API хэндлер запускается с помощью директив конфига
или внутри .htaccess
Для реализации хэндлера в перл нужно написать модуль .pm и добавить в конфиг
директиву Perl*Handler.
Всего в Perl API существуют 15 директив :
PerlTransHandler,PerlAccessHandler,PerlLogHandler, и т.д.
В перловом модуле нужно создать подпрограмму с именем handler().
Apache Perl модули обычно живут внтри Apache::package namespace.
Обычно Apache Perl module выглядит так :
package Apache::Foo;
use strict;
use Apache::constants qw(:common);
sub handler {
my $r = shift;
# do something
return ;
}
Его декларация в конфиге :
Apache::Foo
Можно написать целый список :
Apache::Foo Apache::Bar Apache::Baz
Apache::Wiz Apache::Waz
Функция хэндлера не обязана называться handler(),
если она например называется do_something(), то:
Apache::Foo::do_something
Perl*Handler не обеспечивает автоматическую загрузку хэндлера .
Нужно использовать директиву PerlModule в конфиге :
PerlModule Apache::Foo
Apache::Foo::do_something
Если модуль не загружается , возникает ошибка , которую можно найти в логе .
С помощью Perl*Handlers можно использовать анонимные процедуры :
PerlChildInitHandler "sub { warn qq(child $$ starting\n) }"
Perl API Configuration Directives
Здесь представлен список конфигурационных директив Perl API .
PerlRequire
PerlModule
Эти директивы используются для загрузки модулей с диска .
На перле аналогом является оператор require .
Конфиг-строка
PerlModule Apache::Plotter
соответствует коду на перл :
require Apache::Plotter;
use Apache::Plotter ();
Для директивы PerlRequire нужно указывать абсолютный путь :
PerlRequire /opt/www/lib/directory_colorizer.pl
PerlRequire scripts/delete_temporary_files.pl
и то же самое на перле:
require '/opt/www/lib/directory_colorizer.pl';
require 'scripts/delete_temporary_files.pl';
Обе этих директивы возможны со списком модулей :
PerlModule CGI LWP::Simple Apache::Plotter
PerlRequire scripts/startup.pl scripts/config.pl
Старт апача с рутовыми правами , доступ к конфигам и скриптам таит в себе опасность .
Директива PerlOpmask и PERL_ OPMASK_DEFAULT предназначена
для снижения возможного риска .
PerlChildInitHandler
Этот хэндлер срабатывает каждый раз , когда на апаче форкается дочерний процесс .
Пример :
PerlChildInitHandler Apache::DBLogin
Эта директива может находиться в конфиге внутри секции виртуального хоста ,
но не внутри <Directory>, <Location>,или <Files>
или .htaccess .
PerlPostReadRequestHandler
Этот хэндлер срабатывает каждый раз , когда апач получает запрос .
Удобное место для обработки старта транзакции .
Пример :
PerlPostReadRequestHandler Apache::StartTimer
Порядок размещения этой директивы аналогично предыдущей .
PerlTransHandler
Срабатывает тогда , когда происходит перевод URI в имя файла
Пример :
PerlTransHandler Apache::AdBlocker
Хэндлеры :
PerlHeaderParserHandler
PerlAccessHandler
PerlAuthenHandler
PerlAuthzHandler
PerlTypeHandler
PerlFixupHandler
PerlHandler
PerlLogHandler
PerlCleanupHandler
PerlChildExitHandler
PerlFreshRestart
PerlDispatchHandler
PerlRestartHandler
|