use ring for serving resources.

This commit is contained in:
Tommi Reiman 2018-04-25 08:32:01 +03:00
parent fc0b634865
commit f8a43f0996
7 changed files with 104 additions and 145 deletions

View file

@ -25,6 +25,12 @@
* new helper `reitit.ring/router` to compose routes outside of a router.
* `reitit.ring/create-resource-handler` function to serve static routes. See (docs)[https://metosin.github.io/reitit/ring/static.html].
* new dependencies:
```clj
[ring/ring-core "1.6.3"]
```
### `reitit-swagger`
* New module to produce swagger-docs from routing tree, including `Coercion` definitions. Works with both middleware & interceptors and Schema & Spec. See [docs](https://metosin.github.io/reitit/swagger.html).

View file

@ -8,7 +8,6 @@ There are two options to serve the files.
This is good option if static files can be from non-conflicting paths, e.g. `"/assets/*"`.
```clj
(require '[reitit.ring :as ring])
@ -50,13 +49,16 @@ To serve files from conflicting paths, e.g. `"/*"`, one option is to mount them
`reitit.ring/create-resource-handler` takes optionally an options map to configure how the files are being served.
| key | description |
| -------------|-------------|
| :parameter | optional name of the wildcard parameter, defaults to unnamed keyword `:`
| :root | optional resource root, defaults to `"public"`
| :mime-types | optional extension->mime-type mapping, defaults to `reitit.ring.mime/default-types`
| :path | optional path to mount the handler to. Works only if mounted outside of a router.
| key | description |
| -----------------|-------------|
| :parameter | optional name of the wildcard parameter, defaults to unnamed keyword `:`
| :root | optional resource root, defaults to `"public"`
| :path | optional path to mount the handler to. Works only if mounted outside of a router.
| :loader | optional class loader to resolve the resources
| :allow-symlinks? | allow symlinks that lead to paths outside the root classpath directories, defaults to `false`
### TODO
* support for things like `:cache`, `:last-modified?`, `:index-files` and `:gzip`
* support for ClojureScript
* serve from file-system

View file

@ -6,4 +6,5 @@
:plugins [[lein-parent "0.3.2"]]
:parent-project {:path "../../project.clj"
:inherit [:deploy-repositories :managed-dependencies]}
:dependencies [[metosin/reitit-core]])
:dependencies [[metosin/reitit-core]
[ring/ring-core]])

View file

@ -1,11 +1,11 @@
(ns reitit.ring
(:require [meta-merge.core :refer [meta-merge]]
[reitit.middleware :as middleware]
[reitit.ring.mime :as mime]
[reitit.core :as r]
[reitit.impl :as impl]
#?(:clj
[clojure.java.io :as io])))
#?@(:clj [
[ring.util.mime-type :as mime-type]
[ring.util.response :as response]])))
(def http-methods #{:get :head :post :put :delete :connect :options :trace :patch})
(defrecord Methods [get head post put delete connect options trace patch])
@ -71,42 +71,36 @@
(defn create-resource-handler
"A ring handler for serving classpath resources, configured via options:
| key | description |
| key | description |
| -----------------|-------------|
| :parameter | optional name of the wildcard parameter, defaults to unnamed keyword `:`
| :root | optional resource root, defaults to `\"public\"`
| :mime-types | optional extension->mime-type mapping, defaults to `reitit.ring.mime/default-types`
| :parameter | optional name of the wildcard parameter, defaults to unnamed keyword `:`
| :root | optional resource root, defaults to `\"public\"`
| :path | optional path to mount the handler to. Works only if mounted outside of a router.
| :loader | optional class loader to resolve the resources
| :allow-symlinks? | allow symlinks that lead to paths outside the root classpath directories, defaults to `false`"
([]
(create-resource-handler nil))
([{:keys [parameter root mime-types path loader allow-symlinks?]
([{:keys [parameter root path loader allow-symlinks?]
:or {parameter (keyword "")
root "public"
mime-types mime/default-mime-types}}]
(let [response (fn [file]
{:status 200
:body file
:headers {"Content-Type" (mime/ext-mime-type (.getName file) mime-types)}})]
(if path
(let [path-size (count path)
serve (fn [req]
(let [uri (:uri req)]
(if (and (>= (count uri) path-size))
(some->> (str root (subs uri path-size)) io/resource io/file response))))]
(fn
([req]
(serve req))
([req respond _]
(respond (serve req)))))
(let [serve (fn [req]
(or (some->> req :path-params parameter (str root "/") io/resource io/file response)
{:status 404}))]
(fn ([req]
(serve req))
([req respond _]
(respond (serve req))))))))))
root "public"}}]
(let [options {:root root, :loader loader, :allow-symlinks? allow-symlinks?}
path-size (count path)
create (fn [handler]
(fn
([request] (handler request))
([request respond _] (respond (handler request)))))
response (fn [path]
(if-let [response (response/resource-response path options)]
(response/content-type response (mime-type/ext-mime-type path))))
handler (if path
(fn [request]
(let [uri (:uri request)]
(if (>= (count uri) path-size)
(response (subs uri path-size)))))
(fn [request]
(let [path (-> request :path-params parameter)]
(or (response path) {:status 404}))))]
(create handler)))))
(defn ring-handler
"Creates a ring-handler out of a ring-router.

View file

@ -1,99 +0,0 @@
(ns reitit.ring.mime
(:require [clojure.string :as str]))
(def default-mime-types
"A map of file extensions to mime-types."
{"7z" "application/x-7z-compressed"
"aac" "audio/aac"
"ai" "application/postscript"
"appcache" "text/cache-manifest"
"asc" "text/plain"
"atom" "application/atom+xml"
"avi" "video/x-msvideo"
"bin" "application/octet-stream"
"bmp" "image/bmp"
"bz2" "application/x-bzip"
"class" "application/octet-stream"
"cer" "application/pkix-cert"
"crl" "application/pkix-crl"
"crt" "application/x-x509-ca-cert"
"css" "text/css"
"csv" "text/csv"
"deb" "application/x-deb"
"dart" "application/dart"
"dll" "application/octet-stream"
"dmg" "application/octet-stream"
"dms" "application/octet-stream"
"doc" "application/msword"
"dvi" "application/x-dvi"
"edn" "application/edn"
"eot" "application/vnd.ms-fontobject"
"eps" "application/postscript"
"etx" "text/x-setext"
"exe" "application/octet-stream"
"flv" "video/x-flv"
"flac" "audio/flac"
"gif" "image/gif"
"gz" "application/gzip"
"htm" "text/html"
"html" "text/html"
"ico" "image/x-icon"
"iso" "application/x-iso9660-image"
"jar" "application/java-archive"
"jpe" "image/jpeg"
"jpeg" "image/jpeg"
"jpg" "image/jpeg"
"js" "text/javascript"
"json" "application/json"
"lha" "application/octet-stream"
"lzh" "application/octet-stream"
"mov" "video/quicktime"
"m4v" "video/mp4"
"mp3" "audio/mpeg"
"mp4" "video/mp4"
"mpe" "video/mpeg"
"mpeg" "video/mpeg"
"mpg" "video/mpeg"
"oga" "audio/ogg"
"ogg" "audio/ogg"
"ogv" "video/ogg"
"pbm" "image/x-portable-bitmap"
"pdf" "application/pdf"
"pgm" "image/x-portable-graymap"
"png" "image/png"
"pnm" "image/x-portable-anymap"
"ppm" "image/x-portable-pixmap"
"ppt" "application/vnd.ms-powerpoint"
"ps" "application/postscript"
"qt" "video/quicktime"
"rar" "application/x-rar-compressed"
"ras" "image/x-cmu-raster"
"rb" "text/plain"
"rd" "text/plain"
"rss" "application/rss+xml"
"rtf" "application/rtf"
"sgm" "text/sgml"
"sgml" "text/sgml"
"svg" "image/svg+xml"
"swf" "application/x-shockwave-flash"
"tar" "application/x-tar"
"tif" "image/tiff"
"tiff" "image/tiff"
"ttf" "application/x-font-ttf"
"txt" "text/plain"
"webm" "video/webm"
"wmv" "video/x-ms-wmv"
"woff" "application/font-woff"
"xbm" "image/x-xbitmap"
"xls" "application/vnd.ms-excel"
"xml" "text/xml"
"xpm" "image/x-xpixmap"
"xwd" "image/x-xwindowdump"
"zip" "application/zip"})
(defn file-ext [name]
(if-let [i (str/last-index-of name ".")]
(subs name (inc i))))
(defn ext-mime-type [name mime-types]
(-> name file-ext mime-types))

View file

@ -4,7 +4,10 @@
[reitit.ring :as ring]
[clojure.java.io :as io]
[criterium.core :as cc]
[ring.util.mime-type :as mime]))
[ring.util.response]
[ring.middleware.defaults]
[ring.middleware.resource]
[ring.util.mime-type]))
;;
;; start repl with `lein perf repl`
@ -21,14 +24,14 @@
;; Memory: 16 GB
;;
(def app
(def app1
(ring/ring-handler
(ring/router
[["/ping" (constantly {:status 200, :body "pong"})]
["/files/*" (ring/create-resource-handler)]])
(ring/create-default-handler)))
(def app
(def app2
(ring/ring-handler
(ring/router
["/ping" (constantly {:status 200, :body "pong"})])
@ -36,19 +39,69 @@
(ring/create-resource-handler {:path "/files"})
(ring/create-default-handler))))
(def wrap-resource
(-> (constantly {:status 200, :body "pong"})
(ring.middleware.resource/wrap-resource "public")))
(def wrap-defaults
(-> (constantly {:status 200, :body "pong"})
(ring.middleware.defaults/wrap-defaults ring.middleware.defaults/site-defaults)))
(comment
(def server (web/run #'app {:port 3000, :dispatch? false, :server {:always-set-keep-alive false}}))
(routing-test))
(defn bench-resources []
;; 134µs
(cc/quick-bench
(ring.util.response/resource-response "hello.json" {:root "public"}))
;; 144µs
(cc/quick-bench
(app1 {:request-method :get, :uri "/files/hello.json"}))
;; 144µs
(cc/quick-bench
(app2 {:request-method :get, :uri "/files/hello.json"}))
;; 143µs
(cc/quick-bench
(wrap-resource {:request-method :get, :uri "/hello.json"}))
;; 163µs
(cc/quick-bench
(wrap-defaults {:request-method :get, :uri "/hello.json"})))
(defn bench-handler []
;; 140ns
(cc/quick-bench
(app1 {:request-method :get, :uri "/ping"}))
;; 134ns
(cc/quick-bench
(app2 {:request-method :get, :uri "/ping"}))
;; 108µs
(cc/quick-bench
(wrap-resource {:request-method :get, :uri "/ping"}))
;; 146µs
(cc/quick-bench
(wrap-defaults {:request-method :get, :uri "/ping"})))
(comment
(bench-resources)
(bench-handler)
(let [file (-> "logback.xml" io/resource io/file)
name (.getName file)]
;; 639ns
(cc/quick-bench
(mime/ext-mime-type name))
(ring.util.mime-type/ext-mime-type name))
;; 106ns
(cc/quick-bench
(ext-mime-type name))))
(reitit.ring.mime/ext-mime-type name reitit.ring.mime/default-mime-types))))

View file

@ -17,6 +17,7 @@
[metosin/reitit-swagger "0.1.1-SNAPSHOT"]
[meta-merge "1.0.0"]
[ring/ring-core "1.6.3"]
[metosin/spec-tools "0.6.2-SNAPSHOT"]
[metosin/schema-tools "0.10.2-SNAPSHOT"]]
@ -60,6 +61,7 @@
"-Dclojure.compiler.direct-linking=true"]
:test-paths ["perf-test/clj"]
:dependencies [[compojure "1.6.1"]
[ring/ring-defaults "0.3.1"]
[ikitommi/immutant-web "3.0.0-alpha1"]
[io.pedestal/pedestal.route "0.5.3"]
[org.clojure/core.async "0.4.474"]