Mercurial Hosting > d2o
view README.md @ 2:d19133be91ba
ndex and smarter parser
| author | Atarwn Gard <a@qwa.su> |
|---|---|
| date | Mon, 09 Mar 2026 01:55:11 +0500 |
| parents | 3e7247db5c6e |
| children |
line wrap: on
line source
# d2o Минималистичный веб-сервер на Go с конфигурацией в формате ICF. ## Структура проекта ``` d2o/ go.mod cmd/d2o/ main.go — точка входа, HTTP-обработчик, сборка листеров icf/ icf.go — парсер формата ICF (переиспользуемая библиотека) fcgi/ fcgi.go — минимальный FastCGI-клиент ``` ## Сборка и запуск ```sh go build -o d2o ./cmd/d2o/ ./d2o # читает /etc/d2obase ./d2o /path/to/config # альтернативный путь ``` --- ## Формат конфигурации ICF **ICF (Inherited Configuration Format)** — текстовый формат правил вида «паттерн → директивы». Вдохновлён синтаксисом bash и базами данных CoreDNS. ### Основные правила синтаксиса ``` ; Это комментарий ; Переменная (без пробелов вокруг =) KEY=value ; Абстрактный блок (миксин) @name |> directive arg1 arg2 ; Конкретный блок с наследованием миксина block.id @mixin |> directive arg1 arg2 ; Блок с именованным capture-группой <sub>.example.com |> root /srv/$sub ``` ### Переменные Объявляются как `KEY=value` на уровне файла. Подставляются через `$KEY` в аргументах директив. ``` WWW=/srv/www CERT=/etc/acme/example.com example.com |> root $WWW/root |> tls $CERT.{crt,key} ``` ### Brace expansion Аргумент `prefix.{a,b,c}` раскрывается в три отдельных аргумента: `prefix.a`, `prefix.b`, `prefix.c`. Порядок важен — используется для `tls`, где первый аргумент сертификат, второй ключ. ``` |> tls /etc/acme/example.com.{crt,key} ; эквивалентно: |> tls /etc/acme/example.com.crt /etc/acme/example.com.key ``` ### Capture-группы В идентификаторе блока `<name>` захватывает любую подстроку до следующего литерала. `<_>` — анонимный wildcard, ничего не сохраняет. ``` <sub>.example.com |> root /srv/$sub ; $sub подставляется из захваченного значения <_>.static.example.com |> root /srv/static ; совпадает с чем угодно, capture не нужен ``` Capture жадный слева: `<sub>.example.com` на запрос `a.b.example.com` даст `sub=a.b`. ### Кавычки в аргументах Аргументы с пробелами берутся в двойные кавычки: ``` |> fcgi unix:/run/php-fpm.sock "*.php" ``` ### Абстрактные блоки и наследование `@name` задаёт набор директив без привязки к паттерну. Конкретный блок наследует их через `block.id @name` — директивы миксина идут первыми, директивы блока их перекрывают. ``` @base |> port 80 |> port+tls 443 example.com @base |> root /srv/www ; наследует port 80 и port+tls 443 other.com @base |> root /srv/other ; то же самое ``` ### Матчинг блоков - Домен матчится точно (с учётом capture-групп). - Путь матчится как префикс: блок `example.com/api` сработает на `/api/users`. - При нескольких совпадениях выигрывает наиболее специфичный (больше совпавших литеральных символов). - Сначала проверяется `host/path`, потом `host`. ``` example.com |> root /srv/www example.com/api |> rprx 127.0.0.1:8080 ; перекрывает блок выше для /api/* ``` --- ## Конфигурация d2o Файл по умолчанию: `/etc/d2obase` ### @d2o — настройки сервера ``` @d2o |> threads 512 ; GOMAXPROCS ``` | Директива | Аргументы | Описание | |------------|-----------|----------| | `threads` | N | Количество потоков ОС (GOMAXPROCS) | ### port — HTTP-листенер ``` |> port 80 ``` | # | Тип | Описание | |---|-------|--------------| | 1 | `int` | Номер порта | ### port+tls — HTTPS-листенер ``` |> port+tls 443 ; сертификат берётся из директивы tls того же блока ``` Знак `+` в имени директивы — обычный символ, не оператор. | # | Тип | Описание | |---|--------|-----------------------------------------------| | 1 | `int` | Номер порта | | 2 | `path` | Путь к сертификату (необязателен, если есть `tls`) | | 3 | `path` | Путь к ключу (необязателен, если есть `tls`) | ### tls — пути к сертификату и ключу ``` |> tls /etc/acme/example.com.{crt,key} ``` Используется как источник сертификата для `port+tls` в том же блоке, если пути не указаны явно. | # | Тип | Описание | |---|--------|-------------------| | 1 | `path` | Путь к сертификату | | 2 | `path` | Путь к ключу | ### root — отдача статики ``` |> root /srv/www/example.com |> root /srv/www/example.com hide ; листинг запрещён (по умолчанию) |> root /srv/www/example.com show ; листинг разрешён, index-файл index.html |> root /srv/www/example.com show index.php index.html ; свои index-файлы ``` | # | Тип | Описание | |---|--------|---------------------------------| | 1 | `path` | Корневая директория | | 2 | `show\|hide` | Режим директорий (по умолчанию `hide`) | | 3–14 | `filename` | Index-файлы (только с `show`), проверяются по порядку | При `show`: сначала ищутся index-файлы по списку, если ни один не найден — отдаётся листинг директории. При `hide` или без аргумента: запрос к директории возвращает 403. ### fcgi — FastCGI ``` |> fcgi unix:/run/php-fpm.sock |> fcgi unix:/run/php-fpm.sock "*.php" ; только .php файлы |> fcgi 127.0.0.1:9000 "*.php" ``` | # | Тип | Описание | |---|---------|----------------------------------------------------| | 1 | `addr` | Адрес сокета: `unix:/path` или `host:port` | | 2 | `glob` | Паттерн файлов (по умолчанию `*`, все запросы) | Если glob не совпадает, запрос падает в `root` (если задан). ### rprx — обратный прокси ``` |> rprx 127.0.0.1:3000 |> rprx http://127.0.0.1:3000 ``` | # | Тип | Описание | |---|-------|---------------------------------| | 1 | `url` | Адрес backend (схема необязательна, по умолчанию `http://`) | ### Приоритет директив При одновременном наличии нескольких директив порядок обработки: **rprx > fcgi > root**. --- ## Пример полного конфига ``` ; /etc/d2obase ACME=/etc/acme/qwaderton.org WWW=/srv/www/qwaderton.org @d2o |> threads 512 @ports |> tls $ACME.{crt,key} |> port 80 |> port+tls 443 qwaderton.org @ports |> root $WWW/root show index.php index.html qwaderton.org/webfeather @ports |> root $WWW/root/webfeather |> fcgi unix:/run/php-fpm.sock *.php <sub>.qwaderton.org @ports |> root $WWW/$sub ``` --- ## Известные ограничения - **Один миксин на блок.** Множественное наследование не поддерживается. - **Brace expansion без вложенности.** `{a,{b,c}}` не работает. - **FastCGI — один запрос на соединение.** Keep-alive с FPM не реализован. - **Нет HTTP→HTTPS редиректа** из коробки — нужно реализовывать отдельным блоком на порту 80. - **Нет hot reload** конфига — требуется перезапуск процесса.
