Merge remote-tracking branch 'origin/master'

This commit is contained in:
kunfei 2022-05-07 20:49:22 +08:00
commit c0ba24b6aa
9 changed files with 88 additions and 64 deletions

View File

@ -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())
}
/**

View File

@ -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)
}
}

View File

@ -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)

View File

@ -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> {

View File

@ -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()
}

View File

@ -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()
}
}

View File

@ -192,9 +192,6 @@ class AnalyzeUrl(
}
}
}
headerMap[UA_NAME] ?: let {
headerMap[UA_NAME] = AppConfig.userAgent
}
urlNoQuery = url
when (method) {
RequestMethod.GET -> {

View File

@ -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))
}
}

View File

@ -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" />