Skip to content

Commit bf81e57

Browse files
committed
retry on 502, don't log when logger is disabled
1 parent 3077dfa commit bf81e57

File tree

4 files changed

+78
-48
lines changed

4 files changed

+78
-48
lines changed

src/main/kotlin/com/adamratzman/spotify/main/SpotifyAPI.kt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,8 @@ class SpotifyApiBuilder {
212212
url = "https://accounts.spotify.com/api/token",
213213
method = HttpRequestMethod.POST,
214214
body = "grant_type=authorization_code&code=$authorizationCode&redirect_uri=$redirectUri",
215-
contentType = "application/x-www-form-urlencoded"
215+
contentType = "application/x-www-form-urlencoded",
216+
api = null
216217
).execute(
217218
HttpHeader(
218219
"Authorization",
@@ -386,7 +387,8 @@ class SpotifyClientAPI internal constructor(
386387
url = "https://accounts.spotify.com/api/token",
387388
method = HttpRequestMethod.POST,
388389
body = "grant_type=refresh_token&refresh_token=${token.refreshToken ?: ""}",
389-
contentType = "application/x-www-form-urlencoded"
390+
contentType = "application/x-www-form-urlencoded",
391+
api = this
390392
).execute(HttpHeader("Authorization", "Basic ${"$clientId:$clientSecret".byteEncode()}")).body
391393
.toObjectNullable<Token>(null)
392394
if (tempToken?.accessToken == null) {
@@ -431,7 +433,8 @@ private fun getCredentialedToken(clientId: String, clientSecret: String) =
431433
url = "https://accounts.spotify.com/api/token",
432434
method = HttpRequestMethod.POST,
433435
body = "grant_type=client_credentials",
434-
contentType = "application/x-www-form-urlencoded"
436+
contentType = "application/x-www-form-urlencoded",
437+
api = null
435438
).execute(HttpHeader("Authorization", "Basic ${"$clientId:$clientSecret".byteEncode()}")).body
436439
.toObjectNullable<Token>(null)
437440

src/main/kotlin/com/adamratzman/spotify/main/SpotifyLogger.kt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,16 @@ package com.adamratzman.spotify.main
44
class SpotifyLogger(var enabled: Boolean) {
55
val redString = "\u001B[31m"
66
val resetString = "\u001B[0m"
7-
fun logInfo(message: String) = println("Spotify Logger Info: $message")
8-
fun logWarning(message: String) = println("${redString}Spotify Logger Warning: $message$resetString")
7+
fun logInfo(message: String) {
8+
if (enabled) println("Spotify Logger Info: $message")
9+
}
10+
fun logWarning(message: String) {
11+
if (enabled) println("${redString}Spotify Logger Warning: $message$resetString")
12+
}
913

1014
fun logError(fatal: Boolean, message: String?, throwable: Throwable?) {
15+
if (!enabled) return
16+
1117
val sb = StringBuilder("${redString}Spotify Logger ")
1218
sb.append(if (fatal) "FATAL" else "Error")
1319
if (message != null) sb.append(": $message")

src/main/kotlin/com/adamratzman/spotify/utils/Endpoints.kt

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,19 @@ abstract class SpotifyEndpoint(val api: SpotifyAPI) {
2525
}
2626

2727
internal fun delete(
28-
url: String,
29-
body: String? = null,
30-
contentType: String? = null
28+
url: String,
29+
body: String? = null,
30+
contentType: String? = null
3131
): String {
3232
return execute(url, body, HttpRequestMethod.DELETE, contentType = contentType)
3333
}
3434

3535
private fun execute(
36-
url: String,
37-
body: String? = null,
38-
method: HttpRequestMethod = HttpRequestMethod.GET,
39-
retry202: Boolean = true,
40-
contentType: String? = null
36+
url: String,
37+
body: String? = null,
38+
method: HttpRequestMethod = HttpRequestMethod.GET,
39+
retry202: Boolean = true,
40+
contentType: String? = null
4141
): String {
4242
if (api is SpotifyAppAPI && System.currentTimeMillis() >= api.expireTime) api.refreshToken()
4343

@@ -50,25 +50,25 @@ abstract class SpotifyEndpoint(val api: SpotifyAPI) {
5050
}
5151

5252
val document = createConnection(url, body, method, contentType).execute(
53-
cacheState?.eTag?.let {
54-
HttpHeader("If-None-Match", it)
55-
}
53+
cacheState?.eTag?.let {
54+
HttpHeader("If-None-Match", it)
55+
}
5656
)
5757

5858
return handleResponse(document, cacheState, spotifyRequest, retry202) ?: execute(
59-
url,
60-
body,
61-
method,
62-
false,
63-
contentType
59+
url,
60+
body,
61+
method,
62+
false,
63+
contentType
6464
)
6565
}
6666

6767
private fun handleResponse(
68-
document: HttpResponse,
69-
cacheState: CacheState?,
70-
spotifyRequest: SpotifyRequest,
71-
retry202: Boolean
68+
document: HttpResponse,
69+
cacheState: CacheState?,
70+
spotifyRequest: SpotifyRequest,
71+
retry202: Boolean
7272
): String? {
7373
val statusCode = document.responseCode
7474

@@ -82,7 +82,7 @@ abstract class SpotifyEndpoint(val api: SpotifyAPI) {
8282
document.headers.find { it.key == "Cache-Control" }?.also { cacheControlHeader ->
8383
if (api.useCache) {
8484
cache[spotifyRequest] = (cacheState ?: CacheState(
85-
responseBody, document.headers
85+
responseBody, document.headers
8686
.find { it.key == "ETag" }?.value
8787
)).update(cacheControlHeader.value)
8888
}
@@ -100,16 +100,17 @@ abstract class SpotifyEndpoint(val api: SpotifyAPI) {
100100
}
101101

102102
private fun createConnection(
103-
url: String,
104-
body: String? = null,
105-
method: HttpRequestMethod = HttpRequestMethod.GET,
106-
contentType: String? = null
103+
url: String,
104+
body: String? = null,
105+
method: HttpRequestMethod = HttpRequestMethod.GET,
106+
contentType: String? = null
107107
) = HttpConnection(
108-
url,
109-
method,
110-
body,
111-
contentType,
112-
HttpHeader("Authorization", "Bearer ${api.token.accessToken}")
108+
url,
109+
method,
110+
body,
111+
contentType,
112+
HttpHeader("Authorization", "Bearer ${api.token.accessToken}"),
113+
api = api
113114
)
114115

115116
internal fun <T> toAction(supplier: Supplier<T>) = SpotifyRestAction(api, supplier)
@@ -150,9 +151,9 @@ internal class SpotifyCache {
150151
}
151152

152153
internal data class SpotifyRequest(
153-
val url: String,
154-
val method: HttpRequestMethod,
155-
val body: String?
154+
val url: String,
155+
val method: HttpRequestMethod,
156+
val body: String?
156157
)
157158

158159
internal data class CacheState(val data: String, val eTag: String?, val expireBy: Long = 0) {
@@ -164,7 +165,7 @@ internal data class CacheState(val data: String, val eTag: String?, val expireBy
164165
val time = group?.getOrNull(1)?.toLongOrNull() ?: throw BadRequestException("Unable to match regex")
165166

166167
return this.copy(
167-
expireBy = System.currentTimeMillis() + 1000 * time
168+
expireBy = System.currentTimeMillis() + 1000 * time
168169
)
169170
}
170171
}

src/main/kotlin/com/adamratzman/spotify/utils/HttpConnection.kt

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,23 @@
11
/* Created by Adam Ratzman (2018) */
22
package com.adamratzman.spotify.utils
33

4+
import com.adamratzman.spotify.main.SpotifyAPI
45
import java.net.HttpURLConnection
56
import java.net.URL
67

78
internal enum class HttpRequestMethod { GET, POST, PUT, DELETE }
89
internal data class HttpHeader(val key: String, val value: String)
910

1011
internal class HttpConnection(
11-
private val url: String,
12-
private val method: HttpRequestMethod,
13-
private val body: String?,
14-
private val contentType: String?,
15-
private vararg val headers: HttpHeader
12+
private val url: String,
13+
private val method: HttpRequestMethod,
14+
private val body: String?,
15+
private val contentType: String?,
16+
private vararg val headers: HttpHeader,
17+
val api: SpotifyAPI? = null
1618
) {
1719

18-
fun execute(vararg additionalHeaders: HttpHeader?): HttpResponse {
20+
fun execute(vararg additionalHeaders: HttpHeader?, retryIf502: Boolean = true): HttpResponse {
1921
val connection = URL(url).openConnection() as HttpURLConnection
2022
connection.requestMethod = method.toString()
2123

@@ -36,18 +38,36 @@ internal class HttpConnection(
3638
connection.connect()
3739

3840
val responseCode = connection.responseCode
41+
42+
if (responseCode == 502 && retryIf502) {
43+
api?.logger?.logWarning("Received 502 (Invalid response) for URL $url and $this\nRetrying..")
44+
return execute(*additionalHeaders, retryIf502 = false)
45+
}
46+
47+
if (responseCode == 502 && !retryIf502) api?.logger?.logInfo("502 retry successful for $this")
48+
3949
val body = (connection.errorStream ?: connection.inputStream).bufferedReader().use {
4050
val text = it.readText()
4151
it.close()
4252
text
4353
}
4454

4555
return HttpResponse(
46-
responseCode = responseCode,
47-
body = body,
48-
headers = connection.headerFields.keys.filterNotNull().map { HttpHeader(it, connection.getHeaderField(it)) }
56+
responseCode = responseCode,
57+
body = body,
58+
headers = connection.headerFields.keys.filterNotNull().map { HttpHeader(it, connection.getHeaderField(it)) }
4959
).also { connection.disconnect() }
5060
}
61+
62+
override fun toString(): String {
63+
return """HttpConnection (
64+
|url=$url,
65+
|method=$method,
66+
|body=$body,
67+
|contentType=$contentType,
68+
|headers=${headers.toList()}
69+
| )""".trimMargin()
70+
}
5171
}
5272

5373
internal data class HttpResponse(val responseCode: Int, val body: String, val headers: List<HttpHeader>)

0 commit comments

Comments
 (0)