Squash integration of the reviewed Go core, frontend audit fixes, release tooling, and deployment hygiene. CI passed all Server/Client jobs.
202 lines
6.7 KiB
Go
202 lines
6.7 KiB
Go
package protocol
|
|
|
|
import (
|
|
"errors"
|
|
"regexp"
|
|
"slices"
|
|
"strings"
|
|
)
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// src/shared/media-copy-accounting.ts
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// EndpointMediaCopyPhase is EndpointMediaCopyPhase.
|
|
type EndpointMediaCopyPhase string
|
|
|
|
// Endpoint media copy phases.
|
|
const (
|
|
EndpointMediaCopySteady EndpointMediaCopyPhase = "steady"
|
|
EndpointMediaCopyTransition EndpointMediaCopyPhase = "transition"
|
|
)
|
|
|
|
// ErrEndpointMediaCopyCapacity is the assertEndpointMediaCopyCapacity throw.
|
|
var ErrEndpointMediaCopyCapacity = errors.New(
|
|
"Endpoint media copy capacity must be 1, 2, or 3")
|
|
|
|
// EndpointMediaCopyLimit ports endpointMediaCopyLimit. It panics on an invalid
|
|
// capacity, which is where the TypeScript throws.
|
|
func EndpointMediaCopyLimit(capacity int, phase EndpointMediaCopyPhase) int {
|
|
if err := AssertEndpointMediaCopyCapacity(capacity); err != nil {
|
|
panic(err)
|
|
}
|
|
if phase == EndpointMediaCopySteady {
|
|
return capacity
|
|
}
|
|
return min(capacity+1, MaxEndpointMediaCopyCapacity)
|
|
}
|
|
|
|
// EndpointMediaCopyCountFits ports endpointMediaCopyCountFits.
|
|
func EndpointMediaCopyCountFits(count, capacity int, phase EndpointMediaCopyPhase) bool {
|
|
return count >= 0 &&
|
|
IsEndpointMediaCopyCapacity(capacity) &&
|
|
count <= EndpointMediaCopyLimit(capacity, phase)
|
|
}
|
|
|
|
// IsEndpointMediaCopyCapacity ports isEndpointMediaCopyCapacity. The
|
|
// TypeScript Number.isSafeInteger guard is implicit in Go's int.
|
|
func IsEndpointMediaCopyCapacity(capacity int) bool {
|
|
return capacity >= 1 && capacity <= MaxEndpointMediaCopyCapacity
|
|
}
|
|
|
|
// AssertEndpointMediaCopyCapacity ports assertEndpointMediaCopyCapacity.
|
|
func AssertEndpointMediaCopyCapacity(capacity int) error {
|
|
if !IsEndpointMediaCopyCapacity(capacity) {
|
|
return ErrEndpointMediaCopyCapacity
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// src/shared/nat-candidate.ts
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// CandidateSignalOrigin is CandidateSignalOrigin.
|
|
type CandidateSignalOrigin string
|
|
|
|
// Candidate signal origins.
|
|
const (
|
|
CandidateOriginEnd CandidateSignalOrigin = "end"
|
|
CandidateOriginOrdinary CandidateSignalOrigin = "ordinary"
|
|
CandidateOriginPredicted CandidateSignalOrigin = "predicted"
|
|
)
|
|
|
|
// The JavaScript \s class, spelled out because Go's \s is ASCII only.
|
|
var predictedCandidatePattern = regexp.MustCompile(
|
|
`(?i)^candidate:s[pm][0-9]+[\t\n\v\f\r \x{00a0}\x{1680}\x{2000}-\x{200a}` +
|
|
`\x{2028}\x{2029}\x{202f}\x{205f}\x{3000}\x{feff}]`)
|
|
|
|
// CandidateSignalOriginOf ports candidateSignalOrigin. An empty string stands
|
|
// in for the TypeScript null (both yield "end").
|
|
func CandidateSignalOriginOf(candidate string) CandidateSignalOrigin {
|
|
if strings.TrimFunc(candidate, IsJSWhitespace) == "" {
|
|
return CandidateOriginEnd
|
|
}
|
|
if predictedCandidatePattern.MatchString(candidate) {
|
|
return CandidateOriginPredicted
|
|
}
|
|
return CandidateOriginOrdinary
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// src/shared/packet-loss.ts
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// PacketLossPercentFromDeltas ports packetLossPercentFromDeltas. A nil
|
|
// argument is the TypeScript null; ok is false where it returns null.
|
|
func PacketLossPercentFromDeltas(packetsReceivedDelta, packetsLostDelta *float64) (float64, bool) {
|
|
if packetsReceivedDelta == nil || packetsLostDelta == nil ||
|
|
*packetsReceivedDelta < 0 || *packetsLostDelta < 0 {
|
|
return 0, false
|
|
}
|
|
packetDelta := *packetsReceivedDelta + *packetsLostDelta
|
|
if packetDelta <= 0 {
|
|
return 0, false
|
|
}
|
|
return (*packetsLostDelta / packetDelta) * 100, true
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// src/shared/video-codec-evidence.ts
|
|
// ---------------------------------------------------------------------------
|
|
|
|
type codecParameterRule struct {
|
|
profileKey string
|
|
parameterKeys []string
|
|
validators map[string]*regexp.Regexp
|
|
}
|
|
|
|
var videoCodecParameterRules = map[string]codecParameterRule{
|
|
"video/h264": {
|
|
profileKey: "profile-level-id",
|
|
parameterKeys: []string{"packetization-mode", "level-asymmetry-allowed"},
|
|
validators: map[string]*regexp.Regexp{
|
|
"profile-level-id": regexp.MustCompile(`^[0-9a-f]{6}$`),
|
|
"packetization-mode": regexp.MustCompile(`^[0-2]$`),
|
|
"level-asymmetry-allowed": regexp.MustCompile(`^[01]$`),
|
|
},
|
|
},
|
|
"video/vp9": {
|
|
profileKey: "profile-id",
|
|
parameterKeys: []string{"max-fr", "max-fs"},
|
|
validators: map[string]*regexp.Regexp{
|
|
"profile-id": regexp.MustCompile(`^[0-3]$`),
|
|
"max-fr": regexp.MustCompile(`^[1-9][0-9]{0,9}$`),
|
|
"max-fs": regexp.MustCompile(`^[1-9][0-9]{0,9}$`),
|
|
},
|
|
},
|
|
"video/vp8": {
|
|
profileKey: "",
|
|
parameterKeys: []string{"max-fr", "max-fs"},
|
|
validators: map[string]*regexp.Regexp{
|
|
"max-fr": regexp.MustCompile(`^[1-9][0-9]{0,9}$`),
|
|
"max-fs": regexp.MustCompile(`^[1-9][0-9]{0,9}$`),
|
|
},
|
|
},
|
|
"video/av1": {
|
|
profileKey: "profile",
|
|
parameterKeys: []string{"level-idx", "tier"},
|
|
validators: map[string]*regexp.Regexp{
|
|
"profile": regexp.MustCompile(`^[0-2]$`),
|
|
"level-idx": regexp.MustCompile(`^(?:[0-9]|[12][0-9]|3[01])$`),
|
|
"tier": regexp.MustCompile(`^[01]$`),
|
|
},
|
|
},
|
|
}
|
|
|
|
var videoMimeTypePattern = regexp.MustCompile(`(?i)^video/[A-Za-z0-9.+-]{1,32}$`)
|
|
|
|
// IsCanonicalVideoCodecEvidence ports isCanonicalVideoCodecEvidence. Empty
|
|
// strings are the TypeScript nulls; the schema rejects a genuinely empty codec
|
|
// string before this refine runs.
|
|
func IsCanonicalVideoCodecEvidence(codec, codecProfile, codecParameters string) bool {
|
|
rule, ok := videoCodecParameterRules[strings.ToLower(codec)]
|
|
if codec == "" || !ok {
|
|
return codecProfile == "" && codecParameters == ""
|
|
}
|
|
profileKeys := []string{}
|
|
if rule.profileKey != "" {
|
|
profileKeys = append(profileKeys, rule.profileKey)
|
|
}
|
|
return canonicalValuesMatch(codecProfile, profileKeys, rule.validators) &&
|
|
canonicalValuesMatch(codecParameters, rule.parameterKeys, rule.validators)
|
|
}
|
|
|
|
func canonicalValuesMatch(
|
|
encoded string, orderedKeys []string, validators map[string]*regexp.Regexp,
|
|
) bool {
|
|
if encoded == "" {
|
|
return true
|
|
}
|
|
segments := strings.Split(encoded, "; ")
|
|
if len(segments) > len(orderedKeys) {
|
|
return false
|
|
}
|
|
previousKeyIndex := -1
|
|
for _, segment := range segments {
|
|
separator := strings.Index(segment, "=")
|
|
key, candidate := "", ""
|
|
if separator >= 1 {
|
|
key, candidate = segment[:separator], segment[separator+1:]
|
|
}
|
|
keyIndex := slices.Index(orderedKeys, key)
|
|
validator, known := validators[key]
|
|
if keyIndex <= previousKeyIndex || keyIndex < 0 ||
|
|
!known || !validator.MatchString(candidate) {
|
|
return false
|
|
}
|
|
previousKeyIndex = keyIndex
|
|
}
|
|
return true
|
|
}
|