0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-01-08 16:00:19 -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:
- `:instances` - number of instances to launch of the same verticle.
- `:worker` - use worker thread pool or the default event-loop.
### Event Bus
@ -196,7 +197,7 @@ Lets start with a complete example:
```clojure
(require '[vertx.http :as vh])
(require '[vertx.web :as vw])
(require '[vertx.web.interceptors :as vwi])
(require '[vertx.web.middleware :as vwm])
(defn hello-world-handler
[req]
@ -205,8 +206,9 @@ Lets start with a complete example:
(defn on-start
[ctx]
(let [routes [["/" {:interceptors [(vwi/cookies)]
:all hello-world-handler}]]
(let [routes [["/" {:middleware [vwm/cookies]
:handler hello-world-handler
:method :get}]]
handler (vw/handler ctx
(vw/assets "/static/*" {:root "resources/public/static"})
(vw/router routes))]
@ -217,25 +219,24 @@ Lets start with a complete example:
(vc/deploy! system))
```
The routes are defined using `reitit-core` and the interceptors are
using `sieppari` as underlying implementation. The request object is
very similar to the one explained in `vertx.http`.
The routes are defined using `reitit-core`. The request object is very
similar to the one explained in `vertx.http`.
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`
keyword on the request.
All additional features such that reading the query/form params,
parse/write cookies, cors and file uploads are provided with
interceptors as pluggable pieces:
parse/write cookies, cors and file uploads are provided with additional middleware
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.
- `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
them under `:params`.
- `vertx.web.interceptors/cors` properly sets the CORS headers.
- `vertx.web.interceptors/cookies` handles the cookies reading from
- `vertx.web.middleware/cors` properly sets the CORS headers.
- `vertx.web.middleware/cookies` handles the cookies reading from
the request and cookies writing from the response.

View file

@ -2,7 +2,6 @@
{org.clojure/tools.logging {:mvn/version "0.5.0"}
funcool/promesa {:mvn/version "5.0.0"}
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"}
io.vertx/vertx-core {: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>
<version>0.7.559</version>
</dependency>
<dependency>
<groupId>metosin</groupId>
<artifactId>sieppari</artifactId>
<version>0.0.0-alpha8</version>
</dependency>
</dependencies>
<repositories>
<repository>

View file

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

View file

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

View file

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

View file

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