Skip to content

Commit e35cbef

Browse files
committed
Make requests with body one shot
The proxer api can't handle retransmission of bodies and this way we enforce it to never happen.
1 parent 67a3f06 commit e35cbef

6 files changed

Lines changed: 103 additions & 3 deletions

File tree

gradle/dependencies.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ dependencies {
1010
kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshiVersion"
1111

1212
testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion"
13+
testImplementation "org.junit.jupiter:junit-jupiter-params:$junitVersion"
1314
testImplementation "org.assertj:assertj-core:$assertjVersion"
1415
testImplementation "org.mockito:mockito-core:$mockitoVersion"
1516
testImplementation "com.squareup.okhttp3:okhttp-tls:$okHttpVersion"

gradle/versions.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ ext {
1515
buildConfigPluginVersion = "1.1.8"
1616

1717
retrofitVersion = "2.5.0"
18-
okHttpVersion = "3.13.1"
18+
okHttpVersion = "3.14.0"
1919
moshiVersion = "1.8.0"
2020
moshiLazyAdaptersVersion = "2.2"
2121

2222
junitVersion = "5.4.0"
2323
assertjVersion = "3.11.1"
24-
mockitoVersion = "2.24.0"
24+
mockitoVersion = "2.25.1"
2525
}

library/src/main/kotlin/me/proxer/library/ProxerApi.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import me.proxer.library.internal.adapter.UnitAdapter
3636
import me.proxer.library.internal.interceptor.HeaderInterceptor
3737
import me.proxer.library.internal.interceptor.HttpsEnforcingInterceptor
3838
import me.proxer.library.internal.interceptor.LoginTokenInterceptor
39+
import me.proxer.library.internal.interceptor.OneShotInterceptor
3940
import me.proxer.library.util.ProxerUrls
4041
import okhttp3.CertificatePinner
4142
import okhttp3.OkHttpClient
@@ -214,7 +215,8 @@ class ProxerApi private constructor(retrofit: Retrofit) {
214215
0, listOf(
215216
HeaderInterceptor(apiKey, buildUserAgent()),
216217
LoginTokenInterceptor(buildLoginTokenManager()),
217-
HttpsEnforcingInterceptor()
218+
HttpsEnforcingInterceptor(),
219+
OneShotInterceptor()
218220
)
219221
)
220222
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package me.proxer.library.internal
2+
3+
import okhttp3.RequestBody
4+
import okio.BufferedSink
5+
6+
/**
7+
* @author Ruben Gees
8+
*/
9+
internal class OneShotDelegatingRequestBody(private val delegate: RequestBody) : RequestBody() {
10+
11+
override fun contentType() = delegate.contentType()
12+
override fun contentLength() = delegate.contentLength()
13+
override fun writeTo(sink: BufferedSink) = delegate.writeTo(sink)
14+
override fun isDuplex() = delegate.isDuplex
15+
16+
override fun isOneShot() = true
17+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package me.proxer.library.internal.interceptor
2+
3+
import me.proxer.library.internal.OneShotDelegatingRequestBody
4+
import me.proxer.library.util.ProxerUrls
5+
import okhttp3.Interceptor
6+
import okhttp3.Response
7+
8+
/**
9+
* @author Ruben Gees
10+
*/
11+
internal class OneShotInterceptor : Interceptor {
12+
13+
override fun intercept(chain: Interceptor.Chain): Response {
14+
val oldRequest = chain.request()
15+
val body = oldRequest.body()
16+
17+
if (ProxerUrls.hasProxerHost(oldRequest.url())) {
18+
val newRequest = if (body != null) {
19+
oldRequest.newBuilder()
20+
.method(oldRequest.method(), OneShotDelegatingRequestBody(body))
21+
.build()
22+
} else {
23+
oldRequest
24+
}
25+
26+
return chain.proceed(newRequest)
27+
} else {
28+
throw IllegalArgumentException("Only use ProxerLib's OkHttp instance with Proxer.Me URLs!")
29+
}
30+
}
31+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package me.proxer.library.internal.interceptor
2+
3+
import okhttp3.Interceptor
4+
import okhttp3.MediaType
5+
import okhttp3.Request
6+
import okhttp3.RequestBody
7+
import okhttp3.Response
8+
import org.assertj.core.api.Assertions.assertThat
9+
import org.junit.jupiter.api.Test
10+
import org.mockito.ArgumentCaptor
11+
import org.mockito.Mockito.`when`
12+
import org.mockito.Mockito.mock
13+
import org.mockito.Mockito.notNull
14+
import org.mockito.Mockito.verify
15+
16+
class OneShotInterceptorTest {
17+
18+
private val interceptor = OneShotInterceptor()
19+
private val chain = mock(Interceptor.Chain::class.java)
20+
21+
@Test
22+
fun testOneShotSetForRequestsWithBodies() {
23+
val requestCaptor = ArgumentCaptor.forClass(Request::class.java)
24+
val body = RequestBody.create(MediaType.parse("text/plain"), "hello")
25+
val request = Request.Builder().post(body).url("https://proxer.me/fake").build()
26+
27+
`when`(chain.request()).thenReturn(request)
28+
`when`(chain.proceed(notNull())).thenReturn(mock(Response::class.java))
29+
30+
interceptor.intercept(chain)
31+
32+
verify(chain).proceed(requestCaptor.capture())
33+
assertThat(requestCaptor.value.body()?.isOneShot).isTrue()
34+
}
35+
36+
@Test
37+
fun testNoModificationForRequestsWithoutBodies() {
38+
val requestCaptor = ArgumentCaptor.forClass(Request::class.java)
39+
val request = Request.Builder().url("https://proxer.me/fake").build()
40+
41+
`when`(chain.request()).thenReturn(request)
42+
`when`(chain.proceed(notNull())).thenReturn(mock(Response::class.java))
43+
44+
interceptor.intercept(chain)
45+
46+
verify(chain).proceed(requestCaptor.capture())
47+
assertThat(requestCaptor.value).isEqualTo(request)
48+
}
49+
}

0 commit comments

Comments
 (0)