parent
c23a774dd8
commit
3fd103d616
@ -0,0 +1,31 @@ |
|||||||
|
package com.arialyy.aria.http.download |
||||||
|
|
||||||
|
import com.arialyy.aria.core.task.AbsTaskUtil |
||||||
|
|
||||||
|
/** |
||||||
|
* @Author laoyuyu |
||||||
|
* @Description |
||||||
|
* @Date 1:47 PM 2023/1/28 |
||||||
|
**/ |
||||||
|
internal class HttpDTaskUtil : AbsTaskUtil() { |
||||||
|
|
||||||
|
init { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
override fun isRunning(): Boolean { |
||||||
|
TODO("Not yet implemented") |
||||||
|
} |
||||||
|
|
||||||
|
override fun cancel() { |
||||||
|
TODO("Not yet implemented") |
||||||
|
} |
||||||
|
|
||||||
|
override fun stop() { |
||||||
|
TODO("Not yet implemented") |
||||||
|
} |
||||||
|
|
||||||
|
override fun start() { |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,203 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
package com.arialyy.aria.http.download |
||||||
|
|
||||||
|
import android.net.TrafficStats |
||||||
|
import android.net.Uri |
||||||
|
import android.os.Looper |
||||||
|
import android.os.Process |
||||||
|
import android.text.TextUtils |
||||||
|
import com.arialyy.aria.core.processor.IHttpFileLenAdapter |
||||||
|
import com.arialyy.aria.core.task.ITask |
||||||
|
import com.arialyy.aria.core.task.ITaskInterceptor |
||||||
|
import com.arialyy.aria.core.task.TaskChain |
||||||
|
import com.arialyy.aria.core.task.TaskResp |
||||||
|
import com.arialyy.aria.http.HttpUtil |
||||||
|
import com.arialyy.aria.http.request.IRequest |
||||||
|
import com.arialyy.aria.util.CheckUtil |
||||||
|
import timber.log.Timber |
||||||
|
import java.io.BufferedReader |
||||||
|
import java.io.IOException |
||||||
|
import java.io.InputStreamReader |
||||||
|
import java.net.HttpURLConnection |
||||||
|
import java.util.UUID |
||||||
|
|
||||||
|
/** |
||||||
|
* @Author laoyuyu |
||||||
|
* @Description |
||||||
|
* @Date 2:27 PM 2023/1/28 |
||||||
|
**/ |
||||||
|
internal class HttpHeaderInterceptor : ITaskInterceptor { |
||||||
|
private lateinit var task: ITask |
||||||
|
private lateinit var taskOption: HttpDTaskOption |
||||||
|
|
||||||
|
companion object { |
||||||
|
private val CODE_30X = listOf( |
||||||
|
HttpURLConnection.HTTP_MOVED_TEMP, HttpURLConnection.HTTP_MOVED_PERM, |
||||||
|
HttpURLConnection.HTTP_SEE_OTHER, HttpURLConnection.HTTP_CREATED, 307 |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
override fun interceptor(chain: TaskChain): TaskResp { |
||||||
|
if (Looper.myLooper() == Looper.getMainLooper()) { |
||||||
|
throw IllegalThreadStateException("Io operations cannot be in the main thread") |
||||||
|
} |
||||||
|
task = chain.getTask() |
||||||
|
taskOption = task.getTaskOption(HttpDTaskOption::class.java) |
||||||
|
return try { |
||||||
|
val fileSize = getFileSize() |
||||||
|
|
||||||
|
chain.proceed(task) |
||||||
|
} catch (e: IOException) { |
||||||
|
Timber.e( |
||||||
|
"download fail, url: ${ |
||||||
|
chain.getTask().getTaskOption(HttpDTaskOption::class.java).sourUrl |
||||||
|
}" |
||||||
|
) |
||||||
|
TaskResp(TaskResp.CODE_GET_FILE_INFO_FAIL) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Throws(IOException::class) |
||||||
|
private fun getFileSize(): Long { |
||||||
|
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND) |
||||||
|
TrafficStats.setThreadStatsTag(UUID.randomUUID().toString().hashCode()) |
||||||
|
val conn: HttpURLConnection = IRequest.getRequest(taskOption.httpOption!!) |
||||||
|
.getDConnection(taskOption.sourUrl!!, taskOption.httpOption!!) |
||||||
|
// https://httpwg.org/specs/rfc9110.html#byte.ranges |
||||||
|
// conn.setRequestProperty("Range", "bytes=" + 0 + "-") |
||||||
|
conn.setRequestProperty("Range", "bytes=0-1") // 尝试获取1个字节 |
||||||
|
conn.connect() |
||||||
|
return handleConnect(conn) |
||||||
|
} |
||||||
|
|
||||||
|
@Throws(IOException::class) |
||||||
|
private fun handleConnect(conn: HttpURLConnection): Long { |
||||||
|
|
||||||
|
val code = conn.responseCode |
||||||
|
when { |
||||||
|
code == HttpURLConnection.HTTP_PARTIAL -> return getFileSizeFromHeader( |
||||||
|
conn.headerFields, |
||||||
|
taskOption |
||||||
|
) |
||||||
|
code == HttpURLConnection.HTTP_OK -> { |
||||||
|
val len = getFileSizeFromHeader(conn.headerFields, taskOption) |
||||||
|
if (len > 0) { |
||||||
|
return len |
||||||
|
} |
||||||
|
val contentType = conn.getHeaderField("Content-Type") |
||||||
|
if (contentType == "text/html") { |
||||||
|
val reader = BufferedReader(InputStreamReader(HttpUtil.convertInputStream(conn))) |
||||||
|
val sb = StringBuilder() |
||||||
|
var line: String? |
||||||
|
while (reader.readLine().also { line = it } != null) { |
||||||
|
sb.append(line) |
||||||
|
} |
||||||
|
reader.close() |
||||||
|
return handleUrlReTurn(conn, HttpUtil.getWindowReplaceUrl(sb.toString())) |
||||||
|
} |
||||||
|
// code is 200, but file size cannot be obtained. |
||||||
|
return -1 |
||||||
|
} |
||||||
|
code in CODE_30X -> { |
||||||
|
Timber.d("handle 30x turn, code: $code") |
||||||
|
return handleUrlReTurn(conn, conn.getHeaderField("Location")) |
||||||
|
} |
||||||
|
code >= HttpURLConnection.HTTP_BAD_REQUEST -> { |
||||||
|
Timber.e("download fail, code: $code") |
||||||
|
return -1 |
||||||
|
} |
||||||
|
else -> { |
||||||
|
return -1 |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Throws(IOException::class) private fun handleUrlReTurn( |
||||||
|
oldConn: HttpURLConnection, |
||||||
|
newUrl: String? |
||||||
|
): Long { |
||||||
|
Timber.i("handle 30x turn, new url: $newUrl") |
||||||
|
if (newUrl.isNullOrEmpty() || newUrl.equals("null", ignoreCase = true)) { |
||||||
|
return -1 |
||||||
|
} |
||||||
|
var tempUrl = newUrl |
||||||
|
if (tempUrl.startsWith("/")) { |
||||||
|
val uri = Uri.parse(taskOption.sourUrl!!) |
||||||
|
tempUrl = uri.host + newUrl |
||||||
|
} |
||||||
|
if (!CheckUtil.checkUrl(tempUrl)) { |
||||||
|
Timber.e("get redirect url fail, $tempUrl") |
||||||
|
return -1 |
||||||
|
} |
||||||
|
|
||||||
|
taskOption.redirectUrl = newUrl |
||||||
|
val cookies = oldConn.getHeaderField("Set-Cookie") |
||||||
|
oldConn.disconnect() |
||||||
|
|
||||||
|
val newConn: HttpURLConnection = IRequest.getRequest(taskOption.httpOption!!) |
||||||
|
.getDConnection(taskOption.sourUrl!!, taskOption.httpOption!!) |
||||||
|
newConn.setRequestProperty("Cookie", cookies) |
||||||
|
newConn.setRequestProperty("Range", "bytes=" + 0 + "-") |
||||||
|
|
||||||
|
newConn.connect() |
||||||
|
return handleConnect(newConn) |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* get file size from header, if user not set [IHttpFileLenAdapter], use [FileLenAdapter] |
||||||
|
*/ |
||||||
|
private fun getFileSizeFromHeader( |
||||||
|
header: Map<String, List<String>>, |
||||||
|
taskOption: HttpDTaskOption |
||||||
|
): Long { |
||||||
|
var lenAdapter = taskOption.fileSizeAdapter |
||||||
|
if (lenAdapter == null) { |
||||||
|
lenAdapter = FileLenAdapter() |
||||||
|
} |
||||||
|
return lenAdapter.handleFileLen(header) |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* https://httpwg.org/specs/rfc9110.html#field.content-range |
||||||
|
*/ |
||||||
|
private class FileLenAdapter : IHttpFileLenAdapter { |
||||||
|
override fun handleFileLen(headers: Map<String, List<String>>): Long { |
||||||
|
if (headers.isEmpty()) { |
||||||
|
Timber.e("header is empty, get file size fail") |
||||||
|
return -1 |
||||||
|
} |
||||||
|
val sLength = headers["Content-Length"] |
||||||
|
if (sLength == null || sLength.isEmpty()) { |
||||||
|
return -1 |
||||||
|
} |
||||||
|
val temp = sLength[0] |
||||||
|
var len = if (TextUtils.isEmpty(temp)) -1 else temp.toLong() |
||||||
|
// 某些服务,如果设置了conn.setRequestProperty("Range", "bytes=" + 0 + "-"); |
||||||
|
// 会返回 Content-Range: bytes 0-225427911/225427913 |
||||||
|
if (len < 0) { |
||||||
|
val sRange = headers["Content-Range"] |
||||||
|
len = if (sRange == null || sRange.isEmpty()) { |
||||||
|
-1 |
||||||
|
} else { |
||||||
|
val start = temp.indexOf("/") |
||||||
|
temp.substring(start + 1).toLong() |
||||||
|
} |
||||||
|
} |
||||||
|
return len |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,59 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
package com.arialyy.aria.http.request |
||||||
|
|
||||||
|
import com.arialyy.aria.core.common.RequestEnum.GET |
||||||
|
import com.arialyy.aria.http.HttpOption |
||||||
|
import com.arialyy.aria.util.CommonUtil |
||||||
|
import java.net.HttpURLConnection |
||||||
|
import java.net.URL |
||||||
|
import java.net.URLEncoder |
||||||
|
|
||||||
|
/** |
||||||
|
* @Author laoyuyu |
||||||
|
* @Description |
||||||
|
* @Date 9:52 AM 2023/1/29 |
||||||
|
**/ |
||||||
|
object GetRequest : IRequest { |
||||||
|
|
||||||
|
override fun getDConnection(url: String, option: HttpOption): HttpURLConnection { |
||||||
|
val params: Map<String, String> = option.getParams() |
||||||
|
|
||||||
|
val realUrl = if (params.isNotEmpty()) { |
||||||
|
val sb = StringBuilder() |
||||||
|
sb.append(url) |
||||||
|
if (!url.contains("?")) { |
||||||
|
sb.append("?") |
||||||
|
} |
||||||
|
|
||||||
|
val keys = params.keys |
||||||
|
for (key in keys) { |
||||||
|
sb.append(URLEncoder.encode(key, Charsets.UTF_8.toString())) |
||||||
|
.append("=") |
||||||
|
.append(URLEncoder.encode(params[key], Charsets.UTF_8.toString())) |
||||||
|
.append("&") |
||||||
|
} |
||||||
|
var temp = sb.toString() |
||||||
|
temp = temp.substring(0, temp.length - 1) |
||||||
|
URL(CommonUtil.convertUrl(temp)) |
||||||
|
} else { |
||||||
|
URL(CommonUtil.convertUrl(url)) |
||||||
|
} |
||||||
|
val conn = createConnection(realUrl, option) |
||||||
|
conn.requestMethod = GET.name |
||||||
|
return conn |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,59 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
package com.arialyy.aria.http.request |
||||||
|
|
||||||
|
import com.arialyy.aria.core.common.RequestEnum |
||||||
|
import com.arialyy.aria.http.HttpOption |
||||||
|
import com.arialyy.aria.util.CommonUtil |
||||||
|
import java.net.HttpURLConnection |
||||||
|
import java.net.URL |
||||||
|
import java.net.URLEncoder |
||||||
|
|
||||||
|
/** |
||||||
|
* @Author laoyuyu |
||||||
|
* @Description |
||||||
|
* @Date 9:53 AM 2023/1/29 |
||||||
|
**/ |
||||||
|
internal object HeadRequest : IRequest { |
||||||
|
|
||||||
|
override fun getDConnection(url: String, option: HttpOption): HttpURLConnection { |
||||||
|
val params: Map<String, String> = option.getParams() |
||||||
|
|
||||||
|
val realUrl = if (params.isNotEmpty()) { |
||||||
|
val sb = StringBuilder() |
||||||
|
sb.append(url) |
||||||
|
if (!url.contains("?")) { |
||||||
|
sb.append("?") |
||||||
|
} |
||||||
|
|
||||||
|
val keys = params.keys |
||||||
|
for (key in keys) { |
||||||
|
sb.append(URLEncoder.encode(key, Charsets.UTF_8.toString())) |
||||||
|
.append("=") |
||||||
|
.append(URLEncoder.encode(params[key], Charsets.UTF_8.toString())) |
||||||
|
.append("&") |
||||||
|
} |
||||||
|
var temp = sb.toString() |
||||||
|
temp = temp.substring(0, temp.length - 1) |
||||||
|
URL(CommonUtil.convertUrl(temp)) |
||||||
|
} else { |
||||||
|
URL(CommonUtil.convertUrl(url)) |
||||||
|
} |
||||||
|
val conn = createConnection(realUrl, option) |
||||||
|
conn.requestMethod = RequestEnum.HEAD.name |
||||||
|
return conn |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,152 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
package com.arialyy.aria.http.request |
||||||
|
|
||||||
|
import android.text.TextUtils |
||||||
|
import com.arialyy.aria.core.AriaConfig |
||||||
|
import com.arialyy.aria.core.ProtocolType |
||||||
|
import com.arialyy.aria.core.common.RequestEnum.GET |
||||||
|
import com.arialyy.aria.core.common.RequestEnum.HEAD |
||||||
|
import com.arialyy.aria.core.common.RequestEnum.POST |
||||||
|
import com.arialyy.aria.http.HttpOption |
||||||
|
import com.arialyy.aria.util.SSLContextUtil |
||||||
|
import java.io.IOException |
||||||
|
import java.net.HttpURLConnection |
||||||
|
import java.net.URL |
||||||
|
import java.net.URLConnection |
||||||
|
import javax.net.ssl.HttpsURLConnection |
||||||
|
|
||||||
|
/** |
||||||
|
* @Author laoyuyu |
||||||
|
* @Description |
||||||
|
* @Date 19:48 AM 2023/1/29 |
||||||
|
**/ |
||||||
|
interface IRequest { |
||||||
|
|
||||||
|
companion object { |
||||||
|
fun getRequest(option: HttpOption): IRequest { |
||||||
|
return when (option.getRequestMethod()) { |
||||||
|
GET -> GetRequest |
||||||
|
POST -> PostRequest |
||||||
|
HEAD -> HeadRequest |
||||||
|
else -> throw UnsupportedOperationException("unsupported method ${option.getRequestMethod()}") |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun getDConnection(url: String, option: HttpOption): HttpURLConnection |
||||||
|
|
||||||
|
@Throws(IOException::class) fun createConnection( |
||||||
|
url: URL, |
||||||
|
option: HttpOption |
||||||
|
): HttpURLConnection { |
||||||
|
val conn: HttpURLConnection |
||||||
|
val urlConn: URLConnection = if (option.getProxy() != null) { |
||||||
|
url.openConnection(option.getProxy()) |
||||||
|
} else { |
||||||
|
url.openConnection() |
||||||
|
} |
||||||
|
if (urlConn is HttpsURLConnection) { |
||||||
|
val config = AriaConfig.getInstance() |
||||||
|
conn = urlConn |
||||||
|
var sslContext = SSLContextUtil.getSSLContextFromAssets( |
||||||
|
config.dConfig.caName, |
||||||
|
config.dConfig.caPath, ProtocolType.Default |
||||||
|
) |
||||||
|
if (sslContext == null) { |
||||||
|
sslContext = SSLContextUtil.getDefaultSLLContext(ProtocolType.Default) |
||||||
|
} |
||||||
|
val ssf = sslContext!!.socketFactory |
||||||
|
conn.sslSocketFactory = ssf |
||||||
|
conn.hostnameVerifier = SSLContextUtil.HOSTNAME_VERIFIER |
||||||
|
} else { |
||||||
|
conn = urlConn as HttpURLConnection |
||||||
|
} |
||||||
|
setHeader(conn, option) |
||||||
|
setConnectAttr(conn) |
||||||
|
return conn |
||||||
|
} |
||||||
|
|
||||||
|
private fun setConnectAttr(conn: HttpURLConnection) { |
||||||
|
conn.connectTimeout = AriaConfig.getInstance().dConfig.connectTimeOut |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 设置头部参数 |
||||||
|
*/ |
||||||
|
private fun setHeader(conn: HttpURLConnection, option: HttpOption) { |
||||||
|
option.getHeaders().forEach { |
||||||
|
conn.setRequestProperty(it.key, it.value) |
||||||
|
} |
||||||
|
|
||||||
|
if (conn.getRequestProperty("Accept-Language") == null) { |
||||||
|
conn.setRequestProperty("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8,ja;q=0.7") |
||||||
|
} |
||||||
|
if (conn.getRequestProperty("Accept-Encoding") == null) { |
||||||
|
conn.setRequestProperty("Accept-Encoding", "identity") |
||||||
|
} |
||||||
|
if (conn.getRequestProperty("Accept-Charset") == null) { |
||||||
|
conn.setRequestProperty("Accept-Charset", "UTF-8") |
||||||
|
} |
||||||
|
if (conn.getRequestProperty("Connection") == null) { |
||||||
|
conn.setRequestProperty("Connection", "Keep-Alive") |
||||||
|
} |
||||||
|
if (conn.getRequestProperty("Charset") == null) { |
||||||
|
conn.setRequestProperty("Charset", "UTF-8") |
||||||
|
} |
||||||
|
if (conn.getRequestProperty("User-Agent") == null) { |
||||||
|
conn.setRequestProperty( |
||||||
|
"User-Agent", |
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36" |
||||||
|
) |
||||||
|
} |
||||||
|
if (conn.getRequestProperty("Accept") == null) { |
||||||
|
val accept = StringBuilder() |
||||||
|
accept.append("image/gif, ") |
||||||
|
.append("image/jpeg, ") |
||||||
|
.append("image/pjpeg, ") |
||||||
|
.append("image/webp, ") |
||||||
|
.append("image/apng, ") |
||||||
|
.append("application/xml, ") |
||||||
|
.append("application/xaml+xml, ") |
||||||
|
.append("application/xhtml+xml, ") |
||||||
|
.append("application/x-shockwave-flash, ") |
||||||
|
.append("application/x-ms-xbap, ") |
||||||
|
.append("application/x-ms-application, ") |
||||||
|
.append("application/msword, ") |
||||||
|
.append("application/vnd.ms-excel, ") |
||||||
|
.append("application/vnd.ms-xpsdocument, ") |
||||||
|
.append("application/vnd.ms-powerpoint, ") |
||||||
|
.append("application/signed-exchange, ") |
||||||
|
.append("text/plain, ") |
||||||
|
.append("text/html, ") |
||||||
|
.append("*/*") |
||||||
|
conn.setRequestProperty("Accept", accept.toString()) |
||||||
|
} |
||||||
|
//302获取重定向地址 |
||||||
|
conn.instanceFollowRedirects = false |
||||||
|
val manager = option.getCookieManager() |
||||||
|
if (manager != null) { |
||||||
|
val store = manager.cookieStore |
||||||
|
if (store != null && store.cookies.size > 0) { |
||||||
|
conn.setRequestProperty( |
||||||
|
"Cookie", |
||||||
|
TextUtils.join(";", store.cookies) |
||||||
|
) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,74 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
package com.arialyy.aria.http.request |
||||||
|
|
||||||
|
import com.arialyy.aria.core.common.RequestEnum |
||||||
|
import com.arialyy.aria.http.HttpOption |
||||||
|
import com.arialyy.aria.util.CommonUtil |
||||||
|
import java.io.BufferedWriter |
||||||
|
import java.io.OutputStreamWriter |
||||||
|
import java.net.HttpURLConnection |
||||||
|
import java.net.URL |
||||||
|
import java.net.URLEncoder |
||||||
|
|
||||||
|
/** |
||||||
|
* @Author laoyuyu |
||||||
|
* @Description |
||||||
|
* @Date 19:52 AM 2023/1/29 |
||||||
|
**/ |
||||||
|
internal object PostRequest : IRequest { |
||||||
|
override fun getDConnection(url: String, option: HttpOption): HttpURLConnection { |
||||||
|
val conn = createConnection(URL(CommonUtil.convertUrl(url)), option) |
||||||
|
conn.doInput = true |
||||||
|
conn.doOutput = true |
||||||
|
conn.useCaches = false |
||||||
|
conn.requestMethod = RequestEnum.POST.name |
||||||
|
|
||||||
|
if (option.getParams().isNotEmpty() |
||||||
|
|| !option.getBody().isNullOrEmpty() |
||||||
|
|| option.getBodyBinary() != null |
||||||
|
) { |
||||||
|
conn.outputStream.use { |
||||||
|
val writer = BufferedWriter(OutputStreamWriter(it, "UTF-8")) |
||||||
|
if (option.getParams().isNotEmpty()) { |
||||||
|
writer.write(getQuery(option.getParams())) |
||||||
|
} |
||||||
|
if (!option.getBody().isNullOrEmpty()) { |
||||||
|
writer.write(option.getBody()) |
||||||
|
} |
||||||
|
if (option.getBodyBinary() != null) { |
||||||
|
it.write(option.getBodyBinary()) |
||||||
|
} |
||||||
|
writer.flush() |
||||||
|
writer.close() |
||||||
|
it.close() |
||||||
|
} |
||||||
|
} |
||||||
|
return conn |
||||||
|
} |
||||||
|
|
||||||
|
private fun getQuery(params: Map<String, String>): String { |
||||||
|
val result = StringBuilder() |
||||||
|
var first = true |
||||||
|
for (kv in params) { |
||||||
|
if (first) first = false else result.append("&") |
||||||
|
result.append(URLEncoder.encode(kv.key, Charsets.UTF_8.toString())) |
||||||
|
result.append("=") |
||||||
|
result.append(URLEncoder.encode(kv.value, Charsets.UTF_8.toString())) |
||||||
|
} |
||||||
|
return result.toString() |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,63 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
package com.arialyy.aria.core.task |
||||||
|
|
||||||
|
import com.arialyy.aria.core.inf.ITaskUtil |
||||||
|
import com.arialyy.aria.core.listener.IEventListener |
||||||
|
import com.arialyy.aria.core.task.ITaskInterceptor.IChain |
||||||
|
|
||||||
|
/** |
||||||
|
* @Author laoyuyu |
||||||
|
* @Description |
||||||
|
* @Date 1:12 PM 2023/1/28 |
||||||
|
**/ |
||||||
|
abstract class AbsTaskUtil : ITaskUtil { |
||||||
|
protected lateinit var mTask: ITask |
||||||
|
protected lateinit var mEventListener: IEventListener |
||||||
|
|
||||||
|
private val mUserInterceptor = mutableListOf<ITaskInterceptor>() |
||||||
|
private val mCoreInterceptor = mutableListOf<ITaskInterceptor>() |
||||||
|
|
||||||
|
override fun init(task: ITask, listener: IEventListener) { |
||||||
|
mTask = task |
||||||
|
mEventListener = listener |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* add user interceptor |
||||||
|
*/ |
||||||
|
open fun setInterceptors(userInterceptors: List<ITaskInterceptor>) { |
||||||
|
mUserInterceptor.addAll(userInterceptors) |
||||||
|
} |
||||||
|
|
||||||
|
protected open fun addCoreInterceptor(interceptor: ITaskInterceptor) { |
||||||
|
mCoreInterceptor.add(interceptor) |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* if interruption occurred, stop cmd |
||||||
|
*/ |
||||||
|
protected open fun interceptor(): TaskResp? { |
||||||
|
if (mUserInterceptor.isEmpty()) { |
||||||
|
return null |
||||||
|
} |
||||||
|
val interceptors: MutableList<ITaskInterceptor> = ArrayList() |
||||||
|
interceptors.addAll(mUserInterceptor) |
||||||
|
interceptors.addAll(mCoreInterceptor) |
||||||
|
val chain: IChain = TaskChain(interceptors, 0, mTask) |
||||||
|
return chain.proceed(mTask) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,31 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
package com.arialyy.aria.core.task |
||||||
|
|
||||||
|
/** |
||||||
|
* @Author laoyuyu |
||||||
|
* @Description |
||||||
|
* @Date 1:19 PM 2023/1/28 |
||||||
|
**/ |
||||||
|
interface ITaskInterceptor { |
||||||
|
|
||||||
|
fun interceptor(chain: TaskChain): TaskResp |
||||||
|
|
||||||
|
interface IChain { |
||||||
|
fun getTask(): ITask |
||||||
|
fun proceed(task: ITask): TaskResp |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,38 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
package com.arialyy.aria.core.task |
||||||
|
|
||||||
|
/** |
||||||
|
* @Author laoyuyu |
||||||
|
* @Description |
||||||
|
* @Date 11:06 AM 2023/1/27 |
||||||
|
**/ |
||||||
|
class TaskChain( |
||||||
|
private val interceptors: List<ITaskInterceptor>, |
||||||
|
private val index: Int = 0, |
||||||
|
private val task: ITask, |
||||||
|
) : ITaskInterceptor.IChain { |
||||||
|
|
||||||
|
override fun getTask(): ITask { |
||||||
|
return task |
||||||
|
} |
||||||
|
|
||||||
|
override fun proceed(task: ITask): TaskResp { |
||||||
|
val next = TaskChain(interceptors, index, task) |
||||||
|
val interceptor = interceptors[index] |
||||||
|
return interceptor.interceptor(next) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,17 @@ |
|||||||
|
package com.arialyy.aria.core.task |
||||||
|
|
||||||
|
/** |
||||||
|
* @Author laoyuyu |
||||||
|
* @Description |
||||||
|
* @Date 1:43 PM 2023/1/28 |
||||||
|
**/ |
||||||
|
class TaskResp(val code: Int = CODE_DEF) { |
||||||
|
companion object { |
||||||
|
const val CODE_COMPLETE = 1 |
||||||
|
const val CODE_INTERRUPT = 999 |
||||||
|
const val CODE_DEF = 0 |
||||||
|
const val CODE_GET_FILE_INFO_FAIL = 2 |
||||||
|
} |
||||||
|
|
||||||
|
var fileSize: Long = 0 |
||||||
|
} |
Loading…
Reference in new issue