Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -95,11 +95,17 @@ interface BaseSource : JsExtensions {
|
||||
* 保存登录头部信息,map格式,访问时自动添加
|
||||
*/
|
||||
fun putLoginHeader(header: String) {
|
||||
val headerMap = GSON.fromJsonObject<Map<String, String>>(header).getOrNull()
|
||||
val cookie = headerMap?.get("Cookie") ?: headerMap?.get("cookie")
|
||||
cookie?.let {
|
||||
CookieStore.replaceCookie(getKey(), it)
|
||||
}
|
||||
CacheManager.put("loginHeader_${getKey()}", header)
|
||||
}
|
||||
|
||||
fun removeLoginHeader() {
|
||||
CacheManager.delete("loginHeader_${getKey()}")
|
||||
CookieStore.removeCookie(getKey())
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,7 +11,7 @@ import splitties.init.appCtx
|
||||
object CacheManager {
|
||||
|
||||
private val queryTTFMap = hashMapOf<String, Pair<Long, QueryTTF>>()
|
||||
private val memoryLruCache = object : LruCache<String, Cache>(100) {}
|
||||
private val memoryLruCache = object : LruCache<String, String>(100) {}
|
||||
|
||||
/**
|
||||
* saveTime 单位为秒
|
||||
@@ -25,15 +25,23 @@ object CacheManager {
|
||||
is ByteArray -> ACache.get(appCtx).put(key, value, saveTime)
|
||||
else -> {
|
||||
val cache = Cache(key, value.toString(), deadline)
|
||||
memoryLruCache.put(key, cache)
|
||||
putMemory(key, value.toString())
|
||||
appDb.cacheDao.insert(cache)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun putMemory(key: String, value: Any) {
|
||||
val cache = Cache(key, value.toString(), 0)
|
||||
memoryLruCache.put(key, cache)
|
||||
fun putMemory(key: String, value: String) {
|
||||
memoryLruCache.put(key, value)
|
||||
}
|
||||
|
||||
//从内存中获取数据 使用lruCache
|
||||
fun getFromMemory(key: String): String? {
|
||||
return memoryLruCache.get(key)
|
||||
}
|
||||
|
||||
fun deleteMemory(key: String) {
|
||||
memoryLruCache.remove(key)
|
||||
}
|
||||
|
||||
fun get(key: String): String? {
|
||||
@@ -42,24 +50,12 @@ object CacheManager {
|
||||
}
|
||||
val cache = appDb.cacheDao.get(key)
|
||||
if (cache != null && (cache.deadline == 0L || cache.deadline > System.currentTimeMillis())) {
|
||||
memoryLruCache.put(key, cache)
|
||||
putMemory(key, cache.value ?: "")
|
||||
return cache.value
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
//从内存中获取数据 使用lruCache 支持过期功能
|
||||
private fun getFromMemory(key: String): String? {
|
||||
val cache = memoryLruCache.get(key) ?: return null
|
||||
val deadline = cache.deadline
|
||||
return if (deadline == 0L || deadline > System.currentTimeMillis()) {
|
||||
cache.value
|
||||
} else {
|
||||
memoryLruCache.remove(key)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun getInt(key: String): Int? {
|
||||
return get(key)?.toIntOrNull()
|
||||
}
|
||||
@@ -100,7 +96,7 @@ object CacheManager {
|
||||
|
||||
fun delete(key: String) {
|
||||
appDb.cacheDao.delete(key)
|
||||
memoryLruCache.remove(key)
|
||||
deleteMemory(key)
|
||||
ACache.get(appCtx).remove(key)
|
||||
}
|
||||
}
|
||||
@@ -8,9 +8,11 @@ import com.bumptech.glide.load.data.DataFetcher
|
||||
import com.bumptech.glide.load.model.GlideUrl
|
||||
import com.bumptech.glide.util.ContentLengthInputStream
|
||||
import com.bumptech.glide.util.Preconditions
|
||||
import io.legado.app.constant.AppConst
|
||||
import io.legado.app.data.appDb
|
||||
import io.legado.app.exception.NoStackTraceException
|
||||
import io.legado.app.help.http.okHttpClient
|
||||
import io.legado.app.help.http.addHeaders
|
||||
import io.legado.app.utils.isWifiConnect
|
||||
import okhttp3.Call
|
||||
import okhttp3.Request
|
||||
@@ -37,16 +39,19 @@ class OkHttpStreamFetcher(private val url: GlideUrl, private val options: Option
|
||||
return
|
||||
}
|
||||
val requestBuilder: Request.Builder = Request.Builder().url(url.toStringUrl())
|
||||
val headerMap = HashMap<String, String>()
|
||||
|
||||
options.get(OkHttpModelLoader.sourceOriginOption)?.let { sourceUrl ->
|
||||
val source = appDb.bookSourceDao.getBookSource(sourceUrl)
|
||||
?: appDb.rssSourceDao.getByKey(sourceUrl)
|
||||
source?.getHeaderMap(true)?.forEach {
|
||||
requestBuilder.addHeader(it.key, it.value)
|
||||
source?.getHeaderMap(true)?.let {
|
||||
headerMap.putAll(it)
|
||||
}
|
||||
}
|
||||
for ((key, value) in url.headers.entries) {
|
||||
requestBuilder.addHeader(key, value)
|
||||
headerMap.put(key, value)
|
||||
}
|
||||
requestBuilder.addHeaders(headerMap)
|
||||
val request: Request = requestBuilder.build()
|
||||
this.callback = callback
|
||||
call = okHttpClient.newCall(request)
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.text.TextUtils
|
||||
import io.legado.app.data.appDb
|
||||
import io.legado.app.data.entities.Cookie
|
||||
import io.legado.app.help.http.api.CookieManager
|
||||
import io.legado.app.help.CacheManager
|
||||
import io.legado.app.utils.NetworkUtils
|
||||
|
||||
object CookieStore : CookieManager {
|
||||
@@ -14,7 +15,9 @@ object CookieStore : CookieManager {
|
||||
*保存cookie到数据库,会自动识别url的二级域名
|
||||
*/
|
||||
override fun setCookie(url: String, cookie: String?) {
|
||||
val cookieBean = Cookie(NetworkUtils.getSubDomain(url), cookie ?: "")
|
||||
val domain = NetworkUtils.getSubDomain(url)
|
||||
CacheManager.putMemory("${domain}_cookie", cookie ?: "")
|
||||
val cookieBean = Cookie(domain, cookie ?: "")
|
||||
appDb.cookieDao.insert(cookieBean)
|
||||
}
|
||||
|
||||
@@ -37,8 +40,12 @@ object CookieStore : CookieManager {
|
||||
*获取url所属的二级域名的cookie
|
||||
*/
|
||||
override fun getCookie(url: String): String {
|
||||
val cookieBean = appDb.cookieDao.get(NetworkUtils.getSubDomain(url))
|
||||
return cookieBean?.cookie ?: ""
|
||||
val domain = NetworkUtils.getSubDomain(url)
|
||||
CacheManager.getFromMemory("${domain}_cookie")?.let { return it }
|
||||
val cookieBean = appDb.cookieDao.get(domain)
|
||||
val cookie = cookieBean?.cookie ?: ""
|
||||
CacheManager.putMemory(url, cookie ?: "")
|
||||
return cookie
|
||||
}
|
||||
|
||||
fun getKey(url: String, key: String): String {
|
||||
@@ -48,7 +55,9 @@ object CookieStore : CookieManager {
|
||||
}
|
||||
|
||||
override fun removeCookie(url: String) {
|
||||
appDb.cookieDao.delete(NetworkUtils.getSubDomain(url))
|
||||
val domain = NetworkUtils.getSubDomain(url)
|
||||
CacheManager.deleteMemory("${domain}_cookie")
|
||||
appDb.cookieDao.delete(domain)
|
||||
}
|
||||
|
||||
override fun cookieToMap(cookie: String): MutableMap<String, String> {
|
||||
|
||||
@@ -4,10 +4,7 @@ import io.legado.app.constant.AppConst
|
||||
import io.legado.app.help.config.AppConfig
|
||||
import io.legado.app.help.http.cronet.CronetInterceptor
|
||||
import io.legado.app.help.http.cronet.CronetLoader
|
||||
import okhttp3.ConnectionSpec
|
||||
import okhttp3.Credentials
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.*
|
||||
import java.net.InetSocketAddress
|
||||
import java.net.Proxy
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
@@ -17,6 +14,22 @@ private val proxyClientCache: ConcurrentHashMap<String, OkHttpClient> by lazy {
|
||||
ConcurrentHashMap()
|
||||
}
|
||||
|
||||
val cookieJar by lazy {
|
||||
object : CookieJar {
|
||||
|
||||
override fun loadForRequest(url: HttpUrl): List<Cookie> {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
|
||||
cookies.forEach {
|
||||
CookieStore.replaceCookie(url.toString(), "${it.name}=${it.value}")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
val okHttpClient: OkHttpClient by lazy {
|
||||
val specs = arrayListOf(
|
||||
ConnectionSpec.MODERN_TLS,
|
||||
@@ -29,6 +42,7 @@ val okHttpClient: OkHttpClient by lazy {
|
||||
.writeTimeout(15, TimeUnit.SECONDS)
|
||||
.readTimeout(15, TimeUnit.SECONDS)
|
||||
.callTimeout(60, TimeUnit.SECONDS)
|
||||
.cookieJar(cookieJar = cookieJar)
|
||||
.sslSocketFactory(SSLHelper.unsafeSSLSocketFactory, SSLHelper.unsafeTrustManager)
|
||||
.retryOnConnectionFailure(true)
|
||||
.hostnameVerifier(SSLHelper.unsafeHostnameVerifier)
|
||||
@@ -47,7 +61,7 @@ val okHttpClient: OkHttpClient by lazy {
|
||||
chain.proceed(builder.build())
|
||||
})
|
||||
if (!AppConfig.isGooglePlay && AppConfig.isCronet && CronetLoader.install()) {
|
||||
builder.addInterceptor(CronetInterceptor(null))
|
||||
builder.addInterceptor(CronetInterceptor(cookieJar = cookieJar))
|
||||
}
|
||||
builder.build()
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package io.legado.app.help.http.cronet
|
||||
|
||||
import android.os.Build
|
||||
import io.legado.app.help.http.CookieStore
|
||||
import io.legado.app.utils.printOnDebug
|
||||
import okhttp3.*
|
||||
import okhttp3.internal.http.receiveHeaders
|
||||
|
||||
|
||||
class CronetInterceptor(private val cookieJar: CookieJar?) : Interceptor {
|
||||
class CronetInterceptor(private val cookieJar: CookieJar = CookieJar.NO_COOKIES) : Interceptor {
|
||||
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
val original: Request = chain.request()
|
||||
@@ -18,15 +18,18 @@ class CronetInterceptor(private val cookieJar: CookieJar?) : Interceptor {
|
||||
//移除Keep-Alive,手动设置会导致400 BadRequest
|
||||
builder.removeHeader("Keep-Alive")
|
||||
builder.removeHeader("Accept-Encoding")
|
||||
val cookieStr = getCookie(original.url)
|
||||
//设置Cookie
|
||||
if (cookieStr.length > 3) {
|
||||
builder.header("Cookie", cookieStr)
|
||||
if (cookieJar != CookieJar.NO_COOKIES) {
|
||||
val cookieStr = getCookie(original.url)
|
||||
//设置Cookie
|
||||
if (cookieStr.length > 3) {
|
||||
builder.addHeader("Cookie", cookieStr)
|
||||
}
|
||||
}
|
||||
val new = builder.build()
|
||||
proceedWithCronet(new, chain.call())?.let { response ->
|
||||
|
||||
val newReq = builder.build()
|
||||
proceedWithCronet(newReq, chain.call())?.let { response ->
|
||||
//从Response 中保存Cookie到CookieJar
|
||||
cookieJar?.saveFromResponse(new.url, Cookie.parseAll(new.url, response.headers))
|
||||
cookieJar.receiveHeaders(newReq.url, response.headers)
|
||||
response
|
||||
} ?: chain.proceed(original)
|
||||
} catch (e: Exception) {
|
||||
@@ -54,21 +57,14 @@ class CronetInterceptor(private val cookieJar: CookieJar?) : Interceptor {
|
||||
return null
|
||||
}
|
||||
|
||||
private fun getCookie(url: HttpUrl): String {
|
||||
val sb = StringBuilder()
|
||||
//处理从 Cookiejar 获取到的Cookies
|
||||
if (cookieJar != null) {
|
||||
val cookies = cookieJar.loadForRequest(url)
|
||||
for (cookie in cookies) {
|
||||
sb.append(cookie.name).append("=").append(cookie.value).append("; ")
|
||||
}
|
||||
|
||||
/** Returns a 'Cookie' HTTP request header with all cookies, like `a=b; c=d`. */
|
||||
private fun getCookie(url: HttpUrl): String = buildString {
|
||||
val cookies = cookieJar.loadForRequest(url)
|
||||
cookies.forEachIndexed { index, cookie ->
|
||||
if (index > 0) append("; ")
|
||||
append(cookie.name).append('=').append(cookie.value)
|
||||
}
|
||||
//处理自定义的Cookie
|
||||
val cookie = CookieStore.getCookie(url.toString())
|
||||
if (cookie.length > 3) {
|
||||
sb.append(cookie)
|
||||
}
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -192,9 +192,6 @@ class AnalyzeUrl(
|
||||
}
|
||||
}
|
||||
}
|
||||
headerMap[UA_NAME] ?: let {
|
||||
headerMap[UA_NAME] = AppConfig.userAgent
|
||||
}
|
||||
urlNoQuery = url
|
||||
when (method) {
|
||||
RequestMethod.GET -> {
|
||||
|
||||
@@ -7,6 +7,7 @@ import android.view.ViewGroup
|
||||
import android.view.MenuItem
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import io.legado.app.R
|
||||
import io.legado.app.base.BaseDialogFragment
|
||||
import io.legado.app.databinding.DialogVerificationCodeViewBinding
|
||||
@@ -46,7 +47,6 @@ class VerificationCodeDialog() : BaseDialogFragment(R.layout.dialog_verification
|
||||
binding.run {
|
||||
toolBar.setBackgroundColor(primaryColor)
|
||||
val sourceOrigin = arguments?.getString("sourceOrigin")
|
||||
val key = "${sourceOrigin}_verificationResult"
|
||||
arguments?.getString("imageUrl")?.let { imageUrl ->
|
||||
ImageLoader.load(requireContext(), imageUrl).apply {
|
||||
sourceOrigin?.let {
|
||||
@@ -58,8 +58,10 @@ class VerificationCodeDialog() : BaseDialogFragment(R.layout.dialog_verification
|
||||
)
|
||||
}
|
||||
}.error(R.drawable.image_loading_error)
|
||||
.into(ivImage)
|
||||
ivImage.setOnClickListener {
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||
.skipMemoryCache(true)
|
||||
.into(verificationCodeImageView)
|
||||
verificationCodeImageView.setOnClickListener {
|
||||
showDialogFragment(PhotoDialog(imageUrl, sourceOrigin))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,11 +18,10 @@
|
||||
app:titleTextAppearance="@style/ToolbarTitle" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/iv_image"
|
||||
android:scaleType="fitXY"
|
||||
android:id="@+id/verification_code_image_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:padding="3dp"
|
||||
android:layout_height="100dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
tools:ignore="UnusedAttribute" />
|
||||
|
||||
|
||||
Reference in New Issue
Block a user