@@ -19,13 +19,16 @@ package com.coralogix.sbtprotofetch
1919import org .apache .commons .compress .archivers .tar .TarArchiveInputStream
2020import org .apache .commons .compress .compressors .gzip .GzipCompressorInputStream
2121import sbt .util .Logger
22- import sbt .{ File , URL }
22+ import sbt .File
2323
2424import java .io .{BufferedInputStream , IOException , InputStream }
25- import java .net .URI
25+ import java .net .{HttpURLConnection , URI , URL }
26+ import java .nio .charset .StandardCharsets
2627import java .nio .file .attribute .{BasicFileAttributes , PosixFilePermission }
2728import java .nio .file .{FileVisitResult , Files , Path , SimpleFileVisitor }
2829import java .util
30+ import scala .annotation .tailrec
31+ import scala .util .Random
2932
3033object ProtofetchBinary {
3134
@@ -66,9 +69,7 @@ object ProtofetchBinary {
6669 delete(targetPath)
6770 }
6871
69- val stream = url.openStream()
70- try extract(logger, stream, targetPath)
71- finally stream.close()
72+ downloadRetrying(url, extract(logger, _, targetPath))
7273
7374 val binary = system.platform match {
7475 case Platform .Linux | Platform .Mac => " protofetch"
@@ -78,6 +79,37 @@ object ProtofetchBinary {
7879 targetPath.resolve(" bin" ).resolve(binary).toFile
7980 }
8081
82+ // Downloading release assets from GitHub often fails with 503 or 504,
83+ // so we want to try a few times before giving up.
84+ // The retry behavior is rather arbitrary, we will make up to 5 attempts
85+ // with a random delay up to 5s between attempts.
86+ // @tailrec
87+ private def downloadRetrying [T ](
88+ url : URL ,
89+ block : InputStream => T ,
90+ retries : Int = 5
91+ ): T = {
92+ val connection = url.openConnection()
93+ try {
94+ val stream = connection.getInputStream
95+ try block(stream)
96+ finally stream.close()
97+ } catch {
98+ case e : IOException =>
99+ connection match {
100+ case httpConnection : HttpURLConnection =>
101+ val code = httpConnection.getResponseCode
102+ if (code >= 500 && code < 600 && retries > 0 ) {
103+ Thread .sleep(Random .nextInt(5000 ))
104+ downloadRetrying(url, block, retries - 1 )
105+ } else {
106+ throw e
107+ }
108+ case _ => throw e
109+ }
110+ }
111+ }
112+
81113 private def delete (path : Path ): Unit = {
82114 Files .walkFileTree(
83115 path,
@@ -138,4 +170,5 @@ object ProtofetchBinary {
138170
139171 result
140172 }
173+
141174}
0 commit comments