fdroid-client/download/src/commonMain/kotlin/org/fdroid/download/MirrorChooser.kt

65 lines
2.2 KiB
Kotlin

package org.fdroid.download
import io.ktor.client.features.ResponseException
import io.ktor.http.Url
import mu.KotlinLogging
public interface MirrorChooser {
public fun orderMirrors(downloadRequest: DownloadRequest): List<Mirror>
public suspend fun <T> mirrorRequest(
downloadRequest: DownloadRequest,
request: suspend (mirror: Mirror, url: Url) -> T,
): T
}
internal abstract class MirrorChooserImpl : MirrorChooser {
companion object {
protected val log = KotlinLogging.logger {}
}
/**
* Executes the given request on the best mirror and tries the next best ones if that fails.
*/
override suspend fun <T> mirrorRequest(
downloadRequest: DownloadRequest,
request: suspend (mirror: Mirror, url: Url) -> T,
): T {
val mirrors = if (downloadRequest.proxy == null) {
// if we don't use a proxy, filter out onion mirrors (won't work without Orbot)
val orderedMirrors =
orderMirrors(downloadRequest).filter { mirror -> !mirror.isOnion() }
// if we only have onion mirrors, take what we have and expect errors
orderedMirrors.ifEmpty { downloadRequest.mirrors }
} else {
orderMirrors(downloadRequest)
}
mirrors.forEachIndexed { index, mirror ->
val url = mirror.getUrl(downloadRequest.path)
try {
return request(mirror, url)
} catch (e: ResponseException) {
val wasLastMirror = index == downloadRequest.mirrors.size - 1
log.warn(e) {
if (wasLastMirror) "Last mirror, rethrowing..."
else "Trying other mirror now..."
}
if (wasLastMirror) throw e
}
}
error("Reached code that was thought to be unreachable.")
}
}
internal class MirrorChooserRandom : MirrorChooserImpl() {
/**
* Returns a list of mirrors with the best mirrors first.
*/
override fun orderMirrors(downloadRequest: DownloadRequest): List<Mirror> {
// simple random selection for now
return downloadRequest.mirrors.toMutableList().apply { shuffle() }
}
}