Skip to content

Commit 7e6073b

Browse files
authored
ServiceUnavailableException: add getDelayUntil (#67)
* Add support for sending additional headers with put requests * Add retry delay calculation for ServiceUnavailableException
1 parent 9678c50 commit 7e6073b

File tree

2 files changed

+61
-8
lines changed

2 files changed

+61
-8
lines changed

src/main/kotlin/at/bitfire/dav4jvm/DavResource.kt

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,27 @@
66

77
package at.bitfire.dav4jvm
88

9+
import at.bitfire.dav4jvm.DavResource.Companion.MAX_REDIRECTS
910
import at.bitfire.dav4jvm.XmlUtils.insertTag
1011
import at.bitfire.dav4jvm.XmlUtils.propertyName
11-
import at.bitfire.dav4jvm.exception.*
12+
import at.bitfire.dav4jvm.exception.ConflictException
13+
import at.bitfire.dav4jvm.exception.DavException
14+
import at.bitfire.dav4jvm.exception.ForbiddenException
15+
import at.bitfire.dav4jvm.exception.HttpException
16+
import at.bitfire.dav4jvm.exception.NotFoundException
17+
import at.bitfire.dav4jvm.exception.PreconditionFailedException
18+
import at.bitfire.dav4jvm.exception.ServiceUnavailableException
19+
import at.bitfire.dav4jvm.exception.UnauthorizedException
1220
import at.bitfire.dav4jvm.property.caldav.NS_CALDAV
1321
import at.bitfire.dav4jvm.property.carddav.NS_CARDDAV
1422
import at.bitfire.dav4jvm.property.webdav.NS_WEBDAV
1523
import at.bitfire.dav4jvm.property.webdav.SyncToken
16-
import okhttp3.*
24+
import okhttp3.Headers
25+
import okhttp3.HttpUrl
1726
import okhttp3.MediaType.Companion.toMediaType
27+
import okhttp3.OkHttpClient
28+
import okhttp3.Request
29+
import okhttp3.RequestBody
1830
import okhttp3.RequestBody.Companion.toRequestBody
1931
import okhttp3.Response
2032
import org.xmlpull.v1.XmlPullParser

src/main/kotlin/at/bitfire/dav4jvm/exception/ServiceUnavailableException.kt

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,33 @@
77
package at.bitfire.dav4jvm.exception
88

99
import at.bitfire.dav4jvm.HttpUtils
10+
import at.bitfire.dav4jvm.exception.ServiceUnavailableException.Companion.DELAY_UNTIL_DEFAULT
11+
import at.bitfire.dav4jvm.exception.ServiceUnavailableException.Companion.DELAY_UNTIL_MAX
12+
import at.bitfire.dav4jvm.exception.ServiceUnavailableException.Companion.DELAY_UNTIL_MIN
1013
import okhttp3.Response
1114
import java.net.HttpURLConnection
1215
import java.time.Instant
1316
import java.util.logging.Level
1417
import java.util.logging.Logger
1518

16-
class ServiceUnavailableException: HttpException {
19+
class ServiceUnavailableException : HttpException {
1720

1821
private val logger
1922
get() = Logger.getLogger(javaClass.name)
2023

21-
var retryAfter: Instant? = null
24+
val retryAfter: Instant?
2225

23-
constructor(message: String?): super(HttpURLConnection.HTTP_UNAVAILABLE, message)
26+
constructor(message: String?) : super(HttpURLConnection.HTTP_UNAVAILABLE, message) {
27+
retryAfter = null
28+
}
2429

25-
constructor(response: Response): super(response) {
30+
constructor(response: Response) : super(response) {
2631
// Retry-After = "Retry-After" ":" ( HTTP-date | delta-seconds )
2732
// HTTP-date = rfc1123-date | rfc850-date | asctime-date
2833

34+
var retryAfterValue: Instant? = null
2935
response.header("Retry-After")?.let { after ->
30-
retryAfter = HttpUtils.parseDate(after) ?:
36+
retryAfterValue = HttpUtils.parseDate(after) ?:
3137
// not a HTTP-date, must be delta-seconds
3238
try {
3339
val seconds = after.toLong()
@@ -37,6 +43,41 @@ class ServiceUnavailableException: HttpException {
3743
null
3844
}
3945
}
46+
47+
retryAfter = retryAfterValue
48+
}
49+
50+
51+
/**
52+
* Returns appropriate sync retry delay in seconds, considering the servers suggestion
53+
* in [retryAfter] ([DELAY_UNTIL_DEFAULT] if no server suggestion).
54+
*
55+
* Takes current time into account to calculate intervals. Interval
56+
* will be restricted to values between [DELAY_UNTIL_MIN] and [DELAY_UNTIL_MAX].
57+
*
58+
* @param start timestamp to calculate the delay from (default: now)
59+
*
60+
* @return until when to wait before sync can be retried
61+
*/
62+
fun getDelayUntil(start: Instant = Instant.now()): Instant {
63+
if (retryAfter == null)
64+
return start.plusSeconds(DELAY_UNTIL_DEFAULT)
65+
66+
// take server suggestion, but restrict to plausible min/max values
67+
return retryAfter.coerceIn(
68+
minimumValue = start.plusSeconds(DELAY_UNTIL_MIN),
69+
maximumValue = start.plusSeconds(DELAY_UNTIL_MAX)
70+
)
71+
}
72+
73+
74+
companion object {
75+
76+
// default values for getDelayUntil
77+
const val DELAY_UNTIL_DEFAULT = 15 * 60L // 15 min
78+
const val DELAY_UNTIL_MIN = 1 * 60L // 1 min
79+
const val DELAY_UNTIL_MAX = 2 * 60 * 60L // 2 hours
80+
4081
}
4182

42-
}
83+
}

0 commit comments

Comments
 (0)