in sources/core/src/org/jetbrains/amper/core/downloader/downloader.kt [68:141]
suspend fun downloadFileToCacheLocation(
url: String,
userCacheRoot: AmperUserCacheRoot,
infoLog: Boolean = true,
): Path {
val target = getTargetFile(userCacheRoot, url)
fileLocks.withLock(target) {
if (target.exists()) {
Span.current().addEvent(
"use asset from cache", Attributes.of(
AttributeKey.stringKey("url"), url,
AttributeKey.stringKey("target"), target.pathString,
)
)
// update file modification time to maintain FIFO caches, i.e., in persistent cache folder on TeamCity agent
try {
target.setLastModifiedTime(FileTime.from(Instant.now()))
} catch (t: Throwable) {
logger.warn("Unable to update mtime: $target", t)
}
return target
}
if (infoLog) {
logger.info("Downloading $url to ${target.pathString}")
}
return spanBuilder("download").setAttribute("url", url).setAttribute("target", target.pathString).use {
// TODO check if this is redundant with Ktor's retry mechanism.
// It might be necessary due to expectSuccess=false.
suspendingRetryWithExponentialBackOff {
// save to the same disk to ensure that move will be atomic and not as a copy
val tempFile = target.parent
.resolve("${target.fileName}-${(Instant.now().epochSecond - 1634886185).toString(36)}-${Instant.now().nano.toString(36)}".take(255))
tempFile.deleteIfExists()
// Add a hook, so interruption won't leave garbage files.
tempFile.toFile().deleteOnExit()
target.parent.createDirectories()
try {
val response = archivesDownloadClient.prepareGet(url) {
// we manually handle errors below
expectSuccess = false
}.execute {
coroutineScope {
it.bodyAsChannel().copyAndClose(writeChannel(tempFile))
}
it
}
val statusCode = response.status.value
if (statusCode != 200) {
failWithResponseData(response, tempFile, statusCode, url)
}
checkContentLength(response, url, tempFile)
tempFile.moveTo(target, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING)
} catch (httpException: HttpStatusException) {
if (httpException.statusCode == 404) {
// do not retry 404
throw NoMoreRetriesException(httpException.toString(), httpException)
} else {
throw httpException
}
} finally {
tempFile.deleteIfExists()
}
}
target
}
}
}