mirror of
https://github.com/Suwayomi/Tachidesk.git
synced 2025-12-10 14:52:05 +01:00
Add setting to use the flaresolverr response (#990)
This commit is contained in:
@@ -23,6 +23,7 @@ import okhttp3.MediaType.Companion.toMediaType
|
|||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.RequestBody.Companion.toRequestBody
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
|
import okhttp3.ResponseBody.Companion.toResponseBody
|
||||||
import suwayomi.tachidesk.server.serverConfig
|
import suwayomi.tachidesk.server.serverConfig
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
@@ -56,11 +57,38 @@ class CloudflareInterceptor(
|
|||||||
originalResponse.close()
|
originalResponse.close()
|
||||||
// network.cookieStore.remove(originalRequest.url.toUri())
|
// network.cookieStore.remove(originalRequest.url.toUri())
|
||||||
|
|
||||||
val request =
|
val flareResponseFallback = serverConfig.flareSolverrAsResponseFallback.value
|
||||||
|
val flareResponse =
|
||||||
runBlocking {
|
runBlocking {
|
||||||
CFClearance.resolveWithFlareSolverr(setUserAgent, originalRequest)
|
CFClearance.resolveWithFlareSolver(originalRequest, !flareResponseFallback)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (flareResponse.message.contains("not detected", ignoreCase = true)) {
|
||||||
|
logger.debug { "FlareSolverr failed to detect Cloudflare challenge" }
|
||||||
|
|
||||||
|
if (flareResponseFallback &&
|
||||||
|
flareResponse.solution.status in 200..299 &&
|
||||||
|
flareResponse.solution.response != null
|
||||||
|
) {
|
||||||
|
val isImage = flareResponse.solution.response.contains(CHROME_IMAGE_TEMPLATE_REGEX)
|
||||||
|
if (!isImage) {
|
||||||
|
logger.debug { "Falling back to FlareSolverr response" }
|
||||||
|
|
||||||
|
setUserAgent(flareResponse.solution.userAgent)
|
||||||
|
|
||||||
|
return originalResponse
|
||||||
|
.newBuilder()
|
||||||
|
.code(flareResponse.solution.status)
|
||||||
|
.body(flareResponse.solution.response.toResponseBody())
|
||||||
|
.build()
|
||||||
|
} else {
|
||||||
|
logger.debug { "FlareSolverr response is an image html template, not falling back" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val request = CFClearance.requestWithFlareSolverr(flareResponse, setUserAgent, originalRequest)
|
||||||
|
|
||||||
chain.proceed(request)
|
chain.proceed(request)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
// Because OkHttp's enqueue only handles IOExceptions, wrap the exception so that
|
// Because OkHttp's enqueue only handles IOExceptions, wrap the exception so that
|
||||||
@@ -73,6 +101,7 @@ class CloudflareInterceptor(
|
|||||||
private val ERROR_CODES = listOf(403, 503)
|
private val ERROR_CODES = listOf(403, 503)
|
||||||
private val SERVER_CHECK = arrayOf("cloudflare-nginx", "cloudflare")
|
private val SERVER_CHECK = arrayOf("cloudflare-nginx", "cloudflare")
|
||||||
private val COOKIE_NAMES = listOf("cf_clearance")
|
private val COOKIE_NAMES = listOf("cf_clearance")
|
||||||
|
private val CHROME_IMAGE_TEMPLATE_REGEX = Regex("""<title>(.*?) \(\d+×\d+\)</title>""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,13 +182,13 @@ object CFClearance {
|
|||||||
val version: String,
|
val version: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
suspend fun resolveWithFlareSolverr(
|
suspend fun resolveWithFlareSolver(
|
||||||
setUserAgent: (String) -> Unit,
|
|
||||||
originalRequest: Request,
|
originalRequest: Request,
|
||||||
): Request {
|
onlyCookies: Boolean,
|
||||||
|
): FlareSolverResponse {
|
||||||
val timeout = serverConfig.flareSolverrTimeout.value.seconds
|
val timeout = serverConfig.flareSolverrTimeout.value.seconds
|
||||||
val flareSolverResponse =
|
|
||||||
with(json) {
|
return with(json) {
|
||||||
mutex.withLock {
|
mutex.withLock {
|
||||||
client.value.newCall(
|
client.value.newCall(
|
||||||
POST(
|
POST(
|
||||||
@@ -175,7 +204,7 @@ object CFClearance {
|
|||||||
network.cookieStore.get(originalRequest.url).map {
|
network.cookieStore.get(originalRequest.url).map {
|
||||||
FlareSolverCookie(it.name, it.value)
|
FlareSolverCookie(it.name, it.value)
|
||||||
},
|
},
|
||||||
returnOnlyCookies = true,
|
returnOnlyCookies = onlyCookies,
|
||||||
maxTimeout = timeout.inWholeMilliseconds.toInt(),
|
maxTimeout = timeout.inWholeMilliseconds.toInt(),
|
||||||
),
|
),
|
||||||
).toRequestBody(jsonMediaType),
|
).toRequestBody(jsonMediaType),
|
||||||
@@ -183,7 +212,13 @@ object CFClearance {
|
|||||||
).awaitSuccess().parseAs<FlareSolverResponse>()
|
).awaitSuccess().parseAs<FlareSolverResponse>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun requestWithFlareSolverr(
|
||||||
|
flareSolverResponse: FlareSolverResponse,
|
||||||
|
setUserAgent: (String) -> Unit,
|
||||||
|
originalRequest: Request,
|
||||||
|
): Request {
|
||||||
if (flareSolverResponse.solution.status in 200..299) {
|
if (flareSolverResponse.solution.status in 200..299) {
|
||||||
setUserAgent(flareSolverResponse.solution.userAgent)
|
setUserAgent(flareSolverResponse.solution.userAgent)
|
||||||
val cookies =
|
val cookies =
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ class SettingsMutation {
|
|||||||
updateSetting(settings.flareSolverrTimeout, serverConfig.flareSolverrTimeout)
|
updateSetting(settings.flareSolverrTimeout, serverConfig.flareSolverrTimeout)
|
||||||
updateSetting(settings.flareSolverrSessionName, serverConfig.flareSolverrSessionName)
|
updateSetting(settings.flareSolverrSessionName, serverConfig.flareSolverrSessionName)
|
||||||
updateSetting(settings.flareSolverrSessionTtl, serverConfig.flareSolverrSessionTtl)
|
updateSetting(settings.flareSolverrSessionTtl, serverConfig.flareSolverrSessionTtl)
|
||||||
|
updateSetting(settings.flareSolverrAsResponseFallback, serverConfig.flareSolverrAsResponseFallback)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setSettings(input: SetSettingsInput): SetSettingsPayload {
|
fun setSettings(input: SetSettingsInput): SetSettingsPayload {
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ interface Settings : Node {
|
|||||||
val flareSolverrTimeout: Int?
|
val flareSolverrTimeout: Int?
|
||||||
val flareSolverrSessionName: String?
|
val flareSolverrSessionName: String?
|
||||||
val flareSolverrSessionTtl: Int?
|
val flareSolverrSessionTtl: Int?
|
||||||
|
val flareSolverrAsResponseFallback: Boolean?
|
||||||
}
|
}
|
||||||
|
|
||||||
data class PartialSettingsType(
|
data class PartialSettingsType(
|
||||||
@@ -151,6 +152,7 @@ data class PartialSettingsType(
|
|||||||
override val flareSolverrTimeout: Int?,
|
override val flareSolverrTimeout: Int?,
|
||||||
override val flareSolverrSessionName: String?,
|
override val flareSolverrSessionName: String?,
|
||||||
override val flareSolverrSessionTtl: Int?,
|
override val flareSolverrSessionTtl: Int?,
|
||||||
|
override val flareSolverrAsResponseFallback: Boolean?,
|
||||||
) : Settings
|
) : Settings
|
||||||
|
|
||||||
class SettingsType(
|
class SettingsType(
|
||||||
@@ -213,6 +215,7 @@ class SettingsType(
|
|||||||
override val flareSolverrTimeout: Int,
|
override val flareSolverrTimeout: Int,
|
||||||
override val flareSolverrSessionName: String,
|
override val flareSolverrSessionName: String,
|
||||||
override val flareSolverrSessionTtl: Int,
|
override val flareSolverrSessionTtl: Int,
|
||||||
|
override val flareSolverrAsResponseFallback: Boolean,
|
||||||
) : Settings {
|
) : Settings {
|
||||||
constructor(config: ServerConfig = serverConfig) : this(
|
constructor(config: ServerConfig = serverConfig) : this(
|
||||||
config.ip.value,
|
config.ip.value,
|
||||||
@@ -270,5 +273,6 @@ class SettingsType(
|
|||||||
config.flareSolverrTimeout.value,
|
config.flareSolverrTimeout.value,
|
||||||
config.flareSolverrSessionName.value,
|
config.flareSolverrSessionName.value,
|
||||||
config.flareSolverrSessionTtl.value,
|
config.flareSolverrSessionTtl.value,
|
||||||
|
config.flareSolverrAsResponseFallback.value,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ class ServerConfig(getConfig: () -> Config, val moduleName: String = SERVER_CONF
|
|||||||
val flareSolverrTimeout: MutableStateFlow<Int> by OverrideConfigValue(IntConfigAdapter)
|
val flareSolverrTimeout: MutableStateFlow<Int> by OverrideConfigValue(IntConfigAdapter)
|
||||||
val flareSolverrSessionName: MutableStateFlow<String> by OverrideConfigValue(StringConfigAdapter)
|
val flareSolverrSessionName: MutableStateFlow<String> by OverrideConfigValue(StringConfigAdapter)
|
||||||
val flareSolverrSessionTtl: MutableStateFlow<Int> by OverrideConfigValue(IntConfigAdapter)
|
val flareSolverrSessionTtl: MutableStateFlow<Int> by OverrideConfigValue(IntConfigAdapter)
|
||||||
|
val flareSolverrAsResponseFallback: MutableStateFlow<Boolean> by OverrideConfigValue(BooleanConfigAdapter)
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
fun <T> subscribeTo(
|
fun <T> subscribeTo(
|
||||||
|
|||||||
@@ -67,3 +67,4 @@ server.flareSolverrUrl = "http://localhost:8191"
|
|||||||
server.flareSolverrTimeout = 60 # time in seconds
|
server.flareSolverrTimeout = 60 # time in seconds
|
||||||
server.flareSolverrSessionName = "suwayomi"
|
server.flareSolverrSessionName = "suwayomi"
|
||||||
server.flareSolverrSessionTtl = 15 # time in minutes
|
server.flareSolverrSessionTtl = 15 # time in minutes
|
||||||
|
server.flareSolverrAsResponseFallback = false
|
||||||
|
|||||||
@@ -67,3 +67,4 @@ server.flareSolverrUrl = "http://localhost:8191"
|
|||||||
server.flareSolverrTimeout = 60 # time in seconds
|
server.flareSolverrTimeout = 60 # time in seconds
|
||||||
server.flareSolverrSessionName = "suwayomi"
|
server.flareSolverrSessionName = "suwayomi"
|
||||||
server.flareSolverrSessionTtl = 15 # time in minutes
|
server.flareSolverrSessionTtl = 15 # time in minutes
|
||||||
|
server.flareSolverrAsResponseFallback = false
|
||||||
|
|||||||
Reference in New Issue
Block a user