pull/977/head
parent
f91bc8722a
commit
a45b5844ab
@ -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<String, OkHttpClient> 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) |
||||
} |
@ -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) |
||||
} |
||||
} |
Loading…
Reference in new issue