diff --git a/backend/deps.edn b/backend/deps.edn index 99327c3135..c72e3351aa 100644 --- a/backend/deps.edn +++ b/backend/deps.edn @@ -27,6 +27,9 @@ io.vertx/vertx-pg-client {:mvn/version "3.8.4"} ;; end verx deps + io.github.resilience4j/resilience4j-core {:mvn/version "1.2.0"} + io.github.resilience4j/resilience4j-ratelimiter {:mvn/version "1.2.0"} + lambdaisland/uri {:mvn/version "1.1.0"} danlentz/clj-uuid {:mvn/version "0.1.9"} diff --git a/backend/src/uxbox/http.clj b/backend/src/uxbox/http.clj index 288c5cea7c..a72263bd88 100644 --- a/backend/src/uxbox/http.clj +++ b/backend/src/uxbox/http.clj @@ -16,6 +16,7 @@ [uxbox.http.session :as session] [uxbox.http.handlers :as handlers] [uxbox.http.debug :as debug] + [uxbox.http.ratelimit :as rl] [uxbox.http.ws :as ws] [vertx.core :as vc] [vertx.http :as vh] @@ -44,6 +45,18 @@ interceptors/format-response-body (vxi/errors errors/handle)] + login-handler (rl/ratelimit handlers/login-handler + {:limit 10 + :period 1000 + :timeout 200 + :name "login-handler"}) + + echo-handler (rl/ratelimit handlers/echo-handler + {:limit 100 + :period 1000 + :timeout 1000 + :name "echo-handler"}) + routes [["/sub/:file-id" {:interceptors [(vxi/cookies) (vxi/cors cors-opts) interceptors/format-response-body @@ -51,8 +64,8 @@ :get ws/handler}] ["/api" {:interceptors interceptors} - ["/echo" {:all handlers/echo-handler}] - ["/login" {:post handlers/login-handler}] + ["/echo" {:all echo-handler}] + ["/login" {:post login-handler}] ["/logout" {:post handlers/logout-handler}] ["/register" {:post handlers/register-handler}] ["/debug" diff --git a/backend/src/uxbox/http/ratelimit.clj b/backend/src/uxbox/http/ratelimit.clj new file mode 100644 index 0000000000..f465c81c4d --- /dev/null +++ b/backend/src/uxbox/http/ratelimit.clj @@ -0,0 +1,50 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; This Source Code Form is "Incompatible With Secondary Licenses", as +;; defined by the Mozilla Public License, v. 2.0. +;; +;; Copyright (c) 2016-2019 Andrey Antukh + +(ns uxbox.http.ratelimit + "Rate limit" + (:require + [clojure.spec.alpha :as s] + [uxbox.common.exceptions :as ex] + [uxbox.core :refer [system]] + [vertx.core :as vc] + [promesa.core :as p] + [promesa.exec :as pe]) + (:import + io.github.resilience4j.ratelimiter.RateLimiter + io.github.resilience4j.ratelimiter.RateLimiterConfig + io.github.resilience4j.ratelimiter.RateLimiterRegistry + java.util.concurrent.CompletableFuture + java.util.function.Supplier + java.time.Duration)) + +;; --- Rate Limiter + +(def ^:private registry (RateLimiterRegistry/ofDefaults)) + +(defn- opts->rate-limiter-config + [{:keys [limit period timeout] :as opts}] + (let [custom (RateLimiterConfig/custom)] + (.limitRefreshPeriod custom (Duration/ofMillis period)) + (.limitForPeriod custom limit) + (.timeoutDuration custom (Duration/ofMillis timeout)) + (.build custom))) + +(defn ratelimit + [f {:keys [name] :as opts}] + (let [config (opts->rate-limiter-config opts) + rl (.rateLimiter registry name config)] + (fn [& args] + (let [ctx (vc/get-or-create-context system)] + (-> (pe/run! #(when-not (.acquirePermission rl 1) + (ex/raise :type :ratelimit + :code :timeout + :context {:name name}))) + (vc/handle-on-context) + (p/bind (fn [_] (p/do! (apply f args)))))))))