mirror of
https://github.com/gedoor/legado.git
synced 2024-09-01 09:34:25 +08:00
优化
This commit is contained in:
parent
14ca962942
commit
1ebf3491f4
@ -2,6 +2,8 @@ package io.legado.app.lib.cronet
|
||||
|
||||
import androidx.annotation.Keep
|
||||
import io.legado.app.help.coroutine.Coroutine
|
||||
import io.legado.app.help.http.CookieManager
|
||||
import io.legado.app.help.http.CookieManager.cookieJarHeader
|
||||
import io.legado.app.help.http.okHttpClient
|
||||
import io.legado.app.utils.DebugLog
|
||||
import io.legado.app.utils.asIOException
|
||||
@ -11,6 +13,8 @@ import okhttp3.*
|
||||
import okhttp3.EventListener
|
||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
import okhttp3.ResponseBody.Companion.asResponseBody
|
||||
import okhttp3.internal.http.HttpMethod
|
||||
import okhttp3.internal.http.StatusLine
|
||||
import okio.Buffer
|
||||
import okio.Source
|
||||
import okio.Timeout
|
||||
@ -29,7 +33,7 @@ import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
@Keep
|
||||
abstract class AbsCallBack(
|
||||
val originalRequest: Request,
|
||||
var originalRequest: Request,
|
||||
val mCall: Call,
|
||||
private val eventListener: EventListener? = null,
|
||||
private val responseCallback: Callback? = null
|
||||
@ -43,6 +47,16 @@ abstract class AbsCallBack(
|
||||
private val callbackResults = ArrayBlockingQueue<CallbackResult>(2)
|
||||
private val urlResponseInfoChain = arrayListOf<UrlResponseInfo>()
|
||||
private var cancelJob: Coroutine<*>? = null
|
||||
private var followRedirect = false
|
||||
private var enableCookieJar = false
|
||||
|
||||
init {
|
||||
if (originalRequest.header(cookieJarHeader) != null) {
|
||||
enableCookieJar = true
|
||||
originalRequest = originalRequest.newBuilder()
|
||||
.removeHeader(cookieJarHeader).build()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Throws(IOException::class)
|
||||
@ -80,15 +94,23 @@ abstract class AbsCallBack(
|
||||
urlResponseInfoChain.add(info)
|
||||
val client = okHttpClient
|
||||
if (originalRequest.url.isHttps && newLocationUrl.startsWith("http://") && client.followSslRedirects) {
|
||||
request.followRedirect()
|
||||
followRedirect = true
|
||||
} else if (!originalRequest.url.isHttps && newLocationUrl.startsWith("https://") && client.followSslRedirects) {
|
||||
request.followRedirect()
|
||||
followRedirect = true
|
||||
} else if (okHttpClient.followRedirects) {
|
||||
request.followRedirect()
|
||||
} else {
|
||||
onError(IOException("Too many redirect"))
|
||||
request.cancel()
|
||||
followRedirect = true
|
||||
}
|
||||
|
||||
if (!followRedirect) {
|
||||
onError(IOException("Too many redirect"))
|
||||
} else {
|
||||
val response = toResponse(originalRequest, info, urlResponseInfoChain)
|
||||
if (enableCookieJar) {
|
||||
CookieManager.saveResponse(response)
|
||||
}
|
||||
originalRequest = buildRedirectRequest(response, originalRequest.method, newLocationUrl)
|
||||
}
|
||||
request.cancel()
|
||||
}
|
||||
|
||||
|
||||
@ -102,29 +124,26 @@ abstract class AbsCallBack(
|
||||
request.cancel()
|
||||
}
|
||||
|
||||
val responseBuilder: Response.Builder
|
||||
val response: Response
|
||||
try {
|
||||
responseBuilder = createResponse(
|
||||
originalRequest,
|
||||
info,
|
||||
CronetBodySource()
|
||||
)
|
||||
response = toResponse(originalRequest, info, urlResponseInfoChain, CronetBodySource())
|
||||
} catch (e: IOException) {
|
||||
request.cancel()
|
||||
cancelJob?.cancel()
|
||||
onError(e)
|
||||
return
|
||||
}
|
||||
val newRequest = originalRequest.newBuilder().url(info.url).build()
|
||||
val response = responseBuilder
|
||||
.request(newRequest)
|
||||
.priorResponse(buildPriorResponse(originalRequest, urlResponseInfoChain, info.urlChain))
|
||||
.build()
|
||||
|
||||
if (enableCookieJar) {
|
||||
CookieManager.saveResponse(response)
|
||||
}
|
||||
|
||||
mResponse = response
|
||||
onSuccess(response)
|
||||
|
||||
//打印协议,用于调试
|
||||
DebugLog.i(javaClass.simpleName, "onResponseStarted[${info.negotiatedProtocol}][${info.httpStatusCode}]${info.url}")
|
||||
val msg = "onResponseStarted[${info.negotiatedProtocol}][${info.httpStatusCode}]${info.url}"
|
||||
DebugLog.i(javaClass.simpleName, msg)
|
||||
if (eventListener != null) {
|
||||
eventListener.responseHeadersEnd(mCall, response)
|
||||
eventListener.responseBodyStart(mCall)
|
||||
@ -167,6 +186,16 @@ abstract class AbsCallBack(
|
||||
}
|
||||
|
||||
override fun onCanceled(request: UrlRequest?, info: UrlResponseInfo?) {
|
||||
if (followRedirect) {
|
||||
followRedirect = false
|
||||
if (enableCookieJar) {
|
||||
val newRequest = CookieManager.loadRequest(originalRequest)
|
||||
buildRequest(newRequest, this)?.start()
|
||||
} else {
|
||||
buildRequest(originalRequest, this)?.start()
|
||||
}
|
||||
return
|
||||
}
|
||||
canceled.set(true)
|
||||
callbackResults.add(CallbackResult(CallbackStep.ON_CANCELED))
|
||||
//DebugLog.i(javaClass.simpleName, "cancel[${info?.negotiatedProtocol}]${info?.url}")
|
||||
@ -293,15 +322,12 @@ abstract class AbsCallBack(
|
||||
private fun buildPriorResponse(
|
||||
request: Request,
|
||||
redirectResponseInfos: List<UrlResponseInfo>,
|
||||
urlChain: List<String>
|
||||
): Response? {
|
||||
var priorResponse: Response? = null
|
||||
if (redirectResponseInfos.isNotEmpty()) {
|
||||
check(urlChain.size == redirectResponseInfos.size + 1) {
|
||||
"The number of redirects should be consistent across URLs and headers!"
|
||||
}
|
||||
for (i in redirectResponseInfos.indices) {
|
||||
val redirectedRequest = request.newBuilder().url(urlChain[i]).build()
|
||||
val url = redirectResponseInfos[i].url
|
||||
val redirectedRequest = request.newBuilder().url(url).build()
|
||||
priorResponse = createResponse(redirectedRequest, redirectResponseInfos[i])
|
||||
.priorResponse(priorResponse)
|
||||
.build()
|
||||
@ -336,6 +362,48 @@ abstract class AbsCallBack(
|
||||
return bodySource.buffer()
|
||||
.asResponseBody(contentType?.toMediaTypeOrNull(), contentLength)
|
||||
}
|
||||
|
||||
private fun buildRedirectRequest(
|
||||
userResponse: Response,
|
||||
method: String,
|
||||
newLocationUrl: String
|
||||
): Request {
|
||||
// Most redirects don't include a request body.
|
||||
val requestBuilder = userResponse.request.newBuilder()
|
||||
if (HttpMethod.permitsRequestBody(method)) {
|
||||
val responseCode = userResponse.code
|
||||
val maintainBody = HttpMethod.redirectsWithBody(method) ||
|
||||
responseCode == StatusLine.HTTP_PERM_REDIRECT ||
|
||||
responseCode == StatusLine.HTTP_TEMP_REDIRECT
|
||||
if (HttpMethod.redirectsToGet(method) && responseCode != StatusLine.HTTP_PERM_REDIRECT && responseCode != StatusLine.HTTP_TEMP_REDIRECT) {
|
||||
requestBuilder.method("GET", null)
|
||||
} else {
|
||||
val requestBody = if (maintainBody) userResponse.request.body else null
|
||||
requestBuilder.method(method, requestBody)
|
||||
}
|
||||
if (!maintainBody) {
|
||||
requestBuilder.removeHeader("Transfer-Encoding")
|
||||
requestBuilder.removeHeader("Content-Length")
|
||||
requestBuilder.removeHeader("Content-Type")
|
||||
}
|
||||
}
|
||||
|
||||
return requestBuilder.url(newLocationUrl).build()
|
||||
}
|
||||
|
||||
private fun toResponse(
|
||||
request: Request,
|
||||
responseInfo: UrlResponseInfo,
|
||||
redirectResponseInfos: List<UrlResponseInfo>,
|
||||
bodySource: Source? = null
|
||||
): Response {
|
||||
val responseBuilder = createResponse(request, responseInfo, bodySource)
|
||||
val newRequest = request.newBuilder().url(responseInfo.url).build()
|
||||
return responseBuilder
|
||||
.request(newRequest)
|
||||
.priorResponse(buildPriorResponse(request, redirectResponseInfos))
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
inner class CronetBodySource : Source {
|
||||
|
@ -6,6 +6,7 @@ package io.legado.app.lib.cronet
|
||||
import androidx.annotation.Keep
|
||||
import io.legado.app.constant.AppConst
|
||||
import io.legado.app.constant.AppLog
|
||||
import io.legado.app.help.http.CookieManager.cookieJarHeader
|
||||
import io.legado.app.help.http.okHttpClient
|
||||
import io.legado.app.utils.DebugLog
|
||||
import okhttp3.Headers
|
||||
@ -80,6 +81,7 @@ fun buildRequest(request: Request, callback: UrlRequest.Callback): UrlRequest? {
|
||||
setHttpMethod(request.method)//设置
|
||||
allowDirectExecutor()
|
||||
headers.forEachIndexed { index, _ ->
|
||||
if (headers.name(index) == cookieJarHeader) return@forEachIndexed
|
||||
addHeader(headers.name(index), headers.value(index))
|
||||
}
|
||||
if (requestBody != null) {
|
||||
|
@ -25,18 +25,11 @@ class CronetInterceptor(private val cookieJar: CookieJar) : Interceptor {
|
||||
//移除Keep-Alive,手动设置会导致400 BadRequest
|
||||
builder.removeHeader("Keep-Alive")
|
||||
builder.removeHeader("Accept-Encoding")
|
||||
if (cookieJar != CookieJar.NO_COOKIES) {
|
||||
val cookieStr = getCookie(original.url)
|
||||
//设置Cookie
|
||||
if (cookieStr.length > 3) {
|
||||
builder.addHeader("Cookie", cookieStr)
|
||||
}
|
||||
}
|
||||
|
||||
val newReq = builder.build()
|
||||
proceedWithCronet(newReq, chain.call())?.let { response ->
|
||||
//从Response 中保存Cookie到CookieJar
|
||||
cookieJar.receiveHeaders(newReq.url, response.headers)
|
||||
//cookieJar.receiveHeaders(newReq.url, response.headers)
|
||||
response
|
||||
} ?: chain.proceed(original)
|
||||
} catch (e: Exception) {
|
||||
|
@ -43,4 +43,8 @@ object AppPattern {
|
||||
val notReadAloudRegex = Regex("^(\\s|\\p{C}|\\p{P}|\\p{Z}|\\p{S})+$")
|
||||
|
||||
val xmlContentTypeRegex = "(application|text)/\\w*\\+?xml.*".toRegex()
|
||||
|
||||
val semicolonRegex = ";".toRegex()
|
||||
|
||||
val equalsRegex = "=".toRegex()
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ import io.legado.app.constant.AppLog
|
||||
import io.legado.app.data.entities.BaseSource
|
||||
import io.legado.app.exception.NoStackTraceException
|
||||
import io.legado.app.help.http.BackstageWebView
|
||||
import io.legado.app.help.http.CookieManager
|
||||
import io.legado.app.help.http.CookieStore
|
||||
import io.legado.app.help.http.SSLHelper
|
||||
import io.legado.app.help.http.StrResponse
|
||||
@ -286,10 +287,8 @@ interface JsExtensions : JsEncodeUtils {
|
||||
.headers(headers)
|
||||
.method(Connection.Method.GET)
|
||||
.execute()
|
||||
val cookies = response.cookies()
|
||||
CookieStore.mapToCookie(cookies)?.let {
|
||||
val domain = NetworkUtils.getSubDomain(urlStr)
|
||||
CacheManager.putMemory("${domain}_cookieJar", it)
|
||||
if (getSource()?.enabledCookieJar == true) {
|
||||
CookieManager.saveResponse(response)
|
||||
}
|
||||
return response
|
||||
}
|
||||
@ -305,10 +304,8 @@ interface JsExtensions : JsEncodeUtils {
|
||||
.headers(headers)
|
||||
.method(Connection.Method.HEAD)
|
||||
.execute()
|
||||
val cookies = response.cookies()
|
||||
CookieStore.mapToCookie(cookies)?.let {
|
||||
val domain = NetworkUtils.getSubDomain(urlStr)
|
||||
CacheManager.putMemory("${domain}_cookieJar", it)
|
||||
if (getSource()?.enabledCookieJar == true) {
|
||||
CookieManager.saveResponse(response)
|
||||
}
|
||||
return response
|
||||
}
|
||||
@ -325,10 +322,8 @@ interface JsExtensions : JsEncodeUtils {
|
||||
.headers(headers)
|
||||
.method(Connection.Method.POST)
|
||||
.execute()
|
||||
val cookies = response.cookies()
|
||||
CookieStore.mapToCookie(cookies)?.let {
|
||||
val domain = NetworkUtils.getSubDomain(urlStr)
|
||||
CacheManager.putMemory("${domain}_cookieJar", it)
|
||||
if (getSource()?.enabledCookieJar == true) {
|
||||
CookieManager.saveResponse(response)
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
148
app/src/main/java/io/legado/app/help/http/CookieManager.kt
Normal file
148
app/src/main/java/io/legado/app/help/http/CookieManager.kt
Normal file
@ -0,0 +1,148 @@
|
||||
package io.legado.app.help.http
|
||||
|
||||
import io.legado.app.data.appDb
|
||||
import io.legado.app.help.CacheManager
|
||||
import io.legado.app.utils.NetworkUtils
|
||||
import okhttp3.Cookie
|
||||
import okhttp3.Headers
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.jsoup.Connection
|
||||
|
||||
object CookieManager {
|
||||
/**
|
||||
* <domain>_session_cookie 会话期 cookie,应用重启后失效
|
||||
* <domain>_cookie cookies 缓存
|
||||
*/
|
||||
|
||||
const val cookieJarHeader = "CookieJar"
|
||||
|
||||
/**
|
||||
* 从响应中保存Cookies
|
||||
*/
|
||||
fun saveResponse(response: Response) {
|
||||
val url = response.request.url
|
||||
val headers = response.headers
|
||||
saveCookiesFromHeaders(url, headers)
|
||||
}
|
||||
|
||||
fun saveResponse(response: Connection.Response) {
|
||||
val url = response.url().toHttpUrlOrNull() ?: return
|
||||
val headers = response.multiHeaders().toHeaders()
|
||||
saveCookiesFromHeaders(url, headers)
|
||||
}
|
||||
|
||||
private fun saveCookiesFromHeaders(url: HttpUrl, headers: Headers) {
|
||||
val domain = NetworkUtils.getSubDomain(url.toString())
|
||||
val cookies = Cookie.parseAll(url, headers)
|
||||
|
||||
val sessionCookie = cookies.filter { !it.persistent }.getString()
|
||||
updateSessionCookie(domain, sessionCookie)
|
||||
|
||||
val cookieString = cookies.filter { it.persistent }.getString()
|
||||
CookieStore.replaceCookie(domain, cookieString)
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载Cookies到请求中
|
||||
*/
|
||||
fun loadRequest(request: Request): Request {
|
||||
val domain = NetworkUtils.getSubDomain(request.url.toString())
|
||||
|
||||
val cookie = CookieStore.getCookie(domain)
|
||||
val requestCookie = request.header("Cookie")
|
||||
|
||||
mergeCookies(cookie, requestCookie)?.let {
|
||||
return request.newBuilder()
|
||||
.header("Cookie", it)
|
||||
.build()
|
||||
}
|
||||
return request
|
||||
}
|
||||
|
||||
private fun getSessionCookieMap(domain: String): MutableMap<String, String>? {
|
||||
return getSessionCookie(domain)?.let { CookieStore.cookieToMap(it) }
|
||||
}
|
||||
|
||||
fun getSessionCookie(domain: String): String? {
|
||||
return CacheManager.getFromMemory("${domain}_session_cookie") as? String
|
||||
}
|
||||
|
||||
private fun updateSessionCookie(domain: String, cookies: String) {
|
||||
val sessionCookie = getSessionCookie(domain)
|
||||
if (sessionCookie.isNullOrEmpty()) {
|
||||
CacheManager.putMemory("${domain}_session_cookie", cookies)
|
||||
return
|
||||
}
|
||||
|
||||
val ck = mergeCookies(sessionCookie, cookies) ?: return
|
||||
CacheManager.putMemory("${domain}_session_cookie", ck)
|
||||
}
|
||||
|
||||
fun mergeCookies(vararg cookies: String?): String? {
|
||||
val cookieMap = mergeCookiesToMap(*cookies)
|
||||
return CookieStore.mapToCookie(cookieMap)
|
||||
}
|
||||
|
||||
fun mergeCookiesToMap(vararg cookies: String?): MutableMap<String, String> {
|
||||
return cookies.filterNotNull().map {
|
||||
CookieStore.cookieToMap(it)
|
||||
}.reduce { acc, cookieMap ->
|
||||
acc.apply { putAll(cookieMap) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除单个Cookie
|
||||
*/
|
||||
fun removeCookie(url: String, key: String) {
|
||||
val domain = NetworkUtils.getSubDomain(url)
|
||||
|
||||
getSessionCookieMap(domain)?.let {
|
||||
it.remove(key)
|
||||
CookieStore.mapToCookie(it)?.let { cookie ->
|
||||
CacheManager.putMemory("${domain}_session_cookie", cookie)
|
||||
}
|
||||
}
|
||||
|
||||
val cookie = getCookieNoSession(url)
|
||||
if (cookie.isNotEmpty()) {
|
||||
val cookieMap = CookieStore.cookieToMap(cookie).apply { remove(key) }
|
||||
CookieStore.mapToCookie(cookieMap)?.let {
|
||||
CookieStore.setCookie(url, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getCookieNoSession(url: String): String {
|
||||
val domain = NetworkUtils.getSubDomain(url)
|
||||
val cacheCookie = CacheManager.getFromMemory("${domain}_cookie") as? String
|
||||
|
||||
return if (cacheCookie != null) {
|
||||
cacheCookie
|
||||
} else {
|
||||
val cookieBean = appDb.cookieDao.get(domain)
|
||||
cookieBean?.cookie ?: ""
|
||||
}
|
||||
}
|
||||
|
||||
fun List<Cookie>.getString() = buildString {
|
||||
this@getString.forEachIndexed { index, cookie ->
|
||||
if (index > 0) append("; ")
|
||||
append(cookie.name).append('=').append(cookie.value)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Map<String, List<String>>.toHeaders(): Headers {
|
||||
return Headers.Builder().apply {
|
||||
this@toHeaders.forEach { (k, v) ->
|
||||
v.forEach {
|
||||
add(k, it)
|
||||
}
|
||||
}
|
||||
}.build()
|
||||
}
|
||||
|
||||
}
|
@ -3,14 +3,18 @@
|
||||
package io.legado.app.help.http
|
||||
|
||||
import android.text.TextUtils
|
||||
import io.legado.app.constant.AppPattern.semicolonRegex
|
||||
import io.legado.app.constant.AppPattern.equalsRegex
|
||||
import io.legado.app.data.appDb
|
||||
import io.legado.app.data.entities.Cookie
|
||||
import io.legado.app.help.CacheManager
|
||||
import io.legado.app.help.http.api.CookieManager
|
||||
import io.legado.app.help.http.CookieManager.getCookieNoSession
|
||||
import io.legado.app.help.http.CookieManager.mergeCookiesToMap
|
||||
import io.legado.app.help.http.api.CookieManagerInterface
|
||||
import io.legado.app.utils.NetworkUtils
|
||||
import io.legado.app.utils.removeCookie
|
||||
|
||||
object CookieStore : CookieManager {
|
||||
object CookieStore : CookieManagerInterface {
|
||||
|
||||
/**
|
||||
*保存cookie到数据库,会自动识别url的二级域名
|
||||
@ -26,7 +30,7 @@ object CookieStore : CookieManager {
|
||||
if (TextUtils.isEmpty(url) || TextUtils.isEmpty(cookie)) {
|
||||
return
|
||||
}
|
||||
val oldCookie = getCookie(url)
|
||||
val oldCookie = getCookieNoSession(url)
|
||||
if (TextUtils.isEmpty(oldCookie)) {
|
||||
setCookie(url, cookie)
|
||||
} else {
|
||||
@ -42,19 +46,26 @@ object CookieStore : CookieManager {
|
||||
*/
|
||||
override fun getCookie(url: String): String {
|
||||
val domain = NetworkUtils.getSubDomain(url)
|
||||
CacheManager.getFromMemory("${domain}_cookie")?.let {
|
||||
if (it is String) return it
|
||||
}
|
||||
|
||||
val cookieBean = appDb.cookieDao.get(domain)
|
||||
val cookie = cookieBean?.cookie ?: ""
|
||||
CacheManager.putMemory(url, cookie)
|
||||
return cookie
|
||||
val cookie = getCookieNoSession(url)
|
||||
val sessionCookie = CookieManager.getSessionCookie(domain)
|
||||
|
||||
val cookieMap = mergeCookiesToMap(cookie, sessionCookie)
|
||||
|
||||
var ck = mapToCookie(cookieMap) ?: ""
|
||||
while (ck.length > 4096) {
|
||||
val removeKey = cookieMap.keys.random()
|
||||
CookieManager.removeCookie(url, removeKey)
|
||||
cookieMap.remove(removeKey)
|
||||
ck = mapToCookie(cookieMap) ?: ""
|
||||
}
|
||||
return ck
|
||||
}
|
||||
|
||||
fun getKey(url: String, key: String): String {
|
||||
val cookie = getCookie(url)
|
||||
val cookieMap = cookieToMap(cookie)
|
||||
val sessionCookie = CookieManager.getSessionCookie(url)
|
||||
val cookieMap = mergeCookiesToMap(cookie, sessionCookie)
|
||||
return cookieMap[key] ?: ""
|
||||
}
|
||||
|
||||
@ -62,6 +73,7 @@ object CookieStore : CookieManager {
|
||||
val domain = NetworkUtils.getSubDomain(url)
|
||||
appDb.cookieDao.delete(domain)
|
||||
CacheManager.deleteMemory("${domain}_cookie")
|
||||
CacheManager.deleteMemory("${domain}_session_cookie")
|
||||
android.webkit.CookieManager.getInstance().removeCookie(domain)
|
||||
}
|
||||
|
||||
@ -70,9 +82,9 @@ object CookieStore : CookieManager {
|
||||
if (cookie.isBlank()) {
|
||||
return cookieMap
|
||||
}
|
||||
val pairArray = cookie.split(";".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||
val pairArray = cookie.split(semicolonRegex).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||
for (pair in pairArray) {
|
||||
val pairs = pair.split("=".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||
val pairs = pair.split(equalsRegex).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||
if (pairs.size == 1) {
|
||||
continue
|
||||
}
|
||||
@ -91,7 +103,7 @@ object CookieStore : CookieManager {
|
||||
}
|
||||
val builder = StringBuilder()
|
||||
cookieMap.keys.forEachIndexed { index, key ->
|
||||
if (index > 0) builder.append(";")
|
||||
if (index > 0) builder.append("; ")
|
||||
builder.append(key).append("=").append(cookieMap[key])
|
||||
}
|
||||
return builder.toString()
|
||||
|
@ -3,6 +3,7 @@ package io.legado.app.help.http
|
||||
import io.legado.app.constant.AppConst
|
||||
import io.legado.app.help.CacheManager
|
||||
import io.legado.app.help.config.AppConfig
|
||||
import io.legado.app.help.http.CookieManager.cookieJarHeader
|
||||
import io.legado.app.utils.NetworkUtils
|
||||
import okhttp3.*
|
||||
import java.net.InetSocketAddress
|
||||
@ -48,7 +49,7 @@ val okHttpClient: OkHttpClient by lazy {
|
||||
.writeTimeout(15, TimeUnit.SECONDS)
|
||||
.readTimeout(15, TimeUnit.SECONDS)
|
||||
.callTimeout(60, TimeUnit.SECONDS)
|
||||
.cookieJar(cookieJar = cookieJar)
|
||||
//.cookieJar(cookieJar = cookieJar)
|
||||
.sslSocketFactory(SSLHelper.unsafeSSLSocketFactory, SSLHelper.unsafeTrustManager)
|
||||
.retryOnConnectionFailure(true)
|
||||
.hostnameVerifier(SSLHelper.unsafeHostnameVerifier)
|
||||
@ -66,8 +67,26 @@ val okHttpClient: OkHttpClient by lazy {
|
||||
builder.addHeader("Keep-Alive", "300")
|
||||
builder.addHeader("Connection", "Keep-Alive")
|
||||
builder.addHeader("Cache-Control", "no-cache")
|
||||
chain.proceed(builder.build())
|
||||
chain.proceed(builder.build()).newBuilder().removeHeader(cookieJarHeader).build()
|
||||
})
|
||||
.addNetworkInterceptor { chain ->
|
||||
var request = chain.request()
|
||||
val enableCookieJar = request.header(cookieJarHeader) != null
|
||||
|
||||
if (enableCookieJar) {
|
||||
val requestBuilder = request.newBuilder()
|
||||
requestBuilder.removeHeader(cookieJarHeader)
|
||||
request = CookieManager.loadRequest(requestBuilder.build())
|
||||
}
|
||||
|
||||
var networkResponse = chain.proceed(request)
|
||||
|
||||
if (enableCookieJar) {
|
||||
CookieManager.saveResponse(networkResponse)
|
||||
networkResponse = networkResponse.newBuilder().header(cookieJarHeader, "1").build()
|
||||
}
|
||||
networkResponse
|
||||
}
|
||||
if (!AppConst.isPlayChannel && AppConfig.isCronet) {
|
||||
if (Cronet.loader?.install() == true) {
|
||||
Cronet.interceptor?.let {
|
||||
|
@ -1,6 +1,6 @@
|
||||
package io.legado.app.help.http.api
|
||||
|
||||
interface CookieManager {
|
||||
interface CookieManagerInterface {
|
||||
|
||||
/**
|
||||
* 保存cookie
|
@ -20,6 +20,7 @@ import io.legado.app.help.JsExtensions
|
||||
import io.legado.app.help.config.AppConfig
|
||||
import io.legado.app.help.glide.GlideHeaders
|
||||
import io.legado.app.help.http.*
|
||||
import io.legado.app.help.http.CookieManager.mergeCookies
|
||||
import io.legado.app.utils.*
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.runBlocking
|
||||
@ -606,13 +607,13 @@ class AnalyzeUrl(
|
||||
CookieStore.getCookie(domain)
|
||||
}
|
||||
if (cookie.isNotEmpty()) {
|
||||
val cookieMap = CookieStore.cookieToMap(cookie)
|
||||
val customCookieMap = CookieStore.cookieToMap(headerMap["Cookie"] ?: "")
|
||||
cookieMap.putAll(customCookieMap)
|
||||
CookieStore.mapToCookie(cookieMap)?.let {
|
||||
mergeCookies(cookie, headerMap["Cookie"])?.let {
|
||||
headerMap.put("Cookie", it)
|
||||
}
|
||||
}
|
||||
if (enabledCookieJar) {
|
||||
headerMap[CookieManager.cookieJarHeader] = "1"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2,6 +2,7 @@ package io.legado.app.utils
|
||||
|
||||
import io.legado.app.BuildConfig
|
||||
import io.legado.app.constant.AppLog
|
||||
import io.legado.app.constant.AppPattern.semicolonRegex
|
||||
import io.legado.app.help.config.AppConfig
|
||||
import io.legado.app.model.analyzeRule.AnalyzeUrl
|
||||
import io.legado.app.model.analyzeRule.CustomUrl
|
||||
@ -102,7 +103,7 @@ object UrlUtil {
|
||||
val redirectUrl: String? = conn.getHeaderField("Location")
|
||||
|
||||
return if (raw != null) {
|
||||
val fileNames = raw.split(";".toRegex()).filter { it.contains("filename") }
|
||||
val fileNames = raw.split(semicolonRegex).filter { it.contains("filename") }
|
||||
val names = hashSetOf<String>()
|
||||
fileNames.forEach {
|
||||
val fileName = it.substringAfter("=")
|
||||
|
Loading…
Reference in New Issue
Block a user