:inject-router? and :inject-match? for ring & http

This commit is contained in:
Tommi Reiman 2019-01-13 14:04:24 +02:00
parent 80dea6cfef
commit 75c4f78f5d
3 changed files with 193 additions and 55 deletions

View file

@ -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))
@ -104,12 +111,15 @@
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"
| `: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}))))

View file

@ -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"
| ------------------|-------------|
| `: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}))))

View file

@ -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,12 +77,12 @@
[["/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))))
@ -94,18 +94,116 @@
(let [request {:request-method :get
:uri "/user/1234/profile/compact/"}]
(handler-reitit request)
;; 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 "/"]))
(cc/quick-bench
(Trie/split "/user/1234/profile/compact/"))
(cc/quick-bench
(.split "/user/1234/profile/compact/" "/" 666)))
(import '[reitit Segment2])