From dd88f473111518343129757c1b79f0996e426fad Mon Sep 17 00:00:00 2001 From: paulbrejla Date: Fri, 1 Dec 2023 11:18:14 +0100 Subject: [PATCH] Adds global rate limit filter --- .../holidays/rest/impl/RateLimitFilter.kt | 45 +++++++++++++++++++ src/main/resources/application.yml | 2 + 2 files changed, 47 insertions(+) create mode 100644 src/main/kotlin/de/paulbrejla/holidays/rest/impl/RateLimitFilter.kt diff --git a/src/main/kotlin/de/paulbrejla/holidays/rest/impl/RateLimitFilter.kt b/src/main/kotlin/de/paulbrejla/holidays/rest/impl/RateLimitFilter.kt new file mode 100644 index 0000000..8f5db65 --- /dev/null +++ b/src/main/kotlin/de/paulbrejla/holidays/rest/impl/RateLimitFilter.kt @@ -0,0 +1,45 @@ +package de.paulbrejla.holidays.rest.impl + +import de.paulbrejla.holidays.application.api.RateLimitService +import org.springframework.beans.factory.annotation.Value +import org.springframework.http.HttpStatus +import org.springframework.stereotype.Component +import org.springframework.web.server.ResponseStatusException +import javax.servlet.Filter +import javax.servlet.FilterChain +import javax.servlet.ServletException +import javax.servlet.ServletRequest +import javax.servlet.ServletResponse +import javax.servlet.http.HttpServletResponse + +@Component +class RateLimitFilter(val rateLimitService: RateLimitService) : Filter { + @Value("\${rateLimit.globalBucket.id}") + var globalBucketId: String = "" + + @Value("\${rateLimit.globalBucket.capacity}") + var globalBucketCapacity: Long = 0 + + + override fun doFilter(request: ServletRequest?, response: ServletResponse?, chain: FilterChain?) { + if (!this.shouldFulfillRequestWithinRateLimit(globalBucketId)) { // Maybe add additional x-rate-limit headers later. + (response as HttpServletResponse).apply { + this.status = HttpStatus.TOO_MANY_REQUESTS.value() + this.addHeader("X-RateLimit-Limit", globalBucketCapacity.toString()) + this.addHeader("X-RateLimit-Remaining", "0") + } + } else { + chain!!.doFilter(request, response) + } + } + + private fun shouldFulfillRequestWithinRateLimit(bucketId: String): Boolean { + return rateLimitService.resolveBucket(bucketId) + .tryConsumeAndReturnRemaining(1).run { + if (this.isConsumed) + true + else + false + } + } +} \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 092614e..2c8760f 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -10,6 +10,8 @@ spring: path: /h2-console server: port: ${SERVER_PORT:80} + error: + include-stacktrace: never loader: source: ${LOADER_SOURCE} remoteURL: ${LOADER_REMOTE_URL}