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