0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-01-23 23:18:48 -05:00

♻️ Replace interceptors with middleware on vertx package.

This commit is contained in:
Andrey Antukh 2020-02-17 09:41:00 +01:00
parent 3507c4acb6
commit fd6362e463
7 changed files with 115 additions and 119 deletions

View file

@ -75,6 +75,7 @@ The `deploy!` function also accepts an additional parameter for
options, and at this momment it only accepts as single option: options, and at this momment it only accepts as single option:
- `:instances` - number of instances to launch of the same verticle. - `:instances` - number of instances to launch of the same verticle.
- `:worker` - use worker thread pool or the default event-loop.
### Event Bus ### Event Bus
@ -196,7 +197,7 @@ Lets start with a complete example:
```clojure ```clojure
(require '[vertx.http :as vh]) (require '[vertx.http :as vh])
(require '[vertx.web :as vw]) (require '[vertx.web :as vw])
(require '[vertx.web.interceptors :as vwi]) (require '[vertx.web.middleware :as vwm])
(defn hello-world-handler (defn hello-world-handler
[req] [req]
@ -205,8 +206,9 @@ Lets start with a complete example:
(defn on-start (defn on-start
[ctx] [ctx]
(let [routes [["/" {:interceptors [(vwi/cookies)] (let [routes [["/" {:middleware [vwm/cookies]
:all hello-world-handler}]] :handler hello-world-handler
:method :get}]]
handler (vw/handler ctx handler (vw/handler ctx
(vw/assets "/static/*" {:root "resources/public/static"}) (vw/assets "/static/*" {:root "resources/public/static"})
(vw/router routes))] (vw/router routes))]
@ -217,25 +219,24 @@ Lets start with a complete example:
(vc/deploy! system)) (vc/deploy! system))
``` ```
The routes are defined using `reitit-core` and the interceptors are The routes are defined using `reitit-core`. The request object is very
using `sieppari` as underlying implementation. The request object is similar to the one explained in `vertx.http`.
very similar to the one explained in `vertx.http`.
The main difference with `vertx.http` is that the handler is called The main difference with `vertx.http` is that the handler is called
when the body is ready to be used and is available under `:body` when the body is ready to be used and is available under `:body`
keyword on the request. keyword on the request.
All additional features such that reading the query/form params, All additional features such that reading the query/form params,
parse/write cookies, cors and file uploads are provided with parse/write cookies, cors and file uploads are provided with additional middleware
interceptors as pluggable pieces: wrappers:
- `vertx.web.interceptors/uploads` parses the vertx uploaded file data - `vertx.web.middleware/uploads` parses the vertx uploaded file data
structure and expose it as clojure maps under `:uploads` key. structure and expose it as clojure maps under `:uploads` key.
- `vertx.web.interceptors/params` parses the query string and form - `vertx.web.middleware/params` parses the query string and form
params in the body if the content-type is appropriate and exposes params in the body if the content-type is appropriate and exposes
them under `:params`. them under `:params`.
- `vertx.web.interceptors/cors` properly sets the CORS headers. - `vertx.web.middleware/cors` properly sets the CORS headers.
- `vertx.web.interceptors/cookies` handles the cookies reading from - `vertx.web.middleware/cookies` handles the cookies reading from
the request and cookies writing from the response. the request and cookies writing from the response.

View file

@ -2,7 +2,6 @@
{org.clojure/tools.logging {:mvn/version "0.5.0"} {org.clojure/tools.logging {:mvn/version "0.5.0"}
funcool/promesa {:mvn/version "5.0.0"} funcool/promesa {:mvn/version "5.0.0"}
metosin/reitit-core {:mvn/version "0.3.10"} metosin/reitit-core {:mvn/version "0.3.10"}
metosin/sieppari {:mvn/version "0.0.0-alpha8"}
org.clojure/core.async {:mvn/version "0.7.559"} org.clojure/core.async {:mvn/version "0.7.559"}
io.vertx/vertx-core {:mvn/version "4.0.0-milestone4"} io.vertx/vertx-core {:mvn/version "4.0.0-milestone4"}
io.vertx/vertx-web {:mvn/version "4.0.0-milestone4"} io.vertx/vertx-web {:mvn/version "4.0.0-milestone4"}

View file

@ -57,11 +57,6 @@
<artifactId>core.async</artifactId> <artifactId>core.async</artifactId>
<version>0.7.559</version> <version>0.7.559</version>
</dependency> </dependency>
<dependency>
<groupId>metosin</groupId>
<artifactId>sieppari</artifactId>
<version>0.0.0-alpha8</version>
</dependency>
</dependencies> </dependencies>
<repositories> <repositories>
<repository> <repository>

View file

@ -10,8 +10,8 @@
[clojure.tools.logging :as log] [clojure.tools.logging :as log]
[clojure.spec.alpha :as s] [clojure.spec.alpha :as s]
[promesa.core :as p] [promesa.core :as p]
[sieppari.core :as sp]
[reitit.core :as rt] [reitit.core :as rt]
[reitit.middleware :as rmw]
[vertx.http :as http] [vertx.http :as http]
[vertx.impl :as impl]) [vertx.impl :as impl])
(:import (:import
@ -57,10 +57,7 @@
(defn handler (defn handler
"Wraps a user defined funcion based handler into a vertx-web aware "Wraps a user defined funcion based handler into a vertx-web aware
handler (with support for multipart uploads. handler (with support for multipart uploads)."
If the handler is a vector, the sieppari intercerptos engine will be used
to resolve the execution of the interceptors + handler."
[vsm & handlers] [vsm & handlers]
(let [^Vertx vsm (impl/resolve-system vsm) (let [^Vertx vsm (impl/resolve-system vsm)
^Router router (Router/router vsm)] ^Router router (Router/router vsm)]
@ -89,23 +86,13 @@
{:status 500 {:status 500
:body "Internal server error!\n"}) :body "Internal server error!\n"})
(defn- run-chain
[ctx chain handler]
(let [d (p/deferred)]
(sp/execute (conj chain handler) ctx #(p/resolve! d %) #(p/reject! d %))
d))
(defn- router-handler (defn- router-handler
[router {:keys [path method] :as ctx}] [router {:keys [path method] :as ctx}]
(let [{:keys [data path-params] :as match} (rt/match-by-path router path) (if-let [{:keys [result path-params] :as match} (rt/match-by-path router path)]
handler-fn (or (get data method) (let [handler-fn (:handler result)
(get data :all) ctx (assoc ctx ::match match :path-params path-params)]
default-handler) (handler-fn ctx))
interceptors (get data :interceptors) (default-handler ctx)))
ctx (assoc ctx ::match match :path-params path-params)]
(if (empty? interceptors)
(handler-fn ctx)
(run-chain ctx interceptors handler-fn))))
(defn router (defn router
([routes] (router routes {})) ([routes] (router routes {}))
@ -120,8 +107,8 @@
log-requests? false log-requests? false
time-response? true} time-response? true}
:as options}] :as options}]
(let [rtr (rt/router routes options) (let [rtr (rt/router routes {:compile rmw/compile-result})
f #(router-handler rtr %)] rtf #(router-handler rtr %)]
(fn [^Router router] (fn [^Router router]
(let [^Route route (.route router)] (let [^Route route (.route router)]
(when time-response? (.handler route (ResponseTimeHandler/create))) (when time-response? (.handler route (ResponseTimeHandler/create)))
@ -149,8 +136,9 @@
(.put ^RoutingContext rc "vertx$clj$req" req) (.put ^RoutingContext rc "vertx$clj$req" req)
(.fail ^RoutingContext rc ^Throwable err))] (.fail ^RoutingContext rc ^Throwable err))]
(try (try
(-> (http/-handle-response (f req) req) (let [result (rtf req)]
(p/catch' efn)) (-> (http/-handle-response result req)
(p/catch' efn)))
(catch Exception err (catch Exception err
(efn err))))))))) (efn err)))))))))
router)))) router))))

View file

@ -10,7 +10,6 @@
(:require (:require
[clojure.spec.alpha :as s] [clojure.spec.alpha :as s]
[promesa.core :as p] [promesa.core :as p]
[sieppari.core :as sp]
[reitit.core :as rt] [reitit.core :as rt]
[vertx.http :as http] [vertx.http :as http]
[vertx.impl :as impl]) [vertx.impl :as impl])

View file

@ -4,8 +4,8 @@
;; ;;
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz> ;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
(ns vertx.web.interceptors (ns vertx.web.middleware
"High level api for http servers." "Common middleware's."
(:require (:require
[clojure.spec.alpha :as s] [clojure.spec.alpha :as s]
[clojure.string :as str] [clojure.string :as str]
@ -13,9 +13,7 @@
[reitit.core :as r] [reitit.core :as r]
[vertx.http :as http] [vertx.http :as http]
[vertx.web :as web] [vertx.web :as web]
[vertx.util :as util] [vertx.util :as util])
[sieppari.context :as spx]
[sieppari.core :as sp])
(:import (:import
clojure.lang.Keyword clojure.lang.Keyword
clojure.lang.MapEntry clojure.lang.MapEntry
@ -41,24 +39,36 @@
(:path data) (.setPath (:path data)) (:path data) (.setPath (:path data))
(:secure data) (.setSecure true))) (:secure data) (.setSecure true)))
(defn cookies (defn- handle-cookies-response
[] [request {:keys [cookies] :as response}]
{:enter (let [^HttpServerResponse res (::http/response request)]
(fn [data] (util/doseq [[key val] cookies]
(let [^HttpServerRequest req (get-in data [:request ::http/request]) (if (nil? val)
parse-cookie (fn [^Cookie item] [(.getName item) (.getValue item)]) (.removeCookie res key)
cookies (into {} (map parse-cookie) (vals (.cookieMap req)))] (.addCookie res (build-cookie key val))))))
(update data :request assoc :cookies cookies)))
:leave (defn- cookie->vector
(fn [data] [^Cookie item]
(let [cookies (get-in data [:response :cookies]) [(.getName item) (.getValue item)])
^HttpServerResponse res (get-in data [:request ::http/response])]
(when (map? cookies) (defn- wrap-cookies
(util/doseq [[key val] cookies] [handler]
(if (nil? val) (let [xf (map cookie->vector)]
(.removeCookie res key) (fn [request]
(.addCookie res (build-cookie key val))))) (let [req (::http/request request)
data))}) cookies (.cookieMap ^HttpServerRequest req)
cookies (into {} xf (vals cookies))]
(-> (p/do! (handler (assoc request :cookies cookies)))
(p/then' (fn [response]
(when (and (map? response)
(map? (:cookies response)))
(handle-cookies-response request response))
response)))))))
(def cookies
{:name ::cookies
:compile (constantly wrap-cookies)})
;; --- Params ;; --- Params
@ -83,46 +93,54 @@
(recur (assoc! m key [prv val])))) (recur (assoc! m key [prv val]))))
(persistent! m))))) (persistent! m)))))
(defn params (defn- wrap-params
([] (params nil)) [handler]
([{:keys [attr] :or {attr :params}}] (fn [request]
{:enter (fn [data] (let [req (::http/request request)
(let [request (get-in data [:request ::http/request]) params (parse-params req)]
params (parse-params request)] (handler (assoc request :params params)))))
(update data :request assoc attr params)))}))
(def params
{:name ::params
:compile (constantly wrap-params)})
;; --- Uploads ;; --- Uploads
(defn uploads (defn- wrap-uploads
([] (uploads nil)) [handler]
([{:keys [attr] :or {attr :uploads}}] (fn [request]
{:enter (fn [data] (let [rctx (::web/routing-context request)
(let [context (get-in data [:request ::web/routing-context]) uploads (.fileUploads ^RoutingContext rctx)
uploads (reduce (fn [acc ^FileUpload upload] uploads (reduce (fn [acc ^FileUpload upload]
(assoc! acc (assoc acc
(keyword (.name upload)) (keyword (.name upload))
{:type :uploaded-file {:type :uploaded-file
:mtype (.contentType upload) :mtype (.contentType upload)
:path (.uploadedFileName upload) :path (.uploadedFileName upload)
:name (.fileName upload) :name (.fileName upload)
:size (.size upload)})) :size (.size upload)}))
(transient {}) {}
(.fileUploads ^RoutingContext context))] uploads)]
(update data :request assoc attr (persistent! uploads))))})) (handler (assoc request :uploads uploads)))))
(def uploads
{:name ::uploads
:compile (constantly wrap-uploads)})
;; --- Errors ;; --- Errors
(defn errors (defn- wrap-errors
"A error handling interceptor." [handler on-error]
[handler-fn] (fn [request]
{:error (-> (p/do! (handler request))
(fn [data] (p/catch (fn [error]
(let [request (:request data) (on-error error request))))))
error (:error data)
response (handler-fn error request)] (def errors
(-> data {:name ::errors
(assoc :response response) :compile (constantly wrap-errors)})
(dissoc :error))))})
;; --- CORS ;; --- CORS
@ -140,8 +158,8 @@
::expose-headers ::expose-headers
::max-age])) ::max-age]))
(defn cors (defn wrap-cors
[opts] [handler opts]
(s/assert ::cors-opts opts) (s/assert ::cors-opts opts)
(letfn [(preflight? [{:keys [method headers] :as ctx}] (letfn [(preflight? [{:keys [method headers] :as ctx}]
(and (= method :options) (and (= method :options)
@ -184,19 +202,16 @@
(:allow-headers opts) (:allow-headers opts)
(assoc "access-control-allow-headers" (assoc "access-control-allow-headers"
(-> (normalize (:allow-headers opts)) (-> (normalize (:allow-headers opts))
(str/lower-case)))))) (str/lower-case))))))]
(fn [request]
(if (preflight? request)
{:status 204 :headers (get-headers request)}
(-> (p/do! (handler request))
(p/then (fn [response]
(if (map? response)
(update response :headers merge (get-headers request))
response))))))))
(enter [data] (def cors
(let [ctx (:request data)] {:name ::cors
(if (preflight? ctx) :compile (constantly wrap-cors)})
(spx/terminate (assoc data ::preflight true))
data)))
(leave [data]
(let [headers (get-headers (:request data))]
(if (::preflight data)
(assoc data :response {:status 204 :headers headers})
(update-in data [:response :headers] merge headers))))]
{:enter enter
:leave leave}))

View file

@ -12,8 +12,7 @@
[vertx.core :as vc] [vertx.core :as vc]
[vertx.eventbus :as ve] [vertx.eventbus :as ve]
[vertx.http :as vh] [vertx.http :as vh]
[vertx.web :as vw] [vertx.web :as vw])
[vertx.web.interceptors :as vwi])
(:import (:import
io.vertx.core.http.HttpServerRequest io.vertx.core.http.HttpServerRequest
io.vertx.core.http.HttpServerResponse)) io.vertx.core.http.HttpServerResponse))