Коротко про то, как написать кастомный модуль для Angie

от автора

Привети, Хабр!

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

Архитектуа Angie

Разберёмся, что такое модуль в контексте Angie (и Nginx, потому что архитектура похожа).

Важные моменты:

  • Модуль — это C‑библиотека, которая загружается динамически (если настроена поддержка DSO) или встраивается на этапе компиляции.

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

  • Основные хуки: preconfiguration, postconfiguration, init_module, init_process, handler и другие.

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

Готовим окружение

Прежде чем написать модуль, установим всё необходимое:

# Ставим зависимости sudo apt update && sudo apt install build-essential libpcre3-dev libssl-dev zlib1g-dev  # Клоним Angie (если у тебя его нет) git clone https://github.com/angie-web/angie.git && cd angie  # Собираем минималку ./configure --with-debug --add-dynamic-module=../my_module make && sudo make install

В --add-dynamic-module указываем путь к нашему будущему модулю.

Пишем минимальный модуль

Модуль состоит из двух частей:

  1. Кода самого модуля

  2. Конфигурационного файла

Создаём структуру проекта:

mkdir -p ~/my_module/src cd ~/my_module

Создаём src/ngx_http_my_module.c и запихиваем туда минимальный рабочий код:

#include <ngx_config.h> #include <ngx_core.h> #include <ngx_http.h>  static ngx_int_t ngx_http_my_handler(ngx_http_request_t *r) {     ngx_str_t response = ngx_string("Hello from my module!");          r->headers_out.status = NGX_HTTP_OK;     r->headers_out.content_length_n = response.len;     ngx_http_send_header(r);          ngx_buf_t *b = ngx_create_temp_buf(r->pool, response.len);     ngx_memcpy(b->pos, response.data, response.len);     b->last = b->pos + response.len;     b->last_buf = 1;          ngx_chain_t out = { .buf = b, .next = NULL };     return ngx_http_output_filter(r, &out); }  static ngx_command_t ngx_http_my_commands[] = {     { ngx_string("my_directive"),       NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,       ngx_conf_set_flag_slot,       NGX_HTTP_LOC_CONF_OFFSET,       offsetof(ngx_http_conf_t, my_enabled),       NULL },     ngx_null_command };  static ngx_http_module_t ngx_http_my_module_ctx = {     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };  ngx_module_t ngx_http_my_module = {     NGX_MODULE_V1,     &ngx_http_my_module_ctx,     ngx_http_my_commands,     NGX_HTTP_MODULE,     NULL, NULL, NULL, NULL, NULL, NULL, NULL,     NGX_MODULE_V1_PADDING };

Этот код добавляет новую директиву my_directive, которая, когда включена, будет отвечать «Hello from my module!» на HTTP‑запрос.

Теперь создадим config файл:

echo "ngx_addon_name=\"ngx_http_my_module\" HTTP_MODULES="\$HTTP_MODULES ngx_http_my_module" NGX_ADDON_SRCS="\$NGX_ADDON_SRCS \$(ngx_feature_path ngx_http_my_module.c)"" > config

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

Компилируем модуль:

cd ~/my_module make -f ../angie/objs/Makefile modules

После сборки появится .so файл в objs/ngx_http_my_module.so. Теперь его можно подключить в angie.conf:

load_module modules/ngx_http_my_module.so;  server {     listen 8080;      location /test {         my_directive;     } }

Рестартуем Angie и проверяем:

curl -i http://localhost:8080/test

Должно вернуться:

HTTP/1.1 200 OK ... Hello from my module!

Теперь модуль готов.

Добавляем фичи

Пока наш модуль тупо шлёт текст, но сделаем что‑то полезное, например:

  • Авторизацию по токену

  • Логирование всех запросов в отдельный файл

Пример с авторизацией:

static ngx_int_t ngx_http_my_auth_handler(ngx_http_request_t *r) {     ngx_str_t token = ngx_string("supersecuretoken");          if (r->headers_in.authorization == NULL ||         ngx_strncmp(r->headers_in.authorization->value.data, token.data, token.len) != 0) {         return NGX_HTTP_FORBIDDEN;     }          return NGX_DECLINED; }

Такой обработчик можно вставить перед отдачей контента, проверяя заголовок Authorization.

Такой обработчик можно вставить перед отдачей контента, проверяя заголовок Authorization.

Логирование всех запросов в отдельный файл

static ngx_int_t ngx_http_my_logger_handler(ngx_http_request_t *r) {     ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "Incoming request: %V", &r->uri);     return NGX_DECLINED; }

Ограничение количества запросов от одного IP

static ngx_int_t ngx_http_rate_limit_handler(ngx_http_request_t *r) {     static ngx_rbtree_t *request_tracker;     static ngx_rbtree_node_t sentinel;          if (request_tracker == NULL) {         request_tracker = ngx_pcalloc(r->pool, sizeof(ngx_rbtree_t));         ngx_rbtree_init(request_tracker, &sentinel, ngx_str_rbtree_insert_value);     }          ngx_rbtree_node_t *node = ngx_rbtree_lookup(request_tracker, &r->connection->addr_text);          if (node == NULL) {         node = ngx_pcalloc(r->pool, sizeof(ngx_rbtree_node_t));         node->key = ngx_crc32_short(r->connection->addr_text.data, r->connection->addr_text.len);         ngx_rbtree_insert(request_tracker, node);     }          if (node->data >= 10) {         return NGX_HTTP_TOO_MANY_REQUESTS;     }          node->data++;     return NGX_DECLINED; }

Код отслеживает количество запросов от одного IP и ограничивает их.

Динамическое изменение заголовков ответа

static ngx_int_t ngx_http_add_dynamic_header_handler(ngx_http_request_t *r) {     ngx_table_elt_t *h = ngx_list_push(&r->headers_out.headers);     h->hash = 1;     ngx_str_set(&h->key, "X-Server-Time");     h->value.data = ngx_pnalloc(r->pool, NGX_TIME_T_LEN);     h->value.len = ngx_sprintf(h->value.data, "%T", ngx_time()) - h->value.data;     return NGX_DECLINED; }

Этот обработчик добавляет заголовок X‑Server‑Time с текущим временем.


Заключение

Дальше можно улучшать: кешировать ответы, проксировать запросы, подключать Redis. Напиши в комментах, какие ещё модули написать!

В заключение напомню про открытые уроки по Angie:

  • 17 марта. Балансировка HTTP и L4 сервисов в Angie.
    Поймёте основные типы балансировки в Angie, научитесь применять различные варианты решений для повышения отказоустойчивости веб-приложений. Записаться

  • 24 марта. Автоматические TLS-сертификаты: модуль ACME.
    Научитесь настраивать модуль ACME в Angie, а также оптимально настраивать HTTPS-подключения на вашем сервере. Записаться


ссылка на оригинал статьи https://habr.com/ru/articles/890026/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *