Automaticaly switch mirrors on network errors
parent
43d55cedae
commit
7bec47b4d8
@ -0,0 +1,88 @@
|
|||||||
|
package org.koitharu.kotatsu.core.network
|
||||||
|
|
||||||
|
import dagger.Lazy
|
||||||
|
import okhttp3.Interceptor
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.Response
|
||||||
|
import okhttp3.internal.canParseAsIpAddress
|
||||||
|
import okhttp3.internal.closeQuietly
|
||||||
|
import okhttp3.internal.publicsuffix.PublicSuffixDatabase
|
||||||
|
import org.koitharu.kotatsu.core.parser.MangaRepository
|
||||||
|
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
|
||||||
|
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class MirrorSwitchInterceptor @Inject constructor(
|
||||||
|
private val mangaRepositoryFactoryLazy: Lazy<MangaRepository.Factory>,
|
||||||
|
) : Interceptor {
|
||||||
|
|
||||||
|
override fun intercept(chain: Interceptor.Chain): Response {
|
||||||
|
val request = chain.request()
|
||||||
|
return try {
|
||||||
|
val response = chain.proceed(request)
|
||||||
|
if (response.isFailed) {
|
||||||
|
val responseCopy = response.newBuilder().build()
|
||||||
|
response.close()
|
||||||
|
trySwitchMirror(request, chain) ?: responseCopy
|
||||||
|
} else {
|
||||||
|
response
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
trySwitchMirror(request, chain) ?: throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun trySwitchMirror(request: Request, chain: Interceptor.Chain): Response? {
|
||||||
|
val source = request.tag(MangaSource::class.java) ?: return null
|
||||||
|
val repository = mangaRepositoryFactoryLazy.get().create(source) as? RemoteMangaRepository ?: return null
|
||||||
|
val mirrors = repository.getAvailableMirrors()
|
||||||
|
if (mirrors.isEmpty()) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return tryMirrors(repository, mirrors, chain, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun tryMirrors(
|
||||||
|
repository: RemoteMangaRepository,
|
||||||
|
mirrors: List<String>,
|
||||||
|
chain: Interceptor.Chain,
|
||||||
|
request: Request,
|
||||||
|
): Response? {
|
||||||
|
val url = request.url
|
||||||
|
val currentDomain = url.topPrivateDomain()
|
||||||
|
if (currentDomain !in mirrors) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
val urlBuilder = url.newBuilder()
|
||||||
|
for (mirror in mirrors) {
|
||||||
|
if (mirror == currentDomain) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
val newHost = hostOf(url.host, mirror) ?: continue
|
||||||
|
val newRequest = request.newBuilder()
|
||||||
|
.url(urlBuilder.host(newHost).build())
|
||||||
|
.build()
|
||||||
|
val response = chain.proceed(newRequest)
|
||||||
|
if (response.isFailed) {
|
||||||
|
response.closeQuietly()
|
||||||
|
} else {
|
||||||
|
repository.domain = mirror
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private val Response.isFailed: Boolean
|
||||||
|
get() = code in 400..599
|
||||||
|
|
||||||
|
private fun hostOf(host: String, newDomain: String): String? {
|
||||||
|
if (newDomain.canParseAsIpAddress()) {
|
||||||
|
return newDomain
|
||||||
|
}
|
||||||
|
val domain = PublicSuffixDatabase.get().getEffectiveTldPlusOne(host) ?: return null
|
||||||
|
return host.removeSuffix(domain) + newDomain
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue