diff --git a/app/src/main/java/io/legado/app/help/exoplayer/ExoPlayerHelper.kt b/app/src/main/java/io/legado/app/help/exoplayer/ExoPlayerHelper.kt index e693c583d..7a1a5bcc9 100644 --- a/app/src/main/java/io/legado/app/help/exoplayer/ExoPlayerHelper.kt +++ b/app/src/main/java/io/legado/app/help/exoplayer/ExoPlayerHelper.kt @@ -133,7 +133,10 @@ object ExoPlayerHelper { * Okhttp DataSource.Factory */ private val okhttpDataFactory by lazy { - OkHttpDataSource.Factory(okHttpClient) + val client = okHttpClient.newBuilder() + .callTimeout(0, TimeUnit.SECONDS) + .build() + OkHttpDataSource.Factory(client) .setCacheControl(CacheControl.Builder().maxAge(1, TimeUnit.DAYS).build()) } diff --git a/app/src/main/java/io/legado/app/lib/cronet/AbsCallBack.kt b/app/src/main/java/io/legado/app/lib/cronet/AbsCallBack.kt index 0a0334db6..18066db62 100644 --- a/app/src/main/java/io/legado/app/lib/cronet/AbsCallBack.kt +++ b/app/src/main/java/io/legado/app/lib/cronet/AbsCallBack.kt @@ -35,6 +35,7 @@ import java.util.concurrent.atomic.AtomicBoolean abstract class AbsCallBack( var originalRequest: Request, val mCall: Call, + var readTimeoutMillis: Int, private val eventListener: EventListener? = null, private val responseCallback: Callback? = null ) : UrlRequest.Callback() { @@ -52,6 +53,9 @@ abstract class AbsCallBack( private var redirectRequest: Request? = null init { + if (readTimeoutMillis == 0) { + readTimeoutMillis = Int.MAX_VALUE + } if (originalRequest.header(cookieJarHeader) != null) { enableCookieJar = true originalRequest = originalRequest.newBuilder() @@ -411,7 +415,7 @@ abstract class AbsCallBack( private var buffer = ByteBuffer.allocateDirect(32 * 1024) private var closed = false - private val timeout = mCall.timeout().timeoutNanos() + private val timeout = readTimeoutMillis.toLong() override fun close() { cancelJob?.cancel() @@ -443,7 +447,7 @@ abstract class AbsCallBack( request?.read(buffer) - val result = callbackResults.poll(timeout, TimeUnit.NANOSECONDS) + val result = callbackResults.poll(timeout, TimeUnit.MILLISECONDS) if (result == null) { request?.cancel() throw IOException("Body Read Timeout") diff --git a/app/src/main/java/io/legado/app/lib/cronet/CronetCoroutineInterceptor.kt b/app/src/main/java/io/legado/app/lib/cronet/CronetCoroutineInterceptor.kt index 87fe65ea7..b6674a1ba 100644 --- a/app/src/main/java/io/legado/app/lib/cronet/CronetCoroutineInterceptor.kt +++ b/app/src/main/java/io/legado/app/lib/cronet/CronetCoroutineInterceptor.kt @@ -5,7 +5,12 @@ import io.legado.app.utils.printOnDebug import kotlinx.coroutines.runBlocking import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.withTimeout -import okhttp3.* +import okhttp3.Call +import okhttp3.CookieJar +import okhttp3.HttpUrl +import okhttp3.Interceptor +import okhttp3.Request +import okhttp3.Response import okhttp3.internal.http.receiveHeaders import org.chromium.net.UrlRequest import org.chromium.net.UrlResponseInfo @@ -43,12 +48,12 @@ class CronetCoroutineInterceptor(private val cookieJar: CookieJar) : Interceptor runBlocking() { if (timeout > 0) { withTimeout(timeout) { - proceedWithCronet(newReq, chain.call()).also { response -> + proceedWithCronet(newReq, chain.call(), chain.readTimeoutMillis()).also { response -> cookieJar.receiveHeaders(newReq.url, response.headers) } } } else { - proceedWithCronet(newReq, chain.call()).also { response -> + proceedWithCronet(newReq, chain.call(), chain.readTimeoutMillis()).also { response -> cookieJar.receiveHeaders(newReq.url, response.headers) } } @@ -68,10 +73,14 @@ class CronetCoroutineInterceptor(private val cookieJar: CookieJar) : Interceptor } - private suspend fun proceedWithCronet(request: Request, call: Call): Response = + private suspend fun proceedWithCronet( + request: Request, + call: Call, + readTimeoutMillis: Int + ): Response = suspendCancellableCoroutine { coroutine -> - val callBack = object : AbsCallBack(originalRequest = request, mCall = call) { + val callBack = object : AbsCallBack(request, call, readTimeoutMillis) { override fun waitForDone(urlRequest: UrlRequest): Response { TODO("Not yet implemented") } diff --git a/app/src/main/java/io/legado/app/lib/cronet/CronetInterceptor.kt b/app/src/main/java/io/legado/app/lib/cronet/CronetInterceptor.kt index 0f779b9f2..f32429ee7 100644 --- a/app/src/main/java/io/legado/app/lib/cronet/CronetInterceptor.kt +++ b/app/src/main/java/io/legado/app/lib/cronet/CronetInterceptor.kt @@ -27,7 +27,7 @@ class CronetInterceptor(private val cookieJar: CookieJar) : Interceptor { builder.removeHeader("Accept-Encoding") val newReq = builder.build() - proceedWithCronet(newReq, chain.call())/*?.let { response -> + proceedWithCronet(newReq, chain.call(), chain.readTimeoutMillis())/*?.let { response -> //从Response 中保存Cookie到CookieJar //cookieJar.receiveHeaders(newReq.url, response.headers) response @@ -45,11 +45,11 @@ class CronetInterceptor(private val cookieJar: CookieJar) : Interceptor { } @SuppressLint("ObsoleteSdkInt") - private fun proceedWithCronet(request: Request, call: Call): Response? { + private fun proceedWithCronet(request: Request, call: Call, readTimeoutMillis: Int): Response? { val callBack = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - NewCallBack(request, call) + NewCallBack(request, call, readTimeoutMillis) } else { - OldCallback(request, call) + OldCallback(request, call, readTimeoutMillis) } buildRequest(request, callBack)?.runCatching { return callBack.waitForDone(this) diff --git a/app/src/main/java/io/legado/app/lib/cronet/NewCallBack.kt b/app/src/main/java/io/legado/app/lib/cronet/NewCallBack.kt index e3159907f..21a5e3e0b 100644 --- a/app/src/main/java/io/legado/app/lib/cronet/NewCallBack.kt +++ b/app/src/main/java/io/legado/app/lib/cronet/NewCallBack.kt @@ -15,7 +15,8 @@ import java.util.concurrent.TimeUnit @SuppressLint("ObsoleteSdkInt") @Keep @RequiresApi(api = Build.VERSION_CODES.N) -class NewCallBack(originalRequest: Request, mCall: Call) : AbsCallBack(originalRequest, mCall) { +class NewCallBack(originalRequest: Request, mCall: Call, readTimeoutMillis: Int) : + AbsCallBack(originalRequest, mCall, readTimeoutMillis) { private val responseFuture = CompletableFuture() diff --git a/app/src/main/java/io/legado/app/lib/cronet/OldCallback.kt b/app/src/main/java/io/legado/app/lib/cronet/OldCallback.kt index 9cfd277f4..c598eb651 100644 --- a/app/src/main/java/io/legado/app/lib/cronet/OldCallback.kt +++ b/app/src/main/java/io/legado/app/lib/cronet/OldCallback.kt @@ -9,7 +9,8 @@ import org.chromium.net.UrlRequest import java.io.IOException @Keep -class OldCallback(originalRequest: Request, mCall: Call) : AbsCallBack(originalRequest, mCall) { +class OldCallback(originalRequest: Request, mCall: Call, readTimeoutMillis: Int) : + AbsCallBack(originalRequest, mCall, readTimeoutMillis) { private val mResponseCondition = ConditionVariable() private var mException: IOException? = null diff --git a/app/src/main/java/io/legado/app/lib/webdav/WebDav.kt b/app/src/main/java/io/legado/app/lib/webdav/WebDav.kt index ef635e564..d9f3770b5 100644 --- a/app/src/main/java/io/legado/app/lib/webdav/WebDav.kt +++ b/app/src/main/java/io/legado/app/lib/webdav/WebDav.kt @@ -30,6 +30,7 @@ import java.net.URLEncoder import java.time.LocalDateTime import java.time.ZoneOffset import java.time.format.DateTimeFormatter +import java.util.concurrent.TimeUnit @Suppress("unused", "MemberVisibilityCanBePrivate") open class WebDav( @@ -97,6 +98,7 @@ open class WebDav( chain.proceed(request) } okHttpClient.newBuilder().run { + callTimeout(0, TimeUnit.SECONDS) interceptors().add(0, authInterceptor) addNetworkInterceptor(authInterceptor) build() diff --git a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt index 7f16b3621..9ac49ded1 100644 --- a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt +++ b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt @@ -27,12 +27,15 @@ import io.legado.app.utils.* import kotlinx.coroutines.delay import kotlinx.coroutines.runBlocking import okhttp3.MediaType.Companion.toMediaType +import okhttp3.OkHttpClient import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.Response import java.io.ByteArrayInputStream import java.io.InputStream import java.net.URLEncoder +import java.util.concurrent.TimeUnit import java.util.regex.Pattern +import kotlin.math.max /** * Created by GKF on 2018/1/24. @@ -51,6 +54,7 @@ class AnalyzeUrl( private val source: BaseSource? = null, private val ruleData: RuleDataInterface? = null, private val chapter: BookChapter? = null, + private val readTimeout: Long? = null, headerMapF: Map? = null, ) : JsExtensions { companion object { @@ -404,7 +408,7 @@ class AnalyzeUrl( if (this.useWebView && useWebView) { strResponse = when (method) { RequestMethod.POST -> { - val res = getProxyClient(proxy).newCallStrResponse(retry) { + val res = getClient().newCallStrResponse(retry) { addHeaders(headerMap) url(urlNoQuery) if (fieldMap.isNotEmpty() || body.isNullOrBlank()) { @@ -432,7 +436,7 @@ class AnalyzeUrl( ).getStrResponse() } } else { - strResponse = getProxyClient(proxy).newCallStrResponse(retry) { + strResponse = getClient().newCallStrResponse(retry) { addHeaders(headerMap) when (method) { RequestMethod.POST -> { @@ -484,7 +488,7 @@ class AnalyzeUrl( val concurrentRecord = getConcurrentRecord() try { setCookie() - val response = getProxyClient(proxy).newCallResponse(retry) { + val response = getClient().newCallResponse(retry) { addHeaders(headerMap) when (method) { RequestMethod.POST -> { @@ -511,15 +515,24 @@ class AnalyzeUrl( } } + private fun getClient(): OkHttpClient { + val client = getProxyClient(proxy) + if (readTimeout == null) { + return client + } + return client.newBuilder() + .readTimeout(readTimeout, TimeUnit.MILLISECONDS) + .callTimeout(max(60 * 1000L, readTimeout * 2), TimeUnit.MILLISECONDS) + .build() + } + fun getResponse(): Response { return runBlocking { getResponseAwait() } } - @Suppress("UnnecessaryVariable") private fun getByteArrayIfDataUri(): ByteArray? { - @Suppress("RegExpRedundantEscape") val dataUriFindResult = dataUriRegex.find(urlNoQuery) if (dataUriFindResult != null) { val dataUriBase64 = dataUriFindResult.groupValues[1] diff --git a/app/src/main/java/io/legado/app/service/HttpReadAloudService.kt b/app/src/main/java/io/legado/app/service/HttpReadAloudService.kt index 388dc4148..7070861be 100644 --- a/app/src/main/java/io/legado/app/service/HttpReadAloudService.kt +++ b/app/src/main/java/io/legado/app/service/HttpReadAloudService.kt @@ -167,7 +167,8 @@ class HttpReadAloudService : BaseReadAloudService(), speakText = speakText, speakSpeed = speechRate, source = httpTts, - headerMapF = httpTts.getHeaderMap(true) + headerMapF = httpTts.getHeaderMap(true), + readTimeout = 300 * 1000L ) var response = analyzeUrl.getResponseAwait() coroutineContext.ensureActive()