mirror of
https://github.com/openlibrecommunity/olcrtc.git
synced 2026-06-02 06:23:37 +02:00
feat: expose mobile liveness options
This commit is contained in:
+84
-17
@@ -15,6 +15,7 @@ import (
|
|||||||
|
|
||||||
"github.com/openlibrecommunity/olcrtc/internal/app/session"
|
"github.com/openlibrecommunity/olcrtc/internal/app/session"
|
||||||
"github.com/openlibrecommunity/olcrtc/internal/client"
|
"github.com/openlibrecommunity/olcrtc/internal/client"
|
||||||
|
"github.com/openlibrecommunity/olcrtc/internal/control"
|
||||||
"github.com/openlibrecommunity/olcrtc/internal/logger"
|
"github.com/openlibrecommunity/olcrtc/internal/logger"
|
||||||
"github.com/openlibrecommunity/olcrtc/internal/protect"
|
"github.com/openlibrecommunity/olcrtc/internal/protect"
|
||||||
|
|
||||||
@@ -65,23 +66,26 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
mu sync.Mutex //nolint:gochecknoglobals // package-level state intentional
|
mu sync.Mutex //nolint:gochecknoglobals // package-level state intentional
|
||||||
defaults mobileConfig //nolint:gochecknoglobals // package-level state intentional
|
defaults mobileConfig //nolint:gochecknoglobals // package-level state intentional
|
||||||
defaultsSet sync.Once //nolint:gochecknoglobals // package-level state intentional
|
defaultsSet sync.Once //nolint:gochecknoglobals // package-level state intentional
|
||||||
registerSet 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
|
runClientWithReady = client.RunWithReady //nolint:gochecknoglobals // package-level state intentional
|
||||||
cancel context.CancelFunc //nolint:gochecknoglobals // package-level state intentional
|
cancel context.CancelFunc //nolint:gochecknoglobals // package-level state intentional
|
||||||
done chan struct{} //nolint:gochecknoglobals // package-level state intentional
|
done chan struct{} //nolint:gochecknoglobals // package-level state intentional
|
||||||
ready chan struct{} //nolint:gochecknoglobals // package-level state intentional
|
ready chan struct{} //nolint:gochecknoglobals // package-level state intentional
|
||||||
errRun error
|
errRun error
|
||||||
)
|
)
|
||||||
|
|
||||||
type mobileConfig struct {
|
type mobileConfig struct {
|
||||||
link string
|
link string
|
||||||
transport string
|
transport string
|
||||||
dnsServer string
|
dnsServer string
|
||||||
vp8FPS int
|
vp8FPS int
|
||||||
vp8BatchSize int
|
vp8BatchSize int
|
||||||
|
livenessInterval time.Duration
|
||||||
|
livenessTimeout time.Duration
|
||||||
|
livenessFailures int
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetProtector sets the Android VPN socket protector.
|
// SetProtector sets the Android VPN socket protector.
|
||||||
@@ -143,6 +147,21 @@ func SetVP8Options(fps, batchSize int) {
|
|||||||
defaults.vp8BatchSize = clampAtLeastOne(batchSize, 64)
|
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.
|
// SetDebug enables or disables verbose logging.
|
||||||
func SetDebug(enabled bool) {
|
func SetDebug(enabled bool) {
|
||||||
logger.SetVerbose(enabled)
|
logger.SetVerbose(enabled)
|
||||||
@@ -195,6 +214,11 @@ func Check(
|
|||||||
vp8BatchSize int,
|
vp8BatchSize int,
|
||||||
) (int64, error) {
|
) (int64, error) {
|
||||||
registerDefaults()
|
registerDefaults()
|
||||||
|
mu.Lock()
|
||||||
|
ensureDefaultConfigLocked()
|
||||||
|
cfg := defaults
|
||||||
|
mu.Unlock()
|
||||||
|
|
||||||
carrierName = normalizeCarrier(carrierName)
|
carrierName = normalizeCarrier(carrierName)
|
||||||
transportName = normalizeTransport(transportName)
|
transportName = normalizeTransport(transportName)
|
||||||
if err := validateStartArgs(carrierName, roomID, clientID, keyHex); err != nil {
|
if err := validateStartArgs(carrierName, roomID, clientID, keyHex); err != nil {
|
||||||
@@ -227,6 +251,7 @@ func Check(
|
|||||||
DNSServer: defaultDNSServer,
|
DNSServer: defaultDNSServer,
|
||||||
VP8FPS: clampAtLeastOne(vp8FPS, 120),
|
VP8FPS: clampAtLeastOne(vp8FPS, 120),
|
||||||
VP8BatchSize: clampAtLeastOne(vp8BatchSize, 64),
|
VP8BatchSize: clampAtLeastOne(vp8BatchSize, 64),
|
||||||
|
Liveness: livenessConfig(cfg),
|
||||||
},
|
},
|
||||||
func() {
|
func() {
|
||||||
readyOnce.Do(func() {
|
readyOnce.Do(func() {
|
||||||
@@ -271,6 +296,11 @@ func Ping(
|
|||||||
vp8BatchSize int,
|
vp8BatchSize int,
|
||||||
) (int64, error) {
|
) (int64, error) {
|
||||||
registerDefaults()
|
registerDefaults()
|
||||||
|
mu.Lock()
|
||||||
|
ensureDefaultConfigLocked()
|
||||||
|
cfg := defaults
|
||||||
|
mu.Unlock()
|
||||||
|
|
||||||
carrierName = normalizeCarrier(carrierName)
|
carrierName = normalizeCarrier(carrierName)
|
||||||
transportName = normalizeTransport(transportName)
|
transportName = normalizeTransport(transportName)
|
||||||
|
|
||||||
@@ -310,6 +340,7 @@ func Ping(
|
|||||||
DNSServer: defaultDNSServer,
|
DNSServer: defaultDNSServer,
|
||||||
VP8FPS: clampAtLeastOne(vp8FPS, 120),
|
VP8FPS: clampAtLeastOne(vp8FPS, 120),
|
||||||
VP8BatchSize: clampAtLeastOne(vp8BatchSize, 64),
|
VP8BatchSize: clampAtLeastOne(vp8BatchSize, 64),
|
||||||
|
Liveness: livenessConfig(cfg),
|
||||||
},
|
},
|
||||||
func() {
|
func() {
|
||||||
readyOnce.Do(func() {
|
readyOnce.Do(func() {
|
||||||
@@ -557,6 +588,7 @@ func startWithConfig(
|
|||||||
SOCKSPass: socksPass,
|
SOCKSPass: socksPass,
|
||||||
VP8FPS: cfg.vp8FPS,
|
VP8FPS: cfg.vp8FPS,
|
||||||
VP8BatchSize: cfg.vp8BatchSize,
|
VP8BatchSize: cfg.vp8BatchSize,
|
||||||
|
Liveness: livenessConfig(cfg),
|
||||||
},
|
},
|
||||||
func() {
|
func() {
|
||||||
readyOnce.Do(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.
|
// 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
|
//nolint:cyclop // straightforward state-machine waits with multiple terminal conditions
|
||||||
func WaitReady(timeoutMillis int) error {
|
func WaitReady(timeoutMillis int) error {
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
@@ -666,15 +699,38 @@ func waitForCheckDone(doneCh <-chan error) {
|
|||||||
func ensureDefaultConfigLocked() {
|
func ensureDefaultConfigLocked() {
|
||||||
defaultsSet.Do(func() {
|
defaultsSet.Do(func() {
|
||||||
defaults = mobileConfig{
|
defaults = mobileConfig{
|
||||||
link: defaultLink,
|
link: defaultLink,
|
||||||
transport: defaultTransport,
|
transport: defaultTransport,
|
||||||
dnsServer: defaultDNSServer,
|
dnsServer: defaultDNSServer,
|
||||||
vp8FPS: 60,
|
vp8FPS: 60,
|
||||||
vp8BatchSize: 8,
|
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 {
|
func normalizeTransport(value string) string {
|
||||||
switch value {
|
switch value {
|
||||||
case dataTransport, "data", "dc":
|
case dataTransport, "data", "dc":
|
||||||
@@ -734,6 +790,17 @@ func clampAtLeastOne(value, maxValue int) int {
|
|||||||
return value
|
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.
|
// logBridge adapts LogWriter to io.Writer.
|
||||||
type logBridge struct {
|
type logBridge struct {
|
||||||
w LogWriter
|
w LogWriter
|
||||||
|
|||||||
+51
-9
@@ -10,6 +10,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/openlibrecommunity/olcrtc/internal/client"
|
"github.com/openlibrecommunity/olcrtc/internal/client"
|
||||||
|
"github.com/openlibrecommunity/olcrtc/internal/control"
|
||||||
"github.com/openlibrecommunity/olcrtc/internal/logger"
|
"github.com/openlibrecommunity/olcrtc/internal/logger"
|
||||||
"github.com/openlibrecommunity/olcrtc/internal/protect"
|
"github.com/openlibrecommunity/olcrtc/internal/protect"
|
||||||
)
|
)
|
||||||
@@ -83,12 +84,15 @@ func TestDefaultsAndSetters(t *testing.T) {
|
|||||||
SetLink("direct")
|
SetLink("direct")
|
||||||
SetDNS("9.9.9.9:53")
|
SetDNS("9.9.9.9:53")
|
||||||
SetVP8Options(-1, 999)
|
SetVP8Options(-1, 999)
|
||||||
|
SetLivenessOptions(2500, 750, -1)
|
||||||
|
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
got := defaults
|
got := defaults
|
||||||
mu.Unlock()
|
mu.Unlock()
|
||||||
if got.transport != dataTransport || got.link != defaultLink || got.dnsServer != "9.9.9.9:53" ||
|
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)
|
t.Fatalf("defaults = %+v", got)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,15 +172,19 @@ func TestStartWithInjectedRunnerLifecycle(t *testing.T) {
|
|||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
resetMobileGlobals(t)
|
resetMobileGlobals(t)
|
||||||
})
|
})
|
||||||
|
SetLivenessOptions(2500, 750, 4)
|
||||||
|
|
||||||
runClientWithReady = func(ctx context.Context, cfg client.Config, onReady func()) error {
|
runClientWithReady = func(ctx context.Context, cfg client.Config, onReady func()) error {
|
||||||
if cfg.Link != defaultLink || cfg.Transport != dataTransport || cfg.Carrier != carrierJazz ||
|
if cfg.Link != defaultLink || cfg.Transport != dataTransport || cfg.Carrier != carrierJazz ||
|
||||||
cfg.RoomURL != "any" || cfg.DeviceID != "client" || cfg.LocalAddr != "127.0.0.1:1080" ||
|
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(
|
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.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()
|
onReady()
|
||||||
@@ -208,9 +216,12 @@ func TestStartUsesDefaultsAndCheckWithInjectedRunner(t *testing.T) {
|
|||||||
|
|
||||||
runClientWithReady = func(ctx context.Context, cfg client.Config, onReady func()) error {
|
runClientWithReady = func(ctx context.Context, cfg client.Config, onReady func()) error {
|
||||||
if cfg.Transport != defaultTransport || cfg.RoomURL != "https://telemost.yandex.ru/j/room" ||
|
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" {
|
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.Liveness.Interval != control.DefaultInterval ||
|
||||||
cfg.Transport, cfg.RoomURL, cfg.LocalAddr, cfg.SOCKSUser, cfg.SOCKSPass)
|
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()
|
onReady()
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
@@ -225,9 +236,14 @@ func TestStartUsesDefaultsAndCheckWithInjectedRunner(t *testing.T) {
|
|||||||
}
|
}
|
||||||
Stop()
|
Stop()
|
||||||
|
|
||||||
|
SetLivenessOptions(3000, 1000, 5)
|
||||||
runClientWithReady = func(ctx context.Context, cfg client.Config, onReady func()) error {
|
runClientWithReady = func(ctx context.Context, cfg client.Config, onReady func()) error {
|
||||||
if cfg.Transport != dataTransport || cfg.VP8FPS != 1 || cfg.VP8BatchSize != 64 {
|
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)
|
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()
|
onReady()
|
||||||
<-ctx.Done()
|
<-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) {
|
func TestCheckTimeoutAndRunError(t *testing.T) {
|
||||||
resetMobileGlobals(t)
|
resetMobileGlobals(t)
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
|
|||||||
Reference in New Issue
Block a user