diff --git a/modules/reitit-http/src/reitit/http.cljc b/modules/reitit-http/src/reitit/http.cljc index cb694b49..75eda84d 100644 --- a/modules/reitit-http/src/reitit/http.cljc +++ b/modules/reitit-http/src/reitit/http.cljc @@ -73,12 +73,22 @@ (r/router data opts)))) (defn routing-interceptor - "A Pedestal-style routing interceptor that enqueus the interceptors into context." - [router default-handler {:keys [interceptors executor]}] + "Creates a Pedestal-style routing interceptor that enqueus the interceptors into context. + Takes http-router, default ring-handler and and options map, with the following keys: + + | key | description | + | ------------------|-------------| + | `:executor` | `reitit.interceptor.Executor` for the interceptor chain + | `:interceptors` | Optional sequence of interceptors that are always run before any other interceptors, even for the default handler + | `:inject-match?` | Boolean to inject `match` into request under `:reitit.core/match` key (default true) + | `:inject-router?` | Boolean to inject `router` into request under `:reitit.core/router` key (default true)" + [router default-handler {:keys [interceptors executor inject-match? inject-router?] + :or {inject-match? true, inject-router? true}}] (let [default-handler (or default-handler (fn ([_]))) default-interceptors (->> interceptors (map #(interceptor/into-interceptor % nil (r/options router)))) - default-queue (interceptor/queue executor default-interceptors)] + default-queue (interceptor/queue executor default-interceptors) + enrich-request (ring/create-enrich-request inject-match? inject-router?)] {:name ::router :enter (fn [{:keys [request] :as context}] (if-let [match (r/match-by-path router (:uri request))] @@ -86,10 +96,7 @@ path-params (:path-params match) endpoint (-> match :result method) interceptors (or (:queue endpoint) (:interceptors endpoint)) - request (-> request - (impl/fast-assoc :path-params path-params) - (impl/fast-assoc ::r/match match) - (impl/fast-assoc ::r/router router)) + request (enrich-request request path-params match router) context (assoc context :request request) queue (interceptor/queue executor (concat default-interceptors interceptors))] (interceptor/enqueue executor context queue)) @@ -103,13 +110,16 @@ "Creates a ring-handler out of a http-router, optional default ring-handler and options map, with the following keys: - | key | description | - | ----------------|-------------| - | `:executor` | `reitit.interceptor.Executor` for the interceptor chain - | `:interceptors` | Optional sequence of interceptors that are always run before any other interceptors, even for the default handler" + | key | description | + | ------------------|-------------| + | `:executor` | `reitit.interceptor.Executor` for the interceptor chain + | `:interceptors` | Optional sequence of interceptors that are always run before any other interceptors, even for the default handler + | `:inject-match?` | Boolean to inject `match` into request under `:reitit.core/match` key (default true) + | `:inject-router?` | Boolean to inject `router` into request under `:reitit.core/router` key (default true)" ([router opts] (ring-handler router nil opts)) - ([router default-handler {:keys [executor interceptors]}] + ([router default-handler {:keys [executor interceptors inject-match? inject-router?] + :or {inject-match? true, inject-router? true}}] (let [default-handler (or default-handler (fn ([_]) ([_ respond _] (respond nil)))) default-queue (->> [default-handler] (concat interceptors) @@ -120,7 +130,9 @@ (dissoc :data) ; data is already merged into routes (cond-> (seq interceptors) (update-in [:data :interceptors] (partial into (vec interceptors))))) - router (reitit.http/router (r/routes router) router-opts)] + router (reitit.http/router (r/routes router) router-opts) + enrich-request (ring/create-enrich-request inject-match? inject-router?) + enrich-default-request (ring/create-enrich-default-request inject-router?)] (with-meta (fn ([request] @@ -129,13 +141,10 @@ path-params (:path-params match) endpoint (-> match :result method) interceptors (or (:queue endpoint) (:interceptors endpoint)) - request (-> request - (impl/fast-assoc :path-params path-params) - (impl/fast-assoc ::r/match match) - (impl/fast-assoc ::r/router router))] + request (enrich-request request path-params match router)] (or (interceptor/execute executor interceptors request) (interceptor/execute executor default-queue request))) - (interceptor/execute executor default-queue (impl/fast-assoc request ::r/router router)))) + (interceptor/execute executor default-queue (enrich-default-request request)))) ([request respond raise] (let [default #(interceptor/execute executor default-queue % respond raise)] (if-let [match (r/match-by-path router (:uri request))] @@ -143,10 +152,7 @@ path-params (:path-params match) endpoint (-> match :result method) interceptors (or (:queue endpoint) (:interceptors endpoint)) - request (-> request - (impl/fast-assoc :path-params path-params) - (impl/fast-assoc ::r/match match) - (impl/fast-assoc ::r/router router)) + request (enrich-request request path-params match router) respond' (fn [response] (if response (respond response) @@ -154,7 +160,7 @@ (if interceptors (interceptor/execute executor interceptors request respond' raise) (default request))) - (default (impl/fast-assoc request ::r/router router)))) + (default (enrich-default-request request)))) nil)) {::r/router router})))) diff --git a/modules/reitit-ring/src/reitit/ring.cljc b/modules/reitit-ring/src/reitit/ring.cljc index 42ebcd4f..a0913c2f 100644 --- a/modules/reitit-ring/src/reitit/ring.cljc +++ b/modules/reitit-ring/src/reitit/ring.cljc @@ -226,20 +226,54 @@ (not-found-handler request)))))] (create handler))))) +(defn create-enrich-request [inject-match? inject-router?] + (cond + (and inject-match? inject-router?) + (fn enrich-request [request path-params match router] + (-> request + (impl/fast-assoc :path-params path-params) + (impl/fast-assoc ::r/match match) + (impl/fast-assoc ::r/router router))) + inject-router? + (fn enrich-request [request path-params _ router] + (-> request + (impl/fast-assoc :path-params path-params) + (impl/fast-assoc ::r/router router))) + inject-match? + (fn enrich-request [request path-params match _] + (-> request + (impl/fast-assoc :path-params path-params) + (impl/fast-assoc ::r/match match))) + :else + (fn enrich-request [request path-params _ _] + (-> request + (impl/fast-assoc :path-params path-params))))) + +(defn create-enrich-default-request [inject-router?] + (if inject-router? + (fn enrich-request [request router] + (impl/fast-assoc request ::r/router router)) + identity)) + (defn ring-handler "Creates a ring-handler out of a router, optional default ring-handler and options map, with the following keys: - | key | description | - | --------------|-------------| - | `:middleware` | Optional sequence of middleware that wrap the ring-handler" + | key | description | + | ------------------|-------------| + | `:middleware` | Optional sequence of middleware that wrap the ring-handler + | `:inject-match?` | Boolean to inject `match` into request under `:reitit.core/match` key (default true) + | `:inject-router?` | Boolean to inject `router` into request under `:reitit.core/router` key (default true)" ([router] (ring-handler router nil)) ([router default-handler] (ring-handler router default-handler nil)) - ([router default-handler {:keys [middleware]}] + ([router default-handler {:keys [middleware inject-match? inject-router?] + :or {inject-match? true, inject-router? true}}] (let [default-handler (or default-handler (fn ([_]) ([_ respond _] (respond nil)))) - wrap (if middleware (partial middleware/chain middleware) identity)] + wrap (if middleware (partial middleware/chain middleware) identity) + enrich-request (create-enrich-request inject-match? inject-router?) + enrich-default-request (create-enrich-default-request inject-router?)] (with-meta (wrap (fn @@ -249,24 +283,18 @@ path-params (:path-params match) result (:result match) handler (-> result method :handler (or default-handler)) - request (-> request - (impl/fast-assoc :path-params path-params) - (impl/fast-assoc ::r/match match) - (impl/fast-assoc ::r/router router))] + request (enrich-request request path-params match router)] (or (handler request) (default-handler request))) - (default-handler (impl/fast-assoc request ::r/router router)))) + (default-handler (enrich-default-request request)))) ([request respond raise] (if-let [match (r/match-by-path router (:uri request))] (let [method (:request-method request) path-params (:path-params match) result (:result match) handler (-> result method :handler (or default-handler)) - request (-> request - (impl/fast-assoc :path-params path-params) - (impl/fast-assoc ::r/match match) - (impl/fast-assoc ::r/router router))] + request (enrich-request request path-params match router)] ((routes handler default-handler) request respond raise)) - (default-handler (impl/fast-assoc request ::r/router router) respond raise)) + (default-handler (enrich-default-request request) respond raise)) nil))) {::r/router router})))) diff --git a/perf-test/clj/reitit/calf_perf_test.clj b/perf-test/clj/reitit/calf_perf_test.clj index 9b923a46..5137b088 100644 --- a/perf-test/clj/reitit/calf_perf_test.clj +++ b/perf-test/clj/reitit/calf_perf_test.clj @@ -7,7 +7,7 @@ [reitit.impl :as impl] [reitit.ring :as ring] [reitit.core :as r]) - (:import (reitit Trie))) + (:import (reitit Trie Trie$Matcher))) ;; ;; start repl with `lein perf repl` @@ -77,35 +77,133 @@ [["/user/:id/profile/:type/" {:get (fn [{{:keys [id type]} :path-params}] (h11 id type)) :put (fn [{{:keys [id type]} :path-params}] (h12 id type)) :handler (fn [_] (h1x))}] - #_["/user/:id/permissions/" {:get (fn [{{:keys [id]} :path-params}] (h21 id)) + ["/user/:id/permissions/" {:get (fn [{{:keys [id]} :path-params}] (h21 id)) :put (fn [{{:keys [id]} :path-params}] (h22 id)) :handler (fn [_] (h2x))}] - #_["/company/:cid/dept/:did/" {:put (fn [{{:keys [cid did]} :path-params}] (h30 cid did)) + ["/company/:cid/dept/:did/" {:put (fn [{{:keys [cid did]} :path-params}] (h30 cid did)) :handler (fn [_] (h3x))}] - #_["/this/is/a/static/route" {:put (fn [_] (h40)) + ["/this/is/a/static/route" {:put (fn [_] (h40)) :handler (fn [_] (h4x))}]]) (fn [_] (hxx)))) #_(let [request {:request-method :put - :uri "/this/is/a/static/route"}] - (handler-reitit request) - (cc/quick-bench - (handler-reitit request))) + :uri "/this/is/a/static/route"}] + (handler-reitit request) + (cc/quick-bench + (handler-reitit request))) (let [request {:request-method :get :uri "/user/1234/profile/compact/"}] - (handler-reitit request) - ;; OLD: 1338ns - ;; NEW: 981ns + ;; OLD: 1338ns + ;; NEW: 981ns + ;; JAVA: 805ns + ;; NO-INJECT: 704ns #_(cc/quick-bench - (handler-reitit request))) + (handler-reitit request)) + (handler-reitit request)) + +(comment + (impl/segments "/user/1234/profile/compact") + ;; 145ns + (cc/quick-bench + (impl/segments "/user/1234/profile/compact"))) + +(comment + (Trie/split "/user/1234/profile/compact") + ;; 91ns + (cc/quick-bench + (Trie/split "/user/1234/profile/compact"))) + +(comment + (let [router (r/router ["/user/:id/profile/:type"])] + (cc/quick-bench + (r/match-by-path router "/user/1234/profile/compact")))) + +(let [lookup ^Trie$Matcher (Trie/sample)] + (Trie/lookup lookup "/user/1234/profile/compact") + #_(cc/quick-bench + (Trie/lookup lookup "/user/1234/profile/compact"))) + +(let [router (r/router [["/user/:id" ::1] + ["/user/:id/permissions" ::2] + ["/company/:cid/dept/:did" ::3] + ["/this/is/a/static/route" ::4]])] + #_(cc/quick-bench + (r/match-by-path router "/user/1234/profile/compact")) + (r/match-by-path router "/user/1234")) + +;; 281ns +(let [router (r/router [["/user/:id/profile/:type" ::1] + ["/user/:id/permissions" ::2] + ["/company/:cid/dept/:did" ::3] + ["/this/is/a/static/route" ::4]])] + #_(cc/quick-bench + (r/match-by-path router "/user/1234/profile/compact")) + (r/match-by-path router "/user/1234/profile/compact")) + +(read-string + (str + (.matcher + (doto (Trie.) + (.add "/user" 1) + #_(.add "/user/id/permissions" 2) + (.add "/user/id/permissions2" 3))))) + +(Trie/lookup + (.matcher + (doto (Trie.) + (.add "/user/1" 1) + (.add "/user/1/permissions" 2))) + "/user/1") + +(.matcher + (doto (Trie.) + (.add "/user/1" 1) + (.add "/user/1/permissions" 2))) + +;; 137ns +(let [m (.matcher + (doto (Trie.) + (.add "/user/:id/profile/:type" 1)))] + #_(cc/quick-bench + (Trie/lookup m "/user/1234/profile/compact")) + (Trie/lookup m "/user/1234/profile/compact")) (comment + (let [matcher ^Trie$Matcher (Trie/sample)] + (Trie/lookup matcher "/user/1234/profile/compact") + (cc/quick-bench + (Trie/lookup matcher "/user/1234/profile/compact"))) + + ;; 173ns + (let [lookup ^Trie$Matcher (Trie/tree2)] + (Trie/lookup lookup "/user/1234/profile/compact") + (cc/quick-bench + (Trie/lookup lookup "/user/1234/profile/compact"))) + + + ;; 140ns + (let [lookup ^Trie$Matcher (Trie/tree1)] + (Trie/lookup lookup "/user/1234/profile/compact") + (cc/quick-bench + (Trie/lookup lookup "/user/1234/profile/compact"))) + ;; 849ns (clojure, original) ;; 599ns (java, initial) - ;; 810ns (linear) + ;; 173ns (fast split) (let [router (r/router ["/user/:id/profile/:type"])] + (r/match-by-path router "/user/1234/profile/compact") + (cc/quick-bench + (r/match-by-path router "/user/1234/profile/compact"))) + + ;; 849ns (clojure, original) + ;; 599ns (java, initial) + ;; 173ns (java, optimized) + (let [router (r/router [["/user/:id/profile/:type/" ::1] + ["/user/:id/permissions/" ::2] + ["/company/:cid/dept/:did/" ::3] + ["/this/is/a/static/route" ::4]])] (cc/quick-bench (r/match-by-path router "/user/1234/profile/compact"))) @@ -129,10 +227,16 @@ (import '[reitit Util]) -#_(cc/quick-bench - (Trie/split "/this/is/a/static/route")) +(comment + (Util/matchURI "/user/1234/profile/compact/" ["/user/" :id "/profile/" :type "/"]) + (cc/quick-bench + (Util/matchURI "/user/1234/profile/compact/" ["/user/" :id "/profile/" :type "/"])) -(Util/matchURI "/user/1234/profile/compact/" ["/user/" :id "/profile/" :type "/"]) + (cc/quick-bench + (Trie/split "/user/1234/profile/compact/")) + + (cc/quick-bench + (.split "/user/1234/profile/compact/" "/" 666))) (import '[reitit Segment2]) @@ -161,7 +265,7 @@ (segment/lookup segment "/user/1/permissions/")))) #_(cc/quick-bench - (Trie/split "/user/1/profile/compat")) + (Trie/split "/user/1/profile/compat")) #_(Trie/split "/user/1/profile/compat")