From 164473cc75f6ed7690e6e9aacbab08cb314ea964 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Mon, 4 Dec 2017 22:44:13 +0200 Subject: [PATCH 1/5] :gel-wrap => :compile --- doc/ring/compiling_middleware.md | 22 +++++------ doc/ring/data_driven_middleware.md | 2 +- modules/reitit-core/src/reitit/impl.cljc | 3 ++ .../reitit-ring/src/reitit/ring/coercion.cljc | 6 +-- .../src/reitit/ring/middleware.cljc | 28 ++++++-------- test/cljc/reitit/impl_test.cljc | 3 ++ test/cljc/reitit/middleware_test.cljc | 38 +++++++++---------- 7 files changed, 51 insertions(+), 51 deletions(-) diff --git a/doc/ring/compiling_middleware.md b/doc/ring/compiling_middleware.md index 11b62b08..a64c61c3 100644 --- a/doc/ring/compiling_middleware.md +++ b/doc/ring/compiling_middleware.md @@ -4,9 +4,9 @@ The [dynamic extensions](dynamic_extensions.md) is a easy way to extend the syst But, we can do much better. As we know the exact route that middleware/interceptor is linked to, we can pass the (compiled) route information into the middleware/interceptor at creation-time. It can do local reasoning: extract and transform relevant data just for it and pass it into the actual request-handler via a closure - yielding much faster runtime processing. It can also decide not to mount itself by returning `nil`. Why mount a `wrap-enforce-roles` middleware for a route if there are no roles required for it? -To enable this we use [middleware records](data_driven_middleware.md) `:gen-wrap` key instead of the normal `:wrap`. `:gen-wrap` expects a function of `route-data router-opts => ?wrap`. +To enable this we use [middleware records](data_driven_middleware.md) `:compile` key instead of the normal `:wrap`. `:compile` expects a function of `route-data router-opts => ?wrap`. -To demonstrate the two approaches, below are response coercion middleware written as normal ring middleware function and as middleware record with `:gen-wrap`. +To demonstrate the two approaches, below are response coercion middleware written as normal ring middleware function and as middleware record with `:compile`. ## Normal Middleware @@ -57,15 +57,15 @@ To demonstrate the two approaches, below are response coercion middleware writte and :responses from route data, otherwise does not mount." (middleware/create {:name ::coerce-response - :gen-wrap (fn [{:keys [coercion responses opts]} _] - (if (and coercion responses) - (let [coercers (response-coercers coercion responses opts)] - (fn [handler] - (fn - ([request] - (coerce-response coercers request (handler request))) - ([request respond raise] - (handler request #(respond (coerce-response coercers request %)) raise)))))))})) + :compile (fn [{:keys [coercion responses opts]} _] + (if (and coercion responses) + (let [coercers (response-coercers coercion responses opts)] + (fn [handler] + (fn + ([request] + (coerce-response coercers request (handler request))) + ([request respond raise] + (handler request #(respond (coerce-response coercers request %)) raise)))))))})) ``` The latter has 50% less code, is easier to reason about and is much faster. diff --git a/doc/ring/data_driven_middleware.md b/doc/ring/data_driven_middleware.md index 8b9f59d3..2803475f 100644 --- a/doc/ring/data_driven_middleware.md +++ b/doc/ring/data_driven_middleware.md @@ -19,7 +19,7 @@ Records can have arbitrary keys, but the following keys have a special purpose: | ---------------|-------------| | `:name` | Name of the middleware as a qualified keyword (optional) | `:wrap` | The actual middleware function of `handler & args => request => response` -| `:gen-wrap` | Middleware function generation function, see [compiling middleware](compiling_middleware.md). +| `:compile` | Middleware function generation function, see [compiling middleware](compiling_middleware.md). Middleware Records are accessible in their raw form in the compiled route results, thus available for inventories, creating api-docs etc. diff --git a/modules/reitit-core/src/reitit/impl.cljc b/modules/reitit-core/src/reitit/impl.cljc index 4a962b48..34c0db92 100644 --- a/modules/reitit-core/src/reitit/impl.cljc +++ b/modules/reitit-core/src/reitit/impl.cljc @@ -153,3 +153,6 @@ (defn fast-get #?@(:clj [[^java.util.HashMap m k] (.get m k)] :cljs [[m k] (m k)])) + +(defn strip-nils [m] + (->> m (remove (comp nil? second)) (into {}))) diff --git a/modules/reitit-ring/src/reitit/ring/coercion.cljc b/modules/reitit-ring/src/reitit/ring/coercion.cljc index 7ef4a807..42174d5f 100644 --- a/modules/reitit-ring/src/reitit/ring/coercion.cljc +++ b/modules/reitit-ring/src/reitit/ring/coercion.cljc @@ -125,7 +125,7 @@ and :parameters from route data, otherwise does not mount." (middleware/create {:name ::coerce-parameters - :gen-wrap (fn [{:keys [coercion parameters]} opts] + :compile (fn [{:keys [coercion parameters]} opts] (if (and coercion parameters) (let [coercers (request-coercers coercion parameters opts)] (fn [handler] @@ -143,7 +143,7 @@ and :responses from route data, otherwise does not mount." (middleware/create {:name ::coerce-response - :gen-wrap (fn [{:keys [coercion responses]} opts] + :compile (fn [{:keys [coercion responses]} opts] (if (and coercion responses) (let [coercers (response-coercers coercion responses opts)] (fn [handler] @@ -159,7 +159,7 @@ and :parameters or :responses from route data, otherwise does not mount." (middleware/create {:name ::coerce-exceptions - :gen-wrap (fn [{:keys [coercion parameters responses]} _] + :compile (fn [{:keys [coercion parameters responses]} _] (if (and coercion (or parameters responses)) (fn [handler] (fn diff --git a/modules/reitit-ring/src/reitit/ring/middleware.cljc b/modules/reitit-ring/src/reitit/ring/middleware.cljc index e8fc7669..0c208c58 100644 --- a/modules/reitit-ring/src/reitit/ring/middleware.cljc +++ b/modules/reitit-ring/src/reitit/ring/middleware.cljc @@ -1,6 +1,7 @@ (ns reitit.ring.middleware (:require [meta-merge.core :refer [meta-merge]] - [reitit.core :as r])) + [reitit.core :as r] + [reitit.impl :as impl])) (defprotocol IntoMiddleware (into-middleware [this data opts])) @@ -8,11 +9,11 @@ (defrecord Middleware [name wrap]) (defrecord Endpoint [data handler middleware]) -(defn create [{:keys [name wrap gen-wrap] :as m}] - (when (and wrap gen-wrap) +(defn create [{:keys [name wrap compile] :as m}] + (when (and wrap compile) (throw (ex-info - (str "Middleware can't both :wrap and :gen-wrap defined " m) m))) + (str "Middleware can't both :wrap and :compile defined " m) m))) (map->Middleware m)) (extend-protocol IntoMiddleware @@ -40,14 +41,14 @@ (into-middleware (create this) data opts)) Middleware - (into-middleware [{:keys [wrap gen-wrap] :as this} data opts] - (if-not gen-wrap + (into-middleware [{:keys [wrap compile] :as this} data opts] + (if-not compile this - (if-let [wrap (gen-wrap data opts)] + (if-let [middeware (into-middleware (compile data opts) data opts)] (map->Middleware - (-> this - (dissoc :gen-wrap) - (assoc :wrap wrap)))))) + (merge + (dissoc this :create) + (impl/strip-nils middeware)))))) nil (into-middleware [_ _ _])) @@ -68,13 +69,6 @@ (defn compile-handler [middleware handler] ((apply comp identity (keep :wrap middleware)) handler)) -(compile-handler - [(map->Middleware - {:wrap - (fn [handler] - (fn [request] - (handler request)))})] identity) - (defn compile-result ([route opts] (compile-result route opts nil)) diff --git a/test/cljc/reitit/impl_test.cljc b/test/cljc/reitit/impl_test.cljc index dced731a..45032008 100644 --- a/test/cljc/reitit/impl_test.cljc +++ b/test/cljc/reitit/impl_test.cljc @@ -7,3 +7,6 @@ (into [] (impl/segments "/api/ipa/beer/craft/bisse")))) (is (= ["" "a" "" "b" "" "c" ""] (into [] (impl/segments "/a//b//c/"))))) + +(deftest strip-nils-test + (is (= {:a 1, :c false} (impl/strip-nils {:a 1, :b nil, :c false})))) diff --git a/test/cljc/reitit/middleware_test.cljc b/test/cljc/reitit/middleware_test.cljc index 5a68c03a..e2ab3bb6 100644 --- a/test/cljc/reitit/middleware_test.cljc +++ b/test/cljc/reitit/middleware_test.cljc @@ -10,14 +10,14 @@ (testing "middleware records" - (testing ":wrap & :gen-wrap are exclusive" + (testing ":wrap & :compile are exclusive" (is (thrown-with-msg? ExceptionInfo - #"Middleware can't both :wrap and :gen-wrap defined" + #"Middleware can't both :wrap and :compile defined" (middleware/create {:name ::test :wrap identity - :gen-wrap (constantly identity)})))) + :compile (constantly identity)})))) (testing "middleware" (let [calls (atom 0) @@ -74,12 +74,12 @@ (testing "compiled Middleware" (let [calls (atom 0) - mw {:gen-wrap (fn [data _] - (swap! calls inc) - (fn [handler value] - (swap! calls inc) - (fn [request] - [data value request])))} + mw {:compile (fn [data _] + (swap! calls inc) + (fn [handler value] + (swap! calls inc) + (fn [request] + [data value request])))} ->app (fn [ast handler] (middleware/compile-handler (middleware/expand ast :data {}) @@ -100,8 +100,8 @@ (is (= 2 @calls))))) (testing "nil unmounts the middleware" - (let [app (->app [{:gen-wrap (constantly nil)} - {:gen-wrap (constantly nil)}] identity)] + (let [app (->app [{:compile (constantly nil)} + {:compile (constantly nil)}] identity)] (dotimes [_ 10] (is (= :request (app :request)))))))))) @@ -145,9 +145,9 @@ (testing "with nested middleware" (is (= [:api :admin :ok :admin :api] (app "/api/admin/ping")))) - (testing ":gen-wrap middleware can be unmounted at creation-time" - (let [mw1 {:name ::mw1, :gen-wrap (constantly #(mw % ::mw1))} - mw2 {:name ::mw2, :gen-wrap (constantly nil)} + (testing ":compile middleware can be unmounted at creation-time" + (let [mw1 {:name ::mw1, :compile (constantly #(mw % ::mw1))} + mw2 {:name ::mw2, :compile (constantly nil)} mw3 {:name ::mw3, :wrap #(mw % ::mw3)} router (middleware/router ["/api" {:name ::api @@ -176,13 +176,13 @@ (let [mw (fn [handler value] #(conj (handler (conj % value)) value)) handler #(conj % :ok) - mw1 {:gen-wrap (constantly #(mw % ::mw1))} - mw2 {:gen-wrap (constantly nil)} + mw1 {:compile (constantly #(mw % ::mw1))} + mw2 {:compile (constantly nil)} mw3 {:wrap #(mw % ::mw3)} mw4 #(mw % ::mw4) - mw5 {:gen-wrap (fn [{:keys [mount?]} _] - (when mount? - #(mw % ::mw5)))} + mw5 {:compile (fn [{:keys [mount?]} _] + (when mount? + #(mw % ::mw5)))} chain1 (middleware/chain [mw1 mw2 mw3 mw4 mw5] handler {:mount? true}) chain2 (middleware/chain [mw1 mw2 mw3 mw4 mw5] handler {:mount? false})] (is (= [::mw1 ::mw3 ::mw4 ::mw5 :ok ::mw5 ::mw4 ::mw3 ::mw1] (chain1 []))) From 22bbe38b8c808843b0dfc465caa9a3a654581ca3 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Mon, 4 Dec 2017 23:58:05 +0200 Subject: [PATCH 2/5] reitit.ring.middleware => reitit.middleware --- doc/ring/compiling_middleware.md | 2 +- doc/ring/data_driven_middleware.md | 8 +-- .../src/example/server.clj | 2 +- .../src/reitit}/middleware.cljc | 2 +- modules/reitit-ring/src/reitit/ring.cljc | 4 +- .../reitit-ring/src/reitit/ring/coercion.cljc | 64 +++++++++---------- test/cljc/reitit/middleware_test.cljc | 2 +- test/cljc/reitit/ring_test.cljc | 2 +- 8 files changed, 43 insertions(+), 43 deletions(-) rename modules/{reitit-ring/src/reitit/ring => reitit-core/src/reitit}/middleware.cljc (99%) diff --git a/doc/ring/compiling_middleware.md b/doc/ring/compiling_middleware.md index a64c61c3..4fa35f02 100644 --- a/doc/ring/compiling_middleware.md +++ b/doc/ring/compiling_middleware.md @@ -49,7 +49,7 @@ To demonstrate the two approaches, below are response coercion middleware writte * Mounts only if `:coercion` and `:responses` are defined for the route ```clj -(require '[reitit.ring.middleware :as middleware]) +(require '[reitit.middleware :as middleware]) (def coerce-response-middleware "Middleware for pluggable response coercion. diff --git a/doc/ring/data_driven_middleware.md b/doc/ring/data_driven_middleware.md index 2803475f..d7c028da 100644 --- a/doc/ring/data_driven_middleware.md +++ b/doc/ring/data_driven_middleware.md @@ -11,7 +11,7 @@ Reitit defines middleware as data: ## Middleware as data -All values in the `:middleware` vector in the route data are coerced into `reitit.ring.middleware/Middleware` Records with using the `reitit.ring.middleware/IntoMiddleware` Protocol. By default, functions, maps and `Middleware` records are allowed. +All values in the `:middleware` vector in the route data are coerced into `reitit.middleware/Middleware` Records with using the `reitit.middleware/IntoMiddleware` Protocol. By default, functions, maps and `Middleware` records are allowed. Records can have arbitrary keys, but the following keys have a special purpose: @@ -19,7 +19,7 @@ Records can have arbitrary keys, but the following keys have a special purpose: | ---------------|-------------| | `:name` | Name of the middleware as a qualified keyword (optional) | `:wrap` | The actual middleware function of `handler & args => request => response` -| `:compile` | Middleware function generation function, see [compiling middleware](compiling_middleware.md). +| `:compile` | Middleware compilation function, see [compiling middleware](compiling_middleware.md). Middleware Records are accessible in their raw form in the compiled route results, thus available for inventories, creating api-docs etc. @@ -40,7 +40,7 @@ The following produce identical middleware runtime function. ### Record ```clj -(require '[reitit.ring.middleware :as middleware]) +(require '[reitit.middleware :as middleware]) (def wrap2 (middleware/create @@ -89,7 +89,7 @@ Middleware can be optimized against an endpoint using [middleware compilation](c ## Transforming the middleware chain -There is an extra option in ring-router (actually, in the undelaying middleware-router): `:reitit.ring.middleware/transform` to transform the middleware chain per endpoint. It sees the vector of compiled middleware and should return a new vector of middleware. +There is an extra option in ring-router (actually, in the undelaying middleware-router): `:reitit.middleware/transform` to transform the middleware chain per endpoint. It sees the vector of compiled middleware and should return a new vector of middleware. #### Adding debug middleware between all other middleware diff --git a/examples/just-coercion-with-ring/src/example/server.clj b/examples/just-coercion-with-ring/src/example/server.clj index 33b1e029..776a5127 100644 --- a/examples/just-coercion-with-ring/src/example/server.clj +++ b/examples/just-coercion-with-ring/src/example/server.clj @@ -1,6 +1,6 @@ (ns example.server (:require [ring.adapter.jetty :as jetty] - [reitit.ring.middleware :as middleware] + [reitit.middleware :as middleware] [reitit.ring.coercion :as coercion])) (defonce ^:private server (atom nil)) diff --git a/modules/reitit-ring/src/reitit/ring/middleware.cljc b/modules/reitit-core/src/reitit/middleware.cljc similarity index 99% rename from modules/reitit-ring/src/reitit/ring/middleware.cljc rename to modules/reitit-core/src/reitit/middleware.cljc index 0c208c58..370c4f81 100644 --- a/modules/reitit-ring/src/reitit/ring/middleware.cljc +++ b/modules/reitit-core/src/reitit/middleware.cljc @@ -1,4 +1,4 @@ -(ns reitit.ring.middleware +(ns reitit.middleware (:require [meta-merge.core :refer [meta-merge]] [reitit.core :as r] [reitit.impl :as impl])) diff --git a/modules/reitit-ring/src/reitit/ring.cljc b/modules/reitit-ring/src/reitit/ring.cljc index 51560688..e9270895 100644 --- a/modules/reitit-ring/src/reitit/ring.cljc +++ b/modules/reitit-ring/src/reitit/ring.cljc @@ -1,6 +1,6 @@ (ns reitit.ring (:require [meta-merge.core :refer [meta-merge]] - [reitit.ring.middleware :as middleware] + [reitit.middleware :as middleware] [reitit.core :as r] [reitit.impl :as impl])) @@ -82,7 +82,7 @@ :delete {:middleware [wrap-delete] :handler delete-user}}]]) - See router options from [[reitit.core/router]] and [[reitit.ring.middleware/router]]." + See router options from [[reitit.core/router]] and [[reitit.middleware/router]]." ([data] (router data nil)) ([data opts] diff --git a/modules/reitit-ring/src/reitit/ring/coercion.cljc b/modules/reitit-ring/src/reitit/ring/coercion.cljc index 42174d5f..77d1f1b4 100644 --- a/modules/reitit-ring/src/reitit/ring/coercion.cljc +++ b/modules/reitit-ring/src/reitit/ring/coercion.cljc @@ -1,7 +1,7 @@ (ns reitit.ring.coercion (:require [clojure.walk :as walk] [spec-tools.core :as st] - [reitit.ring.middleware :as middleware] + [reitit.middleware :as middleware] [reitit.ring.coercion.protocol :as protocol] [reitit.ring :as ring] [reitit.impl :as impl])) @@ -126,16 +126,16 @@ (middleware/create {:name ::coerce-parameters :compile (fn [{:keys [coercion parameters]} opts] - (if (and coercion parameters) - (let [coercers (request-coercers coercion parameters opts)] - (fn [handler] - (fn - ([request] - (let [coerced (coerce-request coercers request)] - (handler (impl/fast-assoc request :parameters coerced)))) - ([request respond raise] - (let [coerced (coerce-request coercers request)] - (handler (impl/fast-assoc request :parameters coerced) respond raise))))))))})) + (if (and coercion parameters) + (let [coercers (request-coercers coercion parameters opts)] + (fn [handler] + (fn + ([request] + (let [coerced (coerce-request coercers request)] + (handler (impl/fast-assoc request :parameters coerced)))) + ([request respond raise] + (let [coerced (coerce-request coercers request)] + (handler (impl/fast-assoc request :parameters coerced) respond raise))))))))})) (def coerce-response-middleware "Middleware for pluggable response coercion. @@ -144,14 +144,14 @@ (middleware/create {:name ::coerce-response :compile (fn [{:keys [coercion responses]} opts] - (if (and coercion responses) - (let [coercers (response-coercers coercion responses opts)] - (fn [handler] - (fn - ([request] - (coerce-response coercers request (handler request))) - ([request respond raise] - (handler request #(respond (coerce-response coercers request %)) raise)))))))})) + (if (and coercion responses) + (let [coercers (response-coercers coercion responses opts)] + (fn [handler] + (fn + ([request] + (coerce-response coercers request (handler request))) + ([request respond raise] + (handler request #(respond (coerce-response coercers request %)) raise)))))))})) (def coerce-exceptions-middleware "Middleware for handling coercion exceptions. @@ -160,16 +160,16 @@ (middleware/create {:name ::coerce-exceptions :compile (fn [{:keys [coercion parameters responses]} _] - (if (and coercion (or parameters responses)) - (fn [handler] - (fn - ([request] - (try - (handler request) - (catch #?(:clj Exception :cljs js/Error) e - (handle-coercion-exception e identity #(throw %))))) - ([request respond raise] - (try - (handler request respond #(handle-coercion-exception % respond raise)) - (catch #?(:clj Exception :cljs js/Error) e - (handle-coercion-exception e respond raise))))))))})) + (if (and coercion (or parameters responses)) + (fn [handler] + (fn + ([request] + (try + (handler request) + (catch #?(:clj Exception :cljs js/Error) e + (handle-coercion-exception e identity #(throw %))))) + ([request respond raise] + (try + (handler request respond #(handle-coercion-exception % respond raise)) + (catch #?(:clj Exception :cljs js/Error) e + (handle-coercion-exception e respond raise))))))))})) diff --git a/test/cljc/reitit/middleware_test.cljc b/test/cljc/reitit/middleware_test.cljc index e2ab3bb6..0b09eb32 100644 --- a/test/cljc/reitit/middleware_test.cljc +++ b/test/cljc/reitit/middleware_test.cljc @@ -1,6 +1,6 @@ (ns reitit.middleware-test (:require [clojure.test :refer [deftest testing is are]] - [reitit.ring.middleware :as middleware] + [reitit.middleware :as middleware] [clojure.set :as set] [reitit.core :as r]) #?(:clj diff --git a/test/cljc/reitit/ring_test.cljc b/test/cljc/reitit/ring_test.cljc index 26f290a1..34297e24 100644 --- a/test/cljc/reitit/ring_test.cljc +++ b/test/cljc/reitit/ring_test.cljc @@ -1,7 +1,7 @@ (ns reitit.ring-test (:require [clojure.test :refer [deftest testing is]] [clojure.set :as set] - [reitit.ring.middleware :as middleware] + [reitit.middleware :as middleware] [reitit.ring :as ring] [reitit.core :as r]) #?(:clj From 259c8e01bbd810ad7e506a4d9241a9fd1b9fcde3 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Tue, 5 Dec 2017 08:40:21 +0200 Subject: [PATCH 3/5] Test for deeply compiled middleware --- modules/reitit-core/src/reitit/middleware.cljc | 2 +- test/cljc/reitit/middleware_test.cljc | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/modules/reitit-core/src/reitit/middleware.cljc b/modules/reitit-core/src/reitit/middleware.cljc index 370c4f81..ccf512c6 100644 --- a/modules/reitit-core/src/reitit/middleware.cljc +++ b/modules/reitit-core/src/reitit/middleware.cljc @@ -13,7 +13,7 @@ (when (and wrap compile) (throw (ex-info - (str "Middleware can't both :wrap and :compile defined " m) m))) + (str "Middleware can't have both :wrap and :compile defined " m) m))) (map->Middleware m)) (extend-protocol IntoMiddleware diff --git a/test/cljc/reitit/middleware_test.cljc b/test/cljc/reitit/middleware_test.cljc index 0b09eb32..c4252436 100644 --- a/test/cljc/reitit/middleware_test.cljc +++ b/test/cljc/reitit/middleware_test.cljc @@ -13,7 +13,7 @@ (testing ":wrap & :compile are exclusive" (is (thrown-with-msg? ExceptionInfo - #"Middleware can't both :wrap and :compile defined" + #"Middleware can't have both :wrap and :compile defined" (middleware/create {:name ::test :wrap identity @@ -80,6 +80,11 @@ (swap! calls inc) (fn [request] [data value request])))} + mw3 {:compile (fn [data _] + (swap! calls inc) + {:compile (fn [data _] + (swap! calls inc) + mw)})} ->app (fn [ast handler] (middleware/compile-handler (middleware/expand ast :data {}) @@ -99,6 +104,13 @@ (is (= [:data :value :request] (app :request))) (is (= 2 @calls))))) + (testing "deeply compiled Middleware" + (reset! calls 0) + (let [app (->app [[(middleware/create mw3) :value]] identity)] + (dotimes [_ 10] + (is (= [:data :value :request] (app :request))) + (is (= 4 @calls))))) + (testing "nil unmounts the middleware" (let [app (->app [{:compile (constantly nil)} {:compile (constantly nil)}] identity)] From 5fdaf609c257e47db8a31ec721293be7d295693b Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Tue, 5 Dec 2017 08:52:53 +0200 Subject: [PATCH 4/5] Guard for infinite middleware compilation --- .../reitit-core/src/reitit/middleware.cljc | 21 +++++++++++++------ test/cljc/reitit/middleware_test.cljc | 4 ++++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/modules/reitit-core/src/reitit/middleware.cljc b/modules/reitit-core/src/reitit/middleware.cljc index ccf512c6..4d9e6de6 100644 --- a/modules/reitit-core/src/reitit/middleware.cljc +++ b/modules/reitit-core/src/reitit/middleware.cljc @@ -16,6 +16,8 @@ (str "Middleware can't have both :wrap and :compile defined " m) m))) (map->Middleware m)) +(def ^:dynamic *max-compile-depth* 10) + (extend-protocol IntoMiddleware #?(:clj clojure.lang.APersistentVector @@ -41,14 +43,21 @@ (into-middleware (create this) data opts)) Middleware - (into-middleware [{:keys [wrap compile] :as this} data opts] + (into-middleware [{:keys [compile] :as this} data opts] (if-not compile this - (if-let [middeware (into-middleware (compile data opts) data opts)] - (map->Middleware - (merge - (dissoc this :create) - (impl/strip-nils middeware)))))) + (let [compiled (::compiled opts 0) + opts (assoc opts ::compiled (inc compiled))] + (when (>= compiled *max-compile-depth*) + (throw + (ex-info + (str "Too deep middleware compilation - " compiled) + {:this this, :data data, :opts opts}))) + (if-let [middeware (into-middleware (compile data opts) data opts)] + (map->Middleware + (merge + (dissoc this :create) + (impl/strip-nils middeware))))))) nil (into-middleware [_ _ _])) diff --git a/test/cljc/reitit/middleware_test.cljc b/test/cljc/reitit/middleware_test.cljc index c4252436..d8ad86e2 100644 --- a/test/cljc/reitit/middleware_test.cljc +++ b/test/cljc/reitit/middleware_test.cljc @@ -111,6 +111,10 @@ (is (= [:data :value :request] (app :request))) (is (= 4 @calls))))) + (testing "too deeply compiled Middleware fails" + (binding [middleware/*max-compile-depth* 2] + (is (thrown? Exception (->app [[(middleware/create mw3) :value]] identity))))) + (testing "nil unmounts the middleware" (let [app (->app [{:compile (constantly nil)} {:compile (constantly nil)}] identity)] From 4b3a4110ab8a56ef67d6d250f786f3b30983a774 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Tue, 5 Dec 2017 08:58:55 +0200 Subject: [PATCH 5/5] Fix (cljs) tests --- test/cljc/reitit/middleware_test.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cljc/reitit/middleware_test.cljc b/test/cljc/reitit/middleware_test.cljc index d8ad86e2..3ad16298 100644 --- a/test/cljc/reitit/middleware_test.cljc +++ b/test/cljc/reitit/middleware_test.cljc @@ -113,7 +113,7 @@ (testing "too deeply compiled Middleware fails" (binding [middleware/*max-compile-depth* 2] - (is (thrown? Exception (->app [[(middleware/create mw3) :value]] identity))))) + (is (thrown? ExceptionInfo (->app [[(middleware/create mw3) :value]] identity))))) (testing "nil unmounts the middleware" (let [app (->app [{:compile (constantly nil)}