Skip to content

Commit cc7b43f

Browse files
committed
add builder option to set request timeout
1 parent e56dce8 commit cc7b43f

File tree

4 files changed

+64
-25
lines changed

4 files changed

+64
-25
lines changed

src/commonMain/kotlin/com.adamratzman.spotify/Builder.kt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,11 @@ class SpotifyApiBuilder(
125125
*/
126126
fun enableLogger(enableLogger: Boolean) = apply { this.options.enableLogger = enableLogger }
127127

128+
/**
129+
* Set the maximum time, in milliseconds, before terminating an http request
130+
*/
131+
fun requestTimeoutMillis(requestTimeoutMillis: Long?) = apply { this.options.requestTimeoutMillis = requestTimeoutMillis }
132+
128133
/**
129134
* Set whether you want to allow splitting too-large requests into smaller, allowable api requests
130135
*/
@@ -361,6 +366,7 @@ class SpotifyClientApiBuilder(
361366
options.testTokenValidity,
362367
options.defaultLimit,
363368
options.allowBulkRequests,
369+
options.requestTimeoutMillis,
364370
options.json
365371
)
366372
} catch (e: CancellationException) {
@@ -381,6 +387,7 @@ class SpotifyClientApiBuilder(
381387
options.testTokenValidity,
382388
options.defaultLimit,
383389
options.allowBulkRequests,
390+
options.requestTimeoutMillis,
384391
options.json
385392
)
386393
authorization.tokenString != null -> SpotifyClientApi(
@@ -402,6 +409,7 @@ class SpotifyClientApiBuilder(
402409
options.testTokenValidity,
403410
options.defaultLimit,
404411
options.allowBulkRequests,
412+
options.requestTimeoutMillis,
405413
options.json
406414
)
407415
else -> throw IllegalArgumentException(
@@ -445,6 +453,7 @@ class SpotifyAppApiBuilder(
445453
options.testTokenValidity,
446454
options.defaultLimit,
447455
options.allowBulkRequests,
456+
options.requestTimeoutMillis,
448457
options.json
449458
)
450459
authorization.tokenString != null -> {
@@ -463,6 +472,7 @@ class SpotifyAppApiBuilder(
463472
options.testTokenValidity,
464473
options.defaultLimit,
465474
options.allowBulkRequests,
475+
options.requestTimeoutMillis,
466476
options.json
467477
)
468478
}
@@ -479,6 +489,7 @@ class SpotifyAppApiBuilder(
479489
options.testTokenValidity,
480490
options.defaultLimit,
481491
options.allowBulkRequests,
492+
options.requestTimeoutMillis,
482493
options.json
483494
)
484495
}
@@ -497,6 +508,7 @@ class SpotifyAppApiBuilder(
497508
options.testTokenValidity,
498509
options.defaultLimit,
499510
options.allowBulkRequests,
511+
options.requestTimeoutMillis,
500512
options.json
501513
)
502514
} catch (e: CancellationException) {
@@ -577,6 +589,8 @@ data class SpotifyUserAuthorization(
577589
* @property json The Json serializer/deserializer instance
578590
* @property enableAllOptions Whether to enable all provided utilities
579591
* @property allowBulkRequests Allow splitting too-large requests into smaller, allowable api requests
592+
* @property requestTimeoutMillis The maximum time, in milliseconds, before terminating an http request
593+
*
580594
*/
581595
class SpotifyApiOptionsBuilder(
582596
var useCache: Boolean = true,
@@ -588,6 +602,7 @@ class SpotifyApiOptionsBuilder(
588602
var enableAllOptions: Boolean = false,
589603
var defaultLimit: Int = 50,
590604
var allowBulkRequests: Boolean = true,
605+
var requestTimeoutMillis: Long? = null,
591606
var json: Json = stableJson
592607
) {
593608
fun build() =
@@ -601,6 +616,7 @@ class SpotifyApiOptionsBuilder(
601616
testTokenValidity = true,
602617
defaultLimit = 50,
603618
allowBulkRequests = true,
619+
requestTimeoutMillis = requestTimeoutMillis,
604620
json = json
605621
)
606622
else
@@ -613,6 +629,7 @@ class SpotifyApiOptionsBuilder(
613629
testTokenValidity,
614630
defaultLimit,
615631
allowBulkRequests,
632+
requestTimeoutMillis,
616633
json
617634
)
618635
}
@@ -629,6 +646,8 @@ class SpotifyApiOptionsBuilder(
629646
* @property defaultLimit The default amount of objects to retrieve in one request
630647
* @property json The Json serializer/deserializer instance
631648
* @property allowBulkRequests Allow splitting too-large requests into smaller, allowable api requests
649+
* @property requestTimeoutMillis The maximum time, in milliseconds, before terminating an http request
650+
*
632651
*/
633652

634653
data class SpotifyApiOptions(
@@ -640,6 +659,7 @@ data class SpotifyApiOptions(
640659
var testTokenValidity: Boolean,
641660
var defaultLimit: Int,
642661
var allowBulkRequests: Boolean,
662+
var requestTimeoutMillis: Long?,
643663
var json: Json
644664
)
645665

src/commonMain/kotlin/com.adamratzman.spotify/SpotifyApi.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ internal const val base = "https://api.spotify.com/v1"
5858
* @property defaultLimit The default amount of objects to retrieve in one request
5959
* @property json The Json serializer/deserializer instance
6060
* @property logger The Spotify event logger
61+
* @property requestTimeoutMillis The maximum time, in milliseconds, before terminating an http request
6162
*
6263
*/
6364
sealed class SpotifyApi<T : SpotifyApi<T, B>, B : ISpotifyApiBuilder<T, B>>(
@@ -72,6 +73,7 @@ sealed class SpotifyApi<T : SpotifyApi<T, B>, B : ISpotifyApiBuilder<T, B>>(
7273
testTokenValidity: Boolean,
7374
var defaultLimit: Int,
7475
var allowBulkRequests: Boolean,
76+
var requestTimeoutMillis: Long?,
7577
var json: Json
7678
) {
7779
var useCache = useCache
@@ -354,6 +356,7 @@ class SpotifyAppApi internal constructor(
354356
testTokenValidity: Boolean,
355357
defaultLimit: Int,
356358
allowBulkRequests: Boolean,
359+
requestTimeoutMillis: Long?,
357360
json: Json
358361
) : SpotifyApi<SpotifyAppApi, SpotifyAppApiBuilder>(
359362
clientId,
@@ -367,6 +370,7 @@ class SpotifyAppApi internal constructor(
367370
testTokenValidity,
368371
defaultLimit,
369372
allowBulkRequests,
373+
requestTimeoutMillis,
370374
json
371375
) {
372376
constructor(
@@ -386,6 +390,7 @@ class SpotifyAppApi internal constructor(
386390
options.testTokenValidity,
387391
options.defaultLimit,
388392
options.allowBulkRequests,
393+
options.requestTimeoutMillis,
389394
options.json
390395
)
391396

@@ -464,6 +469,7 @@ class SpotifyClientApi internal constructor(
464469
testTokenValidity: Boolean,
465470
defaultLimit: Int,
466471
allowBulkRequests: Boolean,
472+
requestTimeoutMillis: Long?,
467473
json: Json
468474
) : SpotifyApi<SpotifyClientApi, SpotifyClientApiBuilder>(
469475
clientId,
@@ -477,6 +483,7 @@ class SpotifyClientApi internal constructor(
477483
testTokenValidity,
478484
defaultLimit,
479485
allowBulkRequests,
486+
requestTimeoutMillis,
480487
json
481488
) {
482489
constructor(
@@ -498,6 +505,7 @@ class SpotifyClientApi internal constructor(
498505
options.testTokenValidity,
499506
options.defaultLimit,
500507
options.allowBulkRequests,
508+
options.requestTimeoutMillis,
501509
options.json
502510
)
503511

src/commonMain/kotlin/com.adamratzman.spotify/SpotifyExceptions.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,6 @@ sealed class SpotifyException(message: String, cause: Throwable? = null) : Excep
4040
class ParseException(message: String, cause: Throwable? = null) : SpotifyException(message, cause)
4141

4242
class AuthenticationException(message: String, cause: Throwable? = null) : SpotifyException(message, cause)
43+
44+
class TimeoutException(message: String, cause: Throwable? = null) : SpotifyException(message, cause)
4345
}

src/commonMain/kotlin/com.adamratzman.spotify/http/Endpoints.kt

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import com.adamratzman.spotify.SpotifyApi
55
import com.adamratzman.spotify.SpotifyClientApi
66
import com.adamratzman.spotify.SpotifyException
77
import com.adamratzman.spotify.SpotifyException.BadRequestException
8+
import com.adamratzman.spotify.SpotifyException.TimeoutException
89
import com.adamratzman.spotify.SpotifyRestAction
910
import com.adamratzman.spotify.SpotifyRestActionPaging
1011
import com.adamratzman.spotify.base
@@ -15,6 +16,8 @@ import com.adamratzman.spotify.models.serialization.toObject
1516
import com.adamratzman.spotify.utils.ConcurrentHashMap
1617
import com.adamratzman.spotify.utils.getCurrentTimeMs
1718
import kotlin.math.ceil
19+
import kotlinx.coroutines.TimeoutCancellationException
20+
import kotlinx.coroutines.withTimeout
1821

1922
abstract class SpotifyEndpoint(val api: SpotifyApi<*, *>) {
2023
val cache = SpotifyCache()
@@ -61,31 +64,37 @@ abstract class SpotifyEndpoint(val api: SpotifyApi<*, *>) {
6164
cache -= spotifyRequest
6265
}
6366

64-
return try {
65-
val document = createConnection(url, body, method, contentType).execute(
66-
cacheState?.eTag?.let {
67-
listOf(HttpHeader("If-None-Match", it))
68-
}
69-
)
70-
71-
handleResponse(document, cacheState, spotifyRequest, retry202) ?: execute(
72-
url,
73-
body,
74-
method,
75-
false,
76-
contentType
77-
)
78-
} catch (e: BadRequestException) {
79-
if (e.statusCode?.equals(401) == true && !attemptedRefresh) {
80-
execute(
81-
url,
82-
body,
83-
method,
84-
retry202,
85-
contentType,
86-
true
87-
)
88-
} else throw e
67+
try {
68+
return withTimeout(api.requestTimeoutMillis ?: 100 * 1000L) {
69+
try {
70+
val document = createConnection(url, body, method, contentType).execute(
71+
cacheState?.eTag?.let {
72+
listOf(HttpHeader("If-None-Match", it))
73+
}
74+
)
75+
76+
handleResponse(document, cacheState, spotifyRequest, retry202) ?: execute(
77+
url,
78+
body,
79+
method,
80+
false,
81+
contentType
82+
)
83+
} catch (e: BadRequestException) {
84+
if (e.statusCode?.equals(401) == true && !attemptedRefresh) {
85+
execute(
86+
url,
87+
body,
88+
method,
89+
retry202,
90+
contentType,
91+
true
92+
)
93+
} else throw e
94+
}
95+
}
96+
} catch (e: TimeoutCancellationException) {
97+
throw TimeoutException(e.message ?: "The request $spotifyRequest timed out after (${api.requestTimeoutMillis ?: 100 * 1000}ms.", e)
8998
}
9099
}
91100

0 commit comments

Comments
 (0)