get file info reconstruct

v4
laoyuyu 2 years ago
parent c23a774dd8
commit 3fd103d616
  1. 0
      FtpComponent/src/main/resources/META-INF/services/com.arialyy.aria.core.inf.ITaskUtil
  2. 28
      Http/src/main/java/com/arialyy/aria/http/ConnectionHelp.java
  3. 74
      Http/src/main/java/com/arialyy/aria/http/HttpOption.kt
  4. 48
      Http/src/main/java/com/arialyy/aria/http/HttpUtil.kt
  5. 30
      Http/src/main/java/com/arialyy/aria/http/download/HttpDStartController.kt
  6. 2
      Http/src/main/java/com/arialyy/aria/http/download/HttpDTaskOption.kt
  7. 31
      Http/src/main/java/com/arialyy/aria/http/download/HttpDTaskUtil.kt
  8. 203
      Http/src/main/java/com/arialyy/aria/http/download/HttpHeaderInterceptor.kt
  9. 59
      Http/src/main/java/com/arialyy/aria/http/request/GetRequest.kt
  10. 59
      Http/src/main/java/com/arialyy/aria/http/request/HeadRequest.kt
  11. 152
      Http/src/main/java/com/arialyy/aria/http/request/IRequest.kt
  12. 74
      Http/src/main/java/com/arialyy/aria/http/request/PostRequest.kt
  13. 0
      Http/src/main/resources/META-INF/services/com.arialyy.aria.core.inf.ITaskUtil
  14. 0
      M3U8Component/src/main/resources/META-INF/services/com.arialyy.aria.core.inf.ITaskUtil
  15. 3
      PublicComponent/src/main/java/com/arialyy/aria/core/common/RequestEnum.java
  16. 1
      PublicComponent/src/main/java/com/arialyy/aria/core/download/DTaskOption.kt
  17. 7
      PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupLoaderUtil.java
  18. 6
      PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsSubDLoadUtil.java
  19. 4
      PublicComponent/src/main/java/com/arialyy/aria/core/group/ISubQueue.java
  20. 28
      PublicComponent/src/main/java/com/arialyy/aria/core/inf/ITaskUtil.java
  21. 4
      PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseListener.java
  22. 7
      PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsNormalLoaderUtil.java
  23. 8
      PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsGroupTask.java
  24. 32
      PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsTask.java
  25. 63
      PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsTaskUtil.kt
  26. 5
      PublicComponent/src/main/java/com/arialyy/aria/core/task/DownloadTask.java
  27. 31
      PublicComponent/src/main/java/com/arialyy/aria/core/task/ITaskInterceptor.kt
  28. 26
      PublicComponent/src/main/java/com/arialyy/aria/core/task/TaskCachePool.kt
  29. 38
      PublicComponent/src/main/java/com/arialyy/aria/core/task/TaskChain.kt
  30. 17
      PublicComponent/src/main/java/com/arialyy/aria/core/task/TaskResp.kt
  31. 4
      PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java
  32. 0
      SFtpComponent/src/main/resources/META-INF/services/com.arialyy.aria.core.inf.ITaskUtil

@ -19,7 +19,6 @@ import android.text.TextUtils;
import com.arialyy.aria.core.AriaConfig;
import com.arialyy.aria.core.ProtocolType;
import com.arialyy.aria.core.common.RequestEnum;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil;
import com.arialyy.aria.util.SSLContextUtil;
import java.io.IOException;
@ -28,7 +27,6 @@ import java.net.CookieManager;
import java.net.CookieStore;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
@ -49,25 +47,33 @@ public final class ConnectionHelp {
/**
* 处理url参数
*/
public static URL handleUrl(String url, HttpTaskOption taskDelegate)
public static URL handleUrl(String url, HttpOption option)
throws MalformedURLException {
Map<String, String> params = taskDelegate.getParams();
if (params != null && taskDelegate.getRequestEnum() == RequestEnum.GET) {
if (url.contains("?")) {
ALog.e(TAG, String.format("设置参数失败,url中已经有?,url: %s", url));
return new URL(CommonUtil.convertUrl(url));
Map<String, String> params = option.getParams();
if (params.isEmpty()) {
return new URL(CommonUtil.convertUrl(url));
}
if (option.getRequestMethod() == RequestEnum.GET) {
if (!url.contains("?")) {
url = url.concat("?");
}
StringBuilder sb = new StringBuilder();
sb.append(url).append("?");
Set<String> keys = params.keySet();
for (String key : keys) {
sb.append(key).append("=").append(URLEncoder.encode(params.get(key))).append("&");
sb.append(URLEncoder.encode(key))
.append("=")
.append(URLEncoder.encode(params.get(key)))
.append("&");
}
String temp = sb.toString();
temp = temp.substring(0, temp.length() - 1);
return new URL(CommonUtil.convertUrl(temp));
} else {
return new URL(CommonUtil.convertUrl(url));
}
if (option.getRequestMethod() == RequestEnum.POST) {
}
}

@ -15,13 +15,85 @@
*/
package com.arialyy.aria.http
import com.arialyy.aria.core.common.RequestEnum
import java.net.CookieManager
import java.net.Proxy
/**
* @Author laoyuyu
* @Description
* @Date 14:27 AM 2023/1/20
**/
class HttpOption {
private val params = hashMapOf<String, String>()
private val headers = hashMapOf<String, String>()
private var requestEnum = RequestEnum.GET
private var proxy: Proxy? = null
private var cookieManager: CookieManager? = null
private var body: String? = null
private var bodyBinary: ByteArray? = null
fun setProxy(proxy: Proxy): HttpOption {
this.proxy = proxy
return this
}
fun getProxy() = proxy
/**
* set http request key
*/
fun setParas(key: String, value: String): HttpOption {
params[key] = value
return this
}
fun setParams(params: Map<String, String>): HttpOption {
this.params.putAll(params)
return this
}
fun getParams() = params
fun getCookieManager(): CookieManager? {
return cookieManager
}
fun setBody(body: String): HttpOption {
this.body = body
return this
}
fun getBody() = body
fun setBodyBinary(body: ByteArray): HttpOption {
this.bodyBinary = body
return this
}
fun getBodyBinary() = bodyBinary
fun setCookieManager(cookieManager: CookieManager): HttpOption {
this.cookieManager = cookieManager
return this
}
fun setHeaders(headers: Map<String, String>): HttpOption {
this.headers.putAll(headers)
return this
}
fun getHeaders() = headers
// fun se
/**
* Attempts to get file information before downloading a file
* by default, the get method is used to get
* if the server supports head requests, please set true
*/
fun setRequestMethod(requestEnum: RequestEnum): HttpOption {
this.requestEnum = requestEnum
return this
}
fun getRequestMethod() = requestEnum
}

@ -15,9 +15,17 @@
*/
package com.arialyy.aria.http
import android.text.TextUtils
import com.arialyy.aria.http.download.HttpDTaskOption
import com.arialyy.aria.util.CheckUtil
import com.arialyy.aria.util.Regular
import timber.log.Timber
import java.io.IOException
import java.io.InputStream
import java.net.HttpURLConnection
import java.util.regex.Pattern
import java.util.zip.GZIPInputStream
import java.util.zip.InflaterInputStream
/**
* @Author laoyuyu
@ -25,6 +33,46 @@ import timber.log.Timber
* @Date 12:40 PM 2023/1/22
**/
internal object HttpUtil {
/**
* 拦截window.location.replace数据
*
* @return 重定向url
*/
fun getWindowReplaceUrl(text: String?): String? {
if (text.isNullOrEmpty()) {
Timber.e("text is null")
return null
}
val reg = Regular.REG_WINLOD_REPLACE
val p = Pattern.compile(reg)
val m = p.matcher(text)
if (m.find()) {
var s = m.group()
s = s.substring(9, s.length - 2)
return s
}
return null
}
/**
* 转换HttpUrlConnect的inputStream流
*
* @return [GZIPInputStream][InflaterInputStream]
* @throws IOException
*/
@Throws(IOException::class) fun convertInputStream(connection: HttpURLConnection): InputStream? {
val encoding = connection.getHeaderField("Content-Encoding")
if (TextUtils.isEmpty(encoding)) {
return connection.inputStream
}
if (encoding.contains("gzip")) {
return GZIPInputStream(connection.inputStream)
}
if (encoding.contains("deflate")) {
return InflaterInputStream(connection.inputStream)
}
return connection.inputStream
}
fun checkHttpDParams(option: HttpDTaskOption?): Boolean {
if (option == null) {

@ -20,13 +20,15 @@ import com.arialyy.aria.core.DuaContext
import com.arialyy.aria.core.command.AddCmd
import com.arialyy.aria.core.command.StartCmd
import com.arialyy.aria.core.inf.IStartController
import com.arialyy.aria.core.processor.IHttpFileLenAdapter
import com.arialyy.aria.core.task.DownloadTask
import com.arialyy.aria.core.task.TaskCachePool
import com.arialyy.aria.http.HttpBaseController
import com.arialyy.aria.http.HttpOption
import com.arialyy.aria.http.HttpUtil
import com.arialyy.aria.orm.EntityCachePool
import com.arialyy.aria.orm.entity.DEntity
import kotlinx.coroutines.launch
import java.net.HttpURLConnection
/**
* @Author laoyuyu
@ -45,16 +47,28 @@ class HttpDStartController(target: Any, val url: String) : HttpBaseController(ta
/**
* set http params, link Header
*/
fun setHttpOption(httpOption: HttpOption): HttpBaseController {
fun setHttpOption(httpOption: HttpOption): HttpDStartController {
httpDTaskOption.httpOption = httpOption
return this
}
fun setListener(listener: HttpDownloadListener): HttpBaseController {
/**
* Maybe the server has special rules, you need set [IHttpFileLenAdapter] to get the file length from [HttpURLConnection.getHeaderFields]
*/
fun setHttpFileLenAdapter(adapter: IHttpFileLenAdapter): HttpDStartController {
httpDTaskOption.fileSizeAdapter = adapter
return this
}
fun setListener(listener: HttpDownloadListener): HttpDStartController {
DuaContext.getLifeManager().addCustomListener(target, listener)
return this
}
/**
* set file save path, eg: /mnt/sdcard/Downloads/test.zip
*/
fun setSavePath(savePath: Uri): HttpDStartController {
httpDTaskOption.savePathUri = savePath
return this
@ -64,10 +78,16 @@ class HttpDStartController(target: Any, val url: String) : HttpBaseController(ta
if (HttpUtil.checkHttpDParams(httpDTaskOption)) {
throw IllegalArgumentException("invalid params")
}
val task = DownloadTask(httpDTaskOption)
val savePath = httpDTaskOption.savePathUri!!.toString()
var util = TaskCachePool.getTaskUtil(savePath)
if (util == null) {
util = HttpDTaskUtil()
TaskCachePool.putTaskUtil(savePath, util)
}
val task = DownloadTask(httpDTaskOption, util)
DuaContext.duaScope.launch {
val dEntity = findDEntityBySavePath(httpDTaskOption)
EntityCachePool.putEntity(task.taskId, dEntity)
TaskCachePool.putEntity(task.taskId, dEntity)
}
return task
}

@ -16,6 +16,7 @@
package com.arialyy.aria.http.download
import com.arialyy.aria.core.download.DTaskOption
import com.arialyy.aria.core.processor.IHttpFileLenAdapter
import com.arialyy.aria.http.HttpOption
/**
@ -26,4 +27,5 @@ import com.arialyy.aria.http.HttpOption
class HttpDTaskOption : DTaskOption() {
var httpOption: HttpOption? = null
var fileSizeAdapter: IHttpFileLenAdapter? = null
}

@ -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()
}
}

@ -20,12 +20,11 @@ package com.arialyy.aria.core.common;
* url请求方式目前支持GETPOST
*/
public enum RequestEnum {
GET("GET"), POST("POST");
GET("GET"), POST("POST"), HEAD("HEAD");
public String name;
RequestEnum(String name) {
this.name = name;
}
}

@ -26,4 +26,5 @@ import com.arialyy.aria.core.inf.ITaskOption
open class DTaskOption : ITaskOption() {
var sourUrl: String? = null
var savePathUri: Uri? = null
var redirectUrl: String? = null
}

@ -15,11 +15,10 @@
*/
package com.arialyy.aria.core.group;
import com.arialyy.aria.core.inf.IUtil;
import com.arialyy.aria.core.inf.ITaskUtil;
import com.arialyy.aria.core.listener.IEventListener;
import com.arialyy.aria.core.loader.LoaderStructure;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
import com.arialyy.aria.core.wrapper.ITaskWrapper;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil;
@ -27,7 +26,7 @@ import com.arialyy.aria.util.CommonUtil;
* Created by AriaL on 2017/6/30.
* 任务组核心逻辑
*/
public abstract class AbsGroupLoaderUtil implements IUtil {
public abstract class AbsGroupLoaderUtil implements ITaskUtil {
protected String TAG = CommonUtil.getClassName(getClass());
private IEventListener mListener;
@ -36,7 +35,7 @@ public abstract class AbsGroupLoaderUtil implements IUtil {
private boolean isStop = false, isCancel = false;
@Override public IUtil setParams(AbsTaskWrapper taskWrapper, IEventListener listener) {
@Override public ITaskUtil setParams(AbsTaskWrapper taskWrapper, IEventListener listener) {
mTaskWrapper = taskWrapper;
mListener = listener;
mLoader = getLoader();

@ -19,7 +19,7 @@ import android.os.Handler;
import com.arialyy.aria.core.TaskRecord;
import com.arialyy.aria.core.download.DTaskWrapper;
import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.inf.IUtil;
import com.arialyy.aria.core.inf.ITaskUtil;
import com.arialyy.aria.core.listener.IEventListener;
import com.arialyy.aria.core.listener.ISchedulers;
import com.arialyy.aria.core.loader.LoaderStructure;
@ -31,7 +31,7 @@ import com.arialyy.aria.util.CommonUtil;
/**
* 子任务下载器工具需要在线程池中执行
*/
public abstract class AbsSubDLoadUtil implements IUtil, Runnable {
public abstract class AbsSubDLoadUtil implements ITaskUtil, Runnable {
protected final String TAG = CommonUtil.getClassName(getClass());
protected SubLoader mDLoader;
@ -51,7 +51,7 @@ public abstract class AbsSubDLoadUtil implements IUtil, Runnable {
this.needGetInfo = needGetInfo;
}
@Override public IUtil setParams(AbsTaskWrapper taskWrapper, IEventListener listener) {
@Override public ITaskUtil setParams(AbsTaskWrapper taskWrapper, IEventListener listener) {
mWrapper = (DTaskWrapper) taskWrapper;
mDLoader = getLoader();
return this;

@ -16,7 +16,7 @@
package com.arialyy.aria.core.group;
import com.arialyy.aria.core.loader.AbsNormalLoader;
import com.arialyy.aria.core.inf.IUtil;
import com.arialyy.aria.core.inf.ITaskUtil;
import com.arialyy.aria.core.config.DGroupConfig;
/**
@ -24,7 +24,7 @@ import com.arialyy.aria.core.config.DGroupConfig;
*
* @param <Fileer> {@link AbsNormalLoader}下载器
*/
interface ISubQueue<Fileer extends IUtil> {
interface ISubQueue<Fileer extends ITaskUtil> {
/**
* 添加任务

@ -16,37 +16,17 @@
package com.arialyy.aria.core.inf;
import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.download.DownloadGroupEntity;
import com.arialyy.aria.core.listener.IEventListener;
import com.arialyy.aria.core.upload.UploadEntity;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
import com.arialyy.aria.core.task.ITask;
import org.jetbrains.annotations.NotNull;
/**
* Created by lyy on 2016/10/31.
* 任务功能接口
*/
public interface IUtil {
public interface ITaskUtil {
IUtil setParams(AbsTaskWrapper taskWrapper, IEventListener listener);
/**
* 获取任务标志
*
* @return {@link DownloadEntity#getKey()}{@link DownloadGroupEntity#getKey()}{@link
* UploadEntity#getKey()}
*/
String getKey();
/**
* 获取文件大小
*/
long getFileSize();
/**
* 获取当前位置
*/
long getCurrentLocation();
void init(@NotNull ITask task, @NotNull IEventListener listener);
/**
* 任务是否正在执行

@ -25,7 +25,7 @@ import com.arialyy.aria.core.task.ITask;
import com.arialyy.aria.core.task.TaskState;
import com.arialyy.aria.core.wrapper.ITaskWrapper;
import com.arialyy.aria.exception.AriaException;
import com.arialyy.aria.orm.EntityCachePool;
import com.arialyy.aria.core.task.TaskCachePool;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.ErrorHelp;
import java.lang.ref.WeakReference;
@ -174,6 +174,6 @@ public abstract class BaseListener implements IEventListener {
if (state == IEntity.STATE_COMPLETE) {
handleComplete();
}
EntityCachePool.INSTANCE.updateState(mTask.getTaskId(), state, location);
TaskCachePool.INSTANCE.updateState(mTask.getTaskId(), state, location);
}
}

@ -16,10 +16,9 @@
package com.arialyy.aria.core.loader;
import com.arialyy.aria.core.inf.IUtil;
import com.arialyy.aria.core.inf.ITaskUtil;
import com.arialyy.aria.core.listener.IEventListener;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
import com.arialyy.aria.core.wrapper.ITaskWrapper;
import com.arialyy.aria.exception.AriaException;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil;
@ -28,7 +27,7 @@ import com.arialyy.aria.util.CommonUtil;
* Created by lyy on 2015/8/25.
* HTTP\FTP单任务下载工具
*/
public abstract class AbsNormalLoaderUtil implements IUtil {
public abstract class AbsNormalLoaderUtil implements ITaskUtil {
protected String TAG = CommonUtil.getClassName(getClass());
private IEventListener mListener;
protected AbsNormalLoader mLoader;
@ -38,7 +37,7 @@ public abstract class AbsNormalLoaderUtil implements IUtil {
protected AbsNormalLoaderUtil() {
}
@Override public IUtil setParams(AbsTaskWrapper taskWrapper, IEventListener listener) {
@Override public ITaskUtil setParams(AbsTaskWrapper taskWrapper, IEventListener listener) {
mTaskWrapper = taskWrapper;
mListener = listener;
mLoader = getLoader();

@ -35,8 +35,8 @@ public abstract class AbsGroupTask<TASK_ENTITY extends AbsGroupTaskWrapper>
* @param url 子任务下载地址
*/
public void startSubTask(String url) {
if (getUtil() != null) {
((AbsGroupLoaderUtil) getUtil()).startSubTask(url);
if (getTaskUtil() != null) {
((AbsGroupLoaderUtil) getTaskUtil()).startSubTask(url);
}
}
@ -46,8 +46,8 @@ public abstract class AbsGroupTask<TASK_ENTITY extends AbsGroupTaskWrapper>
* @param url 子任务下载地址
*/
public void stopSubTask(String url) {
if (getUtil() != null) {
((AbsGroupLoaderUtil) getUtil()).stopSubTask(url);
if (getTaskUtil() != null) {
((AbsGroupLoaderUtil) getTaskUtil()).stopSubTask(url);
}
}
}

@ -18,10 +18,9 @@ package com.arialyy.aria.core.task;
import android.text.TextUtils;
import com.arialyy.aria.core.common.AbsEntity;
import com.arialyy.aria.core.inf.ITaskOption;
import com.arialyy.aria.core.inf.IUtil;
import com.arialyy.aria.core.inf.ITaskUtil;
import com.arialyy.aria.core.inf.TaskSchedulerType;
import com.arialyy.aria.util.CommonUtil;
import com.arialyy.aria.util.ComponentUtil;
import java.util.HashMap;
import java.util.Map;
import timber.log.Timber;
@ -32,7 +31,7 @@ import timber.log.Timber;
public abstract class AbsTask implements ITask {
protected ITaskOption mTaskOption;
private boolean isCancel = false, isStop = false;
private IUtil mUtil;
private ITaskUtil mUtil;
/**
* 该任务的调度类型
*/
@ -41,20 +40,19 @@ public abstract class AbsTask implements ITask {
private int taskId = -1;
private final Map<String, Object> mExpand = new HashMap<>();
protected AbsTask(ITaskOption taskOption) {
protected AbsTask(ITaskOption taskOption, ITaskUtil util) {
mTaskOption = taskOption;
mUtil = util;
taskId = TaskStatePool.INSTANCE.buildTaskId$PublicComponent_debug();
TaskStatePool.INSTANCE.putTaskState(getTaskId(), mTaskState);
util.init(this, taskOption.taskListener);
}
@Override public void setState(int state) {
mTaskState.setState(state);
}
synchronized IUtil getUtil() {
if (mUtil == null) {
mUtil = ComponentUtil.getInstance().buildUtil(mTaskWrapper, mListener);
}
ITaskUtil getTaskUtil() {
return mUtil;
}
@ -154,13 +152,13 @@ public abstract class AbsTask implements ITask {
@Override public void start(int type) {
mSchedulerType = type;
mUtil = getUtil();
mUtil = getTaskUtil();
if (mUtil == null) {
Timber.e("util is null");
return;
}
if (type == TaskSchedulerType.TYPE_START_AND_RESET_STATE) {
if (getUtil().isRunning()) {
if (getTaskUtil().isRunning()) {
Timber.e("task restart fail");
return;
}
@ -168,33 +166,33 @@ public abstract class AbsTask implements ITask {
Timber.e("task restart success");
return;
}
if (getUtil().isRunning()) {
if (getTaskUtil().isRunning()) {
Timber.d("task is running");
return;
}
getUtil().start();
getTaskUtil().start();
}
@Override public void stop(int type) {
mUtil = getUtil();
mUtil = getTaskUtil();
if (mUtil == null) {
Timber.e("util is null");
return;
}
isStop = true;
mSchedulerType = type;
getUtil().stop();
getTaskUtil().stop();
}
@Override public void cancel(int type) {
mUtil = getUtil();
mUtil = getTaskUtil();
if (mUtil == null) {
Timber.e("util is null");
return;
}
isCancel = true;
mSchedulerType = type;
getUtil().cancel();
getTaskUtil().cancel();
}
/**
@ -203,7 +201,7 @@ public abstract class AbsTask implements ITask {
* @return {@code true} 正在下载
*/
@Override public boolean isRunning() {
return getUtil().isRunning();
return getTaskUtil().isRunning();
}
/**

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

@ -18,6 +18,7 @@ package com.arialyy.aria.core.task;
import android.net.Uri;
import com.arialyy.aria.core.download.DTaskOption;
import com.arialyy.aria.core.inf.ITaskUtil;
/**
* Created by lyy on 2016/8/11.
@ -25,8 +26,8 @@ import com.arialyy.aria.core.download.DTaskOption;
*/
public class DownloadTask extends AbsTask {
public DownloadTask(DTaskOption taskOption) {
super(taskOption);
public DownloadTask(DTaskOption taskOption, ITaskUtil util) {
super(taskOption, util);
taskOption.taskListener.setParams(this);
}

@ -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
}
}

@ -13,10 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.arialyy.aria.orm
package com.arialyy.aria.core.task
import com.arialyy.aria.core.inf.BaseEntity
import com.arialyy.aria.core.inf.IEntity
import com.arialyy.aria.core.inf.ITaskUtil
import timber.log.Timber
/**
@ -24,15 +25,34 @@ import timber.log.Timber
* @Description
* @Date 21:43 AM 2023/1/22
**/
object EntityCachePool {
object TaskCachePool {
/**
* key: taskId
*/
private val entityMap = hashMapOf<Int, BaseEntity>()
private val taskUtilMap = hashMapOf<String, ITaskUtil>()
/**
* @param taskKey task unique identifier, like: savePath, sourceUrl
*/
fun putTaskUtil(taskKey: String, taskUtil: ITaskUtil) {
if (taskKey.isEmpty()) {
Timber.e("invalid taskKey: $taskKey")
return
}
taskUtilMap[taskKey] = taskUtil
}
/**
* @param taskKey task unique identifier, like: savePath, sourceUrl
*/
fun getTaskUtil(taskKey: String): ITaskUtil? {
return taskUtilMap[taskKey]
}
fun putEntity(taskId: Int, entity: BaseEntity) {
if (taskId <= 0) {
Timber.e("invalid taskId: ${taskId}")
Timber.e("invalid taskId: $taskId")
return
}
entityMap[taskId] = entity

@ -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
}

@ -19,7 +19,7 @@ import android.os.Handler;
import com.arialyy.aria.core.TaskOptionParams;
import com.arialyy.aria.core.inf.IEventHandler;
import com.arialyy.aria.core.inf.ITaskOption;
import com.arialyy.aria.core.inf.IUtil;
import com.arialyy.aria.core.inf.ITaskUtil;
import com.arialyy.aria.core.listener.IEventListener;
import com.arialyy.aria.core.task.AbsTask;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
@ -104,7 +104,7 @@ public class ComponentUtil {
*
* @return 返回任务工具
*/
public synchronized <T extends IUtil> T buildUtil(AbsTaskWrapper wrapper,
public synchronized <T extends ITaskUtil> T buildUtil(AbsTaskWrapper wrapper,
IEventListener listener) {
int requestType = wrapper.getRequestType();
String className = null;

Loading…
Cancel
Save