44 lines
1.7 KiB
Kotlin
44 lines
1.7 KiB
Kotlin
package org.moire.ultrasonic.api.subsonic.interceptors
|
|
|
|
import java.util.concurrent.TimeUnit.MILLISECONDS
|
|
import okhttp3.Interceptor
|
|
import okhttp3.Interceptor.Chain
|
|
import okhttp3.Response
|
|
|
|
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 {
|
|
override fun intercept(chain: Chain): Response {
|
|
val originalRequest = chain.request()
|
|
val headers = originalRequest.headers()
|
|
return if (headers.names().contains("Range")) {
|
|
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()
|
|
}
|