Automaticaly switch mirrors on network errors
This commit is contained in:
@@ -86,6 +86,7 @@ interface AppModule {
|
||||
fun provideOkHttpClient(
|
||||
localStorageManager: LocalStorageManager,
|
||||
commonHeadersInterceptor: CommonHeadersInterceptor,
|
||||
mirrorSwitchInterceptor: MirrorSwitchInterceptor,
|
||||
cookieJar: CookieJar,
|
||||
settings: AppSettings,
|
||||
): OkHttpClient {
|
||||
@@ -103,6 +104,7 @@ interface AppModule {
|
||||
addInterceptor(GZipInterceptor())
|
||||
addInterceptor(commonHeadersInterceptor)
|
||||
addInterceptor(CloudFlareInterceptor())
|
||||
addInterceptor(mirrorSwitchInterceptor)
|
||||
if (BuildConfig.DEBUG) {
|
||||
addInterceptor(CurlLoggingInterceptor())
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -43,8 +43,11 @@ class RemoteMangaRepository(
|
||||
getConfig().defaultSortOrder = value
|
||||
}
|
||||
|
||||
val domain: String
|
||||
var domain: String
|
||||
get() = parser.domain
|
||||
set(value) {
|
||||
getConfig()[parser.configKeyDomain] = value
|
||||
}
|
||||
|
||||
val headers: Headers?
|
||||
get() = parser.headers
|
||||
@@ -95,6 +98,10 @@ class RemoteMangaRepository(
|
||||
parser.onCreateConfig(it)
|
||||
}
|
||||
|
||||
fun getAvailableMirrors(): List<String> {
|
||||
return parser.configKeyDomain.presetValues?.toList().orEmpty()
|
||||
}
|
||||
|
||||
private fun getConfig() = parser.config as SourceSettings
|
||||
|
||||
private suspend fun <T> asyncSafe(block: suspend CoroutineScope.() -> T): SafeDeferred<T> {
|
||||
|
||||
@@ -28,4 +28,12 @@ class SourceSettings(context: Context, source: MangaSource) : MangaSourceConfig
|
||||
is ConfigKey.ShowSuspiciousContent -> prefs.getBoolean(key.key, key.defaultValue)
|
||||
} as T
|
||||
}
|
||||
|
||||
operator fun <T> set(key: ConfigKey<T>, value: T) = prefs.edit {
|
||||
when (key) {
|
||||
is ConfigKey.Domain -> putString(key.key, value as String?)
|
||||
is ConfigKey.ShowSuspiciousContent -> putBoolean(key.key, value as Boolean)
|
||||
is ConfigKey.UserAgent -> putString(key.key, value as String?)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user