Skip to content

Commit f2cf545

Browse files
committed
fix: preserve typed subtitle throttling errors
1 parent a886f2b commit f2cf545

3 files changed

Lines changed: 17 additions & 1 deletion

File tree

src/main/kotlin/dev/typetype/server/Plugins.kt

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import io.ktor.http.HttpMethod
77
import io.ktor.http.HttpStatusCode
88
import io.ktor.serialization.kotlinx.json.json
99
import io.ktor.server.application.Application
10+
import io.ktor.server.application.ApplicationCall
1011
import io.ktor.server.application.install
1112
import io.ktor.server.plugins.calllogging.CallLogging
1213
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
@@ -17,6 +18,7 @@ import io.ktor.server.plugins.statuspages.StatusPages
1718
import io.ktor.server.request.path
1819
import io.ktor.server.response.respond
1920
import io.ktor.server.websocket.WebSockets
21+
import io.ktor.util.AttributeKey
2022
import kotlinx.serialization.json.Json
2123
import org.slf4j.LoggerFactory
2224
import kotlin.time.Duration.Companion.minutes
@@ -30,6 +32,7 @@ private const val PROXY_STORYBOARD_RATE_LIMIT = 1200
3032
private const val USER_DATA_RATE_LIMIT = 120
3133
private const val MAX_WEBSOCKET_FRAME_BYTES = 64L * 1024L * 1024L
3234
private val RATE_LIMIT_WINDOW = 1.minutes
35+
private val preserveTooManyRequestsBodyAttribute = AttributeKey<Unit>("preserveTooManyRequestsBody")
3336

3437
val EXTRACTION_ZONE = RateLimitName("extraction")
3538
val DEARROW_ZONE = RateLimitName("dearrow")
@@ -40,7 +43,6 @@ val PROXY_STORYBOARD_ZONE = RateLimitName("proxy-storyboard")
4043
val USER_DATA_ZONE = RateLimitName("user-data")
4144

4245
fun Application.configurePlugins(authService: AuthService) {
43-
val log = LoggerFactory.getLogger("RequestLogger")
4446
installRequestObservability()
4547
install(CallLogging) {
4648
format(::requestLogLine)
@@ -94,8 +96,18 @@ fun Application.configurePlugins(authService: AuthService) {
9496
requestKey { call -> userDataRateLimitKey(call, authService) }
9597
}
9698
}
99+
configureStatusPages()
100+
}
101+
102+
internal fun ApplicationCall.preserveTooManyRequestsBody() {
103+
attributes.put(preserveTooManyRequestsBodyAttribute, Unit)
104+
}
105+
106+
internal fun Application.configureStatusPages() {
107+
val log = LoggerFactory.getLogger("RequestLogger")
97108
install(StatusPages) {
98109
status(HttpStatusCode.TooManyRequests) { call, status ->
110+
if (call.attributes.contains(preserveTooManyRequestsBodyAttribute)) return@status
99111
if (!call.response.headers.contains(HttpHeaders.RetryAfter)) call.response.headers.append(HttpHeaders.RetryAfter, "60")
100112
call.respond(status, ErrorResponse("Too many requests", "rate_limited"))
101113
}

src/main/kotlin/dev/typetype/server/routes/YouTubeSubtitleResponse.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dev.typetype.server.routes
22

33
import dev.typetype.server.models.ErrorResponse
4+
import dev.typetype.server.preserveTooManyRequestsBody
45
import dev.typetype.server.services.YouTubeSubtitleContentResult
56
import io.ktor.http.ContentType
67
import io.ktor.http.HttpStatusCode
@@ -47,5 +48,6 @@ private suspend fun ApplicationCall.respondSubtitleError(
4748
message: String,
4849
code: String,
4950
) {
51+
if (status == HttpStatusCode.TooManyRequests) preserveTooManyRequestsBody()
5052
respond(status, ErrorResponse(message, code))
5153
}

src/test/kotlin/dev/typetype/server/YouTubeSubtitleProxyRoutesTest.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ class YouTubeSubtitleProxyRoutesTest {
5959
application {
6060
installRequestObservability()
6161
install(ContentNegotiation) { json(Json { encodeDefaults = true }) }
62+
configureStatusPages()
6263
routing { proxyRoutes(proxyService, service) }
6364
}
6465

@@ -68,6 +69,7 @@ class YouTubeSubtitleProxyRoutesTest {
6869
}
6970

7071
assertEquals(HttpStatusCode.TooManyRequests, response.status)
72+
assertEquals(null, response.headers[HttpHeaders.RetryAfter])
7173
assertEquals("subtitle-request-123", response.headers[REQUEST_ID_HEADER])
7274
assertEquals("subtitle-request-123", tokenRequest?.header(REQUEST_ID_HEADER))
7375
val body = response.bodyAsText()

0 commit comments

Comments
 (0)