diff --git a/src/main/java/org/ice4j/ice/Agent.java b/src/main/java/org/ice4j/ice/Agent.java index a9e68764..19738057 100644 --- a/src/main/java/org/ice4j/ice/Agent.java +++ b/src/main/java/org/ice4j/ice/Agent.java @@ -2656,7 +2656,19 @@ protected Duration getDelayUntilNextRun() { if (shouldRunStunKeepAlive()) { - return Duration.ofMillis(keepAliveSent == 0 ? 0 : consentFreshnessInterval); + if (keepAliveSent == 0) + { + return Duration.ZERO; + } + else + { + double r = 1; + if (config.getRandomizeConsentFreshnessInterval()) + { + r = 0.8d + ThreadLocalRandom.current().nextDouble() * 0.4; + } + return Duration.ofMillis((long) (consentFreshnessInterval * r)); + } } return Duration.ofMillis(-1); } diff --git a/src/main/kotlin/org/ice4j/ice/AgentConfig.kt b/src/main/kotlin/org/ice4j/ice/AgentConfig.kt index d436fe34..5eeb05d2 100644 --- a/src/main/kotlin/org/ice4j/ice/AgentConfig.kt +++ b/src/main/kotlin/org/ice4j/ice/AgentConfig.kt @@ -27,6 +27,10 @@ class AgentConfig { "ice4j.consent-freshness.interval".from(configSource) } + val randomizeConsentFreshnessInterval: Boolean by config { + "ice4j.consent-freshness.randomize".from(configSource) + } + val consentFreshnessOriginalWaitInterval: Duration by config { "org.ice4j.ice.CONSENT_FRESHNESS_WAIT_INTERVAL".from(configSource) .convertFrom { Duration.ofMillis(it) } diff --git a/src/main/resources/reference.conf b/src/main/resources/reference.conf index c572849a..a55a5549 100644 --- a/src/main/resources/reference.conf +++ b/src/main/resources/reference.conf @@ -31,6 +31,9 @@ ice4j { // The maximum number of retransmissions of a STUN Binding request without a valid STUN Binding response after which // consent freshness is to be considered unconfirmed according to `STUN Usage for Consent Freshness` (RFC7675). max-retransmissions = 30 + // Whether to randomize the period between any two checks between 0.8 and 1.2 of the configured interval as + // recommended in RFC7675 Section 5.1. We keep this configurable in case the previous behavior is desired. + randomize-interval = true } // Configuration related to harvesting (aka gathering) of local candidates.