pull/977/head
gedoor 4 years ago
parent f91bc8722a
commit a45b5844ab
  1. 119
      app/src/main/java/io/legado/app/help/http/HttpHelper.kt
  2. 187
      app/src/main/java/io/legado/app/help/http/OkHttpHelper.kt
  3. 114
      app/src/main/java/io/legado/app/help/http/OkHttpUtils.kt
  4. 2
      app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt

@ -1,30 +1,111 @@
package io.legado.app.help.http package io.legado.app.help.http
import kotlinx.coroutines.suspendCancellableCoroutine 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 import kotlin.coroutines.resume
@Suppress("unused") private val proxyClientCache: ConcurrentHashMap<String, OkHttpClient> by lazy {
object HttpHelper { ConcurrentHashMap()
}
suspend fun ajax(params: AjaxWebView.AjaxParams): StrResponse = val okHttpClient: OkHttpClient by lazy {
suspendCancellableCoroutine { block -> val specs = arrayListOf(
val webView = AjaxWebView() ConnectionSpec.MODERN_TLS,
block.invokeOnCancellation { ConnectionSpec.COMPATIBLE_TLS,
webView.destroyWebView() ConnectionSpec.CLEARTEXT
} )
webView.callback = object : AjaxWebView.Callback() {
override fun onResult(response: StrResponse) {
if (!block.isCompleted) val builder = OkHttpClient.Builder()
block.resume(response) .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) * 缓存代理okHttp
block.cancel(error) */
} 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)
}

@ -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<String, OkHttpClient> 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<String, String>, 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<String, String>, 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)
}
}

@ -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<String, String>, 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<String, String>, 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)
}
}

@ -302,7 +302,7 @@ class AnalyzeUrl(
params.sourceRegex = sourceRegex params.sourceRegex = sourceRegex
params.postData = body?.toByteArray() params.postData = body?.toByteArray()
params.tag = tag params.tag = tag
return HttpHelper.ajax(params) return getWebViewSrc(params)
} }
return getProxyClient(proxy).newCallStrResponse { return getProxyClient(proxy).newCallStrResponse {
removeHeader(UA_NAME) removeHeader(UA_NAME)

Loading…
Cancel
Save