Merge remote-tracking branch 'origin/master'

pull/1857/head
kunfei 3 years ago
commit c0ba24b6aa
  1. 6
      app/src/main/java/io/legado/app/data/entities/BaseSource.kt
  2. 34
      app/src/main/java/io/legado/app/help/CacheManager.kt
  3. 11
      app/src/main/java/io/legado/app/help/glide/OkHttpStreamFetcher.kt
  4. 17
      app/src/main/java/io/legado/app/help/http/CookieStore.kt
  5. 24
      app/src/main/java/io/legado/app/help/http/HttpHelper.kt
  6. 42
      app/src/main/java/io/legado/app/help/http/cronet/CronetInterceptor.kt
  7. 3
      app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt
  8. 8
      app/src/main/java/io/legado/app/ui/association/VerificationCodeDialog.kt
  9. 7
      app/src/main/res/layout/dialog_verification_code_view.xml

@ -95,11 +95,17 @@ interface BaseSource : JsExtensions {
* 保存登录头部信息,map格式,访问时自动添加 * 保存登录头部信息,map格式,访问时自动添加
*/ */
fun putLoginHeader(header: String) { 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) CacheManager.put("loginHeader_${getKey()}", header)
} }
fun removeLoginHeader() { fun removeLoginHeader() {
CacheManager.delete("loginHeader_${getKey()}") CacheManager.delete("loginHeader_${getKey()}")
CookieStore.removeCookie(getKey())
} }
/** /**

@ -11,7 +11,7 @@ import splitties.init.appCtx
object CacheManager { object CacheManager {
private val queryTTFMap = hashMapOf<String, Pair<Long, QueryTTF>>() 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 单位为秒 * saveTime 单位为秒
@ -25,15 +25,23 @@ object CacheManager {
is ByteArray -> ACache.get(appCtx).put(key, value, saveTime) is ByteArray -> ACache.get(appCtx).put(key, value, saveTime)
else -> { else -> {
val cache = Cache(key, value.toString(), deadline) val cache = Cache(key, value.toString(), deadline)
memoryLruCache.put(key, cache) putMemory(key, value.toString())
appDb.cacheDao.insert(cache) appDb.cacheDao.insert(cache)
} }
} }
} }
fun putMemory(key: String, value: Any) { fun putMemory(key: String, value: String) {
val cache = Cache(key, value.toString(), 0) memoryLruCache.put(key, value)
memoryLruCache.put(key, cache) }
//从内存中获取数据 使用lruCache
fun getFromMemory(key: String): String? {
return memoryLruCache.get(key)
}
fun deleteMemory(key: String) {
memoryLruCache.remove(key)
} }
fun get(key: String): String? { fun get(key: String): String? {
@ -42,24 +50,12 @@ object CacheManager {
} }
val cache = appDb.cacheDao.get(key) val cache = appDb.cacheDao.get(key)
if (cache != null && (cache.deadline == 0L || cache.deadline > System.currentTimeMillis())) { if (cache != null && (cache.deadline == 0L || cache.deadline > System.currentTimeMillis())) {
memoryLruCache.put(key, cache) putMemory(key, cache.value ?: "")
return cache.value return cache.value
} }
return null 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? { fun getInt(key: String): Int? {
return get(key)?.toIntOrNull() return get(key)?.toIntOrNull()
} }
@ -100,7 +96,7 @@ object CacheManager {
fun delete(key: String) { fun delete(key: String) {
appDb.cacheDao.delete(key) appDb.cacheDao.delete(key)
memoryLruCache.remove(key) deleteMemory(key)
ACache.get(appCtx).remove(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.load.model.GlideUrl
import com.bumptech.glide.util.ContentLengthInputStream import com.bumptech.glide.util.ContentLengthInputStream
import com.bumptech.glide.util.Preconditions import com.bumptech.glide.util.Preconditions
import io.legado.app.constant.AppConst
import io.legado.app.data.appDb import io.legado.app.data.appDb
import io.legado.app.exception.NoStackTraceException import io.legado.app.exception.NoStackTraceException
import io.legado.app.help.http.okHttpClient import io.legado.app.help.http.okHttpClient
import io.legado.app.help.http.addHeaders
import io.legado.app.utils.isWifiConnect import io.legado.app.utils.isWifiConnect
import okhttp3.Call import okhttp3.Call
import okhttp3.Request import okhttp3.Request
@ -37,16 +39,19 @@ class OkHttpStreamFetcher(private val url: GlideUrl, private val options: Option
return return
} }
val requestBuilder: Request.Builder = Request.Builder().url(url.toStringUrl()) val requestBuilder: Request.Builder = Request.Builder().url(url.toStringUrl())
val headerMap = HashMap<String, String>()
options.get(OkHttpModelLoader.sourceOriginOption)?.let { sourceUrl -> options.get(OkHttpModelLoader.sourceOriginOption)?.let { sourceUrl ->
val source = appDb.bookSourceDao.getBookSource(sourceUrl) val source = appDb.bookSourceDao.getBookSource(sourceUrl)
?: appDb.rssSourceDao.getByKey(sourceUrl) ?: appDb.rssSourceDao.getByKey(sourceUrl)
source?.getHeaderMap(true)?.forEach { source?.getHeaderMap(true)?.let {
requestBuilder.addHeader(it.key, it.value) headerMap.putAll(it)
} }
} }
for ((key, value) in url.headers.entries) { for ((key, value) in url.headers.entries) {
requestBuilder.addHeader(key, value) headerMap.put(key, value)
} }
requestBuilder.addHeaders(headerMap)
val request: Request = requestBuilder.build() val request: Request = requestBuilder.build()
this.callback = callback this.callback = callback
call = okHttpClient.newCall(request) call = okHttpClient.newCall(request)

@ -6,6 +6,7 @@ import android.text.TextUtils
import io.legado.app.data.appDb import io.legado.app.data.appDb
import io.legado.app.data.entities.Cookie import io.legado.app.data.entities.Cookie
import io.legado.app.help.http.api.CookieManager import io.legado.app.help.http.api.CookieManager
import io.legado.app.help.CacheManager
import io.legado.app.utils.NetworkUtils import io.legado.app.utils.NetworkUtils
object CookieStore : CookieManager { object CookieStore : CookieManager {
@ -14,7 +15,9 @@ object CookieStore : CookieManager {
*保存cookie到数据库会自动识别url的二级域名 *保存cookie到数据库会自动识别url的二级域名
*/ */
override fun setCookie(url: String, cookie: String?) { 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) appDb.cookieDao.insert(cookieBean)
} }
@ -37,8 +40,12 @@ object CookieStore : CookieManager {
*获取url所属的二级域名的cookie *获取url所属的二级域名的cookie
*/ */
override fun getCookie(url: String): String { override fun getCookie(url: String): String {
val cookieBean = appDb.cookieDao.get(NetworkUtils.getSubDomain(url)) val domain = NetworkUtils.getSubDomain(url)
return cookieBean?.cookie ?: "" 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 { fun getKey(url: String, key: String): String {
@ -48,7 +55,9 @@ object CookieStore : CookieManager {
} }
override fun removeCookie(url: String) { 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> { 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.config.AppConfig
import io.legado.app.help.http.cronet.CronetInterceptor import io.legado.app.help.http.cronet.CronetInterceptor
import io.legado.app.help.http.cronet.CronetLoader import io.legado.app.help.http.cronet.CronetLoader
import okhttp3.ConnectionSpec import okhttp3.*
import okhttp3.Credentials
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import java.net.InetSocketAddress import java.net.InetSocketAddress
import java.net.Proxy import java.net.Proxy
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
@ -17,6 +14,22 @@ private val proxyClientCache: ConcurrentHashMap<String, OkHttpClient> by lazy {
ConcurrentHashMap() 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 okHttpClient: OkHttpClient by lazy {
val specs = arrayListOf( val specs = arrayListOf(
ConnectionSpec.MODERN_TLS, ConnectionSpec.MODERN_TLS,
@ -29,6 +42,7 @@ val okHttpClient: OkHttpClient by lazy {
.writeTimeout(15, TimeUnit.SECONDS) .writeTimeout(15, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS) .readTimeout(15, TimeUnit.SECONDS)
.callTimeout(60, TimeUnit.SECONDS) .callTimeout(60, TimeUnit.SECONDS)
.cookieJar(cookieJar = cookieJar)
.sslSocketFactory(SSLHelper.unsafeSSLSocketFactory, SSLHelper.unsafeTrustManager) .sslSocketFactory(SSLHelper.unsafeSSLSocketFactory, SSLHelper.unsafeTrustManager)
.retryOnConnectionFailure(true) .retryOnConnectionFailure(true)
.hostnameVerifier(SSLHelper.unsafeHostnameVerifier) .hostnameVerifier(SSLHelper.unsafeHostnameVerifier)
@ -47,7 +61,7 @@ val okHttpClient: OkHttpClient by lazy {
chain.proceed(builder.build()) chain.proceed(builder.build())
}) })
if (!AppConfig.isGooglePlay && AppConfig.isCronet && CronetLoader.install()) { if (!AppConfig.isGooglePlay && AppConfig.isCronet && CronetLoader.install()) {
builder.addInterceptor(CronetInterceptor(null)) builder.addInterceptor(CronetInterceptor(cookieJar = cookieJar))
} }
builder.build() builder.build()
} }

@ -1,12 +1,12 @@
package io.legado.app.help.http.cronet package io.legado.app.help.http.cronet
import android.os.Build import android.os.Build
import io.legado.app.help.http.CookieStore
import io.legado.app.utils.printOnDebug import io.legado.app.utils.printOnDebug
import okhttp3.* 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 { override fun intercept(chain: Interceptor.Chain): Response {
val original: Request = chain.request() val original: Request = chain.request()
@ -18,15 +18,18 @@ class CronetInterceptor(private val cookieJar: CookieJar?) : Interceptor {
//移除Keep-Alive,手动设置会导致400 BadRequest //移除Keep-Alive,手动设置会导致400 BadRequest
builder.removeHeader("Keep-Alive") builder.removeHeader("Keep-Alive")
builder.removeHeader("Accept-Encoding") builder.removeHeader("Accept-Encoding")
val cookieStr = getCookie(original.url) if (cookieJar != CookieJar.NO_COOKIES) {
//设置Cookie val cookieStr = getCookie(original.url)
if (cookieStr.length > 3) { //设置Cookie
builder.header("Cookie", cookieStr) 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 //从Response 中保存Cookie到CookieJar
cookieJar?.saveFromResponse(new.url, Cookie.parseAll(new.url, response.headers)) cookieJar.receiveHeaders(newReq.url, response.headers)
response response
} ?: chain.proceed(original) } ?: chain.proceed(original)
} catch (e: Exception) { } catch (e: Exception) {
@ -54,21 +57,14 @@ class CronetInterceptor(private val cookieJar: CookieJar?) : Interceptor {
return null return null
} }
private fun getCookie(url: HttpUrl): String {
val sb = StringBuilder() /** Returns a 'Cookie' HTTP request header with all cookies, like `a=b; c=d`. */
//处理从 Cookiejar 获取到的Cookies private fun getCookie(url: HttpUrl): String = buildString {
if (cookieJar != null) { val cookies = cookieJar.loadForRequest(url)
val cookies = cookieJar.loadForRequest(url) cookies.forEachIndexed { index, cookie ->
for (cookie in cookies) { if (index > 0) append("; ")
sb.append(cookie.name).append("=").append(cookie.value).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 urlNoQuery = url
when (method) { when (method) {
RequestMethod.GET -> { RequestMethod.GET -> {

@ -7,6 +7,7 @@ import android.view.ViewGroup
import android.view.MenuItem import android.view.MenuItem
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import com.bumptech.glide.request.RequestOptions import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.load.engine.DiskCacheStrategy
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.BaseDialogFragment import io.legado.app.base.BaseDialogFragment
import io.legado.app.databinding.DialogVerificationCodeViewBinding import io.legado.app.databinding.DialogVerificationCodeViewBinding
@ -46,7 +47,6 @@ class VerificationCodeDialog() : BaseDialogFragment(R.layout.dialog_verification
binding.run { binding.run {
toolBar.setBackgroundColor(primaryColor) toolBar.setBackgroundColor(primaryColor)
val sourceOrigin = arguments?.getString("sourceOrigin") val sourceOrigin = arguments?.getString("sourceOrigin")
val key = "${sourceOrigin}_verificationResult"
arguments?.getString("imageUrl")?.let { imageUrl -> arguments?.getString("imageUrl")?.let { imageUrl ->
ImageLoader.load(requireContext(), imageUrl).apply { ImageLoader.load(requireContext(), imageUrl).apply {
sourceOrigin?.let { sourceOrigin?.let {
@ -58,8 +58,10 @@ class VerificationCodeDialog() : BaseDialogFragment(R.layout.dialog_verification
) )
} }
}.error(R.drawable.image_loading_error) }.error(R.drawable.image_loading_error)
.into(ivImage) .diskCacheStrategy(DiskCacheStrategy.NONE)
ivImage.setOnClickListener { .skipMemoryCache(true)
.into(verificationCodeImageView)
verificationCodeImageView.setOnClickListener {
showDialogFragment(PhotoDialog(imageUrl, sourceOrigin)) showDialogFragment(PhotoDialog(imageUrl, sourceOrigin))
} }
} }

@ -18,11 +18,10 @@
app:titleTextAppearance="@style/ToolbarTitle" /> app:titleTextAppearance="@style/ToolbarTitle" />
<androidx.appcompat.widget.AppCompatImageView <androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_image" android:id="@+id/verification_code_image_view"
android:scaleType="fitXY"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="50dp" android:layout_height="100dp"
android:padding="3dp" android:scaleType="centerCrop"
android:background="?android:attr/selectableItemBackgroundBorderless" android:background="?android:attr/selectableItemBackgroundBorderless"
tools:ignore="UnusedAttribute" /> tools:ignore="UnusedAttribute" />

Loading…
Cancel
Save