diff --git a/cache/src/main/scala/coursier/Cache.scala b/cache/src/main/scala/coursier/Cache.scala index b8cc14892..d8c4a47f0 100644 --- a/cache/src/main/scala/coursier/Cache.scala +++ b/cache/src/main/scala/coursier/Cache.scala @@ -198,6 +198,7 @@ object Cache { } private val partialContentResponseCode = 206 + private val invalidPartialContentResponseCode = 416 private val handlerClsCache = new ConcurrentHashMap[String, Option[URLStreamHandler]] @@ -561,7 +562,8 @@ object Cache { case conn0: HttpURLConnection if alreadyDownloaded > 0L => conn0.setRequestProperty("Range", s"bytes=$alreadyDownloaded-") - (conn0.getResponseCode == partialContentResponseCode) && { + ((conn0.getResponseCode == partialContentResponseCode) + || (conn0.getResponseCode == invalidPartialContentResponseCode)) && { val ackRange = Option(conn0.getHeaderField("Content-Range")).getOrElse("") ackRange.startsWith(s"bytes $alreadyDownloaded-") || { diff --git a/cli/src/test/scala-2.12/coursier/cli/CliFetchIntegrationTest.scala b/cli/src/test/scala-2.12/coursier/cli/CliFetchIntegrationTest.scala index 5972d20ff..1b6efd1d2 100644 --- a/cli/src/test/scala-2.12/coursier/cli/CliFetchIntegrationTest.scala +++ b/cli/src/test/scala-2.12/coursier/cli/CliFetchIntegrationTest.scala @@ -867,4 +867,30 @@ class CliFetchIntegrationTest extends FlatSpec with CliTestLib { } } + "Wrong range partial artifact resolve" should "succeed with retry" in withTempDir("tmp_dir") { + dir => { + def runFetchJunit = { + val fetchOpt = FetchOptions(common = CommonOptions(mode = "force", cacheOptions = CacheOptions(cache = dir.getAbsolutePath))) + val fetch = Fetch(fetchOpt, RemainingArgs(Seq("junit:junit:4.6"), Seq())) + assert(fetch.files0.map(_.getName).toSet + .equals(Set("junit-4.6.jar"))) + val junitJarPath = fetch.files0.map(_.getAbsolutePath()).filter(_.contains("junit-4.6.jar")) + .head + val junitJarFile = new File(junitJarPath) + junitJarFile + } + + val originalJunitJar: File = runFetchJunit + + val originalJunitJarContent = FileUtil.readAllBytes(originalJunitJar) + + // Move the jar to partial (but complete) download + val newJunitJar: File = new File(originalJunitJar.getAbsolutePath + ".part") + originalJunitJar.renameTo(newJunitJar) + + // Run fetch again and it should pass because of retrying on the partial jar. + val jar = runFetchJunit + assert(FileUtil.readAllBytes(jar).sameElements(originalJunitJarContent)) + } + } }