1+ package org.jitsi.jicofo.bridge
2+
3+ import org.jitsi.utils.logging2.Logger
4+ import org.jitsi.utils.logging2.LoggerImpl
5+ import org.json.simple.JSONObject
6+ import org.json.simple.JSONValue
7+ import java.net.URI
8+ import java.net.http.HttpClient
9+ import java.net.http.HttpRequest
10+ import java.net.http.HttpResponse
11+
12+ @Suppress(" unused" )
13+ class ExternalBridgeSelectionStrategy () : BridgeSelectionStrategy() {
14+ private val httpClient: HttpClient = HttpClient
15+ .newBuilder()
16+ .connectTimeout(ExternalBridgeSelectionStrategyConfig .config.timeout)
17+ .build()
18+
19+ private val logger: Logger = LoggerImpl (ExternalBridgeSelectionStrategy ::class .simpleName)
20+
21+ private val fallbackStrategy: BridgeSelectionStrategy ? by lazy {
22+ val fallbackStrategyName = ExternalBridgeSelectionStrategyConfig .config.fallbackStrategy ? : return @lazy null
23+ try {
24+ val clazz = Class .forName(" ${javaClass.getPackage().name} .$fallbackStrategyName " )
25+ clazz.getConstructor().newInstance() as BridgeSelectionStrategy
26+ } catch (e: Exception ) {
27+ val clazz = Class .forName(fallbackStrategyName)
28+ clazz.getConstructor().newInstance() as BridgeSelectionStrategy
29+ }
30+ }
31+
32+ private fun fallback (
33+ bridges : MutableList <Bridge >? ,
34+ conferenceBridges : MutableMap <Bridge , Int >? ,
35+ participantRegion : String?
36+ ): Bridge {
37+ if (fallbackStrategy == null ) {
38+ throw Exception (" External bridge selection failed and no fallbackStrategy was provided." )
39+ }
40+ return fallbackStrategy!! .doSelect(bridges, conferenceBridges, participantRegion)
41+ }
42+
43+ override fun doSelect (
44+ bridges : MutableList <Bridge >? ,
45+ conferenceBridges : MutableMap <Bridge , Int >? ,
46+ participantRegion : String?
47+ ): Bridge {
48+ val url = ExternalBridgeSelectionStrategyConfig .config.url
49+ ? : throw Exception (" ExternalBridgeSelectionStrategy requires url to be provided" )
50+
51+ val requestBody = JSONObject ()
52+ requestBody[" bridges" ] = bridges?.map {
53+ mapOf (
54+ " jid" to it.jid.toString(),
55+ " version" to it.version,
56+ " colibri2" to it.supportsColibri2(),
57+ " relay_id" to it.relayId,
58+ " region" to it.region,
59+ " stress" to it.stress,
60+ " operational" to it.isOperational,
61+ " graceful_shutdown" to it.isInGracefulShutdown,
62+ " draining" to it.isDraining,
63+ )
64+ }
65+ requestBody[" conference_bridges" ] = conferenceBridges?.mapKeys { it.key.jid.toString() }
66+ requestBody[" participant_region" ] = participantRegion
67+ requestBody[" fallback_strategy" ] = ExternalBridgeSelectionStrategyConfig .config.fallbackStrategy
68+
69+ val request = HttpRequest
70+ .newBuilder()
71+ .POST (HttpRequest .BodyPublishers .ofString(requestBody.toJSONString()))
72+ .uri(URI .create(url))
73+ .headers(" Content-Type" , " application/json" )
74+ .timeout(ExternalBridgeSelectionStrategyConfig .config.timeout)
75+ .build()
76+
77+ val response: HttpResponse <String >
78+ try {
79+ response = httpClient.send(request, HttpResponse .BodyHandlers .ofString())
80+ } catch (exc: Exception ) {
81+ logger.error(" ExternalBridgeSelectionStrategy: HTTP request failed with ${exc} , using fallback strategy" )
82+ return fallback(bridges, conferenceBridges, participantRegion)
83+ }
84+
85+ val statusCode = response.statusCode()
86+ if (statusCode !in 200 .. 299 ) {
87+ logger.error(" ExternalBridgeSelectionStrategy: HTTP request failed with ${statusCode} , using fallback strategy" )
88+ return fallback(bridges, conferenceBridges, participantRegion)
89+ }
90+
91+ val responseBody: JSONObject
92+ try {
93+ responseBody = JSONValue .parseWithException(response.body()) as JSONObject
94+ } catch (exc: Exception ) {
95+ logger.error(" ExternalBridgeSelectionStrategy: HTTP response parsing failed with ${exc} , using fallback strategy" )
96+ return fallback(bridges, conferenceBridges, participantRegion)
97+ }
98+
99+ val selectedBridgeIndex = responseBody[" selected_bridge_index" ] as ? Number
100+
101+ if (selectedBridgeIndex == null ) {
102+ logger.error(" ExternalBridgeSelectionStrategy: HTTP response selected_bridge_index missing or invalid, using fallback strategy" )
103+ return fallback(bridges, conferenceBridges, participantRegion)
104+ }
105+
106+ val bridge = bridges!! [selectedBridgeIndex.toInt()]
107+ logger.info(" ExternalBridgeSelectionStrategy: participantRegion=${participantRegion} , bridge=${bridge} " )
108+ return bridge
109+ }
110+ }
0 commit comments