@@ -3,9 +3,10 @@ package com.adamratzman.spotify.http
3
3
4
4
import com.adamratzman.spotify.SpotifyAPI
5
5
import com.adamratzman.spotify.models.SpotifyRatelimitedException
6
- import java.io.OutputStreamWriter
7
- import java.net.HttpURLConnection
8
- import java.net.URL
6
+ import com.google.api.client.http.ByteArrayContent
7
+ import com.google.api.client.http.GenericUrl
8
+ import com.google.api.client.http.UrlEncodedContent
9
+ import com.google.api.client.http.javanet.NetHttpTransport
9
10
import java.util.concurrent.TimeUnit
10
11
11
12
enum class HttpRequestMethod { GET , POST , PUT , DELETE }
@@ -14,38 +15,56 @@ data class HttpHeader(val key: String, val value: String)
14
15
internal class HttpConnection (
15
16
private val url : String ,
16
17
private val method : HttpRequestMethod ,
17
- private val body : String? ,
18
+ private val bodyMap : Map <Any , Any >? ,
19
+ private val bodyString : String? ,
18
20
private val contentType : String? ,
19
21
private val headers : List <HttpHeader > = listOf(),
20
22
val api : SpotifyAPI ? = null
21
23
) {
22
24
25
+ companion object {
26
+ private val requestFactory = NetHttpTransport ().createRequestFactory()
27
+ }
28
+
23
29
fun execute (additionalHeaders : List <HttpHeader >? = null, retryIf502 : Boolean = true): HttpResponse {
24
- val connection = URL (url).openConnection() as HttpURLConnection
25
- connection.requestMethod = method.toString()
30
+ val genericUrl = GenericUrl (url)
31
+ val request = when (method) {
32
+ HttpRequestMethod .GET -> requestFactory.buildGetRequest(genericUrl)
33
+ HttpRequestMethod .DELETE -> requestFactory.buildDeleteRequest(genericUrl)
34
+ HttpRequestMethod .PUT , HttpRequestMethod .POST -> {
35
+ val content = if (contentType == " application/x-www-form-urlencoded" ) {
36
+ bodyMap?.let { body ->
37
+ UrlEncodedContent (body.map { it.key.toString() to it.value.toString() }.toMap())
38
+ } ? : ByteArrayContent .fromString(contentType, bodyString)
39
+ } else bodyString?.let { ByteArrayContent .fromString(contentType, bodyString) }
40
+
41
+ if (method == HttpRequestMethod .PUT ) requestFactory.buildPutRequest(genericUrl, content)
42
+ else requestFactory.buildPostRequest(genericUrl, content)
43
+ }
44
+ }
26
45
27
- contentType?. let { connection.setRequestProperty( " Content-Type " , contentType) }
46
+ if (method == HttpRequestMethod . DELETE && bodyString != null ) {
28
47
29
- ((additionalHeaders ? : listOf ()) + headers).forEach { (key, value) ->
30
- connection.setRequestProperty(key, value)
48
+ // request.content = ByteArrayContent.fromString(contentType, bodyString)
31
49
}
32
50
33
- if (body != null && method != HttpRequestMethod .GET ) {
34
- connection.doOutput = true
35
- // connection.setFixedLengthStreamingMode(body.toByteArray().size)
36
- val os = connection.outputStream
37
- val osw = OutputStreamWriter (os)
38
- osw.write(body)
39
- osw.apply {
40
- flush()
41
- close()
42
- }
43
- os.close()
51
+ if (contentType != null && method != HttpRequestMethod .POST ) request.headers.contentType = contentType
52
+
53
+ val allHeaders = (additionalHeaders ? : listOf ()) + headers
54
+
55
+ allHeaders.filter { ! it.key.equals(" Authorization" , true ) }.forEach { (key, value) ->
56
+ request.headers[key] = value
57
+ }
58
+
59
+ allHeaders.find { it.key.equals(" Authorization" , true ) }?.let { authHeader ->
60
+ request.headers.authorization = authHeader.value
44
61
}
45
62
46
- connection.connect()
63
+ request.throwExceptionOnExecuteError = false
47
64
48
- val responseCode = connection.responseCode
65
+ val response = request.execute()
66
+
67
+ val responseCode = response.statusCode
49
68
50
69
if (responseCode == 502 && retryIf502) {
51
70
api?.logger?.logError(false , " Received 502 (Invalid response) for URL $url and $this \n Retrying.." , null )
@@ -55,28 +74,28 @@ internal class HttpConnection(
55
74
if (responseCode == 502 && ! retryIf502) api?.logger?.logWarning(" 502 retry successful for $this " )
56
75
57
76
if (responseCode == 429 ) {
58
- val ratelimit = connection.getHeaderField( " Retry-After" ).toLong() + 1L
77
+ val ratelimit = response.headers[ " Retry-After" ] !! .toString( ).toLong() + 1L
59
78
if (api?.retryWhenRateLimited == true ) {
60
79
api.logger.logError(false , " The request ($url ) was ratelimited for $ratelimit seconds at ${System .currentTimeMillis()} " , null )
61
80
62
- var response : HttpResponse ? = null
81
+ var retryResponse : HttpResponse ? = null
63
82
api.executor.schedule({
64
- response = try {
83
+ retryResponse = try {
65
84
execute(additionalHeaders, retryIf502 = retryIf502)
66
85
} catch (e: Throwable ) {
67
86
throw e
68
87
}
69
88
}, ratelimit, TimeUnit .SECONDS ).get()
70
89
71
- return response !!
90
+ return retryResponse !!
72
91
} else throw SpotifyRatelimitedException (ratelimit)
73
92
}
74
93
75
- val body = (connection.errorStream ? : connection.inputStream). bufferedReader().use {
76
- val text = it .readText()
77
- it.close()
78
- text
79
- }
94
+ val contentReader = response.content. bufferedReader()
95
+ val body = contentReader .readText()
96
+
97
+ contentReader.close()
98
+ response.content.close()
80
99
81
100
if (responseCode == 401 && body.contains(" access token" ) &&
82
101
api != null && api.automaticRefresh) {
@@ -87,19 +106,19 @@ internal class HttpConnection(
87
106
return HttpResponse (
88
107
responseCode = responseCode,
89
108
body = body,
90
- headers = connection.headerFields.keys.filterNotNull(). map { HttpHeader (it, connection.getHeaderField(it) ) }
91
- ).also { connection .disconnect() }
109
+ headers = response.headers. map { HttpHeader (it.key, it.value?.toString() ? : " null " ) }
110
+ ).also { response .disconnect() }
92
111
}
93
112
94
113
override fun toString (): String {
95
114
return """ HttpConnection (
96
115
|url=$url ,
97
116
|method=$method ,
98
- |body=$body ,
117
+ |body=${bodyString ? : bodyMap} ,
99
118
|contentType=$contentType ,
100
119
|headers=${headers.toList()}
101
120
| )""" .trimMargin()
102
121
}
103
122
}
104
123
105
- internal data class HttpResponse (val responseCode : Int , val body : String , val headers : List <HttpHeader >)
124
+ internal data class HttpResponse (val responseCode : Int , val body : String , val headers : List <HttpHeader >)
0 commit comments