Implement processing of trickle-ice-sdpfrag.

This is required for WHIP restarts.
This commit is contained in:
Juliusz Chroboczek
2025-03-10 02:49:04 +01:00
parent 9892ddbc5e
commit 8bfea927d3
4 changed files with 543 additions and 79 deletions
+13 -47
View File
@@ -1,8 +1,6 @@
package webserver
import (
"bufio"
"bytes"
"crypto/aes"
"crypto/cipher"
crand "crypto/rand"
@@ -22,6 +20,7 @@ import (
"github.com/jech/galene/group"
"github.com/jech/galene/ice"
"github.com/jech/galene/rtpconn"
"github.com/jech/galene/sdpfrag"
)
var idSecret []byte
@@ -358,9 +357,15 @@ func whipResourceHandler(w http.ResponseWriter, r *http.Request) {
return
}
ufrag, pwd, candidates, err := parseSDPFrag(
http.MaxBytesReader(w, r.Body, sdpLimit),
)
data, err := io.ReadAll(http.MaxBytesReader(w, r.Body, sdpLimit))
if err != nil {
http.Error(w, "internal server error",
http.StatusInternalServerError)
return
}
var frag sdpfrag.SDPFrag
err = frag.Unmarshal(data)
if err != nil {
log.Printf("WHIP trickle ICE: %v", err)
http.Error(w, "bad request", http.StatusBadRequest)
@@ -374,57 +379,18 @@ func whipResourceHandler(w http.ResponseWriter, r *http.Request) {
)
return
}
if u != ufrag || p != pwd {
uu, pp := frag.UFragPwd()
if uu != u || pp != p {
http.Error(w, "ICE restarts are not supported yet",
http.StatusUnprocessableEntity,
)
return
}
for _, init := range candidates {
for _, init := range frag.AllCandidates() {
err := c.GotICECandidate(init)
if err != nil {
log.Printf("WHIP candidate: %v", err)
}
}
w.WriteHeader(http.StatusNoContent)
}
// RFC 8840
func parseSDPFrag(r io.Reader) (string, string, []webrtc.ICECandidateInit, error) {
scanner := bufio.NewScanner(r)
mLineIndex := -1
var mid, ufrag, pwd []byte
var candidates []webrtc.ICECandidateInit
for scanner.Scan() {
l := scanner.Bytes()
if bytes.HasPrefix(l, []byte("a=ice-ufrag:")) {
ufrag = l[len("a=ice-ufrag:"):]
} else if bytes.HasPrefix(l, []byte("a=ice-pwd:")) {
pwd = l[len("a=ice-pwd:"):]
} else if bytes.HasPrefix(l, []byte("m=")) {
mLineIndex++
mid = nil
} else if bytes.HasPrefix(l, []byte("a=mid:")) {
mid = l[len("a=mid:"):]
} else if bytes.HasPrefix(l, []byte("a=candidate:")) {
init := webrtc.ICECandidateInit{
Candidate: string(l[2:]),
}
if len(mid) > 0 {
s := string(mid)
init.SDPMid = &s
}
if mLineIndex >= 0 {
i := uint16(mLineIndex)
init.SDPMLineIndex = &i
}
if len(ufrag) > 0 {
s := string(ufrag)
init.UsernameFragment = &s
}
candidates = append(candidates, init)
}
}
return string(ufrag), string(pwd), candidates, nil
}
-32
View File
@@ -1,32 +0,0 @@
package webserver
import (
"strings"
"testing"
)
func TestParseSDPFrag(t *testing.T) {
sdp := `a=ice-ufrag:FZ0m
a=ice-pwd:NRT+gj1EhsEwMm9MA7ljzBRy
m=audio 9 UDP/TLS/RTP/SAVPF 0
a=mid:0
a=candidate:2930517337 1 udp 2113937151 1eaafdf1-4127-499f-90d4-8c35ea49d5e6.local 44360 typ host generation 0 ufrag FZ0m network-cost 999
2024/09/30 00:07:41 {candidate:2930517337 1 udp 2113937151 1eaafdf1-4127-499f-90d4-8c35ea49d5e6.local 44360 typ host generation 0 ufrag FZ0m network-cost 999 0xc00062a580 0xc000620288 0xc00062a590}
a=end-of-candidates`
r := strings.NewReader(sdp)
ufrag, pwd, candidates, err := parseSDPFrag(r)
if err != nil {
t.Errorf("parseSDPFrag: %v", err)
}
if ufrag != "FZ0m" || pwd != "NRT+gj1EhsEwMm9MA7ljzBRy" {
t.Errorf("Ufrag %v, pwd %v", ufrag, pwd)
}
if len(candidates) != 1 {
t.Errorf("Expected 1, got %v", candidates)
}
if *candidates[0].SDPMLineIndex != 0 ||
*candidates[0].SDPMid != "0" ||
*candidates[0].UsernameFragment != "FZ0m" {
t.Errorf("Got %v", candidates[0])
}
}