parent
4f9e8c0840
commit
eb90446afd
@ -0,0 +1,106 @@ |
|||||||
|
package io.legado.app.help.http.cronet |
||||||
|
|
||||||
|
import io.legado.app.utils.printOnDebug |
||||||
|
import kotlinx.coroutines.runBlocking |
||||||
|
import kotlinx.coroutines.suspendCancellableCoroutine |
||||||
|
import kotlinx.coroutines.withTimeout |
||||||
|
import okhttp3.* |
||||||
|
import okhttp3.internal.http.receiveHeaders |
||||||
|
import org.chromium.net.UrlRequest |
||||||
|
import org.chromium.net.UrlResponseInfo |
||||||
|
import java.io.IOException |
||||||
|
import kotlin.coroutines.resume |
||||||
|
import kotlin.coroutines.resumeWithException |
||||||
|
|
||||||
|
class CronetCoroutineInterceptor(private val cookieJar: CookieJar = CookieJar.NO_COOKIES) : |
||||||
|
Interceptor { |
||||||
|
override fun intercept(chain: Interceptor.Chain): Response { |
||||||
|
if (chain.call().isCanceled()) { |
||||||
|
throw IOException("Canceled") |
||||||
|
} |
||||||
|
val original: Request = chain.request() |
||||||
|
//Cronet未初始化 |
||||||
|
return if (!CronetLoader.install() || cronetEngine == null) { |
||||||
|
chain.proceed(original) |
||||||
|
} else try { |
||||||
|
val builder: Request.Builder = original.newBuilder() |
||||||
|
//移除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() |
||||||
|
val timeout = chain.call().timeout().timeoutNanos() / 1000000 |
||||||
|
runBlocking() { |
||||||
|
if (timeout > 0) { |
||||||
|
withTimeout(timeout) { |
||||||
|
proceedWithCronet(newReq, chain.call()).also { response -> |
||||||
|
cookieJar.receiveHeaders(newReq.url, response.headers) |
||||||
|
} |
||||||
|
} |
||||||
|
} else { |
||||||
|
proceedWithCronet(newReq, chain.call()).also { response -> |
||||||
|
cookieJar.receiveHeaders(newReq.url, response.headers) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} catch (e: Exception) { |
||||||
|
//不能抛出错误,抛出错误会导致应用崩溃 |
||||||
|
//遇到Cronet处理有问题时的情况,如证书过期等等,回退到okhttp处理 |
||||||
|
if (!e.message.toString().contains("ERR_CERT_", true) |
||||||
|
&& !e.message.toString().contains("ERR_SSL_", true) |
||||||
|
) { |
||||||
|
e.printOnDebug() |
||||||
|
} |
||||||
|
chain.proceed(original) |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private suspend fun proceedWithCronet(request: Request, call: Call): Response = |
||||||
|
suspendCancellableCoroutine<Response> { coroutine -> |
||||||
|
|
||||||
|
val callBack = object : AbsCallBack(originalRequest = request, mCall = call) { |
||||||
|
override fun waitForDone(urlRequest: UrlRequest): Response { |
||||||
|
TODO("Not yet implemented") |
||||||
|
} |
||||||
|
|
||||||
|
override fun onError(error: IOException) { |
||||||
|
coroutine.resumeWithException(error) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onSuccess(response: Response) { |
||||||
|
coroutine.resume(response) |
||||||
|
} |
||||||
|
|
||||||
|
override fun onCanceled(request: UrlRequest?, info: UrlResponseInfo?) { |
||||||
|
super.onCanceled(request, info) |
||||||
|
coroutine.cancel() |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
buildRequest(request, callBack)?.start() |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** 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) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue