Take ClientInfo from request. (#3738)

This will allow WHIP client to pass in client info which can provide
more information about WHIP clients for telemetry.
This commit is contained in:
Raja Subramanian
2025-06-16 21:01:15 -07:00
committed by GitHub
parent 8077426312
commit 9d569e2fef
3 changed files with 82 additions and 79 deletions
+8 -4
View File
@@ -169,14 +169,18 @@ func (s *RTCRestService) validateCreate(r *http.Request) (*createRequest, int, e
return nil, http.StatusBadRequest, fmt.Errorf("malformed SDP offer: %s", err)
}
ci := ParseClientInfo(r)
if ci.Protocol == 0 {
// if no client info available (which will be mostly the case with WHIP clients), at least set protocol
ci.Protocol = types.CurrentProtocol
}
pi := routing.ParticipantInit{
Identity: livekit.ParticipantIdentity(claims.Identity),
Name: livekit.ParticipantName(claims.Name),
AutoSubscribe: true,
Client: &livekit.ClientInfo{
Protocol: types.CurrentProtocol,
},
Grants: claims,
Client: ci,
Grants: claims,
CreateRoom: &livekit.CreateRoomRequest{
Name: string(roomName),
RoomPreset: claims.RoomPreset,
+1 -75
View File
@@ -28,7 +28,6 @@ import (
"time"
"github.com/gorilla/websocket"
"github.com/ua-parser/uap-go/uaparser"
"go.uber.org/atomic"
"golang.org/x/exp/maps"
@@ -52,7 +51,6 @@ type RTCService struct {
config *config.Config
isDev bool
limits config.LimitConfig
parser *uaparser.Parser
telemetry telemetry.TelemetryService
mu sync.Mutex
@@ -71,7 +69,6 @@ func NewRTCService(
config: conf,
isDev: conf.Development,
limits: conf.Limit,
parser: uaparser.NewFromSaved(),
telemetry: telemetry,
connections: map[*websocket.Conn]struct{}{},
}
@@ -229,7 +226,7 @@ func (s *RTCService) validateInternal(log logger.Logger, r *http.Request, strict
Identity: livekit.ParticipantIdentity(claims.Identity),
Name: livekit.ParticipantName(claims.Name),
AutoSubscribe: true,
Client: s.ParseClientInfo(r),
Client: ParseClientInfo(r),
Grants: claims,
Region: region,
CreateRoom: createRequest,
@@ -549,77 +546,6 @@ func (s *RTCService) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
}
func (s *RTCService) ParseClientInfo(r *http.Request) *livekit.ClientInfo {
values := r.Form
ci := &livekit.ClientInfo{}
if pv, err := strconv.Atoi(values.Get("protocol")); err == nil {
ci.Protocol = int32(pv)
}
sdkString := values.Get("sdk")
switch sdkString {
case "js":
ci.Sdk = livekit.ClientInfo_JS
case "ios", "swift":
ci.Sdk = livekit.ClientInfo_SWIFT
case "android":
ci.Sdk = livekit.ClientInfo_ANDROID
case "flutter":
ci.Sdk = livekit.ClientInfo_FLUTTER
case "go":
ci.Sdk = livekit.ClientInfo_GO
case "unity":
ci.Sdk = livekit.ClientInfo_UNITY
case "reactnative":
ci.Sdk = livekit.ClientInfo_REACT_NATIVE
case "rust":
ci.Sdk = livekit.ClientInfo_RUST
case "python":
ci.Sdk = livekit.ClientInfo_PYTHON
case "cpp":
ci.Sdk = livekit.ClientInfo_CPP
case "unityweb":
ci.Sdk = livekit.ClientInfo_UNITY_WEB
case "node":
ci.Sdk = livekit.ClientInfo_NODE
}
ci.Version = values.Get("version")
ci.Os = values.Get("os")
ci.OsVersion = values.Get("os_version")
ci.Browser = values.Get("browser")
ci.BrowserVersion = values.Get("browser_version")
ci.DeviceModel = values.Get("device_model")
ci.Network = values.Get("network")
// get real address (forwarded http header) - check Cloudflare headers first, fall back to X-Forwarded-For
ci.Address = GetClientIP(r)
// attempt to parse types for SDKs that support browser as a platform
if ci.Sdk == livekit.ClientInfo_JS ||
ci.Sdk == livekit.ClientInfo_REACT_NATIVE ||
ci.Sdk == livekit.ClientInfo_FLUTTER ||
ci.Sdk == livekit.ClientInfo_UNITY {
client := s.parser.Parse(r.UserAgent())
if ci.Browser == "" {
ci.Browser = client.UserAgent.Family
ci.BrowserVersion = client.UserAgent.ToVersionString()
}
if ci.Os == "" {
ci.Os = client.Os.Family
ci.OsVersion = client.Os.ToVersionString()
}
if ci.DeviceModel == "" {
model := client.Device.Family
if model != "" && client.Device.Model != "" && model != client.Device.Model {
model += " " + client.Device.Model
}
ci.DeviceModel = model
}
}
return ci
}
func (s *RTCService) DrainConnections(interval time.Duration) {
s.mu.Lock()
conns := maps.Clone(s.connections)
+73
View File
@@ -20,10 +20,12 @@ import (
"net"
"net/http"
"regexp"
"strconv"
"strings"
"github.com/livekit/livekit-server/pkg/utils"
"github.com/livekit/protocol/livekit"
"github.com/ua-parser/uap-go/uaparser"
)
func HandleError(w http.ResponseWriter, r *http.Request, status int, err error, keysAndValues ...interface{}) {
@@ -82,3 +84,74 @@ func SetRoomConfiguration(createRequest *livekit.CreateRoomRequest, conf *liveki
createRequest.MaxPlayoutDelay = conf.MaxPlayoutDelay
createRequest.SyncStreams = conf.SyncStreams
}
func ParseClientInfo(r *http.Request) *livekit.ClientInfo {
values := r.Form
ci := &livekit.ClientInfo{}
if pv, err := strconv.Atoi(values.Get("protocol")); err == nil {
ci.Protocol = int32(pv)
}
sdkString := values.Get("sdk")
switch sdkString {
case "js":
ci.Sdk = livekit.ClientInfo_JS
case "ios", "swift":
ci.Sdk = livekit.ClientInfo_SWIFT
case "android":
ci.Sdk = livekit.ClientInfo_ANDROID
case "flutter":
ci.Sdk = livekit.ClientInfo_FLUTTER
case "go":
ci.Sdk = livekit.ClientInfo_GO
case "unity":
ci.Sdk = livekit.ClientInfo_UNITY
case "reactnative":
ci.Sdk = livekit.ClientInfo_REACT_NATIVE
case "rust":
ci.Sdk = livekit.ClientInfo_RUST
case "python":
ci.Sdk = livekit.ClientInfo_PYTHON
case "cpp":
ci.Sdk = livekit.ClientInfo_CPP
case "unityweb":
ci.Sdk = livekit.ClientInfo_UNITY_WEB
case "node":
ci.Sdk = livekit.ClientInfo_NODE
}
ci.Version = values.Get("version")
ci.Os = values.Get("os")
ci.OsVersion = values.Get("os_version")
ci.Browser = values.Get("browser")
ci.BrowserVersion = values.Get("browser_version")
ci.DeviceModel = values.Get("device_model")
ci.Network = values.Get("network")
// get real address (forwarded http header) - check Cloudflare headers first, fall back to X-Forwarded-For
ci.Address = GetClientIP(r)
// attempt to parse types for SDKs that support browser as a platform
if ci.Sdk == livekit.ClientInfo_JS ||
ci.Sdk == livekit.ClientInfo_REACT_NATIVE ||
ci.Sdk == livekit.ClientInfo_FLUTTER ||
ci.Sdk == livekit.ClientInfo_UNITY {
client := uaparser.NewFromSaved().Parse(r.UserAgent())
if ci.Browser == "" {
ci.Browser = client.UserAgent.Family
ci.BrowserVersion = client.UserAgent.ToVersionString()
}
if ci.Os == "" {
ci.Os = client.Os.Family
ci.OsVersion = client.Os.ToVersionString()
}
if ci.DeviceModel == "" {
model := client.Device.Family
if model != "" && client.Device.Model != "" && model != client.Device.Model {
model += " " + client.Device.Model
}
ci.DeviceModel = model
}
}
return ci
}