xs.json подробно · уровень хоста и vhost
Несколько сайтов в одном server: host_default подстраховывает все несовпавшие запросы, hosts[] точно разводит трафик по доменам. Глава объясняет три источника маршрутизации и раздачу сертификатов по хостам.
Обзор
8.1 Введение
Много сайтов на одной машине — обычное требование: основной сайт, API, админ-панель и несколько клиентских площадок в одном процессе. Модель XServer — два уровня: server (эндпоинт) × host (сайт): один server слушает порт, каждый запрос маршрутизируется к хосту по принципу «какому сайту он адресован»; у каждого хоста свои корень статики, скрипт и сертификаты.
8.2 Поля уровня хоста
| Поле | Ограничение | Описание |
|---|---|---|
name | уникально внутри server, непустое | имя хоста; повторы / пустое значение — fail-fast |
host | домены через точку с запятой | для http/ws с включённым vhost обязательно непустое; например "a.com;www.a.com" |
path | каталог должен существовать | корень статики; отсутствие — fail-fast |
devlang / devfile | — | c / static / свой; при static devfile может быть пустым |
tls_ca / tls_cert / tls_key | пути PEM | сертификаты уровня хоста → источник данных таблицы SNI (см. 8.4) |
Один домен привязывается ровно к одному хосту (двойная привязка — fail-fast); один хост может держать несколько доменов (через точку с запятой, сопоставление по правилам регистра доменов).
8.3 Три источника маршрутизации
«Целевой хост» HTTP-запроса может стоять в трёх местах; XServer опрашивает их по порядку:
| Источник | Форма | Типовой сценарий |
|---|---|---|
| Заголовок Host | Host: a.com:8080 | подавляющее большинство запросов (суффикс порта отрезается) |
| absolute-form | GET http://a.com/x HTTP/1.1 | прокси-подобные клиенты |
| authority-form | CONNECT a.com:443 | туннельные запросы |
- HTTP/1.1 требует Host: отсутствие или дублирование — ошибка протокола (400); это корректность поведения, а не необязательная проверка
- Не попал ни в один hosts[] → откат в host_default; смена Host внутри keep-alive-соединения тоже маршрутизируется корректно на каждый запрос
- На TLS-эндпоинте выбор личности происходит в рукопожатии (SNI, следующий раздел); маршрутизация HTTP-слоя идёт после расшифровки — слои согласованы
8.4 Сертификаты уровня хоста и SNI
Каждый http/ws-хост может нести свои tls_ca/tls_cert/tls_key. При сборке они попадают в таблицу SNI; в TLS-рукопожатии клиент называет целевой домен в ClientHello (SNI), сервер по нему выбирает сертификат — сопоставление домена и сертификата завершается в рукопожатии, до начала HTTP-запроса. Серверная cookie привязывает выбор личности из ClientHello и Accept к одному поколению, устраняя гонки рукопожатия между поколениями.
8.5 Контракт
- Маршрутизация: три источника Host → точное совпадение в hosts[] → откат в host_default; HTTP/1.1 без Host — 400.
- Сертификаты: сертификаты уровня хоста раздаются по SNI; хост без сертификатов не сможет завершить выбор личности рукопожатия на TLS-эндпоинте.
- Скрипты: у каждого хоста с devlang=c свой независимый рантайм скрипта — перезагрузка публикуется полным поколением server этого хоста.
Частые ошибки
- Симптом
- Настроили "api.com" и ждёте, что "www.api.com" тоже попадёт — а он уходит в default.
- Причина
- Привязка host — точное совпадение (список через точку с запятой сверяется целиком); ни wildcards, ни префиксов.
"host": "api.com"
/* ждём, что www.api.com тоже попадёт */"host": "api.com;www.api.com"- Симптом
- Старт падает: для http/ws с включённым vhost поле host обязательно непустое.
- Причина
- Пустой домен не имеет смысла маршрутизации — «ловить всё» — работа host_default, а не пустого host.
Упражнения
Настройте один server с host_default (сайт А) + hosts[] (сайт Б, привязан к test.local); в двух wwwroot положите разные index.html.
curl -H "Host: test.local" и запрос без заголовка Host попадают каждый в свой сайт.Через --resolve curl и absolute-form (curl -x http://127.0.0.1:8080 http://test.local/, форма прокси) вызовите источники «заголовок Host» и «absolute-form».
Напишите скрипт, отправляющий по одному keep-alive-соединению два запроса с разными Host, и проверьте маршрутизацию каждого запроса.
Итоги
| Тезис | Вывод |
|---|---|
| Двухуровневая модель | server = эндпоинт; host = сайт (корень статики/скрипт/сертификаты) |
| Маршрутизация | Три источника: Host / absolute-form / authority-form, точное совпадение |
| Откат | host_default существует всегда; несовпавшее уходит туда |
| Сертификаты | Уровень хоста → таблица SNI, раздача в рукопожатии; горячая замена |
- справочник xrt · tls_server — низкоуровневая реализация SNI-селектора и серверной cookie
- справочник xrt · http1 — детали протокола: разбор заголовка Host и absolute-form