Подключение WebRTC: полный Trickle ICE
Браузер отправляет готовый SDP сразу, затем передаёт ICE-кандидаты по мере появления. WebRTC ingress одновременно собирает свои кандидаты и проверяет уже доступные пары. Медленный STUN на одном интерфейсе больше не задерживает рабочий TURN/host путь.
Исходный Mermaid
sequenceDiagram
participant B as Браузер
participant C as WebRTC ingress
B->>C: GET /core/rtc-capabilities
C-->>B: trickle_ice: 1
B->>C: POST /core/offer (trickle: true, SDP)
C-->>B: SDP answer + connection_id + ice_endpoint + ice_token
B->>C: WS /core/ice/{connection_id}, auth (token)
C-->>B: ready
par Кандидаты по мере появления
B->>C: candidate / end
and
C-->>B: candidate / end
end
Note over B,C: ICE checks, DTLS и RTP начинаются до общего end
B->>C: DELETE /core/ice/{connection_id} при остановкеПротокол и доступ
| Маршрут | Контракт |
|---|---|
GET /core/rtc-capabilities |
JSON {"trickle_ice":1}. Только 404 включает legacy; ошибка сети/авторизации видна пользователю |
POST /core/offer |
Прежние sdp, type, session_id; необязательный boolean trickle. Answer сохраняет прежние поля, добавляет connection_id, ice_endpoint, ice_token |
WS /core/ice/{connection_id} |
Первое сообщение {"type":"auth","token":"..."}, затем серверный {"type":"ready"}. Авторизация не более 3 секунд |
candidate |
{"type":"candidate","candidate":RTCIceCandidateInit}; sdpMid/sdpMLineIndex принадлежат согласованному audio media; usernameFragment, если есть, принадлежит этому SDP |
end |
{"type":"end"} отдельно от кандидата; завершает сбор в соответствующую сторону |
error |
Безопасное описание ошибки, без тела входного сообщения |
DELETE /core/ice/{connection_id} |
Заголовок X-ICE-Token; 204 при освобождении, 404 при неизвестном подключении/неверном токене |
Все маршруты входят в существующий защищённый OAuth /core ingress, включая WS
handshake. Токен соединения случайный, только в памяти, не в URL/логах. До его
проверки сервер не выдаёт кандидаты; неверный токен не отменяет попытку владельца.
Очередь сохраняет ранние кандидаты; предел 128, сообщение до 16 КиБ, дубликаты
отбрасываются. Текущий Trickle-профиль — один audio media с RTCP mux, UDP ICE
(включая TURN/TCP как транспорт до relay). TCP host-кандидаты Chrome учитываются
в лимите и пропускаются без отмены доступных UDP-пар. ICE restart и авто-reconnect не реализованы.
Браузерный ICE-обработчик устанавливается до offer. Локальная очередь переживает
получение answer и открытие WS; серверная — появление кандидата до WS auth. Ответ
SDP принимается до обработки серверных WS-кандидатов. Старый клиент без trickle
продолжает использовать полное серверное gathering и прежний answer.
Адаптер и сроки
trickle_ice.py изолирует внутренние API
aiortc==1.14.0 / aioice==0.10.2. Версии проверяются при запуске WebRTC ingress (также в
image build) и создании Trickle peer. Обычные peer не используют этот адаптер.
Host sockets доступны сразу; STUN/TURN выполняются параллельно, каждый результат
публикуется отдельно. ICE не объявляется failed, пока возможны новые кандидаты.
После nomination поздние кандидаты не заменяют выбранную пару. Сохраняются
DATA indication / peer-reflexive и закрытие STUN-транзакций из
aioice_compat.py.
Сигнализация нового клиента — 10 секунд, установление media — 20 секунд после
SDP answer. Разрешение микрофона не входит в эти сроки. Незавершённый сбор
отдельного кандидата не прерывает работающее аудио. Legacy frontend fallback
сохраняет прежний бюджет iceGatherTimeoutMs + 20000.
Потеря ICE-канала при подключении завершает попытку. После connected появляется
предупреждение, аудио продолжается. Явная остановка закрывает WS/peer, освобождает
STUN/TURN sockets, задачи gathering/checks, таймеры и запись соединения. История,
NATS subjects, PCM и ASR mode не меняются.
Проверки и поставка
Реальные aiortc tests устанавливают DTLS, отправляют Opus/RTP и получают PCM при ожидающем STUN на обеих сторонах, в том числе без кандидатов в исходном SDP. Проверяется стабильность поздней пары и закрытие незавершённого TURN allocation. HTTP/WS tests проверяют токен, очередь, MID, ufrag, лимиты, end, DELETE и разрывы. Frontend tests покрывают ранний offer, 404 legacy, ошибки доступа и отмену.
Поставка: backend ingress/Core → consumers Frontend/Ruby с проверкой OCI revision и digest настоящих tasks. Первое переименование заранее готовит полный состав нового namespace. Порядок, сохранение данных и совместимый откат описаны в DEPLOYMENT. TURN и соседние стэки не меняются.
Приёмка в Chrome: пять подключений в той же сети, медиана local SDP → connected менее 3 секунд; выбранный relay/prflx путь, DTLS, растущий RTP, первый decoded PCM, partial → final → ответ ассистента; текстовый чат/история, legacy и отказ новых HTTP/WS без OAuth. HTTP 200 и зелёный CI не заменяют эти сценарии.
Приёмка Trickle ICE · 05.10.2026
Датированный отчёт: пять подключений в той же Chrome-сессии, медиана local SDP → connected 1245 мс вместо 16108 мс. Проверены DTLS/RTP через TURN, partial/final/ответ, текст/история, legacy, OAuth negative и потеря ICE-канала после connected. STUN/TURN сохранены; это измерение подключения в текущей сети, без оценки физического микрофона/WER.