Merge pull request #727 from agorgl/directory-index

Add option to disable index files served on paths that are not directories
This commit is contained in:
Juho Teperi 2025-03-28 15:11:09 +02:00 committed by GitHub
commit 8fb44467a0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 95 additions and 39 deletions

View file

@ -54,15 +54,16 @@ This way, they are only served if none of the actual routes have matched.
`reitit.ring/create-file-handler` and `reitit.ring/create-resource-handler` take optionally an options map to configure how the files are being served. `reitit.ring/create-file-handler` and `reitit.ring/create-resource-handler` take optionally an options map to configure how the files are being served.
| key | description | | key | description |
| -------------------|-------------| | --------------------|-------------|
| :parameter | optional name of the wildcard parameter, defaults to unnamed keyword `:` | :parameter | optional name of the wildcard parameter, defaults to unnamed keyword `:`
| :root | optional resource root, defaults to `\"public\"` | :root | optional resource root, defaults to `\"public\"`
| :path | path to mount the handler to. Required when mounted outside of a router, does not work inside a router. | :path | path to mount the handler to. Required when mounted outside of a router, does not work inside a router.
| :loader | optional class loader to resolve the resources | :loader | optional class loader to resolve the resources
| :index-files | optional vector of index-files to look in a resource directory, defaults to `[\"index.html\"]` | :index-files | optional vector of index-files to look in a resource directory, defaults to `[\"index.html\"]`
| :index-redirect? | optional boolean: if true (default), redirect to index file, if false serve it directly | :index-redirect? | optional boolean: if true (default), redirect to index file, if false serve it directly
| :not-found-handler | optional handler function to use if the requested resource is missing (404 Not Found) | :canonicalize-uris? | optional boolean: if true (default), try to serve index files for non directory paths (paths that end with slash)
| :not-found-handler | optional handler function to use if the requested resource is missing (404 Not Found)
### TODO ### TODO

View file

@ -227,11 +227,12 @@
;; TODO: ring.middleware.head/wrap-head ;; TODO: ring.middleware.head/wrap-head
;; TODO: handle etags ;; TODO: handle etags
(defn -create-file-or-resource-handler (defn -create-file-or-resource-handler
[response-fn {:keys [parameter root path loader allow-symlinks? index-files index-redirect? paths not-found-handler] [response-fn {:keys [parameter root path loader allow-symlinks? index-files index-redirect? canonicalize-uris? paths not-found-handler]
:or {parameter (keyword "") :or {parameter (keyword "")
root "public" root "public"
index-files ["index.html"] index-files ["index.html"]
index-redirect? true index-redirect? true
canonicalize-uris? true
paths (constantly nil)}}] paths (constantly nil)}}]
(let [options {:root root (let [options {:root root
:loader loader :loader loader
@ -254,13 +255,20 @@
(response/content-type response (mime-type/ext-mime-type path)))) (response/content-type response (mime-type/ext-mime-type path))))
path-or-index-response (fn [path uri] path-or-index-response (fn [path uri]
(or (response path) (or (response path)
(loop [[file & files] index-files] (when (or canonicalize-uris? (str/ends-with? uri "/"))
(if file (loop [[file & files] index-files]
(if-let [resp (response (join-paths path file))] (if file
(if index-redirect? (if-let [resp (response (join-paths path file))]
(response/redirect (join-paths uri file)) (cond
resp) index-redirect?
(recur files)))))) (response/redirect (join-paths uri file))
(not (str/ends-with? uri "/"))
(response/redirect (str uri "/"))
:else
resp)
(recur files)))))))
handler (if path handler (if path
(fn [request] (fn [request]
(let [uri (impl/url-decode (:uri request))] (let [uri (impl/url-decode (:uri request))]
@ -278,15 +286,16 @@
(defn create-resource-handler (defn create-resource-handler
"A ring handler for serving classpath resources, configured via options: "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 `:` | :parameter | optional name of the wildcard parameter, defaults to unnamed keyword `:`
| :root | optional resource root, defaults to `\"public\"` | :root | optional resource root, defaults to `\"public\"`
| :path | path to mount the handler to. Required when mounted outside of a router, does not work inside a router. | :path | path to mount the handler to. Required when mounted outside of a router, does not work inside a router.
| :loader | optional class loader to resolve the resources | :loader | optional class loader to resolve the resources
| :index-files | optional vector of index-files to look in a resource directory, defaults to `[\"index.html\"]` | :index-files | optional vector of index-files to look in a resource directory, defaults to `[\"index.html\"]`
| :index-redirect? | optional boolean: if true (default), redirect to index file, if false serve it directly | :index-redirect? | optional boolean: if true (default), redirect to index file, if false serve it directly
| :not-found-handler | optional handler function to use if the requested resource is missing (404 Not Found)" | :canonicalize-uris? | optional boolean: if true (default), try to serve index files for non directory paths (paths that end with slash)
| :not-found-handler | optional handler function to use if the requested resource is missing (404 Not Found)"
([] ([]
(create-resource-handler nil)) (create-resource-handler nil))
([opts] ([opts]
@ -296,15 +305,16 @@
(defn create-file-handler (defn create-file-handler
"A ring handler for serving file resources, configured via options: "A ring handler for serving file resources, configured via options:
| key | description | | key | description |
| -------------------|-------------| | --------------------|-------------|
| :parameter | optional name of the wildcard parameter, defaults to unnamed keyword `:` | :parameter | optional name of the wildcard parameter, defaults to unnamed keyword `:`
| :root | optional resource root, defaults to `\"public\"` | :root | optional resource root, defaults to `\"public\"`
| :path | path to mount the handler to. Required when mounted outside of a router, does not work inside a router. | :path | path to mount the handler to. Required when mounted outside of a router, does not work inside a router.
| :loader | optional class loader to resolve the resources | :loader | optional class loader to resolve the resources
| :index-files | optional vector of index-files to look in a resource directory, defaults to `[\"index.html\"]` | :index-files | optional vector of index-files to look in a resource directory, defaults to `[\"index.html\"]`
| :index-redirect? | optional boolean: if true (default), redirect to index file, if false serve it directly | :index-redirect? | optional boolean: if true (default), redirect to index file, if false serve it directly
| :not-found-handler | optional handler function to use if the requested resource is missing (404 Not Found)" | :canonicalize-uris? | optional boolean: if true (default), try to serve index files for non directory paths (paths that end with slash)
| :not-found-handler | optional handler function to use if the requested resource is missing (404 Not Found)"
([] ([]
(create-file-handler nil)) (create-file-handler nil))
([opts] ([opts]

View file

@ -688,16 +688,61 @@
(testing "without index-redirect" (testing "without index-redirect"
(let [app (ring/ring-handler (let [app (ring/ring-handler
(ring/router (ring/router
["/*" (create {:index-redirect? false})]) ["/*" (create {:canonicalize-uris? false
:index-redirect? false})])
(ring/create-default-handler))] (ring/create-default-handler))]
(testing "index-files" (testing "index-files"
(let [response (app (request "/docs"))] (let [response (app (request "/docs"))]
(is (= 200 (:status response))) (is (= 404 (:status response))))
(is (= "<h1>hello</h1>\n" (slurp (:body response)))))
(let [response (app (request "/docs/"))] (let [response (app (request "/docs/"))]
(is (= 200 (:status response))) (is (= 200 (:status response)))
(is (= "<h1>hello</h1>\n" (slurp (:body response))))))))))))) (is (= "<h1>hello</h1>\n" (slurp (:body response))))))))
(testing "with canonicalize-uris"
(let [app (ring/ring-handler
(ring/router
["/*" (create {:canonicalize-uris? true})])
(ring/create-default-handler))]
(testing "index-files"
(let [response (app (request "/docs"))]
(is (= (redirect "/docs/index.html") response)))
(testing "not found if dir doesn't exist"
(let [response (app (request "/foobar"))]
(is (= 404 (:status response)))))
(let [response (app (request "/docs/"))]
(is (= 302 (:status response))))
(let [response (app (request "/docs/index.html"))]
(is (= 200 (:status response)))))))
(testing "with canonicalize-uris and index-redirect"
(let [app (ring/ring-handler
(ring/router
["/*" (create {:canonicalize-uris? true
:index-redirect? true})])
(ring/create-default-handler))]
(testing "index-files"
(let [response (app (request "/docs"))]
(is (= (redirect "/docs/index.html") response)))
(let [response (app (request "/docs/"))]
(is (= (redirect "/docs/index.html") response))))))
(testing "without canonicalize-uris"
(let [app (ring/ring-handler
(ring/router
["/*" (create {:canonicalize-uris? false
:index-redirect? true})])
(ring/create-default-handler))]
(testing "index-files"
(let [response (app (request "/docs"))]
(is (= 404 (:status response))))
(let [response (app (request "/docs/"))]
(is (= (redirect "/docs/index.html") response)))
(let [response (app (request "/foobar"))]
(is (= 404 (:status response))))))))))))
#?(:clj #?(:clj
(deftest file-resource-handler-not-found-test (deftest file-resource-handler-not-found-test