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() }