Skip to content

Commit

Permalink
Added ExternalBridgeSelectionStrategy and related config
Browse files Browse the repository at this point in the history
  • Loading branch information
jbg committed Mar 10, 2022
1 parent f63d169 commit bc8ab65
Show file tree
Hide file tree
Showing 2 changed files with 139 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package org.jitsi.jicofo.bridge

import org.jitsi.utils.logging2.Logger
import org.jitsi.utils.logging2.LoggerImpl
import org.json.simple.JSONObject
import org.json.simple.JSONValue
import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse

@Suppress("unused")
class ExternalBridgeSelectionStrategy() : BridgeSelectionStrategy() {
private val httpClient: HttpClient = HttpClient
.newBuilder()
.connectTimeout(ExternalBridgeSelectionStrategyConfig.config.timeout)
.build()

private val logger: Logger = LoggerImpl(ExternalBridgeSelectionStrategy::class.simpleName)

private val fallbackStrategy: BridgeSelectionStrategy? by lazy {
val fallbackStrategyName = ExternalBridgeSelectionStrategyConfig.config.fallbackStrategy ?: return@lazy null
try {
val clazz = Class.forName("${javaClass.getPackage().name}.$fallbackStrategyName")
clazz.getConstructor().newInstance() as BridgeSelectionStrategy
} catch (e: Exception) {
val clazz = Class.forName(fallbackStrategyName)
clazz.getConstructor().newInstance() as BridgeSelectionStrategy
}
}

private fun fallback(
bridges: MutableList<Bridge>?,
conferenceBridges: MutableMap<Bridge, Int>?,
participantRegion: String?
): Bridge {
if (fallbackStrategy == null) {
throw Exception("External bridge selection failed and no fallbackStrategy was provided.")
}
return fallbackStrategy!!.doSelect(bridges, conferenceBridges, participantRegion)
}

override fun doSelect(
bridges: MutableList<Bridge>?,
conferenceBridges: MutableMap<Bridge, Int>?,
participantRegion: String?
): Bridge {
val url = ExternalBridgeSelectionStrategyConfig.config.url
?: throw Exception("ExternalBridgeSelectionStrategy requires url to be provided")

val requestBody = JSONObject()
requestBody["bridges"] = bridges?.map {
mapOf(
"jid" to it.jid.toString(),
"version" to it.version,
"colibri2" to it.supportsColibri2(),
"relay_id" to it.relayId,
"region" to it.region,
"stress" to it.stress,
"operational" to it.isOperational,
"graceful_shutdown" to it.isInGracefulShutdown,
"draining" to it.isDraining,
)
}
requestBody["conference_bridges"] = conferenceBridges?.mapKeys { it.key.jid.toString() }
requestBody["participant_region"] = participantRegion
requestBody["fallback_strategy"] = ExternalBridgeSelectionStrategyConfig.config.fallbackStrategy

val request = HttpRequest
.newBuilder()
.POST(HttpRequest.BodyPublishers.ofString(requestBody.toJSONString()))
.uri(URI.create(url))
.headers("Content-Type", "application/json")
.timeout(ExternalBridgeSelectionStrategyConfig.config.timeout)
.build()

val response: HttpResponse<String>
try {
response = httpClient.send(request, HttpResponse.BodyHandlers.ofString())
} catch (exc: Exception) {
logger.error("ExternalBridgeSelectionStrategy: HTTP request failed with ${exc}, using fallback strategy")
return fallback(bridges, conferenceBridges, participantRegion)
}

val statusCode = response.statusCode()
if (statusCode !in 200..299) {
logger.error("ExternalBridgeSelectionStrategy: HTTP request failed with ${statusCode}, using fallback strategy")
return fallback(bridges, conferenceBridges, participantRegion)
}

val responseBody: JSONObject
try {
responseBody = JSONValue.parseWithException(response.body()) as JSONObject
} catch (exc: Exception) {
logger.error("ExternalBridgeSelectionStrategy: HTTP response parsing failed with ${exc}, using fallback strategy")
return fallback(bridges, conferenceBridges, participantRegion)
}

val selectedBridgeIndex = responseBody["selected_bridge_index"] as? Number

if (selectedBridgeIndex == null) {
logger.error("ExternalBridgeSelectionStrategy: HTTP response selected_bridge_index missing or invalid, using fallback strategy")
return fallback(bridges, conferenceBridges, participantRegion)
}

val bridge = bridges!![selectedBridgeIndex.toInt()]
logger.info("ExternalBridgeSelectionStrategy: participantRegion=${participantRegion}, bridge=${bridge}")
return bridge
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.jitsi.jicofo.bridge

import org.jitsi.config.JitsiConfig
import org.jitsi.metaconfig.optionalconfig
import java.time.Duration

class ExternalBridgeSelectionStrategyConfig private constructor() {
val url: String? by optionalconfig {
"${BASE}.url".from(JitsiConfig.newConfig)
}
fun url() = url

val timeout: Duration? by optionalconfig {
"${BASE}.timeout".from(JitsiConfig.newConfig)
}
fun timeout() = timeout

val fallbackStrategy: String? by optionalconfig {
"${BASE}.fallback-strategy".from(JitsiConfig.newConfig)
}
fun fallbackStrategy() = fallbackStrategy

companion object {
const val BASE = "jicofo.bridge.external-selection-strategy"

@JvmField
val config = ExternalBridgeSelectionStrategyConfig()
}
}

0 comments on commit bc8ab65

Please sign in to comment.