From 97840328062eaa7140a0e46b8154faf0cf4882ef Mon Sep 17 00:00:00 2001 From: ag2s20150909 Date: Mon, 2 Aug 2021 13:58:02 +0800 Subject: [PATCH] =?UTF-8?q?Cronet:=E4=B8=80=E7=B3=BB=E5=88=97=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=82=201=EF=BC=8C=E4=BD=BF=E7=94=A8okio.Buffer=20?= =?UTF-8?q?=E6=9B=BF=E4=BB=A3ByteArrayOuputStream=20=E5=92=8C=20WritableBy?= =?UTF-8?q?teChannel=202=EF=BC=8C=E4=BC=98=E5=8C=96Cookie=E5=A4=84?= =?UTF-8?q?=E7=90=86,CronetInterceptor=E6=8F=90=E4=BE=9BOkHttp=E7=9A=84Coo?= =?UTF-8?q?kieJar=20=E6=8E=A5=E5=8F=A3=203=EF=BC=8CCronet=E5=A4=84?= =?UTF-8?q?=E7=90=86=E5=A4=B1=E8=B4=A5=E6=97=B6=E5=B0=9D=E8=AF=95=E5=9B=9E?= =?UTF-8?q?=E9=80=80=E5=88=B0okhttp=E5=A4=84=E7=90=86=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/legado/app/help/http/HttpHelper.kt | 3 +- .../app/help/http/cronet/CronetHelper.kt | 36 +++++-------- .../app/help/http/cronet/CronetInterceptor.kt | 51 ++++++++++++++++--- .../app/help/http/cronet/CronetLoader.kt | 14 ++--- .../http/cronet/CronetUrlRequestCallback.kt | 18 ++++--- .../io/legado/app/ui/dict/DictViewModel.kt | 2 +- 6 files changed, 78 insertions(+), 46 deletions(-) diff --git a/app/src/main/java/io/legado/app/help/http/HttpHelper.kt b/app/src/main/java/io/legado/app/help/http/HttpHelper.kt index a77ebb17e..e67090fb3 100644 --- a/app/src/main/java/io/legado/app/help/http/HttpHelper.kt +++ b/app/src/main/java/io/legado/app/help/http/HttpHelper.kt @@ -45,7 +45,8 @@ val okHttpClient: OkHttpClient by lazy { chain.proceed(request) }) if (AppConfig.isCronet && CronetLoader.install()) { - builder.addInterceptor(CronetInterceptor()) + //提供CookieJar 用于同步Cookie + builder.addInterceptor(CronetInterceptor(null)) } diff --git a/app/src/main/java/io/legado/app/help/http/cronet/CronetHelper.kt b/app/src/main/java/io/legado/app/help/http/cronet/CronetHelper.kt index 0a0efa358..4d01657d4 100644 --- a/app/src/main/java/io/legado/app/help/http/cronet/CronetHelper.kt +++ b/app/src/main/java/io/legado/app/help/http/cronet/CronetHelper.kt @@ -1,6 +1,6 @@ package io.legado.app.help.http.cronet -import io.legado.app.help.http.CookieStore +import android.util.Log import okhttp3.Headers import okhttp3.MediaType import okhttp3.Request @@ -9,29 +9,22 @@ import org.chromium.net.CronetEngine.Builder.HTTP_CACHE_DISK import org.chromium.net.ExperimentalCronetEngine import org.chromium.net.UploadDataProviders import org.chromium.net.UrlRequest -import org.chromium.net.urlconnection.CronetURLStreamHandlerFactory import splitties.init.appCtx -import java.net.URL import java.util.concurrent.Executor import java.util.concurrent.Executors -val executor: Executor by lazy { Executors.newSingleThreadExecutor() } +val executor: Executor by lazy { Executors.newCachedThreadPool() } val cronetEngine: ExperimentalCronetEngine by lazy { CronetLoader.preDownload() val builder = ExperimentalCronetEngine.Builder(appCtx) - //设置自定义so库加载 - .setLibraryLoader(CronetLoader) - //设置缓存路径 - .setStoragePath(appCtx.externalCacheDir?.absolutePath) - //设置缓存模式 - .enableHttpCache(HTTP_CACHE_DISK, (1024 * 1024 * 50)) - //设置支持http/3 - .enableQuic(true) - //设置支持http/2 - .enableHttp2(true) + .setLibraryLoader(CronetLoader)//设置自定义so库加载 + .setStoragePath(appCtx.externalCacheDir?.absolutePath)//设置缓存路径 + .enableHttpCache(HTTP_CACHE_DISK, (1024 * 1024 * 50))//设置缓存模式 + .enableQuic(true)//设置支持http/3 + .enableHttp2(true) //设置支持http/2 .enablePublicKeyPinningBypassForLocalTrustAnchors(true) //.enableNetworkQualityEstimator(true) @@ -39,7 +32,9 @@ val cronetEngine: ExperimentalCronetEngine by lazy { builder.enableBrotli(true) //builder.setExperimentalOptions("{\"quic_version\": \"h3-29\"}") val engine = builder.build() - URL.setURLStreamHandlerFactory(CronetURLStreamHandlerFactory(engine)) + Log.d("Cronet", "Cronet Version:" + engine.versionString) + //这会导致Jsoup的网络请求出现问题,暂时不接管系统URL + //URL.setURLStreamHandlerFactory(CronetURLStreamHandlerFactory(engine)) return@lazy engine } @@ -49,17 +44,10 @@ fun buildRequest(request: Request, callback: UrlRequest.Callback): UrlRequest { val url = request.url.toString() val requestBuilder = cronetEngine.newUrlRequestBuilder(url, callback, executor) requestBuilder.setHttpMethod(request.method) - val cookie = CookieStore.getCookie(url) - if (cookie.length > 1) { - requestBuilder.addHeader("Cookie", cookie) - } + val headers: Headers = request.headers headers.forEachIndexed { index, _ -> - val name = headers.name(index) - if (!name.equals("Keep-Alive", true)) { - requestBuilder.addHeader(name, headers.value(index)) - } - + requestBuilder.addHeader(headers.name(index), headers.value(index)) } val requestBody = request.body diff --git a/app/src/main/java/io/legado/app/help/http/cronet/CronetInterceptor.kt b/app/src/main/java/io/legado/app/help/http/cronet/CronetInterceptor.kt index 3c1c04b6e..0a0e955b3 100644 --- a/app/src/main/java/io/legado/app/help/http/cronet/CronetInterceptor.kt +++ b/app/src/main/java/io/legado/app/help/http/cronet/CronetInterceptor.kt @@ -1,23 +1,62 @@ package io.legado.app.help.http.cronet -import okhttp3.Call -import okhttp3.Interceptor -import okhttp3.Request -import okhttp3.Response +import io.legado.app.help.http.CookieStore +import okhttp3.* import java.io.IOException -class CronetInterceptor : Interceptor { +class CronetInterceptor(private val cookieJar: CookieJar?) : Interceptor { @Throws(IOException::class) override fun intercept(chain: Interceptor.Chain): Response { - return proceedWithCronet(chain.request(), chain.call()) + val original: Request = chain.request() + val builder: Request.Builder = original.newBuilder() + //Cronet未初始化 + return if (!CronetLoader.install()) { + chain.proceed(original) + } else try { + //移除Keep-Alive,手动设置会导致400 BadRequest + builder.removeHeader("Keep-Alive") + val cookieStr = getCookie(original.url) + //设置Cookie + if (cookieStr.length > 3) { + builder.header("Cookie", cookieStr) + } + val new = builder.build() + val response: Response = proceedWithCronet(new, chain.call()) + //从Response 中保存Cookie到CookieJar + cookieJar?.saveFromResponse(new.url, Cookie.parseAll(new.url, response.headers)) + response + } catch (e: Exception) { + //遇到Cronet处理有问题时的情况,如证书过期等等,回退到okhttp处理 + chain.proceed(original) + } + + } @Throws(IOException::class) private fun proceedWithCronet(request: Request, call: Call): Response { + val callback = CronetUrlRequestCallback(request, call) val urlRequest = buildRequest(request, callback) urlRequest.start() return callback.waitForDone() } + private fun getCookie(url: HttpUrl): String { + val sb = StringBuilder() + //处理从 Cookjar 获取到的Cookies + if (cookieJar != null) { + val cookies = cookieJar.loadForRequest(url) + for (cookie in cookies) { + sb.append(cookie.name).append("=").append(cookie.value).append("; ") + } + } + //处理自定义的Cookie + val cookie = CookieStore.getCookie(url.toString()) + if (cookie.length > 3) { + sb.append(cookie) + } + return sb.toString() + } + } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/help/http/cronet/CronetLoader.kt b/app/src/main/java/io/legado/app/help/http/cronet/CronetLoader.kt index bb17f88da..e7ea61ec1 100644 --- a/app/src/main/java/io/legado/app/help/http/cronet/CronetLoader.kt +++ b/app/src/main/java/io/legado/app/help/http/cronet/CronetLoader.kt @@ -142,9 +142,9 @@ object CronetLoader : CronetEngine.Builder.LibraryLoader() { @Suppress("SameParameterValue") private fun getUrlMd5(url: String): String? { //这样在下载成功后,遇到无网条件下,只要版本未发生变化也能获取md5 - if (md5 != null && md5!!.length == 32&& version==ImplVersion.getCronetVersion()) { - appCtx.putPrefString("soMd5",md5) - appCtx.putPrefString("soVersion",ImplVersion.getCronetVersion()) + if (md5 != null && md5!!.length == 32 && version == ImplVersion.getCronetVersion()) { + appCtx.putPrefString("soMd5", md5) + appCtx.putPrefString("soVersion", ImplVersion.getCronetVersion()) return md5 } val inputStream: InputStream @@ -159,11 +159,11 @@ object CronetLoader : CronetEngine.Builder.LibraryLoader() { outputStream.write(buffer, 0, read) outputStream.flush() } - val tmd5=outputStream.toString() + val tmd5 = outputStream.toString() //成功获取到md5后保存md5和版本 - if(tmd5.length==32){ - appCtx.putPrefString("soMd5",tmd5) - appCtx.putPrefString("soVersion",ImplVersion.getCronetVersion()) + if (tmd5.length == 32) { + appCtx.putPrefString("soMd5", tmd5) + appCtx.putPrefString("soVersion", ImplVersion.getCronetVersion()) } return tmd5 diff --git a/app/src/main/java/io/legado/app/help/http/cronet/CronetUrlRequestCallback.kt b/app/src/main/java/io/legado/app/help/http/cronet/CronetUrlRequestCallback.kt index e9e0e4260..41f6fab98 100644 --- a/app/src/main/java/io/legado/app/help/http/cronet/CronetUrlRequestCallback.kt +++ b/app/src/main/java/io/legado/app/help/http/cronet/CronetUrlRequestCallback.kt @@ -6,14 +6,13 @@ import io.legado.app.help.http.okHttpClient import okhttp3.* import okhttp3.EventListener import okhttp3.MediaType.Companion.toMediaTypeOrNull -import okhttp3.ResponseBody.Companion.toResponseBody +import okhttp3.ResponseBody.Companion.asResponseBody +import okio.Buffer import org.chromium.net.CronetException import org.chromium.net.UrlRequest import org.chromium.net.UrlResponseInfo -import java.io.ByteArrayOutputStream import java.io.IOException import java.nio.ByteBuffer -import java.nio.channels.Channels import java.util.* class CronetUrlRequestCallback @JvmOverloads internal constructor( @@ -29,8 +28,7 @@ class CronetUrlRequestCallback @JvmOverloads internal constructor( private var mResponse: Response private var mException: IOException? = null private val mResponseCondition = ConditionVariable() - private val mBytesReceived = ByteArrayOutputStream() - private val mReceiveChannel = Channels.newChannel(mBytesReceived) + private val mBuffer = Buffer() @Throws(IOException::class) fun waitForDone(): Response { @@ -64,6 +62,7 @@ class CronetUrlRequestCallback @JvmOverloads internal constructor( override fun onResponseStarted(request: UrlRequest, info: UrlResponseInfo) { this.mResponse = responseFromResponse(this.mResponse, info) +// 用于调试 // val sb: StringBuilder = StringBuilder(info.url).append("\r\n") // sb.append("[Cached:").append(info.wasCached()).append("][StatusCode:") // .append(info.httpStatusCode).append("][StatusText:").append(info.httpStatusText) @@ -74,6 +73,8 @@ class CronetUrlRequestCallback @JvmOverloads internal constructor( // sb.append("[").append(h.key).append("]").append(h.value).append("\r\n"); // } // Log.e("Cronet", sb.toString()) + //打印协议,用于调试 + Log.e("Cronet", info.negotiatedProtocol) if (eventListener != null) { eventListener.responseHeadersEnd(mCall, this.mResponse) eventListener.responseBodyStart(mCall) @@ -89,7 +90,8 @@ class CronetUrlRequestCallback @JvmOverloads internal constructor( ) { byteBuffer.flip() try { - mReceiveChannel.write(byteBuffer) + //mReceiveChannel.write(byteBuffer) + mBuffer.write(byteBuffer) } catch (e: IOException) { Log.i(TAG, "IOException during ByteBuffer read. Details: ", e) throw e @@ -102,8 +104,10 @@ class CronetUrlRequestCallback @JvmOverloads internal constructor( eventListener?.responseBodyEnd(mCall, info.receivedByteCount) val contentType: MediaType? = (this.mResponse.header("content-type") ?: "text/plain; charset=\"utf-8\"").toMediaTypeOrNull() +// val responseBody: ResponseBody = +// mBytesReceived.toByteArray().toResponseBody(contentType) val responseBody: ResponseBody = - mBytesReceived.toByteArray().toResponseBody(contentType) + mBuffer.asResponseBody(contentType) val newRequest = originalRequest.newBuilder().url(info.url).build() this.mResponse = this.mResponse.newBuilder().body(responseBody).request(newRequest).build() mResponseCondition.open() diff --git a/app/src/main/java/io/legado/app/ui/dict/DictViewModel.kt b/app/src/main/java/io/legado/app/ui/dict/DictViewModel.kt index 473e424d6..27f2724a2 100644 --- a/app/src/main/java/io/legado/app/ui/dict/DictViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/dict/DictViewModel.kt @@ -16,7 +16,7 @@ class DictViewModel(application: Application) : BaseViewModel(application) { fun dict(word: String) { execute { val body = okHttpClient.newCallStrResponse { - get("http://apii.dict.cn/mini.php", mapOf(Pair("q", word))) + get("https://apii.dict.cn/mini.php", mapOf(Pair("q", word))) }.body val jsoup = Jsoup.parse(body!!) jsoup.body()