Плагин Webserver
Плагин webserver предоставляет HTTP и WebSocket конечные точки, перенаправляющие JSON-RPC запросы в плагин json_rpc. Включает кеш ответов, ключом которого служит method + params (не id), инвалидируемый при каждом применённом блоке, и пул потоков для параллельной обработки запросов.
Исходник: plugins/webserver/webserver_plugin.cpp
Зависимости
json_rpc::pluginКонфигурация
| Опция | По умолчанию | Описание |
|---|---|---|
webserver-http-endpoint | — | Адрес прослушивания HTTP, например 0.0.0.0:8090 |
webserver-ws-endpoint | — | Адрес прослушивания WebSocket, например 0.0.0.0:8091 |
webserver-thread-pool-size | 256 | Рабочие потоки для обработки HTTP и WebSocket запросов. |
webserver-cache-enabled | true | Включить кеш ответов. |
webserver-cache-size | 10000 | Максимальное количество кешированных ответов. Кеш полностью вытесняется при достижении этого лимита. |
webserver-stats-enabled | false | Включить учёт запросов по IP, лог статистики и (если не переопределено) лимиты. |
webserver-limits-enabled | значение webserver-stats-enabled | Применять лимиты по IP. Лимитам нужны счётчики из webserver-stats-enabled. |
webserver-limit-m | 1000 | Максимум запросов с одного IP в минуту. |
webserver-limit-h | 10000 | Максимум запросов с одного IP в час. |
webserver-limit-d | 100000 | Максимум запросов с одного IP в сутки. |
webserver-stats-log | <data-dir>/webserver-stats.log | Путь лога статистики; ротация по размеру. |
webserver-stats-log-max-size | 10485760 (10 MiB) | Ротировать лог при превышении размера. |
webserver-stats-log-keep | 5 | Сколько ротированных файлов (.1 … .N) хранить. |
webserver-stats-interval | 60 | Интервал в секундах между снимками в лог статистики. |
webserver-admin-state | <data-dir>/webserver-admin.json | Живое состояние (лимиты, списки IP, флаг статистики), сохраняемое node_admin. |
Для работы плагина должно быть задано хотя бы одно из webserver-http-endpoint или webserver-ws-endpoint. Оба могут быть включены одновременно.
Опции статистики и лимитов — только стартовое значение. После запуска живые значения принадлежат плагину и меняются через node_admin (см. Управление статистикой и лимитами на ходу); каждое изменение применяется к следующему запросу и сохраняется в webserver-admin-state.
Минимальный config.ini
plugin = webserver
webserver-http-endpoint = 0.0.0.0:8090
webserver-ws-endpoint = 0.0.0.0:8091Кеш ответов
Что кешируется
Каждый ответ JSON-RPC только для чтения подходит для кеширования. Ключ кеша — это SHA-256 хеш method + params — намеренно исключая id, чтобы ротация id запроса не могла обойти кеш.
Что никогда не кешируется
| Пространство имён | Причина |
|---|---|
network_broadcast_api.* | Изменяющий состояние (трансляция транзакций/блоков) |
| Некорректные/нераспознаваемые запросы | Невозможно надёжно вычислить ключ |
Пакетные запросы (JSON-массив) обрабатываются как единственная атомарная запись кеша с ключом на основе хеша полного массива.
Инвалидация
Кеш очищается при каждом применённом блоке. Ответы никогда не обслуживаются устаревшими дольше одного интервала блока (3 секунды до HF16, 1 секунда после).
Отключение кеша
webserver-cache-enabled = falseОтключите для узлов, обслуживающих клиентов с высокими требованиями к задержке или клиентов реального времени, для которых окно кеша (3 секунды до HF16, 1 секунда после) неприемлемо.
Пул потоков
Серверы HTTP и WebSocket каждый работают на выделенном экземпляре io_service. Входящие запросы направляются в общий пул потоков из webserver-thread-pool-size рабочих.
Рекомендации по размеру:
- Публичный API-узел со смешанным трафиком чтения/записи:
256(по умолч.) достаточно для большинства нагрузок. - Высокопроизводительные узлы: увеличьте до
512или более. Следите за насыщением CPU — больше потоков, чем ядер CPU, не помогает для операций, ограниченных CPU. - Разработка/локальный узел: достаточно
4–8.
Учёт запросов и лимиты
По умолчанию выключено. webserver-stats-enabled = true включает подсчёт запросов по IP-адресу клиента и лимиты (или задайте webserver-limits-enabled явно, чтобы разделить эти две вещи).
Лимиты
| Окно | По умолчанию |
|---|---|
| минута | 1000 запросов |
| час | 10000 запросов |
| сутки | 100000 запросов |
Окна фиксированные (минутная корзина сбрасывается на границе минуты, часовая — на границе часа, суточная — в полночь UTC), и каждый запрос проверяется по всем трём. Запрос сверх любого лимита отклоняется:
- HTTP:
429 Too Many Requests,Retry-After: 60, телоrate limit exceeded. - WebSocket: ошибка JSON-RPC
-32006 rate limit exceeded.
Это осознанная защита от скрапинга и парсерного трафика: тем, кому нужно больше, следует поднимать свою ноду.
Списки
- Белый список — адреса, обходящие лимиты. Они всё равно считаются и видны в статистике.
- Чёрный список — адреса, получающие
403 Forbidden(HTTP) или-32005 forbidden(WebSocket). Чёрный список важнее белого. Он действует даже при выключенной статистике. - Добавление адреса в один список удаляет его из другого.
Что содержит лог статистики
Каждые webserver-stats-interval секунд плагин дописывает строку на каждый отслеживаемый IP плюс итог:
2026-10-19T12:00:00Z ip=203.0.113.7 total=4210 minute=320 hour=4210 day=4210 blocked=0 limited=3 whitelisted=0 blacklisted=0 last_seen=1760875200
2026-10-19T12:00:00Z summary total_requests=4210 blocked_requests=0 limited_requests=3 tracked_ips=1Файл ротируется по размеру: <имя> становится <имя>.1, старые файлы сдвигаются в .2 … .webserver-stats-log-keep, самый старый удаляется.
Адрес клиента
Плагин использует TCP-адрес пира соединения. За nginx все запросы выглядят приходящими от прокси (127.0.0.1), поэтому лимиты по IP становятся глобальными, а отчёт показывает одну строку. Варианты: публиковать API напрямую, внести прокси в белый список и ограничивать на уровне прокси (limit_req), либо включить поддержку PROXY protocol на уровне прокси/сети.
Управление статистикой и лимитами на ходу
Изменяющие методы требуют скоупа webserver у токена node_admin; чтению достаточно read:
| Метод | Назначение |
|---|---|
admin.webserver.status | Лимиты, итоги, списки IP, число отслеживаемых адресов |
admin.webserver.ips | Счётчики по IP (limit до 5000), сортировка по числу запросов |
admin.webserver.limits | Изменить enabled, per_minute, per_hour, per_day (опущенные поля сохраняют значение) |
admin.webserver.stats | Включить/выключить учёт (`{"enabled": true |
admin.webserver.whitelist_add / whitelist_remove | Правка белого списка ({"ip": "..."}) |
admin.webserver.blacklist_add / blacklist_remove | Правка чёрного списка ({"ip": "..."}) |
admin.webserver.reset | Обнулить счётчики, сохранив списки и лимиты |
admin.webserver.reload | Перечитать webserver-admin-state с диска |
Каждый из них также доступен как MCP-инструмент с тем же именем. Изменения применяются к живому плагину сразу — без перезапуска и без перечитывания config.ini. Они записываются в webserver-admin-state (атомарно: временный файл + rename), который восстанавливает политику при совпадении отпечатка config_policy со стартовыми stats/limits. Изменённые параметры config.ini имеют приоритет; IP-списки восстанавливаются независимо. Старые файлы без отпечатка восстанавливают только IP-списки. Сам config.ini не перезаписывается. reload нужен оператору, правившему JSON руками; обычным изменениям он не требуется.
{"jsonrpc":"2.0","id":1,"method":"admin.webserver.limits",
"params":{"per_minute":600,"per_hour":8000}}WebSocket-подписки
WebSocket-клиенты могут регистрировать колбеки:
| Метод | Описание |
|---|---|
database_api.set_block_applied_callback | Вызывается при каждом применённом блоке с заголовком блока |
database_api.set_pending_transaction_callback | Вызывается, когда транзакция поступает в пул ожидания |
database_api.cancel_all_subscriptions | Отписаться от всех колбеков |
Подписки требуют постоянного WebSocket-соединения. Они недоступны по обычному HTTP.
CORS
Плагин webserver обрабатывает межсайтовые (cross-origin) запросы браузера самостоятельно — для локальных и dev-окружений обратный прокси не требуется.
Preflight-запросы (OPTIONS) получают немедленный ответ с заголовками:
| Заголовок ответа | Значение |
|---|---|
Access-Control-Allow-Origin | * |
Access-Control-Allow-Methods | POST, GET, OPTIONS |
Access-Control-Allow-Headers | Content-Type, Authorization |
Access-Control-Max-Age | 86400 |
Все остальные HTTP-ответы содержат Access-Control-Allow-Origin: *.
Это позволяет браузерным кошелькам и dApp обращаться к JSON-RPC-эндпоинту напрямую. Нода сама разрешает CORS — она проставляет эти заголовки в каждом ответе, в том числе работая за обратным прокси. Поэтому конфиг nginx из раздела «Публикация API через HTTPS (nginx + certbot)» ниже только проксирует запросы и не ставит собственных CORS-заголовков.
Не добавляйте
add_header Access-Control-Allow-Originна уровне nginx, если его уже ставит нода: два одинаковых заголовка заставят браузер отклонить ответ. Если вы дополнительно отдаёте статику (API-эксплорер или SPA) с того же домена, добавляйте CORS-заголовок только на статичных локациях — и только дляGET/HEAD— чтобы у проксируемых ответов оставался единственный заголовок, проставленный нодой.
Безопасность
- Привяжите к localhost (
127.0.0.1) и используйте обратный прокси (nginx/Caddy) для публичного доступа. Привязка к0.0.0.0открывает RPC напрямую в сеть. - Плагин не имеет встроенной аутентификации. Есть необязательные лимиты по IP и списки адресов (см. Учёт запросов и лимиты), но это грубые инструменты — аутентификацию и общую фильтрацию держите на уровне обратного прокси.
- Мутирующие методы (
network_broadcast_api) защищены от отравления кеша по замыслу, но они остаются доступными для вызова с любого подключённого клиента — при необходимости ограничьте доступ на сетевом уровне.
Устранение неполадок
| Симптом | Проверка |
|---|---|
| Порт уже занят при запуске | Другой процесс привязан к настроенному порту; измените порт или завершите конфликтующий процесс |
| Высокое использование памяти | Уменьшите webserver-cache-size или отключите кеширование |
| Медленные ответы под нагрузкой | Увеличьте webserver-thread-pool-size; проверьте насыщение CPU |
| WebSocket-подписки не срабатывают | Подписки требуют WebSocket-соединения, а не HTTP |
| Устаревшие ответы | Если webserver-cache-enabled = true, ответы актуальны в пределах одного интервала блока (~3 с до HF16, ~1 с после); для использования в реальном времени отключите кеш |
Публикация API через HTTPS (nginx + certbot)
Привяжите узел к localhost, затем поставьте перед ним nginx. certbot автоматически дополнит конфиг при запуске certbot --nginx.
1. config.ini узла
webserver-http-endpoint = 127.0.0.1:8090
webserver-ws-endpoint = 127.0.0.1:80912. /etc/nginx/sites-enabled/viz-node
server {
listen 80;
server_name your.domain.com; # ← замените на ваш домен
# ACME-challenge для certbot
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
proxy_pass http://127.0.0.1:8090;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
}3. Получить TLS-сертификат
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d your.domain.comcertbot автоматически добавит блок listen 443 ssl, директивы ssl_certificate и редирект HTTP→HTTPS. После этого узел доступен по адресу https://your.domain.com.
WebSocket через HTTPS
WebSocket-клиентам нужна проксировка заголовка Upgrade. Добавьте отдельный location (или второй server block на порту 8091):
location /ws {
proxy_pass http://127.0.0.1:8091;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 3600s;
}См. также: Обзор плагинов, Database API, JSON-RPC API.
Доверенные reverse proxy и сохранение политики
По умолчанию адрес берётся из TCP-соединения. За nginx явно задайте webserver-trusted-proxy = 127.0.0.1 (повторите для каждого IP своего прокси) и передавайте X-Forwarded-For. Цепочка разбирается справа налево до первого недоверенного узла; произвольные клиентские IP в доверенные не добавляйте.
При достижении 200000 IP удаляются записи старше суток; пока места нет, новые источники отклоняются, а действующие окна лимитов не сбрасываются. Включённые лимиты работают и без статистического логирования. Лог записывает изменённые строки, форматирование и дисковая запись вынесены из mutex приёма запросов. Отпечаток config_policy позволяет новым параметрам config.ini перекрыть старую runtime-политику, сохранив IP-списки.