Increase read timeout.

When range interceptor modifies request it also increases read timeout,
that allows server to do transcoding of the response and skip bytes
to offset value.

Signed-off-by: Yahor Berdnikau <egorr.berd@gmail.com>
This commit is contained in:
Yahor Berdnikau 2017-11-05 21:04:20 +01:00
parent 72333f245f
commit c8c8766b55
3 changed files with 22 additions and 6 deletions

View File

@ -32,4 +32,4 @@ abstract class BaseInterceptorTest {
.url(mockWebServerRule.mockWebServer.url("/"))
.also { additionalParams(it) }
.build()
}
}

View File

@ -31,7 +31,7 @@ class RangeHeaderInterceptorTest : BaseInterceptorTest() {
}
@Test
fun `Should not add range header if request doens't contain it`() {
fun `Should not add range header if request doesnt contain it`() {
mockWebServerRule.mockWebServer.enqueue(MockResponse())
val request = createRequest { }
@ -56,4 +56,4 @@ class RangeHeaderInterceptorTest : BaseInterceptorTest() {
executedRequest.headers.names() `should contain` "Range"
executedRequest.headers["Range"]!! `should equal to` "bytes=$offset-"
}
}
}

View File

@ -3,10 +3,17 @@ package org.moire.ultrasonic.api.subsonic.interceptors
import okhttp3.Interceptor
import okhttp3.Interceptor.Chain
import okhttp3.Response
import java.util.concurrent.TimeUnit.MILLISECONDS
internal const val SOCKET_READ_TIMEOUT_DOWNLOAD = 30 * 1000
// Allow 20 seconds extra timeout pear MB offset.
internal const val TIMEOUT_MILLIS_PER_OFFSET_BYTE = 0.02
/**
* Modifies request "Range" header to be according to HTTP standard.
*
* Also increases read timeout to allow server to transcode response and offset it.
*
* See [range rfc](https://tools.ietf.org/html/rfc7233).
*/
internal class RangeHeaderInterceptor : Interceptor {
@ -14,12 +21,21 @@ internal class RangeHeaderInterceptor : Interceptor {
val originalRequest = chain.request()
val headers = originalRequest.headers()
return if (headers.names().contains("Range")) {
val offset = "bytes=${headers["Range"]}-"
chain.proceed(originalRequest.newBuilder()
val offsetValue = headers["Range"] ?: "0"
val offset = "bytes=$offsetValue-"
chain.withReadTimeout(getReadTimeout(offsetValue.toInt()), MILLISECONDS)
.proceed(originalRequest.newBuilder()
.removeHeader("Range").addHeader("Range", offset)
.build())
} else {
chain.proceed(originalRequest)
}
}
}
// Set socket read timeout. Note: The timeout increases as the offset gets larger. This is
// to avoid the thrashing effect seen when offset is combined with transcoding/downsampling
// on the server. In that case, the server uses a long time before sending any data,
// causing the client to time out.
private fun getReadTimeout(offset: Int)
= (SOCKET_READ_TIMEOUT_DOWNLOAD + offset * TIMEOUT_MILLIS_PER_OFFSET_BYTE).toInt()
}