Skip to content

Commit 6446ea0

Browse files
authored
Merge pull request #191 from adamint/dev
refresh fix
2 parents dffcac8 + c730ace commit 6446ea0

File tree

8 files changed

+17523
-31
lines changed

8 files changed

+17523
-31
lines changed

build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ plugins {
1515
}
1616

1717
group = "com.adamratzman"
18-
version = "3.0.01"
18+
version = "3.0.02"
1919

2020
java {
2121
withSourcesJar()
@@ -70,7 +70,7 @@ kotlin {
7070
dependencies {
7171
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutineVersion")
7272
implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serializationVersion")
73-
implementation("io.ktor:ktor-client-apache:$ktorVersion")
73+
implementation("io.ktor:ktor-client-okhttp:$ktorVersion")
7474
implementation(kotlin("stdlib-jdk8"))
7575
}
7676
}

samples/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ plugins {
33
}
44

55
group 'com.adamratzman'
6-
version '3.0.0'
6+
version '3.0.1'
77

88
repositories {
99
jcenter()

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: 35 additions & 26 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()
@@ -48,7 +51,7 @@ abstract class SpotifyEndpoint(val api: SpotifyApi<*, *>) {
4851
contentType: String? = null,
4952
attemptedRefresh: Boolean = false
5053
): String {
51-
if (api is SpotifyClientApi && getCurrentTimeMs() >= api.expireTime) {
54+
if (getCurrentTimeMs() >= api.expireTime) {
5255
if (!api.automaticRefresh) throw SpotifyException.AuthenticationException("The access token has expired.")
5356
else api.refreshToken()
5457
}
@@ -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

src/commonTest/kotlin/com.adamratzman/spotify/public/PublicTracksAPITest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,10 @@ class PublicTracksAPITest : Spek({
4343
assertFailsWith<SpotifyException.BadRequestException> { t.getAudioFeatures("bad track").complete() }
4444
}
4545
it("known track") {
46-
assertEquals("0.0589", t.getAudioFeatures("6AH3IbS61PiabZYKVBqKAk").complete().acousticness.toString())
46+
assertEquals("0.0592", t.getAudioFeatures("6AH3IbS61PiabZYKVBqKAk").complete().acousticness.toString())
4747
}
4848
it("multiple tracks (all known)") {
49-
assertEquals(listOf(null, "0.0589"), t.getAudioFeatures("hkiuhi", "6AH3IbS61PiabZYKVBqKAk").complete().map { it?.acousticness?.toString() })
49+
assertEquals(listOf(null, "0.0592"), t.getAudioFeatures("hkiuhi", "6AH3IbS61PiabZYKVBqKAk").complete().map { it?.acousticness?.toString() })
5050
}
5151
it("mix of known and unknown tracks") {
5252
assertTrue(t.getAudioFeatures("bad track", "0o4jSZBxOQUiDKzMJSqR4x").complete().let {

0 commit comments

Comments
 (0)