пятница, 31 июля 2009 г.

Написание плагинов для Lighttpd: Введение

Lighttpd — это легкий, быстрый и надежный веб-сервер. Его исходный код вмести со всеми стандартными плагинами составляет всего-на-всего 3,4 МБ (для версии 1.4.23), что довольно мало по сравнения, скажем, с веб-сервером Apache, размер которого достигает 31 МБ (версия 2.2.12). Но меньший размер кода говорит и о меньшей функциональности. Поэтому в некоторых ситуациях встает вопрос о расширении этого функционала за счет написания новых плагинов — динамически подключаемых модулей.

Не смотря на большое количество плюсов, которыми обладает этот замечательный сервер, у него есть и свои минусы. Один из из них, возможно даже наибольший — это очень скудная документация. На сайте проекта этому уделено всего несколько небольших wiki-страничек, которые почти полностью дублируют txt-файлы из каталога doc исходных кодов lighttpd. Из всей этой не богатой документации программистам посвящён только один раздел Plugins, описывающий ловушки, которые сервер предоставляет своим модулям. Остальная же документация, в основном, посвящена конфигурированию самого сервера и его модулей. Основным же руководством для разработчика в такой ситуации остаются только исходный код проекта. Благо он имеет довольно хорошую архитектуру, читабельный вид и не большой размер.

Структура плагина

Итак, наилучшем способом начать знакомство с плагинами lighttpd будет просмотр исходного кода самого простого модуля. Таковым является mod_skeleton.c, находящийся в папке src. Этот небольшой модуль всего-то и делает, что запрещает доступ к файлам, указанным в переменной skeleton.array конфигурационного файла. Но это не основная его обязанность. Основная же его задача — это быть шаблоном для новых плагинов. При создания нового модуля вы можете просто скопировать файл mod_skeleton.c с новым именем в тот же каталог и заменить в нём все "skeleton" на название вашего модуля. Имена исходных файлов могут быть любыми, а вот скомпилированный модуль должен иметь вид mod_имя_модуля.so для POSIX систем и mod_имя_модуля.dll для Windows систем. Теперь рассмотрим основные структуры и функции этого шаблона.
typedef struct {
array *match;
} plugin_config;

Структура plugin_config должна содержать переменные, значения которых будет браться из файла конфигураций.
typedef struct {
PLUGIN_DATA;
buffer *match_buf;
plugin_config **config_storage;
plugin_config conf;
} plugin_data;

Структура plugin_data — это основная структура модуля, содержащая всю информацию необходимую для его работы.
typedef struct {
size_t foo;
} handler_ctx;

Структура handler_ctx содержит данные специфические для каждого соединения.

int mod_skeleton_plugin_init(plugin *p)

Эта фунция вызывается при загрузке модуля сервером. Её имя обязательно должно иметь формат mod_имя_модуля_plugin_init. Здесь нужно заполнить структуру plugin, содержащую информацию о модуле: версию сервера, название модуля, обработчики ловушек.

INIT_FUNC(mod_skeleton_init)

Инициализация плагина. Вызывается после загрузки плагина.

FREE_FUNC(mod_skeleton_free)

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

SETDEFAULTS_FUNC(mod_skeleton_set_defaults)

Вызывается при конфигурировании сервера. Здесь для переменных из plugin_config вводятся соответствия с записями из конфигурационного файла. В данном случае наша переменная match будет соотвествовать записи skeleton.array и иметь тип массив. Подробнее об этой не мало важной функции и о функции mod_skeleton_patch_connection мы узнаем в следующей части этой статьи.

URIHANDLER_FUNC(mod_skeleton_uri_handler)

А это то, ради чего все и затевалось. Эта функция вызывается после того как на сервер пришел запрос на выдачу странички и до того как сервер отправил ответ, а конкретнее после обработки uri на символы типа .. и %20. Здесь мы можем делать с сервером все что угодно, от изменения самого uri, до форсирования отправки запроса. В нашем случае здесь проверяется uri на вхождение в список match.

Все эти функции были указанны в качестве обработчиков определенных событий в mod_skeleton_plugin_init:
p->init = mod_skeleton_init;
p->handle_uri_clean = mod_skeleton_uri_handler;
p->set_defaults = mod_skeleton_set_defaults;
p->cleanup = mod_skeleton_free;

Но кроме этих ловушек есть и другие.

Ловушки сервера

init - вызывается после загрузки плагина
cleanup - вызывается перед выгрузкой плагина
set_defaults - вызывается во время конфигурации
handle_trigger - вызывается один раз в секунду
handle_sighup - вызывается когда сервер получил SIGHUP

Ловушки соединения

handle_uri_raw — вызывается после установки uri.path_raw, uri.authority и uri.scheme
handle_uri_clean — вызывается после "очистки" uri.path от последовательностей типа .. и %20
handle_docroot — вызывается в конце обработки логического пути (указанного пользователем в браузере) при получении docroot
handle_physical — вызывается после формирования физического пути, если не найден другой обработчик этого запроса
handle_subrequest_start — вызывается если установлен и проверен физический путь
handle_subrequest — вызывается в конце http_response_prepare
handle_request_done — вызывается когда запрос окончен
handle_connection_close — вызывается когда соединение закрыто
handle_joblist — called after the connection_state_engine is left again and plugin internal handles have to be called
connection_reset — вызывается при очистки структуры connection

Сборка

Для сборки вашего модуля надо будет добавить нижележащие строки в src/Makefile.am, естественно, заменив skeleton на имя вашего модуля.
lib_LTLIBRARIES += mod_skeleton.la
mod_skeleton_la_SOURCES = mod_skeleton.c
mod_skeleton_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined
mod_skeleton_la_LIBADD = $(common_libadd)

Предупреждение: модули, собранные под одну версию lighttpd, не смогут быть запущены под другой.

Ссылки

1. Официальный сайт Lighttpd
2. Документация для разработчиков Lighttpd

P.S.
Уже после написания этой статьи я нашёл книжку Андре Богуса "Lighttpd". Далее, уже зная, что такая книга существует и даже скачав её, я нашёл на официальном сайте ссылочку на неё, а рядом и ссылку на другую книгу "Lighttpd: The painless way". Правда по этой ссылке находилась только статья "Deploying a Rails application with Lighttpd", которую полноценной книгой ну никак не назовёшь. Впрочем, двухсотстраничной книги Богуса вполне хватит, не только для того чтобы удовлетворить свое любопытство по lighttpd, но и для того чтобы стать гуру в этом веб-сервере. Так, что с документаций, пусть даже и не официальной, у lightttpd все в порядке.

6 комментариев:

  1. Миша мозг!
    кстати, а где применяется lighthttpd ?

    ОтветитьУдалить
  2. blastav, там, где надо быстро и надежно.
    "Зачастую lighttpd используется для отдачи статического содержимого, в то время как его генерацией занимается более сложный веб-сервер. Такие посещаемые сайты как SourceForge.net, Imageshack.us, mirror.dkm.cz используют lighttpd на своих серверах." (с) wikipedia.
    Youtuby тоже его использует.

    ОтветитьУдалить
  3. А что заставило тебя разбираться с написанием плагинов под lighttpd?

    ОтветитьУдалить
  4. Nikolay, работа. Пишу модуль для кеширования страничек.

    ОтветитьУдалить
  5. Подскажите, а возможно изменить как-либо контент перед выдачей его клиенту?

    ОтветитьУдалить
  6. Да возможно. Смотрите в сторону chunk. Для примера можно глянуть модуль mod_cgi.

    ОтветитьУдалить