Files
Piik/internal/server/protocol/helpers.go
T
AAA da47189fc3 refactor: unify Go runtime and audited frontend
Squash integration of the reviewed Go core, frontend audit fixes, release tooling, and deployment hygiene. CI passed all Server/Client jobs.
2026-09-06 23:26:01 +08:00

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
}