mirror of
https://github.com/amir1376/ab-download-manager.git
synced 2025-02-20 11:43:24 +08:00
download web pages without size validation
This commit is contained in:
parent
9c459213ba
commit
30e86011e9
@ -3,14 +3,13 @@ package ir.amirab.downloader.downloaditem
|
|||||||
import ir.amirab.downloader.DownloadManager
|
import ir.amirab.downloader.DownloadManager
|
||||||
import ir.amirab.downloader.connection.DownloaderClient
|
import ir.amirab.downloader.connection.DownloaderClient
|
||||||
import ir.amirab.downloader.connection.response.expectSuccess
|
import ir.amirab.downloader.connection.response.expectSuccess
|
||||||
|
import ir.amirab.downloader.connection.response.isWebPage
|
||||||
import ir.amirab.downloader.destination.SimpleDownloadDestination
|
import ir.amirab.downloader.destination.SimpleDownloadDestination
|
||||||
|
import ir.amirab.downloader.downloaditem.DownloadItem.Companion.LENGTH_UNKNOWN
|
||||||
import ir.amirab.downloader.exception.DownloadValidationException
|
import ir.amirab.downloader.exception.DownloadValidationException
|
||||||
import ir.amirab.downloader.exception.FileChangedException
|
import ir.amirab.downloader.exception.FileChangedException
|
||||||
import ir.amirab.downloader.exception.TooManyErrorException
|
import ir.amirab.downloader.exception.TooManyErrorException
|
||||||
import ir.amirab.downloader.part.Part
|
import ir.amirab.downloader.part.*
|
||||||
import ir.amirab.downloader.part.PartDownloadStatus
|
|
||||||
import ir.amirab.downloader.part.PartDownloader
|
|
||||||
import ir.amirab.downloader.part.awaitIdle
|
|
||||||
import ir.amirab.downloader.utils.ExceptionUtils
|
import ir.amirab.downloader.utils.ExceptionUtils
|
||||||
import ir.amirab.downloader.utils.printStackIfNOtUsual
|
import ir.amirab.downloader.utils.printStackIfNOtUsual
|
||||||
import ir.amirab.downloader.utils.splitToRange
|
import ir.amirab.downloader.utils.splitToRange
|
||||||
@ -81,6 +80,12 @@ class DownloadJob(
|
|||||||
}.orEmpty())
|
}.orEmpty())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// if strict mode is false part downloader going to download data without any validation of content length
|
||||||
|
// this is only acceptable when resume is not supported and multiple get requests results multiple result
|
||||||
|
@Volatile
|
||||||
|
private var strictDownload = true
|
||||||
|
|
||||||
private val _status = MutableStateFlow<DownloadJobStatus>(DownloadJobStatus.IDLE)
|
private val _status = MutableStateFlow<DownloadJobStatus>(DownloadJobStatus.IDLE)
|
||||||
val status = _status.asStateFlow()
|
val status = _status.asStateFlow()
|
||||||
fun expectValid(size: Long, parts: List<LongRange>) {
|
fun expectValid(size: Long, parts: List<LongRange>) {
|
||||||
@ -103,6 +108,7 @@ class DownloadJob(
|
|||||||
downloadItem.status = DownloadStatus.Added
|
downloadItem.status = DownloadStatus.Added
|
||||||
downloadItem.startTime = null
|
downloadItem.startTime = null
|
||||||
downloadItem.completeTime = null
|
downloadItem.completeTime = null
|
||||||
|
strictDownload = true
|
||||||
saveState()
|
saveState()
|
||||||
downloadManager.onDownloadItemChange(downloadItem)
|
downloadManager.onDownloadItemChange(downloadItem)
|
||||||
}
|
}
|
||||||
@ -162,6 +168,8 @@ class DownloadJob(
|
|||||||
) {
|
) {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
destination.outputSize = downloadItem.contentLength
|
destination.outputSize = downloadItem.contentLength
|
||||||
|
.takeIf { strictDownload }
|
||||||
|
?: LENGTH_UNKNOWN
|
||||||
if (!destination.isDownloadedPartsIsValid()) {
|
if (!destination.isDownloadedPartsIsValid()) {
|
||||||
//file deleted or something!
|
//file deleted or something!
|
||||||
parts.forEach { it.resetCurrent() }
|
parts.forEach { it.resetCurrent() }
|
||||||
@ -253,13 +261,21 @@ class DownloadJob(
|
|||||||
listOf(Part(0, null, 0))
|
listOf(Part(0, null, 0))
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
setParts(splitToRange(
|
if (supportsConcurrent){
|
||||||
minPartSize = downloadManager.settings.minPartSize,
|
//split parts
|
||||||
maxPartCount = getRequestedPartitionCount().toLong(),
|
setParts(splitToRange(
|
||||||
size = downloadItem.contentLength,
|
minPartSize = downloadManager.settings.minPartSize,
|
||||||
).map {
|
maxPartCount = getRequestedPartitionCount().toLong(),
|
||||||
Part(it.first, it.last)
|
size = downloadItem.contentLength,
|
||||||
})
|
).map {
|
||||||
|
Part(it.first, it.last)
|
||||||
|
})
|
||||||
|
}else{
|
||||||
|
setParts(
|
||||||
|
listOf(Part(0, (downloadItem.contentLength-1).takeIf { it>=0 }, 0))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// thisLogger().info("dl_$id parts created $parts")
|
// thisLogger().info("dl_$id parts created $parts")
|
||||||
@ -492,6 +508,7 @@ class DownloadJob(
|
|||||||
downloadManager.throttler,
|
downloadManager.throttler,
|
||||||
jobThrottler,
|
jobThrottler,
|
||||||
),
|
),
|
||||||
|
strictMode = strictDownload,
|
||||||
partSplitLock = partSplitLock
|
partSplitLock = partSplitLock
|
||||||
).also { partDownloader: PartDownloader ->
|
).also { partDownloader: PartDownloader ->
|
||||||
partDownloader.onTooManyErrors = {
|
partDownloader.onTooManyErrors = {
|
||||||
@ -526,7 +543,12 @@ class DownloadJob(
|
|||||||
//new download
|
//new download
|
||||||
downloadItem.contentLength = totalLength ?: -1
|
downloadItem.contentLength = totalLength ?: -1
|
||||||
downloadItem.serverETag=newServerETag
|
downloadItem.serverETag=newServerETag
|
||||||
|
// don't strict if it's a webpage let it download and not a link with resume support
|
||||||
|
if (response.isWebPage() && !response.resumeSupport){
|
||||||
|
strictDownload = false
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// at the beginning of download
|
||||||
if (totalLength != downloadItem.contentLength) {
|
if (totalLength != downloadItem.contentLength) {
|
||||||
throw FileChangedException.LengthChangedException(downloadItem.contentLength, totalLength ?: -1)
|
throw FileChangedException.LengthChangedException(downloadItem.contentLength, totalLength ?: -1)
|
||||||
}
|
}
|
||||||
|
@ -34,12 +34,26 @@ import kotlin.math.min
|
|||||||
val PART_MAX_TRIES = 10
|
val PART_MAX_TRIES = 10
|
||||||
const val RetryDelay = 1_000L
|
const val RetryDelay = 1_000L
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param strictMode
|
||||||
|
* `false` - this is not the purpose of its app, so we don't strict here
|
||||||
|
*
|
||||||
|
* download part without checking for length validation
|
||||||
|
* this is where we want to only copy data arrived from server
|
||||||
|
* for example, a web page link that maybe get us different response length
|
||||||
|
* we only need to download it no matter what is inside
|
||||||
|
*
|
||||||
|
* `true` - main purpose of this class
|
||||||
|
*
|
||||||
|
* validate download size before trying to write to the filesystem
|
||||||
|
*/
|
||||||
class PartDownloader(
|
class PartDownloader(
|
||||||
val credentials: IDownloadCredentials,
|
val credentials: IDownloadCredentials,
|
||||||
val getDestWriter: () -> DestWriter,
|
val getDestWriter: () -> DestWriter,
|
||||||
val part: Part,
|
val part: Part,
|
||||||
val client: DownloaderClient,
|
val client: DownloaderClient,
|
||||||
val speedLimiters: List<Throttler>,
|
val speedLimiters: List<Throttler>,
|
||||||
|
val strictMode:Boolean,
|
||||||
private val partSplitLock: Any,
|
private val partSplitLock: Any,
|
||||||
) {
|
) {
|
||||||
class ShouldNotHappened(msg: String?) : RuntimeException(msg)
|
class ShouldNotHappened(msg: String?) : RuntimeException(msg)
|
||||||
@ -238,13 +252,15 @@ class PartDownloader(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (contentLength != partCopy.remainingLength) {
|
if (contentLength != partCopy.remainingLength) {
|
||||||
conn.closeable.close()
|
if (strictMode){
|
||||||
throw ServerPartIsNotTheSameAsWeExpectException(
|
conn.closeable.close()
|
||||||
start = partCopy.current,
|
throw ServerPartIsNotTheSameAsWeExpectException(
|
||||||
end = partCopy.to,
|
start = partCopy.current,
|
||||||
expectedLength = partCopy.remainingLength,
|
end = partCopy.to,
|
||||||
actualLength = contentLength
|
expectedLength = partCopy.remainingLength,
|
||||||
)
|
actualLength = contentLength
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
thread = thread {
|
thread = thread {
|
||||||
if (stop || Thread.currentThread().isInterrupted) {
|
if (stop || Thread.currentThread().isInterrupted) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user