fix(voice): отдавать клиенту базовый адрес LiveKit без сегмента /rtc
LiveKit SDK сам дописывает сигнальный путь (`/rtc`, `/rtc/v1`) к базовому
адресу, поэтому `wss://host/rtc` из конфига превращался в `wss://host/rtc/rtc`
и комната навсегда оставалась в состоянии «Подключение…» (AGENT.md 7.14).
- `voice.ResolveLiveKitURL` снимает завершающий сегмент `/rtc`, сохраняя
префикс прокси (`wss://host/livekit/rtc` → `wss://host/livekit`);
- `POST /channels/{id}/voice/join` отдаёт адрес через `VoiceURLForClient` и
отвечает `voice.disabled`, если адрес не разрешился;
- тесты: таблица адресов, ожидание `wss://gl.test` в голосовом сценарии.
This commit is contained in:
+19
-3
@@ -9,6 +9,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
@@ -241,8 +242,11 @@ func subtleCompare(a, b string) bool {
|
||||
return hmac.Equal([]byte(a), []byte(b))
|
||||
}
|
||||
|
||||
// ResolveLiveKitURL приводит адрес к виду, понятному браузеру: /rtc проксируется
|
||||
// тем же доменом, поэтому оставляем как есть, но проверяем схему.
|
||||
// ResolveLiveKitURL приводит публичный адрес к базовому виду, который ждёт
|
||||
// LiveKit SDK: клиент сам дописывает сигнальный путь (`/rtc`, `/rtc/v1`),
|
||||
// поэтому сегмент `/rtc` из адреса убираем — иначе брандмауэр клиента стучится
|
||||
// в `/rtc/rtc` и комната остаётся в состоянии «Подключение…». Префикс прокси
|
||||
// сохраняется: `wss://host/livekit/rtc` → `wss://host/livekit`.
|
||||
func ResolveLiveKitURL(raw string) (string, error) {
|
||||
trimmed := strings.TrimSpace(raw)
|
||||
if trimmed == "" {
|
||||
@@ -251,7 +255,19 @@ func ResolveLiveKitURL(raw string) (string, error) {
|
||||
if !strings.HasPrefix(trimmed, "ws://") && !strings.HasPrefix(trimmed, "wss://") {
|
||||
return "", fmt.Errorf("voice.invalid_url: %s", trimmed)
|
||||
}
|
||||
return strings.TrimSuffix(trimmed, "/"), nil
|
||||
parsed, err := url.Parse(trimmed)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("voice.invalid_url: %s", trimmed)
|
||||
}
|
||||
path := strings.TrimSuffix(parsed.Path, "/")
|
||||
if path != "/rtc" {
|
||||
path = strings.TrimSuffix(path, "/rtc")
|
||||
} else {
|
||||
path = ""
|
||||
}
|
||||
parsed.Path = path
|
||||
parsed.RawPath = ""
|
||||
return strings.TrimSuffix(parsed.String(), "/"), nil
|
||||
}
|
||||
|
||||
// VoiceURLForClient отдаёт адрес LiveKit так, как его должен использовать
|
||||
|
||||
@@ -3,6 +3,7 @@ package voice_test
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -81,13 +82,30 @@ func TestParseRoomName(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestResolveLiveKitURL(t *testing.T) {
|
||||
url, err := voice.ResolveLiveKitURL("wss://gl.example/rtc/")
|
||||
if err != nil || url != "wss://gl.example/rtc" {
|
||||
t.Fatalf("ResolveLiveKitURL = %q, %v", url, err)
|
||||
cases := map[string]string{
|
||||
"wss://gl.example/rtc/": "wss://gl.example",
|
||||
"wss://gl.example/rtc": "wss://gl.example",
|
||||
"wss://gl.example": "wss://gl.example",
|
||||
"wss://gl.example/": "wss://gl.example",
|
||||
"wss://gl.example/livekit/rtc": "wss://gl.example/livekit",
|
||||
"wss://gl.example:7880/rtc": "wss://gl.example:7880",
|
||||
"ws://192.168.1.247:7880/rtc/": "ws://192.168.1.247:7880",
|
||||
"wss://gl.example/notrtc": "wss://gl.example/notrtc",
|
||||
"wss://gl.example/rtc/extra": "wss://gl.example/rtc/extra",
|
||||
"wss://gl.example/livekit/rtcx": "wss://gl.example/livekit/rtcx",
|
||||
}
|
||||
for raw, want := range cases {
|
||||
got, err := voice.ResolveLiveKitURL(raw)
|
||||
if err != nil || got != want {
|
||||
t.Fatalf("ResolveLiveKitURL(%q) = %q, %v; ожидалось %q", raw, got, err, want)
|
||||
}
|
||||
}
|
||||
if _, err := voice.ResolveLiveKitURL("http://gl.example"); err == nil {
|
||||
t.Fatal("http-адрес должен отклоняться")
|
||||
}
|
||||
if _, err := voice.ResolveLiveKitURL(""); !errors.Is(err, voice.ErrDisabled) {
|
||||
t.Fatalf("пустой адрес = %v, ожидалось ErrDisabled", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyWebhookSignature(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user