mirror of
https://github.com/penpot/penpot.git
synced 2025-07-30 05:18:33 +02:00
152 lines
4.2 KiB
Clojure
152 lines
4.2 KiB
Clojure
;; This Source Code Form is subject to the terms of the Mozilla Public
|
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
;;
|
|
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
|
|
|
(ns vertx.http
|
|
"Enables `raw` access to the http facilites of vertx. If you want more
|
|
clojure idiomatic api, refer to the `vertx.web` namespace."
|
|
(:require [clojure.spec.alpha :as s]
|
|
[promesa.core :as p]
|
|
[vertx.util :as vu])
|
|
(:import
|
|
java.util.Map$Entry
|
|
clojure.lang.MapEntry
|
|
io.vertx.core.Vertx
|
|
io.vertx.core.Verticle
|
|
io.vertx.core.Handler
|
|
io.vertx.core.Future
|
|
io.vertx.core.MultiMap
|
|
io.vertx.core.Context
|
|
io.vertx.core.buffer.Buffer
|
|
io.vertx.core.http.HttpServer
|
|
io.vertx.core.http.HttpServerRequest
|
|
io.vertx.core.http.HttpServerResponse
|
|
io.vertx.core.http.HttpServerOptions
|
|
io.vertx.core.http.ServerWebSocket))
|
|
|
|
(declare opts->http-server-options)
|
|
(declare resolve-handler)
|
|
|
|
;; --- Public Api
|
|
|
|
(declare -handle-response)
|
|
(declare -handle-body)
|
|
|
|
(defn ->headers
|
|
[^MultiMap headers]
|
|
(let [it (.iterator ^MultiMap headers)]
|
|
(loop [m (transient {})]
|
|
(if (.hasNext it)
|
|
(let [^Map$Entry me (.next it)
|
|
key (.toLowerCase (.getKey me))
|
|
val (.getValue me)]
|
|
(recur (assoc! m key val)))
|
|
(persistent! m)))))
|
|
|
|
(defn- ->request
|
|
[^HttpServerRequest request]
|
|
{:method (-> request .rawMethod .toLowerCase keyword)
|
|
:path (.path request)
|
|
:headers (->headers (.headers request))
|
|
::request request
|
|
::response (.response request)})
|
|
|
|
(defn handler
|
|
[vsm f]
|
|
(reify Handler
|
|
(handle [this request]
|
|
(let [ctx (->request request)]
|
|
(-handle-response (f ctx) ctx)))))
|
|
|
|
(s/def :vertx.http/handler
|
|
(s/or :fn fn? :handler #(instance? Handler %)))
|
|
(s/def :vertx.http/host string?)
|
|
(s/def :vertx.http/port pos?)
|
|
(s/def ::server-options
|
|
(s/keys :req-un [:vertx.http/handler]
|
|
:opt-un [:vertx.http/host
|
|
:vertx.http/port]))
|
|
|
|
(defn server
|
|
"Starts a vertx http server."
|
|
[vsm {:keys [handler] :as options}]
|
|
(s/assert ::server-options options)
|
|
(let [^Vertx vsm (vu/resolve-system vsm)
|
|
^HttpServerOptions opts (opts->http-server-options options)
|
|
^HttpServer srv (.createHttpServer vsm opts)
|
|
^Handler handler (resolve-handler handler)]
|
|
(doto srv
|
|
(.requestHandler handler)
|
|
(.listen))
|
|
srv))
|
|
|
|
;; --- Impl
|
|
|
|
(defn- opts->http-server-options
|
|
[{:keys [host port]}]
|
|
(let [opts (HttpServerOptions.)]
|
|
(.setReuseAddress opts true)
|
|
(.setReusePort opts true)
|
|
(.setTcpNoDelay opts true)
|
|
(.setTcpFastOpen opts true)
|
|
(when host (.setHost opts host))
|
|
(when port (.setPort opts port))
|
|
opts))
|
|
|
|
(defn- resolve-handler
|
|
[handler]
|
|
(cond
|
|
(fn? handler) (vu/fn->handler handler)
|
|
(instance? Handler handler) handler
|
|
:else (throw (ex-info "invalid handler" {}))))
|
|
|
|
(defn- assign-status-and-headers!
|
|
[^HttpServerResponse res response]
|
|
(let [headers (:headers response)
|
|
status (:status response 200)]
|
|
(when (map? headers)
|
|
(vu/doseq [[key val] headers]
|
|
(.putHeader res ^String (name key) ^String (str val))))
|
|
(.setStatusCode res status)))
|
|
|
|
(defprotocol IAsyncResponse
|
|
(-handle-response [_ _]))
|
|
|
|
(defprotocol IAsyncBody
|
|
(-handle-body [_ _]))
|
|
|
|
(extend-protocol IAsyncResponse
|
|
java.util.concurrent.CompletionStage
|
|
(-handle-response [data ctx]
|
|
(p/then' data #(-handle-response % ctx)))
|
|
|
|
clojure.lang.IPersistentMap
|
|
(-handle-response [data ctx]
|
|
(let [body (:body data)
|
|
res (::response ctx)]
|
|
(assign-status-and-headers! res data)
|
|
(-handle-body body res)))
|
|
|
|
nil
|
|
(-handle-response [sws ctx]))
|
|
|
|
(extend-protocol IAsyncBody
|
|
(Class/forName "[B")
|
|
(-handle-body [data res]
|
|
(.end ^HttpServerResponse res (Buffer/buffer data)))
|
|
Buffer
|
|
(-handle-body [data res]
|
|
(.end ^HttpServerResponse res ^Buffer data))
|
|
|
|
nil
|
|
(-handle-body [data res]
|
|
(.putHeader ^HttpServerResponse res "content-length" "0")
|
|
(.end ^HttpServerResponse res))
|
|
|
|
String
|
|
(-handle-body [data res]
|
|
(let [length (count data)]
|
|
(.putHeader ^HttpServerResponse res "content-length" (str length))
|
|
(.end ^HttpServerResponse res data))))
|