mirror of
https://github.com/openlibrecommunity/olcrtc.git
synced 2026-05-26 15:13:40 +00:00
feat: expose mobile liveness options
This commit is contained in:
101
mobile/mobile.go
101
mobile/mobile.go
@@ -15,6 +15,7 @@ import (
|
||||
|
||||
"github.com/openlibrecommunity/olcrtc/internal/app/session"
|
||||
"github.com/openlibrecommunity/olcrtc/internal/client"
|
||||
"github.com/openlibrecommunity/olcrtc/internal/control"
|
||||
"github.com/openlibrecommunity/olcrtc/internal/logger"
|
||||
"github.com/openlibrecommunity/olcrtc/internal/protect"
|
||||
|
||||
@@ -65,23 +66,26 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
mu sync.Mutex //nolint:gochecknoglobals // package-level state intentional
|
||||
defaults mobileConfig //nolint:gochecknoglobals // package-level state intentional
|
||||
defaultsSet sync.Once //nolint:gochecknoglobals // package-level state intentional
|
||||
registerSet sync.Once //nolint:gochecknoglobals // package-level state intentional
|
||||
mu sync.Mutex //nolint:gochecknoglobals // package-level state intentional
|
||||
defaults mobileConfig //nolint:gochecknoglobals // package-level state intentional
|
||||
defaultsSet sync.Once //nolint:gochecknoglobals // package-level state intentional
|
||||
registerSet sync.Once //nolint:gochecknoglobals // package-level state intentional
|
||||
runClientWithReady = client.RunWithReady //nolint:gochecknoglobals // package-level state intentional
|
||||
cancel context.CancelFunc //nolint:gochecknoglobals // package-level state intentional
|
||||
done chan struct{} //nolint:gochecknoglobals // package-level state intentional
|
||||
ready chan struct{} //nolint:gochecknoglobals // package-level state intentional
|
||||
cancel context.CancelFunc //nolint:gochecknoglobals // package-level state intentional
|
||||
done chan struct{} //nolint:gochecknoglobals // package-level state intentional
|
||||
ready chan struct{} //nolint:gochecknoglobals // package-level state intentional
|
||||
errRun error
|
||||
)
|
||||
|
||||
type mobileConfig struct {
|
||||
link string
|
||||
transport string
|
||||
dnsServer string
|
||||
vp8FPS int
|
||||
vp8BatchSize int
|
||||
link string
|
||||
transport string
|
||||
dnsServer string
|
||||
vp8FPS int
|
||||
vp8BatchSize int
|
||||
livenessInterval time.Duration
|
||||
livenessTimeout time.Duration
|
||||
livenessFailures int
|
||||
}
|
||||
|
||||
// SetProtector sets the Android VPN socket protector.
|
||||
@@ -143,6 +147,21 @@ func SetVP8Options(fps, batchSize int) {
|
||||
defaults.vp8BatchSize = clampAtLeastOne(batchSize, 64)
|
||||
}
|
||||
|
||||
// SetLivenessOptions configures control-stream ping/pong checks.
|
||||
// Values <= 0 reset that field to its default. Durations are milliseconds.
|
||||
func SetLivenessOptions(intervalMillis, timeoutMillis, failures int) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
ensureDefaultConfigLocked()
|
||||
defaults.livenessInterval = durationFromMillisOrDefault(intervalMillis, control.DefaultInterval)
|
||||
defaults.livenessTimeout = durationFromMillisOrDefault(timeoutMillis, control.DefaultTimeout)
|
||||
if failures <= 0 {
|
||||
defaults.livenessFailures = control.DefaultFailures
|
||||
return
|
||||
}
|
||||
defaults.livenessFailures = failures
|
||||
}
|
||||
|
||||
// SetDebug enables or disables verbose logging.
|
||||
func SetDebug(enabled bool) {
|
||||
logger.SetVerbose(enabled)
|
||||
@@ -195,6 +214,11 @@ func Check(
|
||||
vp8BatchSize int,
|
||||
) (int64, error) {
|
||||
registerDefaults()
|
||||
mu.Lock()
|
||||
ensureDefaultConfigLocked()
|
||||
cfg := defaults
|
||||
mu.Unlock()
|
||||
|
||||
carrierName = normalizeCarrier(carrierName)
|
||||
transportName = normalizeTransport(transportName)
|
||||
if err := validateStartArgs(carrierName, roomID, clientID, keyHex); err != nil {
|
||||
@@ -227,6 +251,7 @@ func Check(
|
||||
DNSServer: defaultDNSServer,
|
||||
VP8FPS: clampAtLeastOne(vp8FPS, 120),
|
||||
VP8BatchSize: clampAtLeastOne(vp8BatchSize, 64),
|
||||
Liveness: livenessConfig(cfg),
|
||||
},
|
||||
func() {
|
||||
readyOnce.Do(func() {
|
||||
@@ -271,6 +296,11 @@ func Ping(
|
||||
vp8BatchSize int,
|
||||
) (int64, error) {
|
||||
registerDefaults()
|
||||
mu.Lock()
|
||||
ensureDefaultConfigLocked()
|
||||
cfg := defaults
|
||||
mu.Unlock()
|
||||
|
||||
carrierName = normalizeCarrier(carrierName)
|
||||
transportName = normalizeTransport(transportName)
|
||||
|
||||
@@ -310,6 +340,7 @@ func Ping(
|
||||
DNSServer: defaultDNSServer,
|
||||
VP8FPS: clampAtLeastOne(vp8FPS, 120),
|
||||
VP8BatchSize: clampAtLeastOne(vp8BatchSize, 64),
|
||||
Liveness: livenessConfig(cfg),
|
||||
},
|
||||
func() {
|
||||
readyOnce.Do(func() {
|
||||
@@ -557,6 +588,7 @@ func startWithConfig(
|
||||
SOCKSPass: socksPass,
|
||||
VP8FPS: cfg.vp8FPS,
|
||||
VP8BatchSize: cfg.vp8BatchSize,
|
||||
Liveness: livenessConfig(cfg),
|
||||
},
|
||||
func() {
|
||||
readyOnce.Do(func() {
|
||||
@@ -576,6 +608,7 @@ func startWithConfig(
|
||||
}
|
||||
|
||||
// WaitReady blocks until the selected transport is connected and the local SOCKS5 listener is ready.
|
||||
//
|
||||
//nolint:cyclop // straightforward state-machine waits with multiple terminal conditions
|
||||
func WaitReady(timeoutMillis int) error {
|
||||
mu.Lock()
|
||||
@@ -666,15 +699,38 @@ func waitForCheckDone(doneCh <-chan error) {
|
||||
func ensureDefaultConfigLocked() {
|
||||
defaultsSet.Do(func() {
|
||||
defaults = mobileConfig{
|
||||
link: defaultLink,
|
||||
transport: defaultTransport,
|
||||
dnsServer: defaultDNSServer,
|
||||
vp8FPS: 60,
|
||||
vp8BatchSize: 8,
|
||||
link: defaultLink,
|
||||
transport: defaultTransport,
|
||||
dnsServer: defaultDNSServer,
|
||||
vp8FPS: 60,
|
||||
vp8BatchSize: 8,
|
||||
livenessInterval: control.DefaultInterval,
|
||||
livenessTimeout: control.DefaultTimeout,
|
||||
livenessFailures: control.DefaultFailures,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func livenessConfig(cfg mobileConfig) control.Config {
|
||||
interval := cfg.livenessInterval
|
||||
if interval <= 0 {
|
||||
interval = control.DefaultInterval
|
||||
}
|
||||
timeout := cfg.livenessTimeout
|
||||
if timeout <= 0 {
|
||||
timeout = control.DefaultTimeout
|
||||
}
|
||||
failures := cfg.livenessFailures
|
||||
if failures <= 0 {
|
||||
failures = control.DefaultFailures
|
||||
}
|
||||
return control.Config{
|
||||
Interval: interval,
|
||||
Timeout: timeout,
|
||||
Failures: failures,
|
||||
}
|
||||
}
|
||||
|
||||
func normalizeTransport(value string) string {
|
||||
switch value {
|
||||
case dataTransport, "data", "dc":
|
||||
@@ -734,6 +790,17 @@ func clampAtLeastOne(value, maxValue int) int {
|
||||
return value
|
||||
}
|
||||
|
||||
func durationFromMillisOrDefault(value int, def time.Duration) time.Duration {
|
||||
if value <= 0 {
|
||||
return def
|
||||
}
|
||||
d := time.Duration(value) * time.Millisecond
|
||||
if d <= 0 {
|
||||
return def
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
// logBridge adapts LogWriter to io.Writer.
|
||||
type logBridge struct {
|
||||
w LogWriter
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/openlibrecommunity/olcrtc/internal/client"
|
||||
"github.com/openlibrecommunity/olcrtc/internal/control"
|
||||
"github.com/openlibrecommunity/olcrtc/internal/logger"
|
||||
"github.com/openlibrecommunity/olcrtc/internal/protect"
|
||||
)
|
||||
@@ -83,12 +84,15 @@ func TestDefaultsAndSetters(t *testing.T) {
|
||||
SetLink("direct")
|
||||
SetDNS("9.9.9.9:53")
|
||||
SetVP8Options(-1, 999)
|
||||
SetLivenessOptions(2500, 750, -1)
|
||||
|
||||
mu.Lock()
|
||||
got := defaults
|
||||
mu.Unlock()
|
||||
if got.transport != dataTransport || got.link != defaultLink || got.dnsServer != "9.9.9.9:53" ||
|
||||
got.vp8FPS != 1 || got.vp8BatchSize != 64 {
|
||||
got.vp8FPS != 1 || got.vp8BatchSize != 64 ||
|
||||
got.livenessInterval != 2500*time.Millisecond || got.livenessTimeout != 750*time.Millisecond ||
|
||||
got.livenessFailures != control.DefaultFailures {
|
||||
t.Fatalf("defaults = %+v", got)
|
||||
}
|
||||
|
||||
@@ -168,15 +172,19 @@ func TestStartWithInjectedRunnerLifecycle(t *testing.T) {
|
||||
t.Cleanup(func() {
|
||||
resetMobileGlobals(t)
|
||||
})
|
||||
SetLivenessOptions(2500, 750, 4)
|
||||
|
||||
runClientWithReady = func(ctx context.Context, cfg client.Config, onReady func()) error {
|
||||
if cfg.Link != defaultLink || cfg.Transport != dataTransport || cfg.Carrier != carrierJazz ||
|
||||
cfg.RoomURL != "any" || cfg.DeviceID != "client" || cfg.LocalAddr != "127.0.0.1:1080" ||
|
||||
cfg.DNSServer != defaultDNSServer || cfg.VP8FPS != 60 || cfg.VP8BatchSize != 8 {
|
||||
cfg.DNSServer != defaultDNSServer || cfg.VP8FPS != 60 || cfg.VP8BatchSize != 8 ||
|
||||
cfg.Liveness.Interval != 2500*time.Millisecond ||
|
||||
cfg.Liveness.Timeout != 750*time.Millisecond ||
|
||||
cfg.Liveness.Failures != 4 {
|
||||
t.Fatalf(
|
||||
"RunWithReady args mismatch: link=%q transport=%q carrier=%q room=%q client=%q local=%q dns=%q vp8=%d/%d",
|
||||
"RunWithReady args mismatch: link=%q transport=%q carrier=%q room=%q client=%q local=%q dns=%q vp8=%d/%d liveness=%+v",
|
||||
cfg.Link, cfg.Transport, cfg.Carrier, cfg.RoomURL, cfg.DeviceID,
|
||||
cfg.LocalAddr, cfg.DNSServer, cfg.VP8FPS, cfg.VP8BatchSize,
|
||||
cfg.LocalAddr, cfg.DNSServer, cfg.VP8FPS, cfg.VP8BatchSize, cfg.Liveness,
|
||||
)
|
||||
}
|
||||
onReady()
|
||||
@@ -208,9 +216,12 @@ func TestStartUsesDefaultsAndCheckWithInjectedRunner(t *testing.T) {
|
||||
|
||||
runClientWithReady = func(ctx context.Context, cfg client.Config, onReady func()) error {
|
||||
if cfg.Transport != defaultTransport || cfg.RoomURL != "https://telemost.yandex.ru/j/room" ||
|
||||
cfg.LocalAddr != "127.0.0.1:1081" || cfg.SOCKSUser != "u" || cfg.SOCKSPass != "p" {
|
||||
t.Fatalf("Start args mismatch: transport=%q room=%q local=%q user/pass=%q/%q",
|
||||
cfg.Transport, cfg.RoomURL, cfg.LocalAddr, cfg.SOCKSUser, cfg.SOCKSPass)
|
||||
cfg.LocalAddr != "127.0.0.1:1081" || cfg.SOCKSUser != "u" || cfg.SOCKSPass != "p" ||
|
||||
cfg.Liveness.Interval != control.DefaultInterval ||
|
||||
cfg.Liveness.Timeout != control.DefaultTimeout ||
|
||||
cfg.Liveness.Failures != control.DefaultFailures {
|
||||
t.Fatalf("Start args mismatch: transport=%q room=%q local=%q user/pass=%q/%q liveness=%+v",
|
||||
cfg.Transport, cfg.RoomURL, cfg.LocalAddr, cfg.SOCKSUser, cfg.SOCKSPass, cfg.Liveness)
|
||||
}
|
||||
onReady()
|
||||
<-ctx.Done()
|
||||
@@ -225,9 +236,14 @@ func TestStartUsesDefaultsAndCheckWithInjectedRunner(t *testing.T) {
|
||||
}
|
||||
Stop()
|
||||
|
||||
SetLivenessOptions(3000, 1000, 5)
|
||||
runClientWithReady = func(ctx context.Context, cfg client.Config, onReady func()) error {
|
||||
if cfg.Transport != dataTransport || cfg.VP8FPS != 1 || cfg.VP8BatchSize != 64 {
|
||||
t.Fatalf("Check args mismatch: transport=%q vp8=%d/%d", cfg.Transport, cfg.VP8FPS, cfg.VP8BatchSize)
|
||||
if cfg.Transport != dataTransport || cfg.VP8FPS != 1 || cfg.VP8BatchSize != 64 ||
|
||||
cfg.Liveness.Interval != 3000*time.Millisecond ||
|
||||
cfg.Liveness.Timeout != time.Second ||
|
||||
cfg.Liveness.Failures != 5 {
|
||||
t.Fatalf("Check args mismatch: transport=%q vp8=%d/%d liveness=%+v",
|
||||
cfg.Transport, cfg.VP8FPS, cfg.VP8BatchSize, cfg.Liveness)
|
||||
}
|
||||
onReady()
|
||||
<-ctx.Done()
|
||||
@@ -242,6 +258,32 @@ func TestStartUsesDefaultsAndCheckWithInjectedRunner(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPingPassesLiveness(t *testing.T) {
|
||||
resetMobileGlobals(t)
|
||||
t.Cleanup(func() {
|
||||
resetMobileGlobals(t)
|
||||
})
|
||||
SetLivenessOptions(4000, 1500, 6)
|
||||
|
||||
seen := make(chan control.Config, 1)
|
||||
runClientWithReady = func(ctx context.Context, cfg client.Config, onReady func()) error {
|
||||
seen <- cfg.Liveness
|
||||
onReady()
|
||||
<-ctx.Done()
|
||||
return nil
|
||||
}
|
||||
|
||||
_, _ = Ping("jazz", "dc", "", "client", "key", 1085, 100, "http://127.0.0.1/", 30, 1)
|
||||
select {
|
||||
case got := <-seen:
|
||||
if got.Interval != 4000*time.Millisecond || got.Timeout != 1500*time.Millisecond || got.Failures != 6 {
|
||||
t.Fatalf("Ping liveness = %+v", got)
|
||||
}
|
||||
default:
|
||||
t.Fatal("Ping did not start client")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckTimeoutAndRunError(t *testing.T) {
|
||||
resetMobileGlobals(t)
|
||||
t.Cleanup(func() {
|
||||
|
||||
Reference in New Issue
Block a user