From a45b5844abf8d12596a7ecfb84dd567c5f43313c Mon Sep 17 00:00:00 2001 From: gedoor Date: Tue, 4 May 2021 09:27:17 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/legado/app/help/http/HttpHelper.kt | 119 +++++++++-- .../io/legado/app/help/http/OkHttpHelper.kt | 187 ------------------ .../io/legado/app/help/http/OkHttpUtils.kt | 114 +++++++++++ .../app/model/analyzeRule/AnalyzeUrl.kt | 2 +- 4 files changed, 215 insertions(+), 207 deletions(-) delete mode 100644 app/src/main/java/io/legado/app/help/http/OkHttpHelper.kt create mode 100644 app/src/main/java/io/legado/app/help/http/OkHttpUtils.kt 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 f87085a9c..cdf6a03bc 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 @@ -1,30 +1,111 @@ package io.legado.app.help.http import kotlinx.coroutines.suspendCancellableCoroutine +import okhttp3.ConnectionSpec +import okhttp3.Credentials +import okhttp3.Interceptor +import okhttp3.OkHttpClient +import java.net.InetSocketAddress +import java.net.Proxy +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.TimeUnit import kotlin.coroutines.resume -@Suppress("unused") -object HttpHelper { +private val proxyClientCache: ConcurrentHashMap by lazy { + ConcurrentHashMap() +} - suspend fun ajax(params: AjaxWebView.AjaxParams): StrResponse = - suspendCancellableCoroutine { block -> - val webView = AjaxWebView() - block.invokeOnCancellation { - webView.destroyWebView() - } - webView.callback = object : AjaxWebView.Callback() { - override fun onResult(response: StrResponse) { +val okHttpClient: OkHttpClient by lazy { + val specs = arrayListOf( + ConnectionSpec.MODERN_TLS, + ConnectionSpec.COMPATIBLE_TLS, + ConnectionSpec.CLEARTEXT + ) - if (!block.isCompleted) - block.resume(response) - } + val builder = OkHttpClient.Builder() + .connectTimeout(15, TimeUnit.SECONDS) + .writeTimeout(15, TimeUnit.SECONDS) + .readTimeout(15, TimeUnit.SECONDS) + .sslSocketFactory(SSLHelper.unsafeSSLSocketFactory, SSLHelper.unsafeTrustManager) + .retryOnConnectionFailure(true) + .hostnameVerifier(SSLHelper.unsafeHostnameVerifier) + .connectionSpecs(specs) + .followRedirects(true) + .followSslRedirects(true) + .addInterceptor(Interceptor { chain -> + val request = chain.request() + .newBuilder() + .addHeader("Keep-Alive", "300") + .addHeader("Connection", "Keep-Alive") + .addHeader("Cache-Control", "no-cache") + .build() + chain.proceed(request) + }) + + builder.build() +} - override fun onError(error: Throwable) { - if (!block.isCompleted) - block.cancel(error) - } +/** + * 缓存代理okHttp + */ +fun getProxyClient(proxy: String? = null): OkHttpClient { + if (proxy.isNullOrBlank()) { + return okHttpClient + } + proxyClientCache[proxy]?.let { + return it + } + val r = Regex("(http|socks4|socks5)://(.*):(\\d{2,5})(@.*@.*)?") + val ms = r.findAll(proxy) + val group = ms.first() + var username = "" //代理服务器验证用户名 + var password = "" //代理服务器验证密码 + val type = if (group.groupValues[1] == "http") "http" else "socks" + val host = group.groupValues[2] + val port = group.groupValues[3].toInt() + if (group.groupValues[4] != "") { + username = group.groupValues[4].split("@")[1] + password = group.groupValues[4].split("@")[2] + } + if (type != "direct" && host != "") { + val builder = okHttpClient.newBuilder() + if (type == "http") { + builder.proxy(Proxy(Proxy.Type.HTTP, InetSocketAddress(host, port))) + } else { + builder.proxy(Proxy(Proxy.Type.SOCKS, InetSocketAddress(host, port))) + } + if (username != "" && password != "") { + builder.proxyAuthenticator { _, response -> //设置代理服务器账号密码 + val credential: String = Credentials.basic(username, password) + response.request.newBuilder() + .header("Proxy-Authorization", credential) + .build() } - webView.load(params) } - + val proxyClient = builder.build() + proxyClientCache[proxy] = proxyClient + return proxyClient + } + return okHttpClient } + +suspend fun getWebViewSrc(params: AjaxWebView.AjaxParams): StrResponse = + suspendCancellableCoroutine { block -> + val webView = AjaxWebView() + block.invokeOnCancellation { + webView.destroyWebView() + } + webView.callback = object : AjaxWebView.Callback() { + override fun onResult(response: StrResponse) { + + if (!block.isCompleted) + block.resume(response) + } + + override fun onError(error: Throwable) { + if (!block.isCompleted) + block.cancel(error) + } + } + webView.load(params) + } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/help/http/OkHttpHelper.kt b/app/src/main/java/io/legado/app/help/http/OkHttpHelper.kt deleted file mode 100644 index f1f015a15..000000000 --- a/app/src/main/java/io/legado/app/help/http/OkHttpHelper.kt +++ /dev/null @@ -1,187 +0,0 @@ -package io.legado.app.help.http - -import io.legado.app.constant.AppConst -import io.legado.app.help.AppConfig -import io.legado.app.utils.EncodingDetect -import io.legado.app.utils.UTF8BOMFighter -import kotlinx.coroutines.suspendCancellableCoroutine -import okhttp3.* -import okhttp3.HttpUrl.Companion.toHttpUrl -import okhttp3.MediaType.Companion.toMediaType -import okhttp3.RequestBody.Companion.toRequestBody -import java.io.IOException -import java.net.InetSocketAddress -import java.net.Proxy -import java.nio.charset.Charset -import java.util.concurrent.ConcurrentHashMap -import java.util.concurrent.TimeUnit -import kotlin.coroutines.resume -import kotlin.coroutines.resumeWithException - -private val proxyClientCache: ConcurrentHashMap by lazy { - ConcurrentHashMap() -} - -val okHttpClient: OkHttpClient by lazy { - - val specs = arrayListOf( - ConnectionSpec.MODERN_TLS, - ConnectionSpec.COMPATIBLE_TLS, - ConnectionSpec.CLEARTEXT - ) - - val builder = OkHttpClient.Builder() - .connectTimeout(15, TimeUnit.SECONDS) - .writeTimeout(15, TimeUnit.SECONDS) - .readTimeout(15, TimeUnit.SECONDS) - .sslSocketFactory(SSLHelper.unsafeSSLSocketFactory, SSLHelper.unsafeTrustManager) - .retryOnConnectionFailure(true) - .hostnameVerifier(SSLHelper.unsafeHostnameVerifier) - .connectionSpecs(specs) - .followRedirects(true) - .followSslRedirects(true) - .addInterceptor(Interceptor { chain -> - val request = chain.request() - .newBuilder() - .addHeader("Keep-Alive", "300") - .addHeader("Connection", "Keep-Alive") - .addHeader("Cache-Control", "no-cache") - .build() - chain.proceed(request) - }) - - builder.build() -} - -/** - * 缓存代理okHttp - */ -fun getProxyClient(proxy: String? = null): OkHttpClient { - if (proxy.isNullOrBlank()) { - return okHttpClient - } - proxyClientCache[proxy]?.let { - return it - } - val r = Regex("(http|socks4|socks5)://(.*):(\\d{2,5})(@.*@.*)?") - val ms = r.findAll(proxy) - val group = ms.first() - var username = "" //代理服务器验证用户名 - var password = "" //代理服务器验证密码 - val type = if (group.groupValues[1] == "http") "http" else "socks" - val host = group.groupValues[2] - val port = group.groupValues[3].toInt() - if (group.groupValues[4] != "") { - username = group.groupValues[4].split("@")[1] - password = group.groupValues[4].split("@")[2] - } - if (type != "direct" && host != "") { - val builder = okHttpClient.newBuilder() - if (type == "http") { - builder.proxy(Proxy(Proxy.Type.HTTP, InetSocketAddress(host, port))) - } else { - builder.proxy(Proxy(Proxy.Type.SOCKS, InetSocketAddress(host, port))) - } - if (username != "" && password != "") { - builder.proxyAuthenticator { _, response -> //设置代理服务器账号密码 - val credential: String = Credentials.basic(username, password) - response.request.newBuilder() - .header("Proxy-Authorization", credential) - .build() - } - } - val proxyClient = builder.build() - proxyClientCache[proxy] = proxyClient - return proxyClient - } - return okHttpClient -} - -suspend fun OkHttpClient.newCall(builder: Request.Builder.() -> Unit): ResponseBody { - val requestBuilder = Request.Builder() - requestBuilder.header(AppConst.UA_NAME, AppConfig.userAgent) - requestBuilder.apply(builder) - val response = this.newCall(requestBuilder.build()).await() - if (!response.isSuccessful) { - throw IOException("服务器没有响应。") - } - return response.body!! -} - -suspend fun OkHttpClient.newCallStrResponse(builder: Request.Builder.() -> Unit): StrResponse { - val requestBuilder = Request.Builder() - requestBuilder.header(AppConst.UA_NAME, AppConfig.userAgent) - requestBuilder.apply(builder) - val response = this.newCall(requestBuilder.build()).await() - if (!response.isSuccessful) { - throw IOException("服务器没有响应。") - } - return StrResponse(response, response.body!!.text()) -} - -suspend fun Call.await(): Response = suspendCancellableCoroutine { block -> - - block.invokeOnCancellation { - cancel() - } - - enqueue(object : Callback { - override fun onFailure(call: Call, e: IOException) { - block.resumeWithException(e) - } - - override fun onResponse(call: Call, response: Response) { - block.resume(response) - } - }) - -} - -fun ResponseBody.text(encode: String? = null): String { - val responseBytes = UTF8BOMFighter.removeUTF8BOM(bytes()) - var charsetName: String? = encode - - charsetName?.let { - return String(responseBytes, Charset.forName(charsetName)) - } - - //根据http头判断 - contentType()?.charset()?.let { - return String(responseBytes, it) - } - - //根据内容判断 - charsetName = EncodingDetect.getHtmlEncode(responseBytes) - return String(responseBytes, Charset.forName(charsetName)) -} - -fun Request.Builder.get(url: String, queryMap: Map, encoded: Boolean = false) { - val httpBuilder = url.toHttpUrl().newBuilder() - queryMap.forEach { - if (encoded) { - httpBuilder.addEncodedQueryParameter(it.key, it.value) - } else { - httpBuilder.addQueryParameter(it.key, it.value) - } - } - url(httpBuilder.build()) -} - -fun Request.Builder.postForm(form: Map, encoded: Boolean = false) { - val formBody = FormBody.Builder() - form.forEach { - if (encoded) { - formBody.addEncoded(it.key, it.value) - } else { - formBody.add(it.key, it.value) - } - } - post(formBody.build()) -} - -fun Request.Builder.postJson(json: String?) { - json?.let { - val requestBody = json.toRequestBody("application/json; charset=UTF-8".toMediaType()) - post(requestBody) - } -} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/help/http/OkHttpUtils.kt b/app/src/main/java/io/legado/app/help/http/OkHttpUtils.kt new file mode 100644 index 000000000..d958fc5a2 --- /dev/null +++ b/app/src/main/java/io/legado/app/help/http/OkHttpUtils.kt @@ -0,0 +1,114 @@ +package io.legado.app.help.http + +import io.legado.app.constant.AppConst +import io.legado.app.help.AppConfig +import io.legado.app.utils.EncodingDetect +import io.legado.app.utils.UTF8BOMFighter +import kotlinx.coroutines.suspendCancellableCoroutine +import okhttp3.* +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.MediaType.Companion.toMediaType +import okhttp3.RequestBody.Companion.toRequestBody +import java.io.IOException +import java.nio.charset.Charset +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException + +suspend fun OkHttpClient.newCall( + retry: Int = 0, + builder: Request.Builder.() -> Unit +): ResponseBody { + val requestBuilder = Request.Builder() + requestBuilder.header(AppConst.UA_NAME, AppConfig.userAgent) + requestBuilder.apply(builder) + for (i in 0..retry) { + val response = this.newCall(requestBuilder.build()).await() + if (response.isSuccessful) { + return response.body!! + } + } + throw IOException("服务器没有响应。") +} + +suspend fun OkHttpClient.newCallStrResponse( + retry: Int = 0, + builder: Request.Builder.() -> Unit +): StrResponse { + val requestBuilder = Request.Builder() + requestBuilder.header(AppConst.UA_NAME, AppConfig.userAgent) + requestBuilder.apply(builder) + for (i in 0..retry) { + val response = this.newCall(requestBuilder.build()).await() + if (response.isSuccessful) { + return StrResponse(response, response.body!!.text()) + } + } + throw IOException("服务器没有响应。") +} + +suspend fun Call.await(): Response = suspendCancellableCoroutine { block -> + + block.invokeOnCancellation { + cancel() + } + + enqueue(object : Callback { + override fun onFailure(call: Call, e: IOException) { + block.resumeWithException(e) + } + + override fun onResponse(call: Call, response: Response) { + block.resume(response) + } + }) + +} + +fun ResponseBody.text(encode: String? = null): String { + val responseBytes = UTF8BOMFighter.removeUTF8BOM(bytes()) + var charsetName: String? = encode + + charsetName?.let { + return String(responseBytes, Charset.forName(charsetName)) + } + + //根据http头判断 + contentType()?.charset()?.let { + return String(responseBytes, it) + } + + //根据内容判断 + charsetName = EncodingDetect.getHtmlEncode(responseBytes) + return String(responseBytes, Charset.forName(charsetName)) +} + +fun Request.Builder.get(url: String, queryMap: Map, encoded: Boolean = false) { + val httpBuilder = url.toHttpUrl().newBuilder() + queryMap.forEach { + if (encoded) { + httpBuilder.addEncodedQueryParameter(it.key, it.value) + } else { + httpBuilder.addQueryParameter(it.key, it.value) + } + } + url(httpBuilder.build()) +} + +fun Request.Builder.postForm(form: Map, encoded: Boolean = false) { + val formBody = FormBody.Builder() + form.forEach { + if (encoded) { + formBody.addEncoded(it.key, it.value) + } else { + formBody.add(it.key, it.value) + } + } + post(formBody.build()) +} + +fun Request.Builder.postJson(json: String?) { + json?.let { + val requestBody = json.toRequestBody("application/json; charset=UTF-8".toMediaType()) + post(requestBody) + } +} \ No newline at end of file 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 c341fb544..eb4230e1a 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 @@ -302,7 +302,7 @@ class AnalyzeUrl( params.sourceRegex = sourceRegex params.postData = body?.toByteArray() params.tag = tag - return HttpHelper.ajax(params) + return getWebViewSrc(params) } return getProxyClient(proxy).newCallStrResponse { removeHeader(UA_NAME)