mirror of
https://github.com/mihonapp/mihon.git
synced 2026-10-20 07:32:28 -07:00
Fix WebView renderer being killed by sources setting an Origin header
This commit is contained in:
@@ -18,6 +18,7 @@ import eu.kanade.tachiyomi.ui.base.activity.BaseActivity
|
||||
import eu.kanade.tachiyomi.util.system.WebViewUtil
|
||||
import eu.kanade.tachiyomi.util.system.openInBrowser
|
||||
import eu.kanade.tachiyomi.util.system.toShareIntent
|
||||
import eu.kanade.tachiyomi.util.system.toWebViewHeaders
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import eu.kanade.tachiyomi.util.view.setComposeContent
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -71,7 +72,7 @@ class WebViewActivity : BaseActivity() {
|
||||
val source = sourceManager.get(intent.extras!!.getLong(SOURCE_KEY)) as? HttpSource
|
||||
value = withContext(Dispatchers.IO) {
|
||||
try {
|
||||
source?.headers?.toMultimap()?.mapValues { it.value.getOrNull(0) ?: "" }.orEmpty()
|
||||
source?.headers?.toWebViewHeaders().orEmpty()
|
||||
} catch (e: Exception) {
|
||||
logcat(LogPriority.ERROR, e) { "Failed to build headers" }
|
||||
emptyMap()
|
||||
|
||||
@@ -15,6 +15,7 @@ import eu.kanade.tachiyomi.network.NetworkHelper
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import eu.kanade.tachiyomi.util.system.openInBrowser
|
||||
import eu.kanade.tachiyomi.util.system.toShareIntent
|
||||
import eu.kanade.tachiyomi.util.system.toWebViewHeaders
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@@ -47,7 +48,7 @@ class WebViewViewModel(
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val source = sourceId?.let { sourceManager.get(it) as? HttpSource }
|
||||
headers.value = try {
|
||||
source?.headers?.toMultimap()?.mapValues { it.value.getOrNull(0) ?: "" }.orEmpty()
|
||||
source?.headers?.toWebViewHeaders().orEmpty()
|
||||
} catch (e: Exception) {
|
||||
logcat(LogPriority.ERROR, e) { "Failed to build headers" }
|
||||
emptyMap()
|
||||
|
||||
+2
-1
@@ -15,6 +15,7 @@ import androidx.webkit.WebViewCompat
|
||||
import androidx.webkit.WebViewFeature
|
||||
import eu.kanade.tachiyomi.network.AndroidCookieJar
|
||||
import eu.kanade.tachiyomi.util.system.isOutdated
|
||||
import eu.kanade.tachiyomi.util.system.toWebViewHeaders
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import logcat.LogPriority
|
||||
@@ -139,7 +140,7 @@ class CloudflareInterceptor(
|
||||
} else {
|
||||
originalRequest.url.newBuilder().encodedPath("/").query(null).fragment(null).build().toString()
|
||||
}
|
||||
val headers = parseHeaders(originalRequest.headers)
|
||||
val headers = originalRequest.headers.toWebViewHeaders()
|
||||
|
||||
executor.execute {
|
||||
webview = createWebView(originalRequest)
|
||||
|
||||
-25
@@ -13,12 +13,10 @@ import eu.kanade.tachiyomi.util.system.toast
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import okhttp3.Headers
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import tachiyomi.i18n.MR
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
@@ -78,16 +76,6 @@ abstract class WebViewInterceptor(
|
||||
return true
|
||||
}
|
||||
|
||||
fun parseHeaders(headers: Headers): Map<String, String> {
|
||||
return headers
|
||||
// Keeping unsafe header makes webview throw [net::ERR_INVALID_ARGUMENT]
|
||||
.filter { (name, value) ->
|
||||
isRequestHeaderSafe(name, value)
|
||||
}
|
||||
.groupBy(keySelector = { (name, _) -> name }) { (_, value) -> value }
|
||||
.mapValues { it.value.getOrNull(0).orEmpty() }
|
||||
}
|
||||
|
||||
fun CountDownLatch.awaitFor30Seconds() {
|
||||
await(30, TimeUnit.SECONDS)
|
||||
}
|
||||
@@ -100,16 +88,3 @@ abstract class WebViewInterceptor(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Based on [IsRequestHeaderSafe] in
|
||||
// https://source.chromium.org/chromium/chromium/src/+/main:services/network/public/cpp/header_util.cc
|
||||
private fun isRequestHeaderSafe(_name: String, _value: String): Boolean {
|
||||
val name = _name.lowercase(Locale.ENGLISH)
|
||||
val value = _value.lowercase(Locale.ENGLISH)
|
||||
if (name in unsafeHeaderNames || name.startsWith("proxy-")) return false
|
||||
if (name == "connection" && value == "upgrade") return false
|
||||
return true
|
||||
}
|
||||
private val unsafeHeaderNames = listOf(
|
||||
"content-length", "host", "trailer", "te", "upgrade", "cookie2", "keep-alive", "transfer-encoding", "set-cookie",
|
||||
)
|
||||
|
||||
@@ -11,7 +11,9 @@ import androidx.webkit.WebSettingsCompat
|
||||
import androidx.webkit.WebViewFeature
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import logcat.LogPriority
|
||||
import okhttp3.Headers
|
||||
import tachiyomi.core.common.util.system.logcat
|
||||
import java.util.Locale
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
object WebViewUtil {
|
||||
@@ -144,6 +146,31 @@ fun WebView.setUserAgent(userAgent: String) {
|
||||
}
|
||||
}
|
||||
|
||||
fun Headers.toWebViewHeaders(): Map<String, String> {
|
||||
return this
|
||||
// Keeping unsafe header makes webview throw [net::ERR_INVALID_ARGUMENT]
|
||||
.filter { (name, value) ->
|
||||
isRequestHeaderSafe(name, value)
|
||||
}
|
||||
.groupBy(keySelector = { (name, _) -> name }) { (_, value) -> value }
|
||||
.mapValues { it.value.getOrNull(0).orEmpty() }
|
||||
}
|
||||
|
||||
// Based on [IsRequestHeaderSafe] in
|
||||
// https://source.chromium.org/chromium/chromium/src/+/main:services/network/public/cpp/header_util.cc
|
||||
private fun isRequestHeaderSafe(_name: String, _value: String): Boolean {
|
||||
val name = _name.lowercase(Locale.ENGLISH)
|
||||
val value = _value.lowercase(Locale.ENGLISH)
|
||||
if (name in unsafeHeaderNames || name.startsWith("proxy-")) return false
|
||||
if (name == "connection" && value == "upgrade") return false
|
||||
return true
|
||||
}
|
||||
private val unsafeHeaderNames = listOf(
|
||||
"content-length", "host", "trailer", "te", "upgrade", "cookie2", "keep-alive", "transfer-encoding", "set-cookie",
|
||||
// Renderer is killed if it doesn't match the request initiator
|
||||
"origin",
|
||||
)
|
||||
|
||||
private const val WEBVIEW_BRAND = "Android WebView"
|
||||
private const val CHROMIUM_BRAND = "Chromium"
|
||||
private const val CHROME_BRAND = "Google Chrome"
|
||||
|
||||
Reference in New Issue
Block a user