diff --git a/gbcs-server/src/main/kotlin/net/woggioni/gbcs/server/GradleBuildCacheServer.kt b/gbcs-server/src/main/kotlin/net/woggioni/gbcs/server/GradleBuildCacheServer.kt index c69b82e..e5072a4 100644 --- a/gbcs-server/src/main/kotlin/net/woggioni/gbcs/server/GradleBuildCacheServer.kt +++ b/gbcs-server/src/main/kotlin/net/woggioni/gbcs/server/GradleBuildCacheServer.kt @@ -309,8 +309,33 @@ class GradleBuildCacheServer(private val cfg: Configuration) { } val pipeline = ch.pipeline() cfg.connection.also { conn -> - pipeline.addLast(IdleStateHandler(false, conn.readTimeout.toMillis(), conn.writeTimeout.toMillis(), 0, TimeUnit.MILLISECONDS)) - pipeline.addLast(IdleStateHandler(true, conn.readIdleTimeout.toMillis(), conn.writeIdleTimeout.toMillis(), conn.idleTimeout.toMillis(), TimeUnit.MILLISECONDS)) + val readTimeout = conn.readTimeout.toMillis() + val writeTimeout = conn.writeTimeout.toMillis() + if(readTimeout > 0 || writeTimeout > 0) { + pipeline.addLast( + IdleStateHandler( + false, + readTimeout, + writeTimeout, + 0, + TimeUnit.MILLISECONDS + ) + ) + } + val readIdleTimeout = conn.readIdleTimeout.toMillis() + val writeIdleTimeout = conn.writeIdleTimeout.toMillis() + val idleTimeout = conn.idleTimeout.toMillis() + if(readIdleTimeout > 0 || writeIdleTimeout > 0 || idleTimeout > 0) { + pipeline.addLast( + IdleStateHandler( + true, + readIdleTimeout, + writeIdleTimeout, + idleTimeout, + TimeUnit.MILLISECONDS + ) + ) + } } pipeline.addLast(object : ChannelInboundHandlerAdapter() { override fun userEventTriggered(ctx: ChannelHandlerContext, evt: Any) { diff --git a/gbcs-server/src/main/kotlin/net/woggioni/gbcs/server/configuration/Parser.kt b/gbcs-server/src/main/kotlin/net/woggioni/gbcs/server/configuration/Parser.kt index f38467b..7052f52 100644 --- a/gbcs-server/src/main/kotlin/net/woggioni/gbcs/server/configuration/Parser.kt +++ b/gbcs-server/src/main/kotlin/net/woggioni/gbcs/server/configuration/Parser.kt @@ -114,9 +114,9 @@ object Parser { "connection" -> { val writeTimeout = child.renderAttribute("write-timeout") - ?.let(Duration::parse) ?: Duration.of(10, ChronoUnit.SECONDS) + ?.let(Duration::parse) ?: Duration.of(0, ChronoUnit.SECONDS) val readTimeout = child.renderAttribute("read-timeout") - ?.let(Duration::parse) ?: Duration.of(10, ChronoUnit.SECONDS) + ?.let(Duration::parse) ?: Duration.of(0, ChronoUnit.SECONDS) val idleTimeout = child.renderAttribute("idle-timeout") ?.let(Duration::parse) ?: Duration.of(30, ChronoUnit.SECONDS) val readIdleTimeout = child.renderAttribute("read-idle-timeout") diff --git a/gbcs-server/src/main/kotlin/net/woggioni/gbcs/server/throttling/ThrottlingHandler.kt b/gbcs-server/src/main/kotlin/net/woggioni/gbcs/server/throttling/ThrottlingHandler.kt index ea70a20..494ce3e 100644 --- a/gbcs-server/src/main/kotlin/net/woggioni/gbcs/server/throttling/ThrottlingHandler.kt +++ b/gbcs-server/src/main/kotlin/net/woggioni/gbcs/server/throttling/ThrottlingHandler.kt @@ -11,8 +11,10 @@ import net.woggioni.gbcs.api.Configuration import net.woggioni.gbcs.common.contextLogger import net.woggioni.gbcs.server.GradleBuildCacheServer import net.woggioni.jwo.Bucket +import net.woggioni.jwo.LongMath import java.net.InetSocketAddress import java.time.Duration +import java.time.temporal.ChronoUnit import java.util.concurrent.TimeUnit @@ -54,24 +56,30 @@ class ThrottlingHandler(cfg: Configuration) : if (buckets.isEmpty()) { return super.channelRead(ctx, msg) } else { - var nextAttempt = Long.MAX_VALUE - for (bucket in buckets) { - val bucketNextAttempt = bucket.removeTokensWithEstimate(1) - if (bucketNextAttempt < 0) { - return super.channelRead(ctx, msg) - } else if (bucketNextAttempt < nextAttempt) { - nextAttempt = bucketNextAttempt - } - } - val waitDuration = Duration.ofNanos(nextAttempt) - if (waitDuration < waitThreshold) { - ctx.executor().schedule({ - ctx.fireChannelRead(msg) - }, waitDuration.toNanos(), TimeUnit.NANOSECONDS) - } else { - sendThrottledResponse(ctx, waitDuration) + handleBuckets(buckets, ctx, msg, true) + } + } + + private fun handleBuckets(buckets : List, ctx : ChannelHandlerContext, msg : Any, delayResponse : Boolean) { + var nextAttempt = -1L + for (bucket in buckets) { + val bucketNextAttempt = bucket.removeTokensWithEstimate(1) + if (bucketNextAttempt > nextAttempt) { + nextAttempt = bucketNextAttempt } } + if(nextAttempt < 0) { + super.channelRead(ctx, msg) + return + } + val waitDuration = Duration.of(LongMath.ceilDiv(nextAttempt, 100_000_000L) * 100L, ChronoUnit.MILLIS) + if (delayResponse && waitDuration < waitThreshold) { + ctx.executor().schedule({ + handleBuckets(buckets, ctx, msg, false) + }, waitDuration.toMillis(), TimeUnit.MILLISECONDS) + } else { + sendThrottledResponse(ctx, waitDuration) + } } private fun sendThrottledResponse(ctx: ChannelHandlerContext, retryAfter: Duration) { @@ -80,7 +88,12 @@ class ThrottlingHandler(cfg: Configuration) : HttpResponseStatus.TOO_MANY_REQUESTS ) response.headers()[HttpHeaderNames.CONTENT_LENGTH] = 0 - response.headers()[HttpHeaderNames.RETRY_AFTER] = retryAfter.seconds + retryAfter.seconds.takeIf { + it > 0 + }?.let { + response.headers()[HttpHeaderNames.RETRY_AFTER] = retryAfter.seconds + } + ctx.writeAndFlush(response) } } \ No newline at end of file diff --git a/gbcs-server/src/main/resources/net/woggioni/gbcs/server/schema/gbcs.xsd b/gbcs-server/src/main/resources/net/woggioni/gbcs/server/schema/gbcs.xsd index 5809ed4..cade632 100644 --- a/gbcs-server/src/main/resources/net/woggioni/gbcs/server/schema/gbcs.xsd +++ b/gbcs-server/src/main/resources/net/woggioni/gbcs/server/schema/gbcs.xsd @@ -34,8 +34,8 @@ - - + + diff --git a/gbcs-server/src/test/kotlin/net/woggioni/gbcs/server/test/TlsServerTest.kt b/gbcs-server/src/test/kotlin/net/woggioni/gbcs/server/test/TlsServerTest.kt index b6b93c3..e753a53 100644 --- a/gbcs-server/src/test/kotlin/net/woggioni/gbcs/server/test/TlsServerTest.kt +++ b/gbcs-server/src/test/kotlin/net/woggioni/gbcs/server/test/TlsServerTest.kt @@ -133,4 +133,19 @@ class TlsServerTest : AbstractTlsServerTest() { val response: HttpResponse = client.send(requestBuilder.build(), HttpResponse.BodyHandlers.ofString()) Assertions.assertEquals(HttpResponseStatus.FORBIDDEN.code(), response.statusCode()) } + + @Test + @Order(8) + fun traceAsAnonymousUser() { + val client: HttpClient = getHttpClient(null) + val requestBuilder = newRequestBuilder("").method( + "TRACE", + HttpRequest.BodyPublishers.ofByteArray("sfgsdgfaiousfiuhsd".toByteArray()) + ) + + val response: HttpResponse = + client.send(requestBuilder.build(), HttpResponse.BodyHandlers.ofByteArray()) + Assertions.assertEquals(HttpResponseStatus.OK.code(), response.statusCode()) + println(String(response.body())) + } } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index cf33dcb..c2612f6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ org.gradle.caching=true gbcs.version = 0.0.11 -lys.version = 2025.01.24 +lys.version = 2025.01.25 gitea.maven.url = https://gitea.woggioni.net/api/packages/woggioni/maven docker.registry.url=gitea.woggioni.net diff --git a/settings.gradle b/settings.gradle index 9bbee00..5386481 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,6 +1,5 @@ pluginManagement { repositories { - mavenLocal() maven { url = getProperty('gitea.maven.url') }