Search
Write a publication
Pull to refresh
620.58
OTUS
Развиваем технологии, обучая их создателей

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

Level of difficultyEasy
Reading time4 min
Views395

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

Сегодня рассмотрим, как написать кастомный модуль для 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-подключения на вашем сервере. Записаться

Tags:
Hubs:
Total votes 5: ↑4 and ↓1+5
Comments0

Articles

Information

Website
otus.ru
Registered
Founded
Employees
101–200 employees
Location
Россия
Representative
OTUS